# Automatic generation of transforms

Transforms has to be generated at some point, they don't just appear like unicorns because you wanted them to (bummer, we know!). Generating transforms is a resource heavy process, and doing it runtime when a page is requested, can be brutal.

This new feature in Imager X aims to mitigate some of the pain of generating transforms. With automatic generation of transforms you can create transforms when an asset is uploaded or when an element is saved. Also, you can use console commands and element actions to run the auto generation any time you want.

TIP

All automatic generation of transforms is done via queue jobs, to keep delays from happening when assets are uploaded, or elements are saved. Please note that if you're running your queue via a cron job, there may be a delay before the transforms are generated, which could result in delays for front end users. We highly recommend running a daemon that listens for new jobs instead, if at all possible.

# Setting it up

To set up automatic generation, create a config file in your config folder named imager-x-generate.php. All configuration settings can be found in the configuration section, but let's ease in with a simple example.

As a start, let's say we want all images uploaded to our volume with handle employeeImages, to have two named transform called employeeThumbnail and employeePoster be auto-generated:

<?php

return [
    'volumes' => [
        'employeeImages' => ['employeeThumbnailTransform', 'employeePoster'],
    ]
];

That was easy enough. Now, let's say we have another volume, a generic one with handle images, where alot of our other images go. We have some frequetly used transforms that we chuck in there:

<?php

return [
    'volumes' => [
        'employeeImages' => ['employeeThumbnail', 'employeePoster'],
        'images' => ['defaultHeroImage', 'listImage', 'eventHeroImage', 'seoGenericImage', 'seoFacebookImage', 'seoTwitterImage'],
    ]
];

That's good and all, but you quickly realize that only a few of the transforms generated will ever be used, since images in this volume is used across many sections and fields.

The eventHeroImage transform will only ever be used for images that's added to the heroImage field in the Events section. This is a good candidate for using the elements generate setting, like this:

<?php

return [
    'volumes' => [
        'employeeImages' => ['employeeThumbnail', 'employeePoster'],
        'images' => ['defaultHeroImage', 'listImage', 'seoGenericImage', 'seoFacebookImage', 'seoTwitterImage'],
    ],
    
    'elements' => [
        [
            'elementType' =>  \craft\elements\Entry::class,
            'criteria' => [
                'section' => 'events',
            ],
            'fields' => ['heroImage'],
            'transforms' => ['eventHeroImage']
        ]
    ],    
];

TIP

The criteria parameter is passed directly to the element query, and takes exactly the same parameters as a standard Craft element query.

In fact, the defaultHeroImage transform is also used in a limited number of sections and fields, so let's split that out into elements too:

<?php

return [
    'volumes' => [
        'employeeImages' => ['employeeThumbnail', 'employeePoster'],
        'images' => ['listImage', 'seoGenericImage', 'seoFacebookImage', 'seoTwitterImage'],
    ],
    
    'elements' => [
        [
            'elementType' =>  \craft\elements\Entry::class,
            'criteria' => [
                'section' => 'events',
            ],
            'fields' => ['heroImage'],
            'transforms' => ['eventHeroImage']
        ],
        [
            'elementType' =>  \craft\elements\Entry::class,
            'criteria' => [
                'section' => ['news', 'articles', 'products'],
            ],
            'fields' => ['heroImage', 'mainImage', 'articleBlocks:images.images'],
            'transforms' => ['defaultHeroImage']
        ],
        
    ],    
];

TIP

Notice that you can target fields inside Matrix or SuperTable fields with the syntax <matrixFieldHandle>:<matrixTypeHandle>.<fieldHandle>.

<matrixTypeHandle> can be set to *, which matches all matrix types. For SuperTable fields, this is the only valid value, as SuperTable doesn't have different block types.

Finally, since you're using SEOMate (opens new window) to set up a waterfall of fields to use for your seo meta image tags, it makes more sense to generate these transforms based on the fields used:

<?php

