Jump to content

Adding array_chunk support to WireArray


Robin S
 Share

Recommended Posts

PHP has a useful array_chunk function: it is used to split an array into a number of smaller arrays ('chunks') of a size you specify, which are returned to you in a new array (i.e. an array of arrays).

ProcessWire doesn't provide a method for WireArrays that is the equivalent of array_chunk, but we can add a new method for this in hook. In /site/init.php...

// Add a new 'chunk' method to WireArray, the equivalent of PHP's array_chunk
$wire->addHookMethod('WireArray::chunk', function($event) {
    $wire_array = $event->object;
    $size = $event->arguments[0];
    if( !((int) $size > 0) ) throw new WireException('WireArray::chunk requires an integer $size argument greater than zero');
    $array = array();
    $count = count($wire_array);
    for($n = 0; $n < $count; $n += $size) {
        $array[] = $wire_array->slice($n, $size);
    }
    $event->return = $array;
});

 

Now we can use this new chunk() method on any WireArray to return an array of smaller WireArrays. Remember that many array-like objects in PW are WireArrays, including PageArrays, Pageimages and Pagefiles.

An example using a PageArray of 'workshop' pages. We are running a series of workshops and there is only time for four workshops per day, so we want to divide the workshops into groups of no more than four and put each group under a heading...

// Get all workshop pages
$workshops = $pages->find("template=workshop"); // say this returns 12 pages

// Split the workshops into PageArrays of no more than 4 pages each
$chunked_workshops = $workshops->chunk(4); // an array of 3 PageArrays of 4 pages each

foreach($chunked_workshops as $key => $chunk) {

    // $key is the zero-based index of the array
    $num = $key + 1;

    // Output a heading followed by the workshop links
    echo "<h3>Day $num</h3>";
    echo $chunk->each("<p><a href='{url}'>{title}</a></p>"); // $chunk is a PageArray
    
}

 

Another example, this time using images. Say we want to divide the images into groups of three or less - maybe they are to be arranged into rows or we are giving the groups some special styling.

// Say this page's 'images' field holds 8 images
// Split the images into Pageimages objects of no more than 3 images each
// 8 does not divide evenly by 3 so the last Pagesimages object will contain only 2 images
$chunked_images = $page->images->chunk(3);

foreach($chunked_images as $chunk) {
    echo "<div class='image-group'>";

    // $chunk is a Pageimages object
    foreach($chunk as $image) {
        echo "<img src='{$image->size(300, 300)->url}'>";
    }

    echo "</div>";
}

 

  • Like 13
Link to comment
Share on other sites

On 2017-5-7 at 9:47 AM, Zeka said:

For sure it would be a great addition to WireArray API.

I do not think too many functions/methods should be implemented in the core, especially this unique one, although I get the usefulness of it as a shortcut.

BTW, here's the docs of $wire->addHookMethod with another example

https://processwire.com/api/ref/wire/add-hook-method/

And for those who want to get more OOP:

https://processwire.com/docs/tutorials/using-custom-page-types-in-processwire/

  • Like 1
Link to comment
Share on other sites

  • 4 months later...
  • 1 year later...

An update to the hook in the first post for PW v3.0.117 or greater.

// Add a new 'chunk' method to WireArray, the equivalent of PHP's array_chunk
$wire->addHookMethod('WireArray::chunk', function(HookEvent $event) {
	/* @var WireArray $wire_array */
	$wire_array = $event->object;
	$size = $event->arguments(0);
	if( !((int) $size > 0) ) throw new WireException('WireArray::chunk requires an integer $size argument greater than zero');
	$chunks = new WireArray();
	for($n = 0; $n < count($wire_array); $n += $size) {
		$chunks->add($wire_array->slice($n, $size));
	}
	$event->return = $chunks;
});

This returns the chunks as a WireArray so you have the option of using WireArray methods.

So if needed you could do something like:

$items = $pages->find("template=foo");
$chunks = $items->chunk(5);
$three_random_chunks = $chunks->find("limit=3, sort=random");

 

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

  • 8 months later...
On 12/1/2018 at 10:43 PM, Robin S said:

An update to the hook in the first post for PW v3.0.117 or greater.


// Add a new 'chunk' method to WireArray, the equivalent of PHP's array_chunk
$wire->addHookMethod('WireArray::chunk', function(HookEvent $event) {
	/* @var WireArray $wire_array */
	$wire_array = $event->object;
	$size = $event->arguments(0);
	if( !((int) $size > 0) ) throw new WireException('WireArray::chunk requires an integer $size argument greater than zero');
	$chunks = new WireArray();
	for($n = 0; $n < count($wire_array); $n += $size) {
		$chunks->add($wire_array->slice($n, $size));
	}
	$event->return = $chunks;
});

This returns the chunks as a WireArray so you have the option of using WireArray methods.

So if needed you could do something like:


$items = $pages->find("template=foo");
$chunks = $items->chunk(5);
$three_random_chunks = $chunks->find("limit=3, sort=random");

 

Is it possible to get this working for a repeater? I seem to get the error Exception: Method RepeaterPageArray::chunk does not exist or is not callable in this context

Link to comment
Share on other sites

11 hours ago, a-ok said:

Is it possible to get this working for a repeater?

A RepeaterPageArray is a type of WireArray so it should work as is. And it's working for me so double-check your code (e.g. invisible characters from forum copy/paste).

2019-08-22_093500.png.3a74282da8a9dad4cb4f841448363b1c.png

  • Like 1
Link to comment
Share on other sites

  • 3 months later...
4 hours ago, maxf5 said:

Isn't there this onboard method?

https://processwire.com/api/ref/wire-array/slices/

That's not the same thing. They are basically the opposite of each other.

The hook method above allows for a specified number of items in each sub-WireArray, with the number of resulting WireArrays not specified.

WireArray::slices() allows for a specified number of sub-WireArrays, with the number of items in each sub-WireArray not specified.

To illustrate:

2019-12-21_091231.png.e91eaea1fd87be1cecf448e74ab9e213.png

 

  • Like 2
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
 Share

  • Recently Browsing   0 members

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