Creating Your Own Package for Laravel Nova: OptimalImage

While working on a new project that Laravel Nova uses to administer , I tested it with Google’s webmaster tools. It turned out that some of the photos on the resource were not optimized - their size could be significantly reduced. Those that are in the project can be processed when building the project using node.js. There are many ready-made packages for this. There are still images uploaded by the user directly from the site administration panel. Of course, you can optimize each image before uploading to the site, but why not make this procedure automatic. So the idea was born to make a package for Laravel Nova: OptimalImage.


The package is quite simple, and therefore does not pretend to be a complete guide to creating packages for Laravel Nova. It does not address a rather extensive topic on working with the visual component of components. If an interesting idea of ​​a component appears, with a visual component - I will write an article on this topic.


Formulation of the problem


You want to create a package that adds a new field. This field will differ from the
usual field with image loading in that optimization will be performed for the loaded image.


Installation and configuration of the environment.


To repeat all the steps in this guide, you will need a project with Laravel and Laravel Nova. Laravel Nova is a paid tool, but in my opinion its price is quite affordable.


I used in the work:


  1. PHP 7.3.16
  2. Laravel 7
  3. Laravel Nova 3.0

.


. — :


cd /path/to/nova/project
php artisan nova:field yarbala/optimal-image

. nova-components OptimalImage.


nova-components .gitignore git init


cd /path/to/nova/project/nova-components/OptimalImage
git init
git add .
git commit -m "First commit"
git remote add origin git@github.com:yarbala/nova-optimal-image-field.git
git push -u origin master


spatie/laravel-image-optimizer, require composer.json


"spatie/laravel-image-optimizer": "^1.6"

Laravel


composer update

.


:


nova-components/OptimalImage/dist
nova-components/OptimalImage/resources

:


nova-components/OptimalImage/webpack.mix.js
nova-components/OptimalImage/package.json
nova-components/OptimalImage/mix-manifest.json 

OptimalImage :


namespace Yarbala\OptimalImage;

use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Laravel\Nova\Fields\Image;

/**
 *       Image.  ,    
 *   ,      .
 *
 * Class OptimalImage
 * @package Yarbala\OptimalImage
 */
class OptimalImage extends Image
{
    /**
     *        Image 'Fields/File.php'.     
     *    optimizeImage  .
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  string  $requestAttribute
     * @return string
     */
    protected function storeFile($request, $requestAttribute)
    {
        if (! $this->storeAsCallback) {
            $fileName = $request->file($requestAttribute)->store($this->getStorageDir(), $this->getStorageDisk());
            if ($fileName) {
                try {
                    $this->optimizeImage($fileName);
                } catch (FileNotFoundException $e) {
                }
            }

            return $fileName;
        }

        $fileName = $request->file($requestAttribute)->storeAs(
            $this->getStorageDir(), call_user_func($this->storeAsCallback, $request), $this->getStorageDisk()
        );

        if ($fileName) {
            try {
                $this->optimizeImage($fileName);
            } catch (FileNotFoundException $e) {
                //
            }
        }

        return $fileName;
    }

    /**
     *    
     *
     * @param $fileName
     * @throws FileNotFoundException
     */
    protected function optimizeImage($fileName)
    {
        // ...
    }
}

Laravel\Nova\Fields\Image.


public $component = 'optimal-image';

storeFile Laravel\Nova\Fields\File, Laravel\Nova\Fields\Image. . , optimizeImage .


optimizeImage:


namespace Yarbala\OptimalImage;

use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Laravel\Nova\Fields\Image;
use Spatie\ImageOptimizer\OptimizerChain as ImageOptimizer;
use Storage;

/**
 *       Image.  ,    
 *   ,      .
 *
 * Class OptimalImage
 * @package Yarbala\OptimalImage
 */
class OptimalImage extends Image
{
    // ...

    /**
     *    
     *
     * @param $fileName
     * @throws FileNotFoundException
     */
    protected function optimizeImage($fileName)
    {
        $needsUploadBack = false;
        $localDisk = 'local';
        $disk = $this->getStorageDisk();

        /*         Storage,  Spatie\ImageOptimizer    
                    
        */
        if (!Storage::disk($localDisk)->exists($fileName)) {
            Storage::disk($localDisk)->put($fileName, Storage::disk($disk)->get($fileName));
            $needsUploadBack = true;
        }

        //       
        $path = Storage::disk($localDisk)->path($fileName);

        //  
        app(ImageOptimizer::class)->optimize($path);

        //    ,   
        if ($needsUploadBack) {
            Storage::disk($disk)->put($fileName, Storage::disk($localDisk)->get($fileName));
            Storage::disk($localDisk)->delete($fileName);
        }
    }
}

Storage, Spatie\ImageOptimizer . - local.


:


class OptimalImage extends Image
{
    // ....

    public function localDisk(string $disk)
    {
        return $this->withMeta(['localDisk' => $disk]);
    }

    protected function getLocalDisk()
    {
        return $this->meta['localDisk'] ?? 'local';
    }
}

OptimalImage :


<?php

namespace Yarbala\OptimalImage;

use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Laravel\Nova\Fields\Image;
use Spatie\ImageOptimizer\OptimizerChain as ImageOptimizer;
use Illuminate\Support\Facades\Storage;

/**
 *       Image.  ,    
 *   ,      .
 *
 * Class OptimalImage
 * @package Yarbala\OptimalImage
 */
class OptimalImage extends Image
{
    /**
     *        Image 'Fields/File.php'.     
     *      optimizeImage  .
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  string  $requestAttribute
     * @return string
     */
    protected function storeFile($request, $requestAttribute)
    {
        if (! $this->storeAsCallback) {
            $fileName = $request->file($requestAttribute)->store($this->getStorageDir(), $this->getStorageDisk());
            if ($fileName) {
                try {
                    $this->optimizeImage($fileName);
                } catch (FileNotFoundException $e) {
                }
            }

            return $fileName;
        }

        $fileName = $request->file($requestAttribute)->storeAs(
            $this->getStorageDir(), call_user_func($this->storeAsCallback, $request), $this->getStorageDisk()
        );

        if ($fileName) {
            try {
                $this->optimizeImage($fileName);
            } catch (FileNotFoundException $e) {
                //
            }
        }

        return $fileName;
    }

    /**
     *    
     *
     * @param $fileName
     * @throws FileNotFoundException
     */
    protected function optimizeImage($fileName)
    {
        $needsUploadBack = false;
        $localDisk = $this->getLocalDisk();
        $disk = $this->getStorageDisk();

        /*         Storage,  Spatie\ImageOptimizer    
                    
        */
        if (!Storage::disk($localDisk)->exists($fileName)) {
            Storage::disk($localDisk)->put($fileName, Storage::disk($disk)->get($fileName));
            $needsUploadBack = true;
        }

        //       
        $path = Storage::disk($localDisk)->path($fileName);

        //  
        app(ImageOptimizer::class)->optimize($path);

        //    ,   
        if ($needsUploadBack) {
            Storage::disk($disk)->put($fileName, Storage::disk($localDisk)->get($fileName));
            Storage::disk($localDisk)->delete($fileName);
        }
    }

    /**
     *      
     *
     * @param string $disk
     * @return OptimalImage
     */
    public function localDisk(string $disk)
    {
        return $this->withMeta(['localDisk' => $disk]);
    }

    /**
     *   
     * 
     * @return string
     */
    protected function getLocalDisk()
    {
        return $this->meta['localDisk'] ?? 'local';
    }
}

. , config/image-optimizer.php :


php artisan vendor:publish --provider="Spatie\LaravelImageOptimizer\ImageOptimizerServiceProvider"

, .


, readme .


, - . . .


.


The source code for the project is available at and distributed under the MIT license.


Packagist package


Novapackages package


All Articles