diff --git a/database/migrations/2024_03_07_100000_create_asset_table.php b/database/migrations/2024_03_07_100000_create_asset_table.php index c2b3ee46..f9dc1b3f 100644 --- a/database/migrations/2024_03_07_100000_create_asset_table.php +++ b/database/migrations/2024_03_07_100000_create_asset_table.php @@ -17,6 +17,14 @@ public function up() $table->char('extension', 10)->index(); $table->string('path')->index(); $table->jsonb('meta')->nullable(); + + $table->integer('duration')->nullable()->default(null); + $table->integer('height')->nullable()->default(null); + $table->integer('last_modified'); + $table->string('mime_type'); + $table->integer('size')->index(); + $table->integer('width')->nullable()->default(null); + $table->timestamps(); $table->unique(['container', 'folder', 'basename']); diff --git a/database/migrations/updates/add_meta_columns_to_assets_table.php.stub b/database/migrations/updates/add_meta_columns_to_assets_table.php.stub new file mode 100644 index 00000000..08446d4a --- /dev/null +++ b/database/migrations/updates/add_meta_columns_to_assets_table.php.stub @@ -0,0 +1,69 @@ +prefix('assets_meta', 'size')) { + return; + } + + Schema::table($this->prefix('assets_meta'), function (Blueprint $table) { + $table->integer('duration')->nullable()->default(null); + $table->integer('height')->nullable()->default(null); + $table->integer('last_modified')->default(0); + $table->string('mime_type')->default(''); + $table->integer('size')->default(0)->index(); + $table->integer('width')->nullable()->default(null); + }); + + AssetModel::query() + ->lazy() + ->each(function ($model) { + $meta = $model->meta; + + $model->duration = Arr::pull($meta, 'duration', null); + $model->height = Arr::pull($meta, 'height', null); + $model->last_modified = Arr::pull($meta, 'last_modified', time()); + $model->mime_type = Arr::pull($meta, 'mime_type', ''); + $model->size = Arr::pull($meta, 'size', 0); + $model->width = Arr::pull($meta, 'height', null); + + $model->meta = $meta['data']; + $model->saveQuietly(); + }); + } + + public function down() + { + if (! Schema::hasColumn($this->prefix('assets_meta', 'size')) { + return; + } + + AssetModel::query() + ->lazy() + ->each(function ($model) { + $meta = $model->meta; + + $meta['duration'] = $model->duration; + $meta['height'] = $model->height; + $meta['last_modified'] = $model->last_modified; + $meta['mime_type'] = $model->mime_type; + $meta['size'] = $model->size; + $meta['width'] = $model->width; + + $model->meta = $meta; + + $model->saveQuietly(); + }); + + Schema::table($this->prefix('assets_meta'), function (Blueprint $table) { + $table->dropColumn(['duration', 'height', 'last_modified', 'mime_type', 'size', 'width']); + }); + } +}; diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index 30e1a45c..e6642ad4 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -31,10 +31,20 @@ public function syncOriginal() public static function fromModel(Model $model) { + $meta = ['data' => $model->meta]; + $meta['duration'] = $model->duration; + $meta['height'] = $model->height; + $meta['last_modified'] = $model->last_modified; + $meta['mime_type'] = $model->mime_type; + $meta['size'] = $model->size; + $meta['width'] = $model->width; + + $meta = Arr::removeNullValues($meta); + $asset = (new static) ->container($model->container) ->path(Str::replace('//', '/', $model->folder.'/'.$model->basename)) - ->hydrateMeta($model->meta) + ->hydrateMeta($meta) ->syncOriginal() ->model($model); @@ -62,8 +72,17 @@ public function meta($key = null) return $meta; } - if ($meta = $this->model()?->meta) { - return $meta; + if ($this->model()?->meta) { + $model = $this->model(); + $meta = ['data' => $model->meta]; + $meta['duration'] = $model->duration; + $meta['height'] = $model->height; + $meta['last_modified'] = $model->last_modified; + $meta['mime_type'] = $model->mime_type; + $meta['size'] = $model->size; + $meta['width'] = $model->width; + + return Arr::removeNullValues($meta); } $meta = $this->generateMeta(); @@ -122,6 +141,13 @@ public function writeMeta($meta) return; } + $model->meta = $meta['data']; + $model->duration = $meta['duration'] ?? null; + $model->height = $meta['height'] ?? null; + $model->last_modified = $meta['last_modified'] ?? time(); + $model->mime_type = $meta['mime_type'] ?? ''; + $model->size = $meta['size'] ?? 0; + $model->width = $meta['width'] ?? null; $model->save(); $this->model($model); @@ -153,12 +179,19 @@ public static function makeModelFromContract(AssetContract $source, $meta = []) } $model->fill([ - 'meta' => $meta, + 'meta' => $meta['data'], 'filename' => $source->filename(), 'extension' => $extension, 'path' => $source->path(), 'folder' => $source->folder(), 'basename' => $source->basename(), + + 'duration' => $source->duration(), + 'height' => $source->height(), + 'last_modified' => $source->lastModified()?->timestamp ?? time(), + 'mime_type' => $source->mimeType(), + 'size' => $source->size(), + 'width' => $source->width(), ]); // Set initial timestamps. diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index 56ccdf25..e9380dcb 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -9,19 +9,13 @@ class AssetQueryBuilder extends EloquentQueryBuilder implements QueryBuilder { const COLUMNS = [ - 'id', 'container', 'folder', 'basename', 'filename', 'extension', 'path', 'created_at', 'updated_at', - ]; - - const META_COLUMNS = [ - 'size', 'width', 'height', 'duration', 'mime_type', 'last_modified', + 'id', 'container', 'folder', 'basename', 'filename', 'extension', 'path', 'size', 'width', 'height', 'duration', 'mime_type', 'last_modified', 'created_at', 'updated_at', ]; protected function column($column) { - if (in_array($column, self::META_COLUMNS)) { + if (! in_array($column, self::COLUMNS)) { $column = 'meta->'.$column; - } elseif (! in_array($column, self::COLUMNS)) { - $column = 'meta->data->'.$column; } return $column; diff --git a/src/Updates/AddMetaColumnsToAssetsTable.php b/src/Updates/AddMetaColumnsToAssetsTable.php new file mode 100644 index 00000000..4b0cb698 --- /dev/null +++ b/src/Updates/AddMetaColumnsToAssetsTable.php @@ -0,0 +1,27 @@ +files->copy($source, $dest); + + $this->console()->info('Migration created'); + $this->console()->comment('Remember to run `php artisan migrate` to apply it to your database.'); + } +} diff --git a/tests/Assets/AssetTest.php b/tests/Assets/AssetTest.php index bce1142a..313231fe 100644 --- a/tests/Assets/AssetTest.php +++ b/tests/Assets/AssetTest.php @@ -72,7 +72,9 @@ public function it_loads_from_asset_model() 'basename' => 'test.jpg', 'filename' => 'test', 'extension' => 'jpg', - 'meta' => ['width' => 100, 'height' => 100, 'data' => ['focus' => '50-50-1']], + 'meta' => ['focus' => '50-50-1'], + 'width' => 100, + 'height' => 100, ]); $asset = (new Asset)->fromModel($model); @@ -83,7 +85,7 @@ public function it_loads_from_asset_model() $this->assertSame('test.jpg', $asset->basename()); $this->assertSame('test', $asset->filename()); $this->assertSame('jpg', $asset->extension()); - $this->assertSame(['width' => 100, 'height' => 100, 'data' => ['focus' => '50-50-1']], $asset->meta()); + $this->assertSame(['data' => ['focus' => '50-50-1'], 'height' => 100, 'width' => 100], $asset->meta()); } #[Test] @@ -96,7 +98,12 @@ public function it_loads_from_an_existing_model_outside_the_query_builder() 'basename' => 'test.jpg', 'filename' => 'test', 'extension' => 'jpg', - 'meta' => ['width' => 100, 'height' => 100, 'data' => ['focus' => '50-50-1']], + 'meta' => ['focus' => '50-50-1'], + 'width' => 100, + 'height' => 100, + 'last_modified' => $lastModified = time(), + 'mime_type' => 'image/jpeg', + 'size' => 1000, ]); $model->save(); @@ -109,7 +116,7 @@ public function it_loads_from_an_existing_model_outside_the_query_builder() $this->assertSame('test.jpg', $asset->basename()); $this->assertSame('test', $asset->filename()); $this->assertSame('jpg', $asset->extension()); - $this->assertSame(['width' => 100, 'height' => 100, 'data' => ['focus' => '50-50-1']], $asset->meta()); + $this->assertSame(['data' => ['focus' => '50-50-1'], 'height' => 100, 'last_modified' => $lastModified, 'mime_type' => 'image/jpeg', 'size' => 1000, 'width' => 100], $asset->meta()); } #[Test] @@ -168,11 +175,9 @@ public function making_and_saving_an_asset_creates_a_new_model() { $this->assertCount(6, AssetModel::all()); - Storage::disk('test')->put('f.jpg', ''); + Storage::disk('test')->put('test.jpg', ''); $asset = Facades\Asset::make()->container('test')->path('test.jpg'); - $this->assertCount(6, AssetModel::all()); - $asset->save(); $this->assertInstanceOf(AssetModel::class, $asset->model()); @@ -199,7 +204,7 @@ public function moving_an_asset_updates_the_same_model() $this->assertSame($model->basename, $asset->basename()); $this->assertSame($model->filename, $asset->filename()); $this->assertSame($model->extension, $asset->extension()); - $this->assertSame($model->meta, $asset->meta()); + $this->assertSame($model->meta, $asset->meta()['data']); $this->assertSame($modelId, $asset->model()->getKey()); }