Jump to content
Juergen

How to get $page on Inputfield::render hook?

Recommended Posts

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?

Share this post


Link to post
Share on other sites

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

 

  • Thanks 1

Share this post


Link to post
Share on other sites
21 minutes ago, Juergen said:

$this->pages->addHookBefore

$this outside class context shouldn't work as well...

Edited by kongondo
  • Thanks 1

Share this post


Link to post
Share on other sites

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 :(

Share this post


Link to post
Share on other sites
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();
}

 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
/**
 * 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);
        }
    }
}

 

  • Like 1

Share this post


Link to post
Share on other sites
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' //...

 

  • Like 7

Share this post


Link to post
Share on other sites
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 :)

  • Like 3

Share this post


Link to post
Share on other sites

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.

  • Like 1

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

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);
});

 

  • Like 1

Share this post


Link to post
Share on other sites
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');
	}
});

 

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...