cms icon indicating copy to clipboard operation
cms copied to clipboard

Empty AssetCollection returned when adding multiple files to field programmatically

Open andjsch opened this issue 2 years ago • 0 comments

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

andjsch avatar Jun 27 '22 16:06 andjsch