Template API
How to work with MediaHub assets in your PHP templates. Getting images, resizing, type checking, metadata, tags, collections, crops, and common output patterns.
A MediaHub field is a standard ProcessWire page reference field. It returns asset pages (or crop pages) rather than files directly. For images, the one extra step is calling ->image() to get a Pageimage; after that, every native PW image method works identically. For non-image assets, use ->file() for the Pagefile or ->fileUrl() for the download URL.
Every method documented below comes from MediaHub's custom page classes: PkdMediahubAssetPage for assets, PkdMediahubCropPage for crops, and MediaHubPageArray for multi-value fields. These extend ProcessWire's Page and PageArray classes, so all standard PW page methods remain available alongside the MediaHub-specific ones.
Coming from ProcessWire's image API
If you have used ProcessWire's native image fields before, some familiar patterns will not behave as expected with MediaHub. The reason is architectural: MediaHub assets are pages, not files. The one-line summary is that where you previously called methods directly on a Pageimage, you first call ->image() to get one, and then everything works identically.
The comparisons below cover the cases most likely to catch you out.
| PW image API | $page->images->find('tags=gallery') |
|---|---|
| What happens | Returns nothing. ProcessWire image tags are a plain text string on each image. MediaHub does not use that system. It has two of its own: display tags (per-reference, set in the page editor) and library tags (global labels on the asset, built on PW page references). Neither is selector-filterable. |
| MediaHub equivalent | Display tags (role on this page):$page->gallery->findTag('gallery')Library tags (global label on the asset): $asset->hasTag('gallery') |
| PW image API | $page->gallery->first()->url |
|---|---|
| What happens | Returns the page URL, not the image file URL. first() returns a PkdMediahubAssetPage, and Page::url is the page's path in the site tree. |
| MediaHub equivalent | $page->gallery->first()->image()->url |
| PW image API | $page->gallery->first()->description |
|---|---|
| What happens | Returns empty string. MediaHub asset pages have no field named description; ProcessWire returns empty for undefined page properties. |
| MediaHub equivalent | $page->gallery->first()->altText() or $page->gallery->description($asset) for per-reference overrides |
| PW image API | $page->gallery->first()->size(400, 300) |
|---|---|
| What happens | Throws an exception. size() exists on Pageimage, not on Page. |
| MediaHub equivalent | $page->gallery->first()->image()->size(400, 300) or $asset->sized(400, 300) |
In full:
// ProcessWire native image field
$page->images->find('tags=gallery'); // works — filters by image tag string
$page->images->first()->url; // works — image file URL
$page->images->first()->description; // works — image description string
$page->images->first()->size(400, 300); // works — returns sized Pageimage
// MediaHub field — adjusted equivalents
$page->gallery->findTag('gallery'); // display tags (per-reference, set in page editor)
$page->gallery->first()->image()->url; // go through image() to get the file URL
$page->gallery->first()->altText(); // asset's global alt text
$page->gallery->first()->image()->size(400, 300); // image() then size()
Once you have a Pageimage from ->image(), every ProcessWire image method works exactly as you would expect: size(), width(), height(), srcset(), and so on.
Single value vs multi-value
How you configured the field determines what it returns:
| Field type | Returns | Example |
|---|---|---|
| Single page | PkdMediahubAssetPage (or NullPage) | $page->hero_image |
| Multiple pages (PageArray) | MediaHubPageArray | $page->gallery |
Single value
$asset = $page->hero_image;
if ($asset && $asset->id) {
$img = $asset->image();
echo "<img src='{$img->url}' alt='{$asset->altText()}'>";
}
Multi-value
foreach ($page->gallery as $asset) {
$img = $asset->image();
if (!$img) continue;
echo "<img src='{$img->url}' alt='{$asset->altText()}'>";
}
Getting the image
| Method | Returns | Description |
|---|---|---|
$asset->image() | Pageimage|null | The primary image. Null if the asset is not an image (e.g. a PDF). |
$asset->file() | Pagefile|null | The primary file. Works for all asset types: images, documents, audio, video. For images, checks the image field first, then falls back to the file field. |
$asset->fileUrl() | string | URL of the primary file, or empty string. |
Use image() when you know you're working with images. Use file() when the asset could be any type.
Resizing and the PW image API
Because image() returns a standard Pageimage, every ProcessWire image method works as expected:
$img = $asset->image();
// Resize to exact dimensions
$sized = $img->size(800, 600);
// Resize by width only (height auto)
$wide = $img->width(1200);
// Resize by height only (width auto)
$tall = $img->height(400);
// Responsive srcset
echo $img->srcset('320, 640, 960, 1280');
There is also a convenience method on the asset page itself:
// Equivalent to $asset->image()->size(800, 600)
$sized = $asset->sized(800, 600);
HTML output helper
imgTag() generates a complete <img> element with alt text, optional sizing, and extra HTML attributes:
// Basic: <img src="..." alt="...">
echo $asset->imgTag();
// Sized: <img src="..." alt="..." width="800" height="600">
echo $asset->imgTag(800, 600);
// With extra attributes
echo $asset->imgTag(800, 600, [
'class' => 'hero-img',
'loading' => 'lazy',
]);
Alt text is taken from the asset's alt text field, falling back to the page title. Returns an empty string for non-image assets.
Type checking
MediaHub supports images, documents, audio, and video. These methods check the asset's MIME type:
| Method | Returns true when |
|---|---|
$asset->isImage() | MIME starts with image/ |
$asset->isDocument() | MIME starts with application/ |
$asset->isVideo() | MIME starts with video/ |
$asset->isAudio() | MIME starts with audio/ |
// Render different markup by type
foreach ($page->media as $asset) {
if ($asset->isImage()) {
echo "<img src='{$asset->image()->width(600)->url}' alt='{$asset->altText()}'>";
} elseif ($asset->isDocument()) {
echo "<a href='{$asset->fileUrl()}'>{$asset->title} ({$asset->extension()})</a>";
} elseif ($asset->isVideo()) {
echo "<video src='{$asset->fileUrl()}' controls></video>";
}
}
Additional introspection methods:
| Method | Returns | Example |
|---|---|---|
$asset->mimeType() | string | "image/jpeg" |
$asset->extension() | string | "jpg" |
Metadata
| Method | Returns | Description |
|---|---|---|
$asset->altText() | string | The asset's global alt text, or empty string. |
$asset->about() | string | The internal admin note (not for HTML output). |
$asset->title | string | The asset's title (standard PW page property). |
Per-reference description
When the same image appears on different pages, you may need a different caption or alt text per page. Two approaches:
// Via the field (multi-value fields)
// Returns the page-level override, or falls back to altText()
$caption = $page->gallery->description($asset);
// Via the asset directly (works for any field type)
$caption = $asset->descriptionFor($page);
// Specify the field explicitly
$caption = $asset->descriptionFor($page, 'gallery');
Full details: Per-Reference Metadata.
Tags
MediaHub has two separate tagging systems. Library tags are used by editors to sort and manage assets in the MediaHub library. Display tags are what you might be more familiar with as a developer: per-reference labels like "hero" or "thumbnail" that you assign to images within a field on a specific page, then query in your template code. Both are explained below.
Library tags describe what an asset is. Assigned to an asset on its detail page in the MediaHub library, they exist to help library users organise assets. Tags are automatically added to a dropdown filter called Tags in the library toolbar and are used for convenience of library organisation, filtering and display in the back-end. Because they live on the asset itself, they are global: tag an image "landscape" and that tag is true on every page that references it. These are a MediaHub concept built on ProcessWire page references. They are not the same as ProcessWire's native image tags, which are plain text strings on each image file. If you have previously tagged images to control front-end display, you are probably looking for Display tags described below. As a developer you might be used to an image field having several images but tagging a subset "hero" or "preview" to pick them out in template code. That is what display tags are for.
Display tags describe the role an asset plays on a specific page. Set in the page editor when you add an image to a MediaHub field, they are per-reference: the same asset can be tagged "hero" on your About page and "thumbnail" on your Blog listing. The asset itself is unchanged.
For example: a photo of your office gets the library tag "interior" in the MediaHub admin. On the About page you add it to the gallery field and display-tag it "hero". On the Contact page you add the same photo and display-tag it "secondary". Same asset, same library tag, different display tag on each page.
Library tag methods are below. Display tag methods are in the Display tags section further down this page.
| Method | Returns | Description |
|---|---|---|
$asset->tags() | PageArray | All tags assigned to this asset. |
$asset->hasTag('landscape') | bool | Check by title or page ID. |
// Filter by tag
foreach ($page->gallery as $asset) {
if ($asset->hasTag('featured')) {
echo $asset->imgTag(1200, 600);
}
}
Collections
Collections (folders, albums, galleries, depending on your admin label) group assets for organisation. An asset can belong to multiple collections.
| Method | Returns | Description |
|---|---|---|
$asset->collections() | PageArray | All collections this asset belongs to. |
$asset->inCollection('branding') | bool | Check by name, title, or page ID. |
$asset->addToCollection($collection) | void | Add to a collection. Pass a Page or page ID. Saves the asset. |
$asset->removeFromCollection($collection) | void | Remove from a collection. Saves the asset. |
Display tags (per-field)
Display tags are separate from library tags. They are per-reference labels assigned in the page editor for controlling front-end output (e.g. marking one image as "hero" on a specific page). These methods are available on multi-value fields (MediaHubPageArray).
| Method | Returns | Description |
|---|---|---|
$page->gallery->getTag('hero') | Page|null | First asset with this display tag. |
$page->gallery->findTag('diagram') | PageArray | All assets with this display tag. |
$page->gallery->getDisplayTags($asset) | string[] | All display tags for a specific asset. |
$page->gallery->hasDisplayTag($asset, 'hero') | bool | Check if an asset has a specific display tag. |
// Use a display tag to pick the hero image from a gallery
$hero = $page->gallery->getTag('hero');
if ($hero) {
echo $hero->imgTag(1400, 600, ['class' => 'hero']);
}
Full details: Per-Reference Metadata.
Crops
Each crop is a separate page with its own image file and preset key. Crops are accessed through the parent asset.
| Method | Returns | Description |
|---|---|---|
$asset->crops() | PageArray | All crop pages for this asset. |
$asset->cropCount() | int | Number of crops. |
$asset->hasCrops() | bool | Whether any crops exist. |
$asset->hasCrop('square') | bool | Whether a crop exists for a specific preset. |
$asset->getCrop('square') | Page | The crop page for a preset (or NullPage). |
$asset->getCropsByPreset('square') | PageArray | All crop pages for a preset (there may be more than one). |
$asset->cropImage('square') | Pageimage|null | The crop's image directly, skipping the intermediate page. |
$asset->cropImage('16_9', 800, 450) | Pageimage|null | Crop image resized to specific dimensions. |
$asset->ensureCropImage('square') | Pageimage|null | Like cropImage(), but auto-generates a centred crop if none exists. The generated crop is a real crop page: it appears in the asset's Crops table in the admin and can be adjusted by an editor. Never overwrites an editor-curated crop. |
Full details: Crops API.
Common patterns
Hero image with crop fallback
$asset = $page->hero_image;
if ($asset && $asset->id) {
// Use the 16:9 crop if it exists, otherwise fall back to the original
$img = $asset->cropImage('16_9', 1400, 788) ?: $asset->image()->size(1400, 788);
$alt = $asset->descriptionFor($page) ?: $asset->altText();
echo "<img src='{$img->url}' alt='{$alt}'>";
}
Gallery with lazy crop generation
foreach ($page->gallery as $asset) {
// ensureCropImage auto-generates a centred square crop on first call
$img = $asset->ensureCropImage('square', 400, 400);
if (!$img) continue;
$desc = $page->gallery->description($asset);
echo "<figure>";
echo " <img src='{$img->url}' alt='{$desc}'>";
if ($desc) echo " <figcaption>{$desc}</figcaption>";
echo "</figure>";
}
Download link for a document
$doc = $page->downloads->first();
if ($doc && $doc->isDocument()) {
$file = $doc->file();
echo "<a href='{$file->url}'>Download {$doc->title} ({$doc->extension()}, " .
number_format($file->filesize / 1024) . " KB)</a>";
}
Mixed media output
foreach ($page->media as $asset) {
if ($asset->isImage()) {
echo $asset->imgTag(800, 600, ['loading' => 'lazy']);
} elseif ($asset->isVideo()) {
echo "<video src='{$asset->fileUrl()}' controls preload='metadata'></video>";
} elseif ($asset->isAudio()) {
echo "<audio src='{$asset->fileUrl()}' controls></audio>";
} elseif ($asset->isDocument()) {
echo "<a href='{$asset->fileUrl()}'>{$asset->title}</a>";
}
}