Recently, I set a goal for one of my sites to score 100 points on Google PageSpeed Insights. One of the requirements to be met is to serve images in next-gen formats. I chosen Google’s WebP standard for that.

Laravel + WebP logos

My SaaS is pretty complicated and I didn’t want to manually convert images in every Controller/Service which handle image upload processing. Also I wanted my website to be supported by older browsers with no WebP support, so I didn’t want to change any of my views to implement any conditions to detect image support. I wanted to make it more OOP way.

Firstly I created a migrations which added columns for my WebP images. Let’s say I’ve got an users table and that table have an avatar column where there is storage path for images in standard formats. I’ve added avatar_webp column for next-gen format image. Note that you don’t need to do that when the names of images would be the same (they can be different only by its extension). I wanted to make it in separate columns because I store images with the name created by checksum from their content.

When I had my columns prepared I made a static property (it needn’t be static if you don't want to) $images = [] in User model with names of columns that contains image paths. Also in that model I mapped my custom events: one for converting images while saving model and another one for displaying that image in WebP if browser supports it.

class User extends Model

{

public static $images = ['avatar']; protected $dispatchesEvents = [

'saving' => ImagesConvert::class,

'retrieved' => ImagesProperFormat::class,

];

}

ImagesConvert.php will be executed when the model is just before saving to database, so in that file we will set our _webp columns content.

ImagesProperFormat.php will be executed every time a model will be retrieved, so inside we will replace original image column with WebP image if the browser support it.

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);

}

}

}

}

I’ve used getDirty() method to make sure that only changed images will be converted.

In this article I do not cover WebP converting itself. You can write your own that use command line tool or use existing packages like buglinjo/laravel-webp, rosell-dk/webp-convert or so.

Important! Note that you should use Job for image processing if you care about your saving speed.

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;

}

}

}

}

And the support_webp() function that is my global function from my Image helper. You can use static helper method if you want.

function support_webp(): bool

{

return isset($_SERVER['HTTP_ACCEPT']) &&

strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false;

}

So, in this simple way you can convert and show your images in next-gen WebP format not worrying about places in your code where images are uploaded or displayed. You just need to edit your models.