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:
- PHP 7.3.16
- Laravel 7
- 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;
class OptimalImage extends Image
{
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;
}
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;
class OptimalImage extends Image
{
protected function optimizeImage($fileName)
{
$needsUploadBack = false;
$localDisk = 'local';
$disk = $this->getStorageDisk();
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;
class OptimalImage extends Image
{
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;
}
protected function optimizeImage($fileName)
{
$needsUploadBack = false;
$localDisk = $this->getLocalDisk();
$disk = $this->getStorageDisk();
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);
}
}
public function localDisk(string $disk)
{
return $this->withMeta(['localDisk' => $disk]);
}
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