Robin S

Add a helper method for building selectors that match an exact Page Reference field value

Recommended Posts

Update: you don't need this method. See my next post below.Ā šŸ™‚

-----

Suppose you have a Page Reference field "countries" in template "traveller" that contains any countries the traveller has visited.

It's easy to find travellers who have visited Albania and Andorra...

$matches = $pages->find("template=traveller, countries=Albania, countries=Andorra");

But what if you want to find travellers who have only visitedĀ Albania and Andorra and not visited any other countries? Then it's not so easy. There's no simple syntax for selectors that allows you to match an exact Page Reference field value, as @adrianĀ highlighted recently. Within your selector you have to include all the countries that you don't want to be in the field value. That's a hassle to do manually, and in some circumstances where new pages are being added all the time you may not know in advance all the pages you need to exclude.

So to make it an easier job to create an exact match selector for Page Reference fields, hereĀ is a helper method you can add in /site/ready.php:

// Returns a selector string for matching pages that have the exact supplied value in the Page Reference field
$wire->addHookMethod('Field(type=FieldtypePage)::getExactSelector', function(HookEvent $event) {
	$field = $event->object;
	$value = $event->arguments(0);
	if($value instanceof PageArray) $value = $value->explode('id');
	if(!is_array($value)) throw new WireException('The $value argument supplied to getExactSelector() must be a PageArray or an array of page IDs.');
	$table = $field->getTable();
	$query = $this->database->query("SELECT data FROM $table GROUP BY data");
	$field_values = $query->fetchAll(\PDO::FETCH_COLUMN);
	$exclude_ids = array_diff($field_values, $value);
	$selector = '';
	foreach($value as $id) $selector .= "$field->name=$id, ";
	if(count($exclude_ids)) $selector .= $field->name . '!=' . implode('|', $exclude_ids);
	$event->return = rtrim($selector, ', ');
});

And you use the method like this:

// Get the Page Reference field you want to use in the selector
$field = $fields->get('countries');
// Get the value you want to match (PageArray)
$value = $pages->find('template=country, title=Albania|Andorra');
// Alternatively $value can be an array of page IDs
// $value = [1105, 1107];
// Use the method to get a selector string
$selector = $field->getExactSelector($value);
// Optional: add anything else to the selector that you want
$selector .= ', template=traveller';
// Find the matching pages
$matches = $pages->find($selector);

Ā 

  • Like 4

Share this post


Link to post
Share on other sites

Sensational, Brilliant, FabulousĀ šŸ™‚

Now this is something we definitely need in the core - I can't believe someone hasn'tĀ realized that we can't already do this.

I think we need to nominate Robin as PW support guru of the year!

  • Like 3

Share this post


Link to post
Share on other sites
1 hour ago, adrian said:

Sensational, Brilliant, Fabulous

Ha ha, you might have spoken too soon.Ā šŸ˜„
I knew that as soon as I posted this a much simpler solution would present itself. You don't need to exclude anything to make an exact match - you just need to match all the pages and the count of the pages.

So no helper method is needed really.

$matches = $pages->find("template=traveller, countries=Albania, countries=Andorra, countries.count=2");

Or for a more complex match where the count isn't immediately obvious:

$value = $pages->find('template=country, title=Albania|Andorra'); // imagine a more complex value than this
$selector = $value->each('countries={id}, ');
$selector .= "countries.count=$value->count, template=traveller";
$matches = $pages->find($selector);

Ā 

  • Like 9

Share this post


Link to post
Share on other sites

Nice!

Interesting you figured out using count for an exact match. In the same multi-parameterĀ selector mentioned in the linked post, I use countĀ to find messages with eitherĀ all the age categories checked, or alternatively, it has at least the user's age category checked. This is used because the user's age is not a mandatory field to be completed so if we don't know their age, we need the message to be for all ages, but if we do know their age, then it must match the ages tagged in the message.

$selector[] = 'ages=(ages.count='.$pages->count('template=age'), ages=(ages='.$user->age.')';

Anyway, obviously getting away from the purpose of the thread, but it's another example of how using a count can be helpful for queries.

  • Like 6

Share this post


Link to post
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.