cyberfly
9/6/2018 - 4:32 AM

Spatie Media Library V6 attachable trait

Custom trait for easier usage of Spatie Media Library (tested on V6)

{
    "data": {
        "id": 25,
    },
    "uploaded_files": {
        "conclusion_files": [
            {
                "id": 1,
                "name": "articles-create",
                "mime_type": "image/png",
                "url": "https://delivery-api.dev/storage/145/articles-create.png"
            }
        ],
        "ta_snp_files": [
            {
                "id": 4,
                "name": "1756170",
                "mime_type": "image/jpeg",
                "url": "https://delivery-api.dev/storage/143/1756170.jpeg"
            },
            {
                "id": 14,
                "name": "acl-mega",
                "mime_type": "image/png",
                "url": "https://delivery-api.dev/storage/144/acl-mega.png"
            }
        ]
    }
}
<?php

namespace App\Http\Controllers\Api\V1;


use App\Traits\Attachable;
use Illuminate\Http\Request;

use App\DeliverySiteVisit;
use App\Http\Requests\Api\V1\StoreDeliverySiteVisitRequest;
use App\Http\Requests\Api\V1\UpdateDeliverySiteVisitRequest;
use App\Http\Resources\Api\V1\DeliverySiteVisitResource;
use App\Filters\Api\V1\DeliverySiteVisitFilter;

class DeliverySiteVisitController extends ApiController
{
    use Attachable;

    protected $delivery_site_visit;

    /**
     * Create a new controller instance.
     *
     * @param DeliverySiteVisit $delivery_site_visit
     */
    public function __construct(DeliverySiteVisit $delivery_site_visit)
    {
        $this->delivery_site_visit = $delivery_site_visit;
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \App\Http\Requests\Api\V1\StoreDeliverySiteVisitRequest $request
     * @return DeliverySiteVisitResource
     */
    public function store(StoreDeliverySiteVisitRequest $request)
    {

        $delivery_site_visit = $this->delivery_site_visit->create($request->all());

        // upload attachments to dedicated collection

        $this->uploadAttachments($delivery_site_visit, $request);
        $this->uploadAttachments($delivery_site_visit, $request, 'ta_snp_files');
        $this->uploadAttachments($delivery_site_visit, $request, 'conclusion_files');

        // get uploaded attachments to include in response

        $uploaded_files = $this->getAllModelAttachments($delivery_site_visit);

        return (new DeliverySiteVisitResource($delivery_site_visit))->additional([
            'uploaded_files' => $uploaded_files
        ]);
    }

    /**
     * Display the specified resource.
     *
     * @param Request $request
     * @param  int $site_visit_id
     * @return DeliverySiteVisitResource
     */
    public function show(Request $request, $site_visit_id)
    {
        $delivery_site_visit = $this
            ->delivery_site_visit
            ->findOrFail($site_visit_id);

        $uploaded_files = $this->getAllModelAttachments($delivery_site_visit);

        return (new DeliverySiteVisitResource($delivery_site_visit))->additional([
            'uploaded_files' => $uploaded_files
        ]);
    }
    
    /**
     * Update the specified resource in storage.
     *
     * @param  \App\Http\Requests\Api\V1\UpdateDeliverySiteVisitRequest $request
     * @param  int $delivery_site_visit_id
     * @return DeliverySiteVisitResource
     */
    public function update(UpdateDeliverySiteVisitRequest $request, $delivery_site_visit_id)
    {
        $delivery_site_visit = $this->delivery_site_visit->whereCompanyId($company_id)->whereId($delivery_site_visit_id)->firstOrFail();

        $delivery_site_visit->fill($request->all());

        $delivery_site_visit->save();

        // sync attachments

        $this->syncAttachments($delivery_site_visit, $request, 'tenancy_spa_agreement_file');

        $uploaded_files = $this->getAllModelAttachments($delivery_site_visit);

        return (new DeliverySiteVisitResource($delivery_site_visit))->additional([
            'uploaded_files' => $uploaded_files
        ]);
    }
}
<?php

namespace App\Traits;

use Spatie\MediaLibrary\Media;

trait Attachable
{
    /**
     * Sync attachments with new attachments and delete attachment
     * Suitable for update form with existing attachments to sync
     * @param $model
     * @param $request
     * @param string $media_collection
     */
    public function syncAttachments($model, $request, $media_collection='attachments')
    {
        // attached new files

        $this->uploadAttachments($model, $request, $media_collection);

        // removed selected files to delete

        $this->deleteAttachments($request->deleted_files);
    }

    /**
     * Upload attachments, default will attach to collection name attachments
     * @param $model
     * @param $request
     * @param string $media_collection
     */
    public function uploadAttachments($model, $request, $media_collection='attachments')
    {
        if ($request->hasFile($media_collection)) {

            $files = $request->file($media_collection);

            foreach ($files as $file) {

                $model
                    ->addMedia($file)
                    ->toMediaCollection($media_collection);
            }
        }
    }

    /**
     * Delete specific media attachments
     * @param $deleted_files
     * @internal param $deleted_files
     */
    public function deleteAttachments($deleted_files)
    {
        Media::find($deleted_files)
            ->each
            ->delete();
    }

    /**
     * Get attachments by collection name, default will fetch collection name attachments
     * @param $model
     * @param string $media_collection
     * @return array
     */
    public function getAttachments($model, $media_collection='attachments')
    {
        return $this->getAttachedMedia($model, $media_collection);
    }

    /**
     * Sugarcoat Spatie GetMedia to decorate the response
     * @param $model
     * @param string $collection_name
     * @return array
     */
    private function getAttachedMedia($model, $collection_name='')
    {
        $uploaded_media = [];

        if (empty($collection_name)) {
            $uploaded_files = $model->getMedia();
        }
        else {
            $uploaded_files = $model->getMedia($collection_name);
        }

        if ($uploaded_files->count()) {

            foreach ($uploaded_files as $file) {

                $uploaded_media[] = [
                    'id' => $file->id,
                    'name' => $file->name,
                    'mime_type' => $file->mime_type,
                    'url' => $file->getFullUrl(),
//                        'full_path' => $file->getPath(),
                ];
            }
        }

        return $uploaded_media;
    }

    /**
     * Get all attachments tied to model
     * @param $model
     * @return array
     */
    public function getAllModelAttachments($model)
    {
        $model_type = get_class($model);
        $model_id_key = $model->getKeyName();
        $model_id = $model->$model_id_key;

        $model_collection_names = Media::whereModelId($model_id)->whereModelType($model_type)->groupBy('collection_name')->pluck('collection_name')->toArray();

        $attachments = [];

        if (!empty($model_collection_names)) {
            foreach ($model_collection_names as $collection_name) {
                $collection_attachments = $this->getAttachedMedia($model, $collection_name);
                $attachments[$collection_name] = $collection_attachments;
            }
        }

        return $attachments;
    }
}