ProcessWire Page Parser
The Page Parser is a flexible tool for converting ProcessWire pages into structured data, ready for API responses. It supports all ProcessWire core field types and offers extensive configuration and hook options.
- Parse single pages or arrays of pages
- Select specific fields to include or exclude
- Optionally include child pages, with depth and selector controls
- Use hooks to customize parsing at various stages (pages, fields, images, files)
Basic usage example
TIP
Note that toArray() or toResponse() starts the actual parsing. Any configuration must be done before calling one of these methods.
use PwJsonApi\PageParser;
$output = (new PageParser())
->input($this->wire->pages->find('template=basic-page'))
->fields('title', 'body')
->toArray();Configuration
Configure the parser using the configure() method, which allows you to control recursion, child page selection, output format, and more.
$parser->configure(function ($config) {
// Recursively parse child pages? (default: false)
$config->parseChildren = false;
// Selector for child pages (default: '')
$config->childrenSelector = '';
// Key name for child pages (default: '_children')
$config->childrenKey = '_children';
// Recursively parse children of page field references? (default: false)
$config->parsePageReferenceChildren = false;
// Maximum depth for recursive parsing (default: 3)
$config->maxDepth = 3;
// Output full file URLs (default: true)
$config->fullFileUrls = true;
// Parse custom fields of files? (default: false)
$config->parseFileCustomFields = false;
// Key name for custom fields of files (default: '_custom_fields')
$config->fileCustomFieldsKey = '_custom_fields';
});Input
Specify the pages to parse with input(). Accepts a Page or PageArray.
$parser->input($this->wire->pages->find('template=basic-page'));Property selection
By default, id and name will be included.
Include properties
Use properties() to include additional Page properties beyond the defaults.
$parser->properties('url', 'numChildren');Exclude properties
Use excludeProperties() to omit certain properties.
$parser->excludeProperties('name');Field Selection
Include fields
By default, all fields of the input pages will be included. Use fields() to include only specific fields.
TIP
If your input contains pages with different templates, you can still specify all fields here. If the parsed template does not have the given field, it will be ignored.
$parser->fields('title', 'body');Exclude fields
Use excludeFields() to omit certain fields.
$parser->excludeFields('body');Child pages
Enable child page parsing via parser configuration. Fine-tune with selectors, depth, and hooks.
Output
As an array
toArray() returns the parsed data as an array.
$result = (new PageParser())
->input($this->wire->pages->find('template=basic-page'))
->fields('title', 'body')
->toArray();As a response
toResponse() returns a Response object.
$response = (new PageParser())
->input($this->wire->pages->find('template=basic-page'))
->fields('title', 'body')
->toResponse();As a paginated response ^2.1
toPaginatedResponse() returns a PaginatedResponse with pagination metadata read from the PageArray input. The input must be a PageArray — an InvalidArgumentException is thrown otherwise.
$response = (new PageParser())
->input($this->wire->pages->find('template=basic-page, start=0, limit=10'))
->fields('title', 'body')
->toPaginatedResponse();Hooks
Hooks allow you to modify data before and after parsing pages, fields, images, and files. This enables advanced customization, such as resizing images, altering field values, or adding extra data.
TIP
Hooks must be defined before the toArray() or toResponse() call.
TIP
All hook callbacks receive an $args->depth property (int) indicating the current recursion depth (starting at 1). Use this to customize behavior at different nesting levels.
Skip ^2.1
The skip() method is available in hookBeforePageParse(), hookBeforePropertyParse(), and hookBeforeFieldParse() callbacks. When called, the item is omitted from the output entirely — the key will not appear (not even as null). This also provides a performance benefit, as recursive fields are not parsed when skipped.
// Skip unpublished pages
$parser->hookBeforePageParse(function ($args) {
if ($args->page->isUnpublished()) {
$args->skip();
}
});
// Skip the body field at depth > 1
$parser->hookBeforeFieldParse(function ($args) {
if ($args->field->name === 'body' && $args->depth > 1) {
$args->skip();
}
});
// Skip the id property
$parser->hookBeforePropertyParse(function ($args) {
if ($args->propertyName === 'id') {
$args->skip();
}
});hookBeforePageParse()
Use this to modify the source page.
$parser->hookBeforePageParse(function ($args) {
// Get only the first three tags
$args->page->tags = $args->page->tags->slice(0, 3);
// Do some other nasty modifications to the source page
$args->page->title = 'Remember that it was your idea...';
// Reconfigure parser for child pages
$args->parser->fields('title');
});hookAfterPageParse()
Use this to modify the final parsed data of the page, add extra keys etc.
$parser->hookAfterPageParse(function ($args) {
$args->parsedPage['_foo'] = 'foo';
if ($args->page->template->name === 'bar') {
$args->parsedPage['_bar'] = 'bar';
}
});hookBeforePropertyParse()
Use this to modify property value before it's parsed.
TIP
This hook applies only to page properties, such as id. Use the hookBeforeFieldParse() hook for fields.
$parser->hookBeforePropertyParse(function ($args) {
if ($args->propertyName === 'id') {
$args->value = 99999;
}
});hookAfterPropertyParse()
Use this to modify final parsed output of property.
TIP
This hook applies only to page properties, such as id. Use the hookAfterFieldParse() hook for fields.
$parser->hookAfterPropertyParse(function ($args) {
if ($args->propertyName === 'id') {
$args->parsedValue = 99999;
}
});hookBeforeFieldParse()
Use this to access field value, source field, source page, and another PageParser instance, which will be used for fields with Page or PageArray as a value.
TIP
At first glance, this hook seems like the right place to resize images. However, since the image field may contain multiple images, resizing images here would force you to re-map the entire Pageimages object. Instead, use the dedicated hookBeforeImageParse() hook.
$parser->hookBeforeFieldParse(function ($args) {
// Reconfigure parser for the field "tags" of
// "basic-page" template.
if (
$args->page->template->name === 'basic-page' &&
$args->field->name === 'tags'
) {
$args->parser->fields('title');
}
});hookAfterFieldParse()
Use this to modify final parsed output of field.
$parser->hookAfterFieldParse(function ($args) {
if ($args->field->name === 'title') {
$args->parsedValue = ucfirst($args->parsedValue);
}
});hookBeforeImageParse()
Use this to modify the source image. This hook will run for every image, regardless of whether the field contains multiple images. This is a good place to resize images to avoid serving them at their original size.
PageParser instance for custom fields of files is exposed in $args.
$parser->hookBeforeImageParse(function ($args) {
if ($args->field->name === 'my_image_field') {
// Resize image
$args->image = $args->image->size(300, 200);
// Reconfigure parser for custom fields
$args->parser->fields('title');
}
});hookAfterImageParse()
Use this to modify final parsed output of an image. The original (unmodified) image is available via $args->originalImage, which is useful for generating thumbnails or alternative sizes from the source image.
$parser->hookAfterImageParse(function ($args) {
if ($args->field->name === 'my_image_field') {
// Even though the parser in $args is primarily used for
// custom fields of the image, you can use it to create thumbnails.
$args->parsedImage['_thumbnails'] = [
'200x200' => $args->parser->parseImage(
$args->originalImage->size(200, 200),
),
'100x100' => $args->parser->parseImage(
$args->originalImage->size(100, 100),
),
];
}
});hookBeforeFileParse()
Use this to modify the source file. This hook will run for every file, regardless of whether the field contains multiple files.
PageParser instance for custom fields of files is exposed in $args.
$parser->hookBeforeFileParse(function ($args) {
if ($args->field->name === 'my_file_field') {
// Reconfigure parser for custom fields
$args->parser->fields('title');
}
});hookAfterFileParse()
Use this to modify final parsed output of a file.
$parser->hookAfterFileParse(function ($args) {
if ($args->field->name === 'my_file_field') {
$args->parsedFile['_foo'] = 'bar';
}
});