Jump to content

Utility function to recursively search through repeaters


MoritzLost
 Share

Recommended Posts

This doesn't warrant a full tutorial, but I wrote a little function that will recursively search through a repeater field to find the first non-empty field matching a list of fields to look for. I needed something like this to generate a fallback for SEO fields (og:image, og:description et c.). Here it is:

<?php
namespace ProcessWire;

/**
 * Find the first non-empty field out of a list of fields in a repeater or repeater matrix field.
 * Will recursively search through nested repeaters until it finds a non-empty
 * field.
 *
 * @param RepeaterPageArray $repeater The field to search through.
 * @param array $fields A list of fields the function should look for.
 * @param array|null $allowed_repeater_types All the Repeater Matrix types the function will check. Leave empty to allow all.
 * @param array|null $allowed_repeater_fields All the repeater fields the function will check recursively. Leave empty to allow all.
 * @return void
 */
function firstRecursiveRepeaterMatch(
    RepeaterPageArray $repeater,
    array $fields,
    ?array $allowed_repeater_types = null,
    ?array $allowed_repeater_fields = null
) {
    // iterate over the items of the repeater
    foreach ($repeater as $current) {
        // if the function is currently inside a repeater matrix field,
        // skip this item if it isn't one of the allowed types, unless
        // allowed_repeater_types is empty (all types allowed)
        if (
            $current instanceof RepeaterMatrixPage
            && is_array($allowed_repeater_types)
            && !in_array($current->type, $allowed_repeater_types)
        ) {
            continue;
        }
        // get all fields of the current item
        foreach ($current->getFields() as $field) {
            $name = $field->name;
            // if the current field is another repeater, check it recursively
            $fieldtype_class = $field->getFieldType()->className();
            if ($fieldtype_class === 'FieldtypeRepeater' || $fieldtype_class === 'FieldtypeRepeaterMatrix') {
                // continue with the next item if the field name isn't one of the
                // allowed repeater fields, unless allowed_repeater_fields empty (null)
                if (
                    is_array($allowed_repeater_fields)
                    && !in_array($name, $allowed_repeater_fields)
                ) {
                    continue;
                }
                $deep_search = firstRecursiveRepeaterMatch(
                    $current->get($name),
                    $fields,
                    $allowed_repeater_types,
                    $allowed_repeater_fields
                );
                // if the deep search inside the repeater
                // finds something, the function ends here
                if ($deep_search !== null) {
                    return $deep_search;
                }
            }
            // if the current field name is one of the requested
            // fields, return it's value if it isn't empty
            if (in_array($name, $fields)) {
                $value = $current->get($name);
                if (
                    // check for empty values
                    !empty($value)
                    // if the value is any wirearray, check
                    // if it has at least one item
                    && (!$value instanceof WireArray || $value->count() > 0)
                ) {
                    return $value;
                }
            }
        }
    }
    // if the function reaches this point, there is no match in the entire tree
    return null;
}

Can be used like this:

$seo_description = firstRecursiveRepeaterMatch(
	// content sections fields, a repeater matrix with multiple types
	$page->sections,

	// look for the first non-empty instance of any of those fields
	['body', 'html_basic', 'html_full'],

	// only check sections of the following types
	['section_text', 'section_columns', 'section_accordion'],

	// only check the following nested repeaters recursively
	['columns', 'accordion']
);

Wanted to share because I thought it could be useful to others. It should be easy to adjust the matching condition from non-emtpy fields to some other condition, depending on the use case ...

  • Like 8
  • Thanks 1
Link to comment
Share on other sites

Thanks a lot for this, it might be useful in the future ?

Could you please point me where I can learn about the question mark on this line?

?array $allowed_repeater_types = null,

I know it should be something about php 7.*, but I cannot find anything due to the fact that the search string "? php" leads, obviously, to wrong results on google ?

Thanks!

  • Like 3
Link to comment
Share on other sites

 Share

×
×
  • Create New...