Crops API

How to check for, retrieve, and output crops in your template code, including lazy generation and the crop-or-fallback pattern.

Each crop in MediaHub is stored as its own ProcessWire page with its own ID, image file, preset key, and crop coordinates. This makes crops independently selectable in page reference fields and queryable in template code.

How crops are created

Crops can be created in three ways:

  1. Manually in the crop editor: an editor opens an asset's detail page, clicks Crop image, selects a preset, positions the crop, and saves. This gives the editor full control over the crop area.
  2. Automatically via inline crop badges: when a MediaHub field has crop presets configured, the page editor auto-generates a centred crop for each preset at render time using ensureCropImage(). The editor sees a filled badge and can click it to adjust the position.
  3. Programmatically via the API: calling ensureCropImage() in template code creates a centred crop on first call and returns it on subsequent calls.

In all three cases the result is the same: a real crop page that appears in the asset's Crops table, is independently selectable in page reference fields, and can be adjusted by an editor at any time. ensureCropImage() never overwrites a crop that an editor has manually positioned.

Crops are accessed through the parent asset's API. The crop page itself also has convenience methods for its own image and metadata.

Checking for crops

$asset->hasCrops();              // does this asset have any crops?
$asset->hasCrop('square');       // does a "square" crop exist?
$asset->hasCrop('16_9');         // does a "16:9" crop exist?
$asset->cropCount();             // how many crops?

Getting a crop image

cropImage() returns the crop's Pageimage directly, skipping the intermediate crop page. This is the most common way to use crops in template output.

// Get the square crop image at its original dimensions
$img = $asset->cropImage('square');

// Get it resized to specific dimensions
$img = $asset->cropImage('square', 400, 400);

// Resize by width only
$img = $asset->cropImage('16_9', 1200);

// Resize by height only
$img = $asset->cropImage('4_5', null, 600);

Returns null if no crop exists for that preset, or if the asset is not an image.

Lazy crop generation with ensureCropImage()

ensureCropImage() returns the existing crop if one exists, or auto-generates a mathematically centred crop on first call. This is the same mechanism that powers inline crop badges in the page editor: when a field has crop presets configured, MediaHub calls ensureCropImage() at render time to guarantee every badge is filled before the editor sees the page.

You can also call it directly in template code to guarantee a crop always exists for front-end output:

// Returns the existing square crop, or creates a centred one
$img = $asset->ensureCropImage('square', 400, 400);

// Works with any preset that has a defined ratio
$img = $asset->ensureCropImage('16_9', 1200, 675);

Key behaviours:

  • The generated crop is a real crop page, identical to one an editor would create manually. It appears in the asset's Crops table in the admin, is independently selectable in the picker, and can be adjusted by an editor at any time.
  • Never overwrites an editor-curated crop. If an editor has already positioned the crop manually, that crop is returned unchanged.
  • Subsequent calls return the existing crop instantly without regenerating.
  • Returns null for freeform presets (no fixed ratio to calculate a centred crop).
  • Returns null if the asset is not an image or GD cannot process the file format.

Crop-or-fallback pattern

A common template pattern: use the crop if it exists, otherwise fall back to the source image resized to the same dimensions.

$asset = $page->hero_image;
if ($asset && $asset->id) {
    // Try the 16:9 crop first, fall back to a sized version of the original
    $img = $asset->cropImage('16_9', 1400, 788)
        ?: $asset->image()->size(1400, 788);
    echo "<img src='{$img->url}' alt='{$asset->altText()}'>";
}

Or use ensureCropImage() to guarantee a crop always exists:

$img = $asset->ensureCropImage('16_9', 1400, 788);
if ($img) {
    echo "<img src='{$img->url}' alt='{$asset->altText()}'>";
}

Working with crop pages directly

If you need the crop page itself (not just its image), use getCrop() or getCropsByPreset():

// Get the first crop page for a preset
$crop = $asset->getCrop('square');

if ($crop->id) {
    echo $crop->presetKey();      // "square"
    echo $crop->image()->url;     // URL of the crop image
    echo $crop->fileUrl();        // same as above
    echo $crop->altText();        // delegated from the master asset
}

// Get all crops for a preset (there may be more than one)
$crops = $asset->getCropsByPreset('16_9');

// Get all crops regardless of preset
$allCrops = $asset->crops();

Crop page methods

Crop pages (PkdMediahubCropPage) have their own API. Most metadata methods delegate to the master asset, so alt text, tags, and the about note stay consistent.

MethodReturnsDescription
$crop->image()Pageimage|nullThe crop image.
$crop->fileUrl()stringURL of the crop image.
$crop->sized(400, 400)Pageimage|nullResized crop image.
$crop->imgTag(400, 400)stringHTML <img> tag with alt text from the master asset.
$crop->master()PageThe source asset page this crop was derived from.
$crop->presetKey()stringThe preset key (e.g. "square", "16_9").
$crop->cropData()array|nullRaw crop coordinates: x, y, width, height, imagePixels.
$crop->altText()stringDelegated from the master asset.
$crop->tags()PageArrayDelegated from the master asset.
$crop->isImage()boolAlways true.
$crop->mimeType()stringDerived from the crop image's extension.
$crop->createdById()intUser ID of who created the crop.
$crop->createdAtIso()stringISO 8601 timestamp of when the crop was created.

Selecting crops in the page editor

When an editor clicks Choose on a MediaHub field, assets with crops show a crop badge on their tile. Clicking the tile opens a crop panel where the editor can select the original source image or any specific crop. Each selection is a distinct page reference, so a single field can hold a mix of source images and crops.

See Adding to Templates for how to configure inline crop badges on a field.

Built-in crop presets

Nine presets are available by default. Use the key value in template code:

KeyLabelRatio
freeformFreeformNo constraint
originalOriginalMatches the image's natural ratio
squareSquare1:1
16_916:916:9
4_54:54:5
5_75:75:7
4_34:34:3
3_53:53:5
3_23:23:2

Custom crop presets

You can create your own presets in Modules → Configure → MediaHub → Crop Presets. Each preset needs a Label, a Key (used in template code), and an optional Ratio in W:H format. Leave the ratio blank for a freeform crop.

Custom presets work identically to built-in ones in the API: use their key with cropImage(), ensureCropImage(), hasCrop(), and getCrop().

// A custom "hero_banner" preset at 16:5
$img = $asset->ensureCropImage('hero_banner', 1600, 500);

// A custom "instagram_story" preset at 9:16
$story = $asset->cropImage('instagram_story', 1080, 1920);

Grouping presets

Each custom preset can be assigned to a Group (e.g. "Social Media", "Editorial", "Front Page"). Presets with the same group name appear together in a collapsible section in the crop editor sidebar, making it easy to find the right preset when you have many. Groups are purely organisational and have no effect on the API.

See Configuration for the full list of built-in presets and the custom preset form.