Adam Kiss Posted May 31, 2012 Share Posted May 31, 2012 Hey all (and Ryan, mostly ) During me playing with PW, I noticed that you can set different PHP Object for pages, instead Page. SO what I do now: I have custom Autoloader added (in the end of config), have custom classes for all templates (extending Page class), and all templates are redirected to index.php, which only calls some default function $page->render(); Why? This allows me now do stuff like this: '$page->children()->renderListItem();', which I find superclean. I also teamed up with Steve (netcarver) to see whether there is some performance hit. There is, but minor one - application went from 24 rqs/s down to 21 rqs/s, which I am pretty much okay with. But (there is always one), I am curious whether there are any other implications? This possiblity is hidden in the advanced settings, which you recommend only to module makers – but I would need to turn this on for each page I'll be working on, so... I am asking. Are there any? Link to comment Share on other sites More sharing options...
Soma Posted May 31, 2012 Share Posted May 31, 2012 I don't understand why and what exactly you did. But for having $page->children()->renderListItem(), you could just simply create a module to add that method to page array objects. I've done similar things in a project. Link to comment Share on other sites More sharing options...
Adam Kiss Posted May 31, 2012 Author Share Posted May 31, 2012 TL;DR: I basically wrap the original Page object (the one that is returned from PW when you call $page or PageArray->eq(0), for instance) into my own PHP class, so I can add my own code to different template classes. This is makes it easier (for me, at least) to separate the logic part (when deciding stuff based on input or urlSegments, for isntance) from the HTML code generation itself – it's very "MVC-ish" Link to comment Share on other sites More sharing options...
apeisa Posted May 31, 2012 Share Posted May 31, 2012 I use that kind of stuff in every project. Here is one of my "MarkupRenderHelper" modules (stripped some code to make it easier to follow - might throw few errors because of that, but should give the idea): <?php class MarkupRenderHelper extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Render Helper', 'version' => 100, 'summary' => 'Simple module that adds few methods to Pagearray and Page object. -Antti', 'singular' => true, 'autoload' => true ); } public function init() { $this->addHook('PageArray::renderArticleList', $this, 'renderArticleList'); $this->addHook('Page::renderListItem', $this, 'renderListItem'); $this->addHook('Page::renderTags', $this, 'renderTags'); $this->addHook('Page::renderMeta', $this, 'renderMeta'); $this->addHook('Page::firstImage', $this, 'firstImage'); } public function renderArticleList($event) { $articles = $event->data['object']; $out = "<ul class='acticles-list'>"; foreach($articles as $p) { $out .= $p->renderListItem(); } $out .= "</ul>"; $event->return = $out; } public function renderListItem($event) { $p = $event->data['object']; $parent = $p->parent(); $out = ""; $img = $p->firstImage(); if ($p->author) $author = $p->author . ","; $out .= "<li class='$parent->name'>"; if ($img) { $img = $img->width(130); $out .= "<img src='$img->url' width='$img->width' height='$img->height' alt='$img->description' />"; } $out .= "<div class='text-holder'>"; $out .= $p->renderMeta(); $out .= "<h2><a href='$p->url'>$p->title</a></h2>"; $out .= "<p>$p->summary</p>"; $out .= "</div>"; $out .= "</li>"; $event->return = $out; } public function renderMeta($event) { $p = $event->data['object']; $parent = $p->parent(); $author = ($p->author) ? $p->author . "," : ""; $out = "<span class='meta'><a href='$parent->url'>$parent->title</a> $author $p->publish_date </span>"; $event->return = $out; } public function renderTags($event) { $p = $event->data['object']; $out = ''; foreach($p->tags as $tag) { $out .= "<a href='$tag->url'>$tag->title</a>"; if ($tag !== $p->tags->last()) $out .= ", "; } $event->return = $out; } public function firstImage($event) { $p = $event->data['object']; if (count($p->images) > 0 ) { $img = $p->images->first(); } else { $img = false; } $event->return = $img; } } Sometimes I add "widgets" for $pages object too. Then I can just do <?= $pages->renderInfobox() ?> or something like that. 3 Link to comment Share on other sites More sharing options...
Adam Kiss Posted May 31, 2012 Author Share Posted May 31, 2012 Antti: but do you do this for your templates too? Edit: Oh, I see what you did there; You are adding a custom hooks to fake class methods, right? Link to comment Share on other sites More sharing options...
Soma Posted May 31, 2012 Share Posted May 31, 2012 apeisa, yes that's what I did. I thought why so complicated if there's a nice method to add those in PW. Sorry to not explain further. Link to comment Share on other sites More sharing options...
ryan Posted May 31, 2012 Share Posted May 31, 2012 When it comes to markup render stuff that is needed throughout the site, I also use that hook/module method mentioned above, adding new hook methods as needed to Page and PageArray. $events = $pages->find("template=event, sort=-date, limit=3"); echo $events->renderEvents(); Either that, or I'll just put them in a separate file and call them up with functions: include("./tools.inc"); echo renderEvents($events); If i'm making a lot of repetitive $pages->find() calls, I might also add a new hook method to $pages to serve as a filter so that I could do this: $events = $pages->events("limit=3"); I think that inheritance isn't ideal if all you need is to add some new methods to a class. While it works, it's adding another level of complexity, so I prefer to plug-in to things where possible, rather than extend them through inheritance. But what works for one doesn't necessarily work for all, and people have different preferences and ways of achieving things, so I would stick with whatever makes the most sense for your project. The inheritance of Page that you see in PW's core (like User, Permission, etc.) is more about having a separate type for typecasting purposes, than it is for extending functionality of Page. Though in PW's case, it does both. But if I didn't need separate typecasting (for argument type hints and such) then I might have utilized hooks for these instead. It was also a way to keep the system consistent with PW 2.0, which had User, Permission, Role classes that weren't Page objects. By making the new classes extend Page, it was a way to avoid potentially breaking older code that expected those types. As for implications, stuff that is in 'Advanced' mode is there because I don't really know the full implications. So if you find everything works, I think you are good. But the only implication I would be worried about is just what happens during upgrades and whether they are more likely to affect the approach you are using. I can't say for certain, as I don't totally understand the approach, but if it works now, it's more likely to continue working than not. That being said, the further you go in using a different approach from others, the more difficult it may be to troubleshoot when something doesn't work. Using an MVC approach with PW also doesn't need to be complex. If you think of your template files as PHP controllers, your $page(s) as your model, and use the TemplateFile class to render your views, you have an easy to use and implement MVC setup. There are applications where I like to use this approach. But for most web sites I find it more convenient and maintainable to use templates, modules and includes in a more traditional way. There are times when layers on top of markup makes things better, and there are times when it makes it worse. 2 Link to comment Share on other sites More sharing options...
thetuningspoon Posted August 3, 2015 Share Posted August 3, 2015 I've been using Ryan's method here of adding hooks instead of extending the Page class, but as my applications grow larger I'm wishing that I had some of the benefits of organizing my code into subclasses and not having to write all of my methods as hooks, which can be a bit of an added pain sometimes (having to pull the object, parameters and check for the page template each time). Ryan- If one were to extend the Page class instead of hooking to it, would you have a recommended way of doing so? Link to comment Share on other sites More sharing options...
sforsman Posted August 7, 2015 Share Posted August 7, 2015 @thetuningspoon: There is only one way to extend the Page -class, e.g. <?php class MyPage extends Page { public function myFunction() { return "I have been created for great endeavours" } } ?> After you have made sure the class exists (preferrably through autoloading), you just enter the name of the class into the 'System' -tab of the template in question (under "Page Class Name"). 5 Link to comment Share on other sites More sharing options...
thetuningspoon Posted August 7, 2015 Share Posted August 7, 2015 Thanks sforsman. From reading Adam's original post I thought there might be more to it than that. That is awesome. So everywhere that a page is instantiated in PW, it will use the alternate class for that page? I'll have to try it out. Link to comment Share on other sites More sharing options...
sforsman Posted August 8, 2015 Share Posted August 8, 2015 So everywhere that a page is instantiated in PW, it will use the alternate class for that page? Yeah, exactly Well to be precise, it will use the alternate class for every page that is using the template for which you have changed the Page Class. The ProcessWire users are implemented exactly like this, so it's definitely a core feature. Also since the classes extend Page, the objects will be valid to any methods that expect Page-objects. Link to comment Share on other sites More sharing options...
LostKobrakai Posted August 10, 2015 Share Posted August 10, 2015 (edited) If methods are really meant to be used only by a single PageType, isn't it better to go the inheritance route? The thing that always bugged me about the hooking way is, that a added methods are available to all Pages, which is almost never what I want it to be. Maybe the conditional hooking will solve this. It does solve the issue. Edited January 10, 2017 by owzim Added link to the blog post about conditional hooks Link to comment Share on other sites More sharing options...
Adam Kiss Posted August 12, 2015 Author Share Posted August 12, 2015 Thanks sforsman. From reading Adam's original post I thought there might be more to it than that. My original post is also three years old Link to comment Share on other sites More sharing options...
sforsman Posted August 25, 2015 Share Posted August 25, 2015 If methods are really meant to be used only by a single PageType, isn't it better to go the inheritance route? For some cases, it's a matter of preference. What's also cool about the inheritance route, is that you can use type hinting in your methods and functions. It makes the code more readable and also protects from errors. Also there is always some overhead (even though very small) when you are executing hooked methods. The thing that always bugged me about the hooking way is, that a added methods are available to all Pages, which is almost never what I want it to be. As you already noticed, you can use conditional hooks. And you can of course check the template of the Page in question inside the hook as well. Anyway, I would go with inheritance in your case. First and foremost because you don't need any autoloading modules, and you never have to wonder 'where the heck did I put that hook'. In my opinion it makes your application more manageable. 2 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