Skip to content

Commit

Permalink
Fix exporting large amounts of data
Browse files Browse the repository at this point in the history
  • Loading branch information
GeniJaho committed May 12, 2024
1 parent 5b7ccbe commit 20ad5e6
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 20 deletions.
11 changes: 5 additions & 6 deletions app/Actions/Photos/ExportPhotosAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@

use App\DTO\PhotoExport;
use App\Models\User;
use Generator;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Support\LazyCollection;

class ExportPhotosAction
{
/**
* @return LazyCollection<int, PhotoExport>
*/
public function run(User $user): LazyCollection
public function run(User $user): Generator
{
$photos = $user
->photos()
Expand All @@ -23,6 +20,8 @@ public function run(User $user): LazyCollection
])
->lazyById();

return PhotoExport::collect($photos);
foreach ($photos as $photo) {
yield PhotoExport::fromModel($photo);
}
}
}
3 changes: 2 additions & 1 deletion app/Console/Commands/GenerateRandomPhotos.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function handle(): void
{
/** @var User $user */
$user = User::query()
->where('email', 'trashkiller@litterhero.com')
->where('email', 'trashkiller@litterapp.com')
->first();

$bar = progress('Generating 1M photos with tags...', 1_000_000);
Expand All @@ -34,6 +34,7 @@ public function handle(): void
$photos[] = [
'user_id' => $user->id,
'path' => 'photos/default.jpg',
'original_file_name' => fake()->unique()->sentence().'default.jpg',
'created_at' => $now,
'updated_at' => $now,
];
Expand Down
5 changes: 2 additions & 3 deletions app/DTO/PhotoExport.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use App\Models\Photo;
use App\Models\PhotoItem;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Spatie\LaravelData\Data;

Expand All @@ -20,7 +19,7 @@ public function __construct(
public ?float $latitude,
public ?float $longitude,
public ?string $taken_at_local,
public Carbon $created_at,
public string $created_at,
public Collection $items,
) {
}
Expand All @@ -33,7 +32,7 @@ public static function fromModel(Photo $photo): static
latitude: $photo->latitude,
longitude: $photo->longitude,
taken_at_local: $photo->taken_at_local,
created_at: $photo->created_at,
created_at: $photo->created_at?->toIso8601String(),
items: $photo->photoItems->map(fn (PhotoItem $photoItem): array => [
'name' => $photoItem->item?->name,
'picked_up' => $photoItem->picked_up,
Expand Down
7 changes: 4 additions & 3 deletions app/Http/Controllers/Photos/ExportPhotosController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ public function __invoke(ExportPhotosAction $action): StreamedResponse

$photos = $action->run($user);

return response()->streamDownload(function () use ($photos): void {
echo $photos->toJson();
}, 'photos.json');
return response()->streamJson(['photos' => $photos], 200, [
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="photos.json"',
]);
}
}
16 changes: 15 additions & 1 deletion tests/Feature/Photos/ExportPhotosTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,23 @@
test('a user can export their photos', function (): void {
$this->actingAs($user = User::factory()->create());

Photo::factory()->for($user)->create(['created_at' => now()]);
$photo = Photo::factory()->for($user)->create();

$response = $this->get('/photos/export');

$response->assertOk();
$response->assertDownload('photos.json');
$response->assertStreamedJsonContent([
'photos' => [[
'id' => $photo->id,
'original_file_name' => $photo->original_file_name,
'latitude' => $photo->latitude,
'longitude' => $photo->longitude,
'taken_at_local' => $photo->taken_at_local,
'created_at' => $photo->created_at->toIso8601String(),
'items' => [],
]],
]);
});

test('if there are no photos the export still works', function (): void {
Expand All @@ -21,4 +32,7 @@

$response->assertOk();
$response->assertDownload('photos.json');
$response->assertStreamedJsonContent([
'photos' => [],
]);
});
18 changes: 12 additions & 6 deletions tests/Unit/Actions/ExportPhotosActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use App\Models\Tag;
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\LazyCollection;

use function Pest\Laravel\freezeTime;

Expand All @@ -30,15 +29,18 @@
$exportPhotosAction = new ExportPhotosAction();
$result = $exportPhotosAction->run($user);

expect($result)->toBeInstanceOf(LazyCollection::class)
->and($result->count())->toBe(1)
->and($result->first())->toBeInstanceOf(PhotoExport::class)
expect($result)->toBeInstanceOf(Generator::class);

$result = iterator_to_array($result);

expect($result)->toHaveCount(1)
->and($result[0])->toBeInstanceOf(PhotoExport::class)
->id->toBe($photo->id)
->original_file_name->toBe($photo->original_file_name)
->latitude->toBe($photo->latitude)
->longitude->toBe($photo->longitude)
->taken_at_local->toBe($photo->taken_at_local)
->created_at->toEqual($photo->created_at)
->created_at->toEqual($photo->created_at->toIso8601String())
->items->toBeInstanceOf(Collection::class)
->items->toHaveCount(1)
->items->first()->toBe([
Expand Down Expand Up @@ -66,6 +68,10 @@
$exportPhotosAction = new ExportPhotosAction();
$result = $exportPhotosAction->run($user);

expect($result)->toBeInstanceOf(Generator::class);

$result = iterator_to_array($result);

expect($result)->toHaveCount(1)
->and($result->first()->id)->toBe($pickedUpPhoto->id);
->and($result[0]->id)->toBe($pickedUpPhoto->id);
});

0 comments on commit 20ad5e6

Please sign in to comment.