Open jmartsch opened 2 years ago
You can do this:
<?php namespace ProcessWire;
use RockMigrations\MagicPage;
class OrderPage extends Page {
use MagicPage;
public function editForm($form) {
$form->get('title')->label = 'hooked label';
}
}
;)
@BernhardBaumrock This requires installing your RockMigrations module. I would prefer to have this natively and not via an additional module. But thank you for showing me this solution, which I think I will use until we have something better.
@jmartsch How about using the wired() method? I think it should do the same thing as an init() method in this case. It is called for any Wire-derived class when the current ProcessWire instance dependency injection is complete. Of course, you could use the construct() method too, but at that point the current instance dependencies have not been injected, so construct() is not an ideal method for adding hooks.
Thanks for your answer @ryancramerdesign. I didn't know about the wired() method. Using it works absolutely fine.
@ryancramerdesign and @jmartsch using wired() might have unexpected side effects! For example if you create a "BasicPagePage" pageclass and add the wired() method, than this wired() method is called for every loaded BasicPage. So if you had 3 BasicPages in your pagetree, then that triggers wired() three times!
Depending on your implementation of wire() this might add your hook more often than you want...
RockMigrations makes sure that for MagicPages the init() method is triggered on "init" of the boot process and "ready" at ready. And both are triggered only ONCE no matter how many pages you have created (with the latest version also if no page is created!).
It does also offer other helper methods like "onSaveReady", which is only triggered on saveReady of this specific pageclass! So you get extremely simple and clean code, see https://www.youtube.com/watch?v=eBOB8dZvRN4&t=517s
I experienced something similar to what @BernhardBaumrock mentioned. I use AdminOnSteroids, which adds a prev and next Button to a ProcessPageEdit page. As the prev and next Button query the PW API, it executes the hook for all pages which have the type OrderPage.
In my hook I add a field (or more) to the ProcessPageEdit form.
So I had three fields added, instead of one.
It would be nice, if there is a native way of doing what Bernhad mentioned. So that the hook only gets added once.
It would be nice, if there is a native way of doing what Bernhad mentioned. So that the hook only gets added once.
I get that you're perhaps looking for a "ProcessWire native" way to do this, but just for the record: this should be quite easy to avoid by using static class property in your OrderPage class. Store the hook ID returned by addHook there, and only add the hook if that property is empty.
Sure, that adds a bit of boilerplate, but we're talking about a couple of lines of code here. I guess it's a question of point of view if we need something in the core for this :)
Bernhad and I recently discussed the same topic, see: https://processwire.com/talk/topic/27419-processwirerocks-my-youtube-channel-%F0%9F%98%8E/?do=findComment&comment=226313
I also used to ask Ryan about a related issue, namely how to handle a frontend page request via a Custom Page method (LoginRegisterPro module topic) module: https://processwire.com/talk/topic/23310-register-link-not-correct/?do=findComment&comment=223150
What I would really like to see is core support for both needs, because initialising a page object at the right time in the page request process is a must. We can agree on that, I think.
It's great that we have the basics and it is documented: https://processwire.com/blog/posts/pw-3.0.152/ but the next step should be official support to make the initialisation process simple and documented too. I wish... :)
@ryancramerdesign Any further thoughts on this? I think a native way of triggering the init or ready method at the right time would be a huge improvement to what i doable with ProcessWire as a software framework.
@ryancramerdesign π
@ryancramerdesign π
As far as I know, Ryan checks the number of "thumbs up" of the initial post of the request. Anyone want to add more?
Hey @ryancramerdesign I still think this would be a great addition! Others try to find a solution for this as well, see See https://processwire.com/talk/topic/29996-a-simple-way-to-have-your-hook-methods-inside-your-custom-page-classes/
I think it would already be enough to trigger init() and ready() properly. No need (impossible) to reflect all hookable methods like buildform or saveready, we could do that on our own, but we'd need at least the ready() and init() method to attach the needed hooks and have everything related to the custom page class inside the custom page class.
This is how RockMigrations is doing it, which works great for years now: https://github.com/baumrock/RockMigrations/blob/5d7bb4b427f953bf79257c332bd9dcfbd8bba261/MagicPages.module.php#L90-L91
I just want to share my follow-up example code I posted originally over here: https://processwire.com/talk/topic/30138-page-classes-diving-into-one-of-processwires-best-features/?do=findComment&comment=242747
/*
* Called when the page is requested on the frontend.
* Called from _init.php via include_once(config()->paths->templates . 'path/to/this/_init_once.php');
* so that it only runs once per request:
* page()->requested();
*/
public function requested() {
$this->request_ws = "\ProcessWire\RqtProduct";
parent::requested();
}
/*
* When initialization of object properties is required right from the beginning.
*/
function __construct(Template $tpl = null) {
parent::__construct($tpl);
$this->factsAy = new Arrayy([]);
}
public function ___loaded() {
parent::___loaded();
// Either building a structured array of all product data and caching it in a variable or reading that from memory.
if (empty($this->facts)) {
$this->encodeFacts();
} else {
$this->factsAy = Arrayy::createFromJson($this->facts);
}
}
/**
* Product page specific hooks. This method is called from site/init.php from my custom loop
* that runs for each frontend template based page.
* if (in_array($templateName, config()->noneFrontendTemplates)) {
* if ($templateName === "user") call_user_func_array("{$namespace}{$className}::initiate", []); // UserPage needs special treatment.
* continue;
* }
* if (class_exists($namespace . $className)) call_user_func_array("{$namespace}{$className}::initiate", []); // Page classes register hooks in their initiate() method this way.
*/
public static function initiate() {
parent::initiate();
wire()->addHookBefore("Pages::saveReady(template=product)", function ($event) {
... more hooks go here ....
So all in all, I am seeking an officially recommended, supported and documented way of handling:
loaded()
can be used for AND also for adding hooks (like my initiate()
method above but in the scope of the object's instance and not as a static method.)Link to Ryan's additional thoughts about this on the forum: https://processwire.com/talk/topic/30194-weekly-update-%E2%80%93%C2%A011-july-2024/?do=findComment&comment=243010
Custom Page classes are great, but they would be even greater if we could use hooks in their
init
method which would be called automatically.One thing I and others like Bernhard are doing when developing custom modules (or modifying ProcessPageEdit pages), is to add hooks to a custom page class like so:
However the
init
method isn't called when you go to a page with theorder
template (in my case in the admin).Bernhard has a workaround where he triggers the
init
method of the custom page class in /site/init.php via$pages->get('template=order')->init();
;Problems with this approach are:
buildFormContent
method which is different on every custom page class. For example on one template I want to use thebuildFormContent
method to add a field to the form, on another template I want to add a tab.If I want to use the workaround Bernhard provided, I would have to return early manually if the template name doesn't match, like so:
Else the hook would be executed on every page.
What you think about this, or are there other ways, to modify things like the form content in the admin via custom page classes?