Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/eloquent-driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
34 changes: 31 additions & 3 deletions src/Assets/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

class Asset extends FileAsset
{
protected $model;

use HasDirtyState {
syncOriginal as traitSyncOriginal;
}
Expand All @@ -26,6 +24,9 @@ public function syncOriginal()
}

protected $existsOnDisk = false;

protected $model;

protected $removedData = [];

public static function fromModel(Model $model)
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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([
Expand Down
20 changes: 19 additions & 1 deletion src/Assets/AssetRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down
51 changes: 51 additions & 0 deletions src/Commands/UpdateAssetReferencesToUseModelKeys.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace Statamic\Eloquent\Commands;

use Illuminate\Console\Command;
use Statamic\Assets\AssetReferenceUpdater;
use Statamic\Console\RunsInPlease;
use Statamic\Contracts\Assets\AssetContainer;
use Statamic\Facades;

class UpdateAssetReferencesToUseModelKeys extends Command
{
use RunsInPlease;

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'statamic:eloquent:update-asset-references-to-use-model-keys {--container=all}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Update container::path references to container::asset_model_key';

/**
* Execute the console command.
*/
public function handle()
{
Facades\AssetContainer::all()
->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());
});
}
}
7 changes: 7 additions & 0 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -98,6 +99,7 @@ public function boot()
Commands\ImportTaxonomies::class,
Commands\ImportSites::class,
Commands\SyncAssets::class,
Commands\UpdateAssetReferencesToUseModelKeys::class,
]);

$this->addAboutCommandInfo();
Expand Down Expand Up @@ -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');
}

Expand Down
50 changes: 50 additions & 0 deletions tests/Assets/AssetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}