thetuningspoon Posted July 10, 2015 Share Posted July 10, 2015 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 More sharing options...
LostKobrakai Posted July 10, 2015 Share Posted July 10, 2015 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. 1 Link to comment Share on other sites More sharing options...
thetuningspoon Posted July 10, 2015 Author Share Posted July 10, 2015 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 More sharing options...
LostKobrakai Posted July 11, 2015 Share Posted July 11, 2015 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. 1 Link to comment Share on other sites More sharing options...
thetuningspoon Posted July 13, 2015 Author Share Posted July 13, 2015 $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 More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now