diff --git a/config/eloquent-driver.php b/config/eloquent-driver.php index 0ba4e51e..bbcf2d32 100644 --- a/config/eloquent-driver.php +++ b/config/eloquent-driver.php @@ -14,6 +14,7 @@ 'driver' => 'file', 'model' => \Statamic\Eloquent\Assets\AssetModel::class, 'asset' => \Statamic\Eloquent\Assets\Asset::class, + 'use_model_keys_for_ids' => false, ], 'blueprints' => [ diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index e4b20586..f95b185b 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -11,8 +11,6 @@ class Asset extends FileAsset { - protected $model; - use HasDirtyState { syncOriginal as traitSyncOriginal; } @@ -26,6 +24,9 @@ public function syncOriginal() } protected $existsOnDisk = false; + + protected $model; + protected $removedData = []; public static function fromModel(Model $model) @@ -61,6 +62,20 @@ public function meta($key = null) return $meta; } + if (config('statamic.eloquent-driver.assets.use_model_keys_for_ids', false)) { + if ($this->model) { + return $this->model->meta; + } + + $meta = $this->generateMeta(); + + if (! $meta['data']) { + $meta['data'] = []; + } + + return $meta; + } + if ($meta = $this->model()?->meta) { return $meta; } @@ -139,7 +154,7 @@ public static function makeModelFromContract(AssetContract $source, $meta = []) $model = false; - if (method_exists($source, 'model')) { + if (config('statamic.eloquent-driver.assets.use_model_keys_for_ids', false) || method_exists($source, 'model')) { $model = $source->model(); } @@ -197,6 +212,19 @@ public function metaPath() return $this->path(); } + public function id($id = null) + { + if ($id || ! config('statamic.eloquent-driver.assets.use_model_keys_for_ids', false)) { + return parent::id($id); + } + + if (! $this->model) { + throw new \Exception('ID is not available until asset is saved'); + } + + return $this->model->getKey(); + } + public function getCurrentDirtyStateAttributes(): array { return array_merge([ diff --git a/src/Assets/AssetRepository.php b/src/Assets/AssetRepository.php index 6338773b..4fc7339f 100644 --- a/src/Assets/AssetRepository.php +++ b/src/Assets/AssetRepository.php @@ -13,12 +13,25 @@ class AssetRepository extends BaseRepository { public function findById($id): ?AssetContract { + $blinkKey = "eloquent-asset-{$id}"; + [$container, $path] = explode('::', $id); + if (config('statamic.eloquent-driver.assets.use_model_keys_for_ids', false) && (str_contains($path, '.') === false)) { + $item = Blink::once($blinkKey, fn () => $this->query()->where('id', $path)->first()); + + if (! $item) { + Blink::forget($blinkKey); + + return null; + } + + return $item; + } + $filename = Str::afterLast($path, '/'); $folder = str_contains($path, '/') ? Str::beforeLast($path, '/') : '/'; - $blinkKey = "eloquent-asset-{$id}"; $item = Blink::once($blinkKey, function () use ($container, $filename, $folder) { return $this->query() ->where('container', $container) @@ -38,6 +51,11 @@ public function findById($id): ?AssetContract public function findByUrl(string $url) { + // handle find('model-key'), with no container + if (! str_contains('.', $url)) { + return $this->findById('::'.$url); + } + if (! $container = $this->resolveContainerFromUrl($url)) { return null; } diff --git a/src/Commands/UpdateAssetReferencesToUseModelKeys.php b/src/Commands/UpdateAssetReferencesToUseModelKeys.php new file mode 100644 index 00000000..6685130f --- /dev/null +++ b/src/Commands/UpdateAssetReferencesToUseModelKeys.php @@ -0,0 +1,51 @@ +reject(fn ($container) => $this->option('container') != 'all' && $this->option('container') != $container->handle()) + ->each(fn ($container) => $this->processContainer($container)); + + $this->info('Complete'); + } + + private function processContainer(AssetContainer $container) + { + $this->info("Container: {$container->handle()}"); + + $container->queryAssets()->get()->each(function ($item) use ($container) { + return AssetReferenceUpdater::item($item) + ->filterByContainer($container) + ->updateReferences($item->path(), $item->id()); + }); + } +} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 0ea43580..3fc3a620 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -40,6 +40,7 @@ use Statamic\Eloquent\Taxonomies\TermRepository; use Statamic\Eloquent\Tokens\TokenRepository; use Statamic\Facades\Stache; +use Statamic\Listeners\UpdateAssetReferences; use Statamic\Providers\AddonServiceProvider; use Statamic\Statamic; @@ -98,6 +99,7 @@ public function boot() Commands\ImportTaxonomies::class, Commands\ImportSites::class, Commands\SyncAssets::class, + Commands\UpdateAssetReferencesToUseModelKeys::class, ]); $this->addAboutCommandInfo(); @@ -270,6 +272,11 @@ private function registerAssets() Statamic::repository(AssetRepositoryContract::class, AssetRepository::class); + // we dont need asset references to run on asset save if we are linking by id, as the references dont update + if (config('statamic.eloquent-driver.assets.use_model_keys_for_ids', false)) { + UpdateAssetReferences::disable(); + } + Stache::exclude('assets'); } diff --git a/tests/Assets/AssetTest.php b/tests/Assets/AssetTest.php index bce1142a..18d08783 100644 --- a/tests/Assets/AssetTest.php +++ b/tests/Assets/AssetTest.php @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Storage; +use Orchestra\Testbench\Attributes\DefineEnvironment; use PHPUnit\Framework\Attributes\Test; use Statamic\Eloquent\Assets\Asset; use Statamic\Eloquent\Assets\AssetModel; @@ -256,4 +257,53 @@ public function can_save_an_asset_made_on_the_container() $this->assertCount(6, AssetModel::all()); } + + #[Test] + public function not_referencing_by_id_gives_a_container_and_path_id() + { + $asset = Facades\Asset::find('test::f.jpg'); + + $this->assertNotSame($asset->id(), $asset->model()->getKey()); + $this->assertStringContainsString('::', $asset->id()); + } + + #[Test] + #[DefineEnvironment('setUseModelKeysConfig')] + public function referencing_by_id_gives_a_model_id() + { + $asset = Facades\Asset::find('test::f.jpg'); + + $this->assertSame($asset->id(), $asset->model()->getKey()); + } + + #[Test] + #[DefineEnvironment('setUseModelKeysConfig')] + public function an_error_is_thrown_when_getting_id_before_asset_is_saved() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('ID is not available until asset is saved'); + + Storage::disk('test')->put('new.jpg', ''); + Facades\Asset::make()->container('test')->path('new.jpg')->id(); + } + + #[Test] + #[DefineEnvironment('setUseModelKeysConfig')] + public function using_find_with_an_id_returns_an_asset() + { + $asset = Facades\Asset::find('test::6'); + + $this->assertInstanceOf(Asset::class, $asset); + $this->assertSame('f.jpg', $asset->basename()); + + $asset = Facades\Asset::find('6'); + + $this->assertInstanceOf(Asset::class, $asset); + $this->assertSame('f.jpg', $asset->basename()); + } + + protected function setUseModelKeysConfig($app) + { + $app['config']->set('statamic.eloquent-driver.assets.use_model_keys_for_ids', true); + } }