Jump to content

Weekly update – 12 January 2024


Recommended Posts

This week on the dev branch are some fixes and improvements, but we'll likely wait another week before bumping the version number up. Some new hooks have been added to $pages, including moveReady(), restoreReady() and renameReady(), to accompany the existing moved(), restored() and renamed() hooks. There was also some refactoring with the way that some hooks are called from $pages to improve their reliability and cover some edge cases where they might have not been called before. See the dev branch commit log for more. 

The biggest addition this week is likely the newly added support for custom page classes for repeater items. This was added to respond to a feature request initiated by @thetuningspoon and @Jonathan Lahijani let me know about earlier in the week. Here's how it works. If you have a repeater field named "menu_items" then you could create a class named MenuItemsRepeaterPage in /site/classes/MenuItemsRepeaterPage.php, and it would use your custom class rather than the regular RepeaterPage. Though note it must extend RepeaterPage. 

<?php namespace ProcessWire;
class MenuItemsRepeaterPage extends RepeaterPage {
  // ...

This works with RepeaterMatrix and FieldsetPage as well, since both are derived from regular repeaters. But since both have their own Page classes you'd want to extend their page classes rather than RepeaterPage. In the case of RepeaterMatrix, it uses a class called RepeaterMatrixPage. So if your Matrix field is named "hello_world" then you'd create /site/classes/HelloWorldRepeaterMatrixPage.php

<?php namespace ProcessWire;
class HelloWorldRepeaterMatrixPage extends RepeaterMatrixPage {}

If you want a custom class for your FieldsetPage field named "seo" then you could create /site/classes/SeoFieldsetPage.php:

<?php namespace ProcessWire;
class SeoFieldsetPage extends FieldsetPage {}

Let's say that you want to use a custom class without using the naming convention and/or file(s) above. You can tell the fieldtype what class to use for its item(s) like this:

$field = $fields->get('your_repeater_field'); 
$field->type->setCustomPageClass($field, 'MyCustomRepeaterPageClass'); 

In the example above, MyCustomRepeaterPageClass would extend RepeaterPage. You'd probably want to do this during the "init" or "ready" state, or at least sometime before you load any items from your Repeater (or Matrix or FieldsetPage) field. If your custom class is not in the ProcessWire namespace, then you'd want to include the namespace in your call, i.e. setCustomPageClass($field, "\\MyNamespace\\MyCustomClass"); If your custom page class is already in /site/classes/, or some other path where it can be autoloaded, then the include_once() line isn't necessary. (API ref pages for getCustomPageClass and setCustomPageClass).

Big thanks this week to typneun Designagentur (https://typneun.de/) for posting more than 20 awesome new sites to our ProcessWire sites directory! That made my day. Thanks for reading, more next week, and have a great weekend! 

  • Like 14
  • Thanks 4
Link to comment
Share on other sites

Hi @ryan,

Thanks for this update.

One easy to overlook but important missing part of the repeater update that was made is what happens if you edit a repeater item directly.  Assume we have a repeater field named 'books' and you edit the repeater item page by going to /admin/repeaters/books/(for-page)/repeater-item-page

When editing it directly, the page to be edited simply gets the RepeaterPage class instead of BooksRepeaterPage class.

I managed to hack-fix this by doing this in /wire/core/Templates.php:

		// determine if custom class available (3.0.152+)
		if($usePageClasses) {
			// generate a CamelCase name + 'Page' from template name, i.e. 'blog-post' => 'BlogPostPage'
			$className = ucwords(str_replace(array('-', '_', '.'), ' ', $template->name));
			// *** *** hack fix: if we are editing a page with a template that starts with 'repeater_', assign the correct page class! *** ***
			if(str_starts_with($template->name, 'repeater_')) {
				$className = __NAMESPACE__ . "\\" . str_replace(' ', '', $className) . 'RepeaterPage';
				$className = str_replace('ProcessWire\Repeater', 'ProcessWire\\', $className);
			} else {
				$className = __NAMESPACE__ . "\\" . str_replace(' ', '', $className) . 'Page';
			if(class_exists($className) && wireInstanceOf($className, $corePageClass)) {
				$pageClass = $className;

Can what I described be supported as well?

  • Like 5
Link to comment
Share on other sites

This is so great!! Thx @ryan and @Jonathan Lahijani and @thetuningspoon for suggesting this.

I've just implemented first class support for this in RockMigrations and it comes in really handy for the development of RockCommerce.

There I have a repeater where clients can add Accessories for every Product. Instead of being RepeaterPages they are now \RockCommerce\Accessory objects which is a lot nicer and makes the code a lot cleaner and I can get rid of many hooks and now my IDE actually understands my code 🤩


For everybody using RockMigrations all you have to do to make that work is to place your file in /site/modules/YourModule/repeaterPageClasses/Accessory.php and add the field constant to that class so that RockMigrations knows which field to apply the pageclass to:


namespace RockCommerce;

use ProcessWire\RepeaterPage;

class Accessory extends RepeaterPage
  const field = "rc_product_accessories";

While working on this I have also improved autoloading of custom page classes and autoloading of regular PHP classes or traits:

  • MyModule/classLoader will auto-load regular PHP classes or traits
  • MyModule/pageClasses will auto-load PW custom pageclasses
  • MyModule/repeaterPageClasses will auto-load PW custom repeater classes (and trigger init() and ready() for them)

Extensive docs are already on the dev branch (https://github.com/baumrock/RockMigrations/tree/dev/docs/classloader) and will be merged to main begin of next month 🚀

On 1/13/2024 at 5:58 PM, Jonathan Lahijani said:

When editing it directly, the page to be edited simply gets the RepeaterPage class instead of BooksRepeaterPage class.

This is also the case when using $pages->newPage(...) - not sure if that's intentional @ryan ?

  • Like 4
Link to comment
Share on other sites

@Jonathan Lahijani Good idea, I'll add an update to make it use it for that case (and the one Bernhard mentioned). It'll likely be the same location as what you've suggested, except it'll get it from FieldtypeRepeater::getCustomPageClass() instead, since that already has all the logic to determine what class to use. 

  • Like 3
Link to comment
Share on other sites

  • 4 weeks later...
On 1/14/2024 at 4:22 PM, bernhard said:
On 1/13/2024 at 5:58 PM, Jonathan Lahijani said:

When editing it directly, the page to be edited simply gets the RepeaterPage class instead of BooksRepeaterPage class.

This is also the case when using $pages->newPage(...) - not sure if that's intentional @ryan ?

Hey @ryan today I also realised that when using $pages->get(123) the page returned is also a RepeaterPage and not a MyFooPage. Is this intentional?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...