cms
cms copied to clipboard
Empty AssetCollection returned when adding multiple files to field programmatically
Bug description
When I try to make an Entry using Entry::make()
with multiple assets (note the photos array), I will always get an empty AssetCollection when accessing $this->entry->photos
while $this->entry->dwg_path
for example returns the expected result.
Using the value function, I do get the expected values in an array. $this->entry->value('photos')
How to reproduce
Create a collection (in my case products) which has an assets field with no maximum number of files.
Try creating adding some files programmatically as well as creating an entry linking these files. See the above.
I added my unit test (sorry, it's Pest so you might need to install that first) along the other necessary files.
<?php
use App\Contracts\Actions\DeletesProduct;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Statamic\Facades\Entry;
use Tests\WithStatamicEntryFaking;
uses(WithStatamicEntryFaking::class);
beforeEach(function () {
Storage::fake('assets');
$photo = UploadedFile::fake()->create('photo.jpg', 10, 'image/jpg');
$dwg = UploadedFile::fake()->create('plan.dwg', 10, 'application/acad');
$pdf = UploadedFile::fake()->create('plan.pdf', 10, 'application/pdf');
Storage::disk('assets')->putFileAs('/products/1234/', $photo, '1.jpg');
Storage::disk('assets')->putFileAs('/products/1234/', $photo, '2.jpg');
Storage::disk('assets')->putFileAs('/products/1234/', $photo, '3.jpg');
Storage::disk('assets')->putFileAs('/products/1234/', $photo, '4.jpg');
Storage::disk('assets')->putFileAs('/dwgs/', $dwg, '1234.dwg');
Storage::disk('assets')->putFileAs('/pdfs/', $pdf, '1234.pdf');
$this->entry = $this->makeEntry(function () {
Entry::make()
->collection('products')
->id('1234')
->slug('ks0101')
->data(
[
'title' => 'Example product',
'article_number' => '1234',
'category' => 'SOMECATEGORY',
'dwg_path' => '/dwgs/1234.dwg',
'pdf_path' => '/pdfs/1234.pdf',
'photos' => [
'/products/1234/1.jpg',
'/products/1234/2.jpg',
'/products/1234/3.jpg',
'/products/1234/4.jpg',
]
])
->save();
return Entry::find('1234');
});
});
afterEach(function () {
$this->cleanupStatamicData();
});
it('mocks the entries', function () {
$products = Entry::query()->where('collection', 'products')->get();
expect($products->count())->toBeOne();
});
test('that the fake entry has an id', function () {
expect($this->entry->id())->toBe('1234');
});
it('deletes all files of a product and the product itself', function () {
$deleteProduct = app(DeletesProduct::class);
expect(Storage::disk('assets')->exists('/products/1234/1.jpg'))->toBeTrue()
->and(Storage::disk('assets')->exists('/products/1234/2.jpg'))->toBeTrue()
->and(Storage::disk('assets')->exists('/products/1234/3.jpg'))->toBeTrue()
->and(Storage::disk('assets')->exists('/products/1234/4.jpg'))->toBeTrue()
->and(Storage::disk('assets')->exists('/dwgs/1234.dwg'))->toBeTrue()
->and(Storage::disk('assets')->exists('/pdfs/1234.pdf'))->toBeTrue();
($deleteProduct)($this->entry);
expect(Storage::disk('assets')->exists('/products/1234/1.jpg'))->toBeFalse()
->and(Storage::disk('assets')->exists('/products/1234/2.jpg'))->toBeFalse()
->and(Storage::disk('assets')->exists('/products/1234/3.jpg'))->toBeFalse()
->and(Storage::disk('assets')->exists('/products/1234/4.jpg'))->toBeFalse()
->and(Storage::disk('assets')->exists('/dwgs/1234.dwg'))->toBeFalse()
->and(Storage::disk('assets')->exists('/pdfs/1234.pdf'))->toBeFalse();
});
<?php
namespace Tests;
use Exception;
use Illuminate\Support\Facades\Storage;
use Statamic\Entries\Entry;
trait WithStatamicEntryFaking
{
protected array $filesToRemove = [];
protected function cleanupStatamicData(): void
{
foreach ($this->filesToRemove as $file) {
Storage::disk('local')->delete($file);
}
}
protected function makeEntry(callable $callback): Entry
{
$entry = $callback();
if (is_null($entry)) {
throw new Exception('Entry not found.');
}
$this->filesToRemove[] = $entry->initialPath();
return $entry;
}
}
<?php
namespace App\Actions;
use App\Contracts\Actions\DeletesProduct;
use Illuminate\Support\Facades\Storage;
use Statamic\Entries\Entry;
class DeleteProduct implements DeletesProduct
{
public function __invoke(Entry $product)
{
$this->deleteAttachedFiles($product);
try {
$product->delete();
} catch (\Exception $e) {
info('Error deleting product with id "'.$product->id().'"');
}
}
private function deleteAttachedFiles(Entry $product)
{
Storage::disk('assets')->delete($product->dwg_path);
Storage::disk('assets')->delete($product->pdf_path);
Storage::disk('assets')->delete($product->value('photos')); // this works
Storage::disk('assets')->delete($product->photos); // while this doesn't giving me an empty collection
}
}
Logs
No response
Versions
Statamic 3.3.15 Pro Laravel 9.17.0 PHP 8.1.7 No addons installed
Installation
Fresh statamic/statamic site via CLI
Antlers Parser
runtime (new)
Additional details
The images and entries are retrieved through an API. I guess the problem has to do with Statamic not knowing about the existence of these files.
Weirdly enough, I can see them inside the CP, but after that, I still get an empty AssetCollection when accessing the entry programmatically again.
Looks like an augmentation bug to me. Running please assets:meta
doesn't change anything