Вложения

Suggest edit

Файлы различных форматов и расширений относящиеся к записи являются вложениями.

Вложения могут быть прикреплены к любой модели посредством связей, для этого необходимо добавить трейт:

namespace App;

use Illuminate\Database\Eloquent\Model;
use Orchid\Attachment\Attachable;

class Hostel extends Model
{
    use Attachable;
    //
}

После этого мы можем добавлять и получать её вложения, например:

$item = Hostel::find(42);
$item->attachment()->get();

Пример загрузки

В действительности вы уже имеете маршрут для загрузки файлов (если конечно, к нему разрешён доступ).

Пример метода контроллера:

use Orchid\Attachment\File;

public function upload(Request $request)
{
    $file = new File($request->file('photo'));
    $attachment = $file->load();

    return response()->json($attachment);
}

Это автоматически загрузит ваш файл в хранилище по умолчанию (public) и создаст запись в базе данных.

$image = $item->attachment()->first();

//Получить URL адрес файла
$image->url();

Примечание. Метод url() сначала проверит наличие пути, а затем получит URL-адрес. При использовании внешнего хранилища, такого как s3, будет выполнено два вызова. Для повышения производительности вы можете использовать caching adapter, рекомендованный Laravel для повышения производительности. Вы также можете просто переопределить этот метод и настроить его под свои нужды.

Загрузка файла через консоль

Иногда нужные файлы уже есть на сервере, тогда для загрузки в нужное хранилище можно использовать следующее:

use Illuminate\Http\UploadedFile;
use Orchid\Attachment\File;

$file = new UploadedFile($path, $originalName);

$attachment = (new File($file))->load();

Повторная загрузка

Благодаря хешу, вложения не загружаются повторно. Вместо этого создаётся ссылка в базе данных на необходимый физический файл, позволяя эффективно использовать ресурсы. Файл будет удалён только тогда, когда все ссылки будут уничтожены.

Удаление вложений

При удалении модели ее вложения не удаляются автоматически. В случае если вложение не должно существовать без модели, его нужно удалить непосредственно перед удалением модели. Если удалить запись о вложении в таблице attachments, файл не будет удален. Очищать вложения нужно через функцию delete() модели Attachment. В этом случае произойдет проверка на существование других ссылок на файл и если их нет, то файл удалится. Это легко можно сделать используя отношения и обсервер.

Вернемся к примеру с hero из “Управление вложенными файлами”

// app/Post.php

use Orchid\Attachment\Models\Attachment;

public function hero()
{
    return $this->hasOne(Attachment::class, 'id', 'hero')->withDefault();
}

При обращении к отношению как к функции $post->hero(), оно вернет экземпляр класса Illuminate\Database\Eloquent\Builder у которого тоже есть функция delete(), однако она выполняет sql запрос. При обращении к отношению как к свойству $post->hero, оно вернет экземпляр модели. В данном случае модели Attachment, которая нам и нужна.

$post->hero()->delete();

Примечание. Отношение важно описать используя функцию withDefault(), чтобы избежать null pointer exception.

Удалять нужно во время события deleting модели. Для этого создадим обсервер нашей модели.

php artisan make:observer PostObserver

В PostObserver создаем функцию deleting

public function deleting(Post $post)
{
    $post->hero()->delete();
}

В случае, когда у нас вложений много, мы используем отношение attachment из трейта Attachable

public function deleting(Post $post)
{
    //load attachment as collection and not query attachment()
    $post->attachment()->each->delete();
}

Примечание. Опытный Laravel разработчик увидит, что здесь есть N+1 проблема. Это сделано намеренно чтобы иметь доступ системе для удаления файла (база данных не сделает это за нас).

Подписываем модель на наш обсервер в AppServiceProvider

public function boot()
{
    ...
    
    Post::observe(PostObserver::class);
}

Конфигурация по умолчанию

По умолчанию каждый загружаемый файл следует стратегии, описанной в config/platform.php:

/*
|--------------------------------------------------------------------------
| Default configuration for attachments.
|--------------------------------------------------------------------------
|
| Strategy properties for the file and storage used.
|
*/

'attachment' => [
    'disk'      => 'public',
    'generator' => \Orchid\Attachment\Engines\Generator::class,
],
  • disk – имя хранилища, используемое для хранения файлов. Все настройки которого должны быть определены в /config/filesystems.php.

  • generator – класс, который определяет, как будут называться загружаемые файлы и в каких каталогах они будут находиться, а также как избежать их дублирования.

Подписка на событие загрузки

Различные варианты обработки файлов могут потребовать дополнительной обработки, например, сжатие видео. Это возможно благодаря событию, на которое можно подписаться стандартными средствами и выполнить задачу в фоне:

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use App\Listener\UploadListener;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event handler mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        UploadFileEvent::class => [
             UploadListener::class,
        ],
    ];

    /**
     * Register any events for your application.
     */
    public function boot()
    {
        parent::boot();
    }
}

Каждая подписка получит объект UploadFileEvent:

namespace App\Listeners;

use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Orchid\Platform\Events\UploadFileEvent;

class UploadListener extends ShouldQueue
{
    use InteractsWithQueue;
    
    /**
     * Handle the event.
     *
     * @param  UploadFileEvent  $event
     * @return void
     */
    public function handle(UploadFileEvent $event)
    {
        //$event->attachment
        //$event->time
    }
}

Our Friends