Skip to content

What's new in Imager 2.0?

André Elvan edited this page Mar 30, 2018 · 1 revision

TL;DR

What's new?

Let's start with what didn't change! All your template code (twig) will be exactly the same as in Imager 1. Yay.

The configuration settings has changed alot. If you're upgrading from Imager 1 you should make sure to familiarize yourself with the configuration section of the readme. Most notably, saving transforms to external services (like Amazon S3 and Google Cloud Storage) now is part of the new Storages architecture, post-optimization tools has a new Optimizations architecture, and Imgix support has been completelty changed partly into the Transformers architecture, and by the new Imgix profiles.

In Imager 1, assets on external asset sources were treated as external urls. This turned out to be a bad decision as it made it impossible to do granular cache breaking on a per asset basis. In Imager 2, this has changed and volumes are treated more equally. The result though, is that the file structure has changed a little bit. If you're upgrading from Craft 2, I'd recommend wiping all your Imager caches and let Imager recreate the transforms. This can be quite taxing on sites with a large amount of transforms, so if you want to avoid doing this, refer to the file structure section below.

One of the main goals with Imager 2 was to make it more extendable for other developers. I still appreciate pull requests, but some things might make more sense outside of Imager's codebase. As a result, effects, optimizers and external storages can all be added from your own plugin or modules. Read on to learn more.

A number of smaller features has been added, like the ability to replace native Craft transforms and thumb creation in the control panel with Imager (see the useForNativeTransforms and useForCpThumbs config settings), thanks to new events added by P&T. The new filenamePattern config setting which can be used to create more SEO-friendly filenames, or to serve up alternative file types (think mytransform.webp.jpg). The transfer to Yii2 queues for post-optimization tasks will also hopefully make post-optimization a lot more stable and powerful. Placeholders (created through base64Pixel template variable in Imager 1) has also gotten an overhaul, and you can now create both SVGs, GIFs and SVG silhouettes.

Let's get into the nitty-gritty...

Transformers

When Imgix support was introduced in Imager 1.6, it was implemented in much the same way as post-optimization tools. That didn't really make sense, since it completely replaced the original transform functionality.

Imager 2.0 introduces the concept of Transformers, and ships with two, Craft and Imgix. Which transformer to use is controlled through the transformer config setting. Some transform parameters might not be available for both transformers, effects for instance is a Craft transformer specific parameter, and imgixParams is an Imgix one. The documentation contains information on what parameters are available for which transformers.

Please note that the type of transformer can be overwritten in your templates (like you would with imgixEnabled in Imager 1). Let's say you're using imgix as your default transformer, but want to create a small placeholder gif for inlining as a base64 encoded image. Example:

{% set transformedImage = craft.imager.transformImage(myAsset, 
    { width: 4, height: 3 }, 
    { format: 'gif', frames: '0' }, 
    { transformer: 'craft' } ) %}

Even though Imager 2 only comes with two transformers, this shift in architecture opens up for adding more down the line. Know of a service or method for creating images faster, better and/or cheaper? Let me know, it could be the next transformer.

Imgix profiles

It's no secret that I love Imgix. One of the biggest issues in Imager 1 though, was using it with images from different sources. If you were transforming images both from cloud asset sources, assets that had been transformed by Imager already, and external images, you'd have to override alot of configuration settings in your templates. In Imager 2, the concept of Imgix profiles makes this super simple.

The config settings imgixProfile and imgixConfig works in tandem to achieve this. First, you'll configure as many profiles as necessary through imgixConfig. Say you have one craft volume with images you want to transform, and also external images that you want to transform through a web proxy imgix source, you'd have something like this:

'imgixConfig' => [
    'default' => [
        'domains' => ['myassets.imgix.net'],
        'useHttps' => true,
        'signKey' => 'XZX',
        'sourceIsWebProxy' => false,
        'useCloudSourcePath' => true,
        'shardStrategy' => 'cycle',
        'getExternalImageDimensions' => true,
        'defaultParams' => [],
    ],
    'webproxy' => [
        'domains' => ['myproxy.imgix.net'],
        'useHttps' => true,
        'signKey' => 'XZX',
        'sourceIsWebProxy' => true,
        'useCloudSourcePath' => true,
        'shardStrategy' => 'cycle',
        'getExternalImageDimensions' => true,
        'defaultParams' => [],
    ]
],

You'd set up your default profile using imgixProfile to 'default', and then you could easily override this in your templates:

{% set transformedImage = craft.imager.transformImage(externalUrl, 
    [{ width: 400 }, { width: 1600 } ], 
    { format: 'jpg' }, 
    { imgixProfile: 'webproxy' } ) %}

This gives you alot of flexibility in how you use Imgix in various scenarios. Remember that the number of sources you have on Imgix doesn't impact the pricing in any way, so it doesn't matter if you create a couple extras if it eases your workflow.

External Storages

Support for uploading transforms to external services has been structured into what's called Storages. Imager 2 comes with two storages, Amazon S3 (aws) and Google Cloud Storage (gcs), and is handled through the storages and storageConfig config settings. storageConfig is an array with keys corresponding to the handle of the different storages, like so:

'storageConfig' => [
    'aws' => [
        'accessKey' => '',
        'secretAccessKey' => '',
        'region' => '',
        'bucket' => '',
        'folder' => '',
        'requestHeaders' => array(),
        'storageType' => 'standard',
        'cloudfrontInvalidateEnabled' => false,
        'cloudfrontDistributionId' => '',
    ],
    'gcs' => [
        'keyFile' => '',
        'bucket' => '',
        'folder' => '',
    ],
]

This only adds the configuration for the storages, use storages to supply an array of storages to upload to:

'storages' => ['aws'] // uploads to AWS
'storages' => ['aws', 'gcs'] // uploads to both AWS and GCS (not sure why you would, but...)

Additional storages can be created by implementing the ImagerStorageInterface, and registrering your class using the ImagerService::registerExternalStorage method, like so:

ImagerService::registerExternalStorage('aws', AwsStorage::class);

As in Imager 1, uploading to external storages doesn't mean that the transform will automatically be served from that location, you still need to set your imagerUrl appropriately. Also, the transformed images are still kept in your imagerSystemPath and used for caching purposes.

Optimizers

As with storages, optimizers have gotten a do-over and now has a similar architecture, handled through the optimizers and optimizerConfig config settings. optimizerConfig provides the configuration for the different optimizers (see the docs for all the different settings), and optimizers indicates which are enabled. Example that adds support for jpegtran, gifsicle and pngquant:

'optimizers' => ['jpegtran', 'gifsicle', 'pngquant']

In addition to all the optimizers that was supported in Imager 1, Kraken, ImageOptim and JPG support for TinyPNG was added.

Like with storages, additional optimizers can now be added from your own plugins or modules. Create a class that implements the ImagerOptimizeInterface, and register it using ImagerService::registerOptimizer, like so:

ImagerService::registerOptimizer('kraken', KrakenOptimizer::class);

Configuration for your optimizer goes into optimizerConfig with the same handle as you registered it with.

Effects

Aaand effects got a proper architecture now and can be extended in the same way as storages and optimizers. All you need to do is create a class that implements ImagerEffectsInterface and register it using ImagerService::registerEffect, like so:

ImagerService::registerEffect('unsharpmask', UnsharpMaskEffect::class);

Imager 2 comes with 18 built-in effects, which you can use as examples when making your own.

File naming, structure and caching

The new filenamePattern adds flexibility for a lot of use-cases. The default setting mimics the default settings in Imager 1, but if you want to keep your old settings you can turn it off using useFilenamePattern, and instead use the old hashFilename.

The filestructure has changed somewhat, and it's advisable to clear all Imager caches when upgrading. But if you want to give it a go, here're some pointers as to what have changed.

In Imager 1, for assets on local asset sources, the path of the asset source was added to the path of the transform, so the path would become:

<imagerSystemPath>/<asset_source_path>/<asset_path>/<asset_id>/<transform_filename>

In Imager 2, the asset source path has been replaced with the volume handle:

<imagerSystemPath>/<asset_volume_handle>/<asset_path>/<asset_id>/<transform_filename>

So for local assets, you can probably just move your files from <imagerSystemPath>/<asset_source_path>/ to <imagerSystemPath>/<asset_volume_handle>/ and Imager will be happy with that.

Since assets on external asset sources were treated as external urls in Imager 1, they had a structure like this:

<imagerSystemPath>/<domain_name_string>/<url_path>/<transform_filename>

This doesn't map at all to the new structure. If you really want to keep your old transformed images, the easiest would be to pass in the url to the asset, instead of the asset itself, in your template code. That would mean that it would be treated as an external url, and would get the same path as in Imager 1 (path's for external urls didn't change). You'd miss out on some improvements to cache breaking, but that's just the trade-off.

Imager will no longer download remote files if the requested transforms already exists. If you want to save diskspace, there is a new config setting named cacheRemoteFiles which can be disabled to delete cached remote files continuously. If you're doing transforms on the same assets different places in your code, this could impact performance in a negavtive way, so use with care. Please note, you can also use the controller actions to clear your external cache on a regular basis.

And, I know this will disappoint someone, the transforms in imagerSystemPath still acts as the cache for Imager, and will have to be there, even if you upload your transforms to an external service. Sry! :)

License, pricing, etc...

Didn't change. Still free. Still MIT. Still accepting beer over at Beerpay. ;)