Недавно я поставил цель для одного из моих сайтов набрать 100 баллов в Google PageSpeed ​​Insights. Одно из требований, которое необходимо выполнить, - обслуживать изображения в форматах следующего поколения. Для этого я выбрал стандарт Google WebP.

Мой SaaS довольно сложен, и я не хотел вручную конвертировать изображения в каждом контроллере / сервисе, который обрабатывает загрузку изображений. Также я хотел, чтобы мой веб-сайт поддерживался более старыми браузерами без поддержки WebP, поэтому я не хотел изменять какие-либо из моих представлений, чтобы реализовать какие-либо условия для обнаружения поддержки изображений. Я хотел сделать это более ООП.

Сначала я создал миграции, которые добавили столбцы для моих изображений WebP. Допустим, у меня есть users таблица, а в этой таблице есть avatar столбец, в котором есть путь для хранения изображений в стандартных форматах. Я добавил avatar_webp столбец для изображений в формате следующего поколения. Учтите, что этого делать не нужно, если названия изображений будут одинаковыми (они могут отличаться только расширением). Я хотел сделать это в отдельных столбцах, потому что я храню изображения с именем, созданным по контрольной сумме из их содержимого.

Когда я подготовил свои столбцы, я создал статическое свойство (оно не обязательно должно быть статическим, если вы этого не хотите) $images = [] в User модели с именами столбцов, которые содержат пути к изображениям. Также в этой модели я сопоставил свои пользовательские события: одно для преобразования изображений при сохранении модели, а другое для отображения этого изображения в WebP, если браузер поддерживает его.

class User extends Model
{
    public static $images = ['avatar'];
    protected $dispatchesEvents = [
        'saving' => ImagesConvert::class,
        'retrieved' => ImagesProperFormat::class,
    ];
}

ImagesConvert.php будет выполняться, когда модель находится непосредственно перед сохранением в базе данных, поэтому в этом файле мы установим содержимое наших _webp столбцов.
ImagesProperFormat.php будет выполняться каждый раз при получении модели, поэтому внутри мы заменим исходный столбец изображения на изображение WebP, если браузер поддерживает его.

class ImagesConvert
{
    const WEBP_SUFFIX = '_webp';

    /**
     * @var Model
     */
    protected $model;

    public function __construct(Model $model)
    {
        $this->model = $model;
        $this->convertImages();
    }

    protected function convertImages(): void
    {
        /** @var array $images */
        foreach ($this->model::$images as $image) {
            if (
                key_exists($image, $this->model->getDirty()) &&
                $imagePath = $this->model->{$image}
            ) {
                $this->model->{$image.self::WEBP_SUFFIX} =
                ImageConvertService::convertToWebp($imagePath);
            }
        }
    }
}

Я использовал метод getDirty(), чтобы конвертировать только измененные изображения.

В этой статье я не рассматриваю саму конвертацию WebP. Вы можете написать свой собственный, который использует инструмент командной строки или использовать существующие пакеты, такие как buglinjo / laravel-webp, rosell-dk / webp-convert или что-то в этом роде.

Важно! Обратите внимание, что вам следует использовать Job для обработки изображений, если вы заботитесь о скорости сохранения.

class ImagesProperFormat
{
    /**
     * @var Model
     */
    protected $model;

    public function __construct(Model $model)
    {
        $this->model = $model;
        $this->setProperImagesFormat();
    }

    protected function setProperImagesFormat(): void
    {
        /** @var array $images */
        foreach ($this->model::$images as $image) {
            if (
                support_webp() &&
                key_exists($image, $this->model->getAttributes()) &&
                $this->model->{$image} &&
                $webpImagePath =
                $this->model->{$image.ImagesConvert::WEBP_SUFFIX}
            ) {
                $this->model->{$image} = $webpImagePath;
            }
        }
    }
}

И функция support_webp(), которая является моей глобальной функцией из моего помощника по изображениям. Вы можете использовать статический вспомогательный метод, если хотите.

function support_webp(): bool
{
    return isset($_SERVER['HTTP_ACCEPT']) &&
           strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false;
}

Таким образом, таким простым способом вы можете конвертировать и показывать свои изображения в формате WebP следующего поколения, не беспокоясь о местах в вашем коде, где изображения загружаются или отображаются. Вам просто нужно отредактировать свои модели.