Jump to content

Fun with hooks: PageArray::groupBy


BitPoet
 Share

Recommended Posts

Not sure where the best place for this is, but I felt like sharing a little snippet for site/ready.php I wrote that creates a PageArray::groupBy method:

<?php

/**
 *
 * Adds a groupBy method to all PageArray instances.
 *
 * Returns a nested array, with the values of the page properties whose names
 * were passed as arguments as the keys.
 *
 * Usage:
 * ======
 *
 * $grouped = $mypagearray->groupBy(field1 [, field2 ...] [, mutator_function]);
 * or
 * $grouped = $mypagearray->groupBy(mutator_function);
 *
 * Example:
 * ========
 *
 * $mypagearray = $pages->find("template=blog-post, sort=year, sort=month");
 * $grouped = $mypagearray->groupBy("year", "month");
 *
 * foreach($grouped as $year => $monthgroup) {
 *    echo "<div class='year'><h2>$year</h2>" . PHP_EOL;
 *    echo "\t<ul class='month'>" . PHP_EOL;
 *
 *    foreach($monthgroup as $month => $mypages) {
 *        echo "\t\t<li><h3>$month</h3>" . PHP_EOL;
 *
 *        echo "\t\t\t<ul class='post'>" . PHP_EOL;
 *
 *        foreach($mypages as $post) {
 *            echo "\t\t\t\t<li><a href='{$post->url}'>{$post->title}</a></li>" . PHP_EOL;
 *        }
 *
 *        echo "\t\t\t</ul>\n" . PHP_EOL;
 *        echo "\t\t</li>\n" . PHP_EOL;
 *    }
 *
 *    echo "\t</li>" . PHP_EOL;
 *    echo "</ul>" . PHP_EOL;
 * }
 *
 * Example Output:
 * ===============
 *
 * <div class='year'><h2>2016</h2>
 *         <ul class='month'>
 *                 <li><h3>1</h3>
 *                         <ul class='post'>
 *                                 <li><a href='/grouptest/grouptest-10/grouptest-10-10/'>Group Test 10 10</a></li>
 *                                 <li><a href='/grouptest/grouptest-6/grouptest-6-10/'>Group Test 6 10</a></li>
 *                                 <li><a href='/grouptest/grouptest-10/grouptest-10-12/'>Group Test 10 12</a></li>
 *                                 <li><a href='/grouptest/grouptest-1/grouptest-1-12/'>Group Test 1 12</a></li>
 *                                 <li><a href='/grouptest/grouptest-5/grouptest-5-3/'>Group Test 5 3</a></li>
 *                                 <li><a href='/grouptest/grouptest-10/grouptest-10-4/'>Group Test 10 4</a></li>
 *                                 <li><a href='/grouptest/grouptest-3/'>Group Test 3</a></li>
 *                                 <li><a href='/grouptest/grouptest-6/grouptest-6-4/'>Group Test 6 4</a></li>
 *                                 <li><a href='/grouptest/grouptest-10/grouptest-10-7/'>Group Test 10 7</a></li>
 *                         </ul>
 * 
 *                 </li>
 * 
 *                 <li><h3>2</h3>
 *                         <ul class='post'>
 *                                 <li><a href='/grouptest/grouptest-5/grouptest-5-10/'>Group Test 5 10</a></li>
 *                                 <li><a href='/grouptest/grouptest-3/grouptest-3-7/'>Group Test 3 7</a></li>
 *                                 <li><a href='/grouptest/grouptest-9/grouptest-9-5/'>Group Test 9 5</a></li>
 *                                 <li><a href='/grouptest/grouptest-7/grouptest-7-12/'>Group Test 7 12</a></li>
 *                                 <li><a href='/grouptest/grouptest-3/grouptest-3-11/'>Group Test 3 11</a></li>
 *                                 <li><a href='/grouptest/grouptest-9/grouptest-9-11/'>Group Test 9 11</a></li>
 *                         </ul>
 * 
 *                 </li>
 * 
 *                 <li><h3>3</h3>
 *                         <ul class='post'>
 *                                 <li><a href='/grouptest/grouptest-7/grouptest-7-10/'>Group Test 7 10</a></li>
 *                                 <li><a href='/grouptest/grouptest-12/grouptest-12-12/'>Group Test 12 12</a></li>
 *                                 <li><a href='/grouptest/grouptest-11/grouptest-11-5/'>Group Test 11 5</a></li>
 *                         </ul>
 * 
 *                 </li>
 * 
 *                 <li><h3>4</h3>
 *                         <ul class='post'>
 *                                 <li><a href='/grouptest/grouptest-8/grouptest-8-3/'>Group Test 8 3</a></li>
 *                                 <li><a href='/grouptest/grouptest-12/grouptest-12-6/'>Group Test 12 6</a></li>
 *                         </ul>
 * 
 *                 </li>
 * 
 *   </li>
 * </ul>
 * 
 * IMPORTANT!
 * ==========
 *
 * Your PageArray needs to be sorted by the fields you group by, or your return array will be an
 * unorderly mess (the grouping is still correct, but the order of keys is arbitrary).
 *
 * Mutator Function:
 * =================
 *
 * Instead of just reading properties from the page, you can also pass a mutator function
 * to groupBy. It gets passed the page object as its first arguments and any optional property
 * names after that.
 *
 * This might come in handy if you want to group by year and month but only have a single
 * DateTime field. You can then use a grouping function like this:
 *
 * $blogposts = $pages->find("template=blog-post, sort=created");
 *   $grouped = $blogposts->groupBy(function($pg) {
 *     return array(strftime('%Y', $pg->created), strftime('%m', $pg->created));
 * });
 *
 */

wire()->addHook("PageArray::groupBy", function(HookEvent $event) {
	$out = array();
	
	$args = $event->arguments();
	
	if(count($args) == 0) throw new InvalidArgumentException("Missing arguments for function PageArray::groupBy, at least 1 required!");
	
	$last = count($args) - 1;
	
	$fnc = is_string($args[$last]) ? FALSE : array_pop($args);
	
	foreach($event->object as $pg) {
		if($fnc) {
			$props = call_user_func_array($fnc, array_merge(array($pg), $args));
		} else {
			$props = array_map(function($propname) use($pg) { return $pg->{$propname}; }, $args);
		}

		$cur = &$out;

		foreach($props as $prop) {
			if(!isset($cur[$prop])) $cur[$prop] = array();
			$cur = &$cur[$prop];
		}

		$cur[] = $pg;
	}

	$event->return = $out;
});

 

Edited by BitPoet
Fixed more typos
  • Like 14
  • Thanks 1
Link to comment
Share on other sites

Thanks @BitPoet!

I think there might be some typos in the mutator example - maybe you meant:

$blogposts = $pages->find("template=blog-post, sort=created");
$grouped = $blogposts->groupBy(function($pg) {
    return array(strftime('%Y', $pg->created), strftime('%m', $pg->created));
});

 

  • Like 6
Link to comment
Share on other sites

  • 5 years later...

Thanks for sharing BitPoet!

I did notice a couple of typos as well:

*    echo "<div class='year'><h2>$year</h3>" . PHP_EOL;

and

 <div class='year'><h2>2016</h3>

Notice the h2 and h3 don't match up?

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...