Juergen Posted November 4, 2017 Share Posted November 4, 2017 Hello, I have written a hook to disable an inputfield on certain conditions: //Disable createevents field if there are no children $this->pages->addHookBefore('Inputfield::render', function($event) { $page = $event->arguments(0); $field = $event->object; if(in_array($page->template->name, array('event_businessvacations','event_specialbusinesshours','event_dates','event_events'))) { if(count($page->children) == 0){ if('createevents' === $field->name ){ $field->attr("disabled" , "disabled"); } } } }); I am getting always following notice from Tracy: PHP Notice: Trying to get property of non-object in /home/.sites/24/site1275/web/site/ready.php:310 This is the line where $page is defined. How can I grab $page in this case correctly or do I have to use another type of Hook? Link to comment Share on other sites More sharing options...
abdus Posted November 4, 2017 Share Posted November 4, 2017 Use $e->object->hasPage property // Inputfield.php * @property null|bool|Fieldtype $hasFieldtype The Fieldtype using this Inputfield, or boolean false when known not to have a Fieldtype, or null when not known. #pw-group-other * @property null|Field $hasField The Field object associated with this Inputfield, or null when not applicable or not known. #pw-group-other * @property null|Page $hasPage The Page object associated with this Inputfield, or null when not applicable or not known. #pw-group-other 1 Link to comment Share on other sites More sharing options...
kongondo Posted November 4, 2017 Share Posted November 4, 2017 (edited) 21 minutes ago, Juergen said: $this->pages->addHookBefore $this outside class context shouldn't work as well... Edited November 4, 2017 by kongondo 1 Link to comment Share on other sites More sharing options...
Juergen Posted November 4, 2017 Author Share Posted November 4, 2017 Both possible solutions dont work $page = $event->object->hasPage; or $pages->addHookBefore('Inputfield::render', function($event) instead of $this->pages->addHookBefore('Inputfield::render', function($event) $page is always empty Link to comment Share on other sites More sharing options...
kixe Posted November 4, 2017 Share Posted November 4, 2017 27 minutes ago, kongondo said: $this outside class context shouldn't work as well... @kongondo $this inside any template file will return the TemplateFile object and inside ready.php the ProcessWire object. @Juergen If you need to disable fields for certain roles or make them view only take this as a starting point. Put the code in your ready.php foreach ($fields as $field) { if ($field->hasFlag(8) && $field->name !== 'title') continue; // skip system fields `Field::flagSystem` $field->addFlag(160); // Add flags `Field::flagAccess` = 32 + `Field::flagAccessEditor` = 128 // Applicable only if the `Field::flagAccess` is set to this field's flags. $field->editRoles = array(); // leave empty if you want to give access to superuser only $field->viewRoles = array(1026); // assign field view permission to specific roles (IDs) // save the field $field->save(); } Link to comment Share on other sites More sharing options...
Juergen Posted November 4, 2017 Author Share Posted November 4, 2017 I have put the code inside the ready.php, but I need to run it only on certain templates and under certain conditions. Therefore I need the page to get the template associated with it. So I dont want to disable them permanently only if the page has no children. If childrens were created the field should be enabled. The idea is to make this field (a checkbox field) checked and disabled if there are no children created. If the user has created at least 1 child page, then this checkbox field will be unchecked and enabled. The automatic checking works with this Hook after a new page (with no children) is added //Set create events to true if there are no child pages $this->pages->addHookAfter('added', function($event) { $page = $event->arguments('page'); $field = $event->arguments('field'); if(in_array($page->template->name, array('event_businessvacations','event_specialbusinesshours','event_dates','event_events'))) { if(count($page->children) == 0){ $page->setAndSave('createevents', true); } } }); To prevent the user to uncheck the checkbox I want to disable it in this case. Link to comment Share on other sites More sharing options...
kongondo Posted November 4, 2017 Share Posted November 4, 2017 27 minutes ago, kixe said: $this inside any template file will return the TemplateFile object and inside ready.php the ProcessWire object. Aah, I didn't know that. Link to comment Share on other sites More sharing options...
kixe Posted November 4, 2017 Share Posted November 4, 2017 /** * check & hide specific checkbox field if page being edited has no children */ if ($page->process == 'ProcessPageEdit' && $input->id) { $field = $fields->get('createevents'); $_page = $pages->get($input->id); if ($_page->template->fields->hasField('createevents')) { if ($page->hasChildren() == false) { $field->set('flags',160); $field->setRoles('edit',array()); // works for non superuser only $_page->setAndSave('createevents', true); } } } 1 Link to comment Share on other sites More sharing options...
Juergen Posted November 4, 2017 Author Share Posted November 4, 2017 Thank you @kixe I will try this tomorrow. Link to comment Share on other sites More sharing options...
Robin S Posted November 4, 2017 Share Posted November 4, 2017 1 hour ago, Juergen said: I have written a hook to disable an inputfield on certain conditions: //Disable createevents field if there are no children $this->pages->addHookBefore('Inputfield::render', function($event) { $page = $event->arguments(0); $field = $event->object; if(in_array($page->template->name, array('event_businessvacations','event_specialbusinesshours','event_dates','event_events'))) { if(count($page->children) == 0){ if('createevents' === $field->name ){ $field->attr("disabled" , "disabled"); } } } }); There are a number of problems here I think. $this->pages->addHookBefore('Inputfield::render', function($event) { You only use $this->pages to add a hook if you are hooking a method of the Pages class. You are hooking a method of the Inputfield class, so you want... $wire->addHookBefore('Inputfield::render', function($event) { Also, you don't need to hook every inputfield render. You are only interested in Checkbox inputfields so just hook the render method of that inputfield class. $wire->addHookBefore('InputfieldCheckbox::render', function($event) { $page = $event->arguments(0); Inputfield::render() and InputfieldCheckbox::render() take no arguments, so $page is not the first argument and you cannot get $page this way. $field = $event->object; The event object is an Inputfield object and not a Field object. It doesn't really matter what you name your variables, but to keep things clear in your head it might help to change this to: $inputfield = $event->object; if('createevents' === $field->name ){ <opinion>Yoda conditions make code less readable and more prone to misinterpretation. The disadvantages outweigh the benefits.</opinion> I would tend to write this hook as: $wire->addHookBefore('InputfieldCheckbox::render', function(HookEvent $event) { if($this->process != 'ProcessPageEdit') return; $page = $this->process->getPage();; $inputfield = $event->object; if(in_array($page->template->name, array('event_businessvacations', 'event_specialbusinesshours', 'event_dates', 'event_events'))) { if($inputfield->name === 'createevents' && !count($page->children)) { $inputfield->attr('disabled' , 'disabled'); } } }); Another tip is that when identifying an inputfield by name, it can be better to check the name of the associated field rather than the inputfield. This is because when an inputfield is inside a repeater its name includes a repeater suffix. Checking the associated field name works in all situations so I prefer that. if($inputfield->hasField == 'createevents' //... Note also the change to the == comparison operator. Not every inputfield has an associated field, so you couldn't be sure that hasField returns an object with the name property if you did: $inputfield->hasField->name === 'createevents' //... 7 Link to comment Share on other sites More sharing options...
adrian Posted November 5, 2017 Share Posted November 5, 2017 6 hours ago, Robin S said: Yoda conditions I didn't know they were called that - awesome! Only 6 weeks to go ... BTW - actually not that big of a Star Wars nerd 3 Link to comment Share on other sites More sharing options...
kixe Posted November 5, 2017 Share Posted November 5, 2017 The task is to change a runtime property (visibility) of a field and setting/saving a page field value if certain conditions are met. Hooks are great but not needed to solve this task. 1 Link to comment Share on other sites More sharing options...
Juergen Posted November 5, 2017 Author Share Posted November 5, 2017 Thank you @Robin S, your solution works!!! Also thank you for your detailed explanation, but where do you get all your knowledge about Hooks ? A big thanks to all other contributors for your help! Link to comment Share on other sites More sharing options...
Jonathan Lahijani Posted September 11, 2019 Share Posted September 11, 2019 If the field being hooked is inside of a repeater, how can I get the repeater's page id? Link to comment Share on other sites More sharing options...
bernhard Posted September 11, 2019 Share Posted September 11, 2019 Not sure if there's a better way but this should work: $wire->addHookAfter("Inputfield::render", function($event) { $field = $event->object; $id = (int)str_replace('repeater_item_', '', $field->name); $page = $this->pages->get($id); }); 1 Link to comment Share on other sites More sharing options...
Robin S Posted September 17, 2019 Share Posted September 17, 2019 On 9/11/2019 at 2:52 PM, Jonathan Lahijani said: If the field being hooked is inside of a repeater, how can I get the repeater's page id? Since this thread was started a hasPage property has been added to Inputfield instances in PW. Quote hasPage null Page The Page object associated with this Inputfield, or null when not applicable or not known. So you can do this: $wire->addHookBefore('Inputfield::render', function(HookEvent $event) { /* @var Inputfield $inputfield */ $inputfield = $event->object; $page = $inputfield->hasPage; if($page) { // Use $page bd($page->name, 'page name'); } }); 3 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