Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use getKeyName to determine key to support UUID based Media models #307

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

GlennBergmans
Copy link

When overriding the default media-library Media model with a custom class that overrides $primaryKey, saving an entity in Nova without changing the image can trigger a Call to a member function determineOrderColumnName() on null in ./vendor/spatie/laravel-medialibrary/src/MediaCollections/Models/Concerns/IsSorted.php. This is because in ./src/Fields/Media.php the name of the primary key is assumed to be id. This pull request fixes that.

@LiamKarlMitchell
Copy link

LiamKarlMitchell commented Oct 29, 2022

Also on download action it uses the id column not the uuid column.

SingleFile uses this.image.id should be able to pass the this.image.uuid or perhaps can improve the DownloadMediaController

return $model::find($mediaId);

To

return $model::where('id', $mediaId)->first();

I was able to extend the models and controller and use an alias for the controller.

AppServiceProvider.php in the register function

        $loader = AliasLoader::getInstance();
        $loader->alias('Ebess\AdvancedNovaMediaLibrary\Http\Controllers\DownloadMediaController', \App\Nova\Controllers\DownloadMediaController::class);

App\Nova\Controllers\DownloadMediaController.php

<?php

namespace App\Nova\Controllers;

use App\Http\Controllers\Controller;
use Spatie\MediaLibrary\MediaCollections\Models\Media;


}

```class DownloadMediaController extends Controller
{
    public function show($mediaId)
    {
        $model = config('media-library.media_model') ?: Media;
        return $model::where('id', $mediaId)->first();
    }
}

The guts of the models looks like this for each had to copy the private methods...

<?php

namespace App\Nova\Fields;

use Ebess\AdvancedNovaMediaLibrary\Fields\HandlesExistingMediaTrait;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Laravel\Nova\Fields\Field;
use Laravel\Nova\Http\Requests\NovaRequest;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\MediaCollections\FileAdder;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class Media extends \Ebess\AdvancedNovaMediaLibrary\Fields\Media
{
    use HandlesExistingMediaTrait;

    // TODO: Make this a trait.
    protected function handleMedia(NovaRequest $request, $model, $attribute, $data)
    {
        $remainingIds = $this->removeDeletedMedia($data, $model->getMedia($attribute));
        $newIds = $this->addNewMedia($request, $data, $model, $attribute);
        $existingIds = $this->addExistingMedia($request, $data, $model, $attribute, $model->getMedia($attribute));
        $this->setOrder($remainingIds->union($newIds)->union($existingIds)->sortKeys()->all());
    }

    // Round about way of applying a fix for uuid mentioned here: https://github.com/ebess/advanced-nova-media-library/pull/307/commits/eb413b16c8af1991353087c0cc09f0212f6c044b
    private function removeDeletedMedia($data, Collection $medias): Collection
    {
        $remainingIds = collect($data)->filter(function ($value) {
            // New files will come in as UploadedFile objects,
            // whereas Vapor-uploaded files will come in as arrays.
            return ! $value instanceof UploadedFile
                && ! is_array($value);
        });

        $mediaClass = config('media-library.media_model');
        $key = app($mediaClass)->getKeyName();

        $medias->pluck($key)->diff($remainingIds)->each(function ($id) use ($medias, $key) {
            /** @var \Ebess\AdvancedNovaMediaLibrary\Fields\Media $media */
            if ($media = $medias->where($key, $id)->first()) {
                $media->delete();
            }
        });

        return $remainingIds->intersect($medias->pluck($key));
    }

    private function addNewMedia(NovaRequest $request, $data, HasMedia $model, string $collection): Collection
    {
        return collect($data)
            ->filter(function ($value) {
                // New files will come in as UploadedFile objects,
                // whereas Vapor-uploaded files will come in as arrays.
                return $value instanceof UploadedFile || is_array($value);
            })->map(function ($file, int $index) use ($request, $model, $collection) {
                if ($file instanceof UploadedFile) {
                    $media = $model->addMedia($file)->withCustomProperties($this->customProperties);
                    $fileName = $file->getClientOriginalName();
                    $fileExtension = $file->getClientOriginalExtension();
                } else {
                    $media = $this->makeMediaFromVaporUpload($file, $model);
                    $fileName = $file['file_name'];
                    $fileExtension = pathinfo($file['file_name'], PATHINFO_EXTENSION);
                }

                if ($this->responsive) {
                    $media->withResponsiveImages();
                }

                if (! empty($this->customHeaders)) {
                    $media->addCustomHeaders($this->customHeaders);
                }

                if (is_callable($this->setFileNameCallback)) {
                    $media->setFileName(
                        call_user_func($this->setFileNameCallback, $fileName, $fileExtension, $model)
                    );
                }

                if (is_callable($this->setNameCallback)) {
                    $media->setName(
                        call_user_func($this->setNameCallback, $fileName, $model)
                    );
                }

                $media = $media->toMediaCollection($collection);

                // fill custom properties for recently created media
                $this->fillMediaCustomPropertiesFromRequest($request, $media, $index, $collection);

                return $media->getKey();
            });
    }

    /**
     * This creates a Media object from a previously, client-side, uploaded file.
     * The file is uploaded using a pre-signed S3 URL, via Vapor.store.
     * This method will use addMediaFromUrl(), passing it the
     * temporary location of the file.
     */
    private function makeMediaFromVaporUpload(array $file, HasMedia $model): FileAdder
    {
        $diskName = config('filesystems.default');
        $disk = config('filesystems.disks.' . $diskName . 'driver') === 's3' ? $diskName : 's3';
        $url = Storage::disk($disk)->temporaryUrl($file['key'], Carbon::now()->addHour());
        return $model->addMediaFromUrl($url)
            ->usingFilename($file['file_name']);
    }

    /**
     * @param \Spatie\MediaLibrary\Models\Media $media
     */
    private function fillMediaCustomPropertiesFromRequest(NovaRequest $request, $media, int $index, string $collection)
    {
        // prevent overriding the custom properties set by other processes like generating convesions
        $media->refresh();

        /** @var Field $field */
        foreach ($this->customPropertiesFields as $field) {
            $targetAttribute = "custom_properties->{$field->attribute}";
            $requestAttribute = "__media-custom-properties__.{$collection}.{$index}.{$field->attribute}";

            $field->fillInto($request, $media, $targetAttribute, $requestAttribute);
        }

        $media->save();
    }

    private function setOrder($ids)
    {
        $mediaClass = config('media-library.media_model');
        $mediaClass::setNewOrder($ids);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants