Jump to content

Question about Selectors, custom Page methods, and performance


thetuningspoon
 Share

Recommended Posts

I've recently grown fond of using hooks to add custom methods to the Page class to operate on pages of a certain template. On larger projects this helps keep logic out of my template files and tied nicely to the objects they operate on. For example, I have a travel site that has a "departures" template with a field called "group_size". On the front end of the site, I want to show the integer value for the group size as it is stored in the page. However, I have another field called "overflow" which is the additional number of travelers beyond the publicly visible group size who are allowed to book a given trip, to account for anticipated cancelations. I wanted an easy way to know when I should close a given tour, so I created a method called isFull() to house the logic that determines this. I also have getMaxRegistrations() (group_size + overflow), getOpenSlots(), and getPublicOpenSlots(), which all call each other as needed and return different values.

The problem then is when I need to use a selector, I have no way (that I know of) of using these methods in the selector. So instead I need to use a simple selector like $pages->get('template=departure') and then loop through the results and use an if statement on each result (i.e. if($result->isFull())... ) to narrow down the selection to the page(s) I really want.

I don't know enough about how selectors work under the hood to know whether this is the same thing as a selector with a field matching parameter within it, or whether this is much less efficient. 

Is there a better/cleaner/more efficient approach to this?

Link to comment
Share on other sites

You could use "addHookProperty" to add runtime "variables" instead of a method. These can be used in runtime selectors and are more or less created the same way as normal hooks, but are called without the function parenthesis and therefore cannot have arguments.

// runtime selector (no db involved)
$pageArray->find("myRuntimeProperty=something");

// db selector (db involved; more features like or groups and so on)
$pages->find($sel);

// internally replaced by $pages->find("has_parent=$page, …");
$page->find($sel); 

If you'd also need the greater efficiency from calling less pages from the db you'd need to cache those values in real fields, so they are available to the database queries as well.

  • Like 1
Link to comment
Share on other sites

Thanks LostKobrakai, that is a big help.

I wasn't sure about the distinction between when the db is and isn't accessed, since the selector syntax nicely hides that distinction from us :)

So what you're saying is that I could create a property, but that it would only work on existing PageArrays and not when calling $pages or when using a selector with $page? I'm a little confused as to why it wouldn't work with $page since $page->myMethod() works just fine, but I get why it wouldn't work with $pages.

So does this mean that the run time selector would not be any more efficient than my php loop with an if statement?

What would be the best way to "cache those values in real fields"? Does this mean creating a new field in ProcessWire and writing a hook to update it on save(), or are there other ways of doing that?

Link to comment
Share on other sites

I'm a little confused as to why it wouldn't work with $page since $page->myMethod() works just fine, but I get why it wouldn't work with $pages.

$page->myProperty would work, as well as $page->is($selector) or $page->matches($selector). Just $page->find() is searching for "new" pages to be loaded from the db. It's internally changed to $pages->find("has_parent=$page, …") and therefore it's hitting the db. 

So does this mean that the run time selector would not be any more efficient than my php loop with an if statement??

It's more efficient in terms of code quality, but not in terms of db querying efficiency, exactly.

What would be the best way to "cache those values in real fields"? Does this mean creating a new field in ProcessWire and writing a hook to update it on save(), or are there other ways of doing that?

The db can just evaluate what's in the db, therefore yes, you need new fields to store things in. I would simply hide them for the admin backend and use a Pages::saveReady hook to populate the field. As you're setting the value each time a page is saved you're also overwriting potential changes, so this should not get "out of sync" with your other fields.

Edit: While runtime selectors do look like they work the same as db selectors, there are lots of goodies that do not work for runtime selectors, like for example or-groups or automatic datestring convertion.

  • Like 1
Link to comment
Share on other sites

$page->myProperty would work, as well as $page->is($selector) or $page->matches($selector). Just $page->find() is searching for "new" pages to be loaded from the db. It's internally changed to $pages->find("has_parent=$page, …") and therefore it's hitting the db. 

Makes sense. I was thinking of things like $page->get('title|parent.title')

Thanks again!

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