return [
   'volumes' => [
       'employeeImages' => ['employeeThumbnail', 'employeePoster'],
       'images' => ['listImage'],
   ],
   
   'elements' => [
       [
           'elementType' =>  \craft\elements\Entry::class,
           'criteria' => [
               'section' => 'events',
           ],
           'fields' => ['heroImage'],
           'transforms' => ['eventHeroImage']
       ],
       [
           'elementType' =>  \craft\elements\Entry::class,
           'criteria' => [
               'section' => ['news', 'articles', 'products'],
           ],
           'fields' => ['heroImage', 'mainImage', 'articleBlocks:images.images'],
           'transforms' => ['defaultHeroImage']
       ],
       
   ],
   
   'fields' => [
       'seoImage' => ['seoGenericImage', 'seoFacebookImage', 'seoTwitterImage'],
       'mainImage' => ['seoGenericImage', 'seoFacebookImage', 'seoTwitterImage'],
       'heroImage' => ['seoGenericImage', 'seoFacebookImage', 'seoTwitterImage'],
       'listImage' => ['seoGenericImage', 'seoFacebookImage', 'seoTwitterImage'],
   ]        
];

TIP

The field handles in fields will target all elements, meaning also for matrix blocks (which are just another element in Craft). So if you have a field called image that is attached to an entry type, and a field called image inside a matrix block, both will match the key image in the example above.

That leaves us with a much more optimized set of transforms resulting in less transforms being generated overall, saving CPU cycles when new assets are uploaded, and a fair amount of disk space.

# Console commands for generating transforms

Imager X comes with a powerful console command imager-x/generate for batch generation of transforms. You can create transforms based on volumes and folders, or fields (as described above).

TIP

If the command doesn't show up when you type ./craft in your project root, or the command doesn't work at all, make sure the plugin is installed, that the PHP version you're running on the command line has access to the database, and that the necessary environment variables are set. Try using one of the built in commands that comes with Craft, for instance ./craft backup/db, if you're unsure if console commands are working in general.

The available parameters for the command are:

# --volume (shorthand: -v)

Handle for volume to generate transforms for.

# --folderId (shorthand: -fid)

Id for folder inside a volume to generate transforms for.

# --recursive (shorthand: -r)

Toggles recursive mode on/off when transforming inside a volume.

# --field (shorthand: -f)

Handle of field to generate transforms for.

# --transforms (shorthand: -t)

Named transforms to generate. This can be a comma-separated list, or a single value.

If no transforms are passed to the command, Imager will look at the generate configuration to figure out which transforms to run. If transforms are passed, the configuration is ignored.

# --limit (shorthand: -l)

Number of assets that should be processed.

# --offset (shorthand: -o)

Offset used when selecting assets to process.

# --queue (shorthand: -q)

When set the console command will not create transforms when running, but instead create queue jobs. When processing many assets, this could be a more reliable way of making sure all the transforms are run (as long as you have a reliable queue runner set up).

# Examples

In the examples below, I use the shorthand syntax after showing the full syntax in the previous example.

# Create all transforms (based on config) for images in the root of volume `images`. 
./craft imager-x/generate --volume=images

# Create all transforms for images in volume `images` - recursively. 
./craft imager-x/generate -v=images --recursive

# Create all transforms for images in volume `images` - recursively, limited to 20 assets, starting with asset 40. 
./craft imager-x/generate -v=images --recursive --limit=20 --offset=40

# Create all transforms for images in volume `images` - recursively, but create queue jobs instead. 
./craft imager-x/generate -v=images --recursive --queue

# Create transforms `sweetTransform` and `hotTransform`, 
# for all images in volume `images`
./craft imager-x/generate -v=images -r --transform=sweetTransform,hotTransform  

# Create all transforms (based on config), for all images in folder with id `59`
# in volume `images`. Note, the folder must be in this volume, or nothing will happen.
./craft imager-x/generate -v=images --folderId=59  

# Create all transforms (based on config) for all images *used* in fields 
# with handle `myImage`. 
./craft imager-x/generate --field=myImage 

# Create transforms `sweetTransform` and `hotTransform` for all images *used* in 
# fields with handle `myImage`. 
./craft imager-x/generate -f=myImage -t=sweetTransform,hotTransform

TIP

Please note that cache settings will be respected also when running console commands. Meaning, if caching is enabled, Imager will only generate the transforms that are missing, or are expired.

# Element action for generating transforms

If you have configured any volumes in your generate config, a new element action will be available for assets, called "Generate transforms". By selecting assets and running this action, all configured transforms for the volume that the assets are in, will be generated.