Issue with isChildOf() and possibly additional custom page method

Hi. I’m getting this error on line 595 of /kirby/src/Cms/Page.php

Error Call to a member function is() on null

Here is what is highlighted beginning on page 593, the return function is failing:

public function isChildOf($parent): bool
    {
        return $this->parent()->is($parent);
    }

I believe this is from code in my menu where I am using grandchildren with a custom page method.

Here is my old custom page method which worked:

<?php
page::$methods['isGrandchildOf'] = function($page, $otherPage) {
  return $otherPage->grandChildren()->has($page);
};

and my attempt at updating it:

Kirby::plugin('rgolightly/grandchildren', [

	'pageMethods' => [

		'isGrandchildOf' => function($page, $otherPage) {
  return $otherPage->grandChildren()->has($page);
	}
  ]

]);

which is placed as an index.php in its own folder

Updated:

I’ve removed the plugin entirely and it is still giving me the same error. It looks like maybe it is actually something to do with isChildOf()

Here’s the full code of the section that is causing the error:

<?php if(($p->isActive()) or ($page->isChildOf($p)) or ($page->isGrandchildOf($p))): ?><span class="dark"><?php endif ?>

If I remove the isGrandChildOf statement it still happens

On a different page, my plugin updated is giving me this error:

Too few arguments to function Kirby\Cms\Page::{closure}(), 1 passed and exactly 2 expected

I think there is an error in your custom method, try this:

Kirby::plugin('rgolightly/grandchildren', [

  'pageMethods' => [

    'isGrandchildOf' => function($otherPage) {
      return $otherPage->grandChildren()->has($this);
	}
  ]

]);

Thanks. I’ll try that, I discovered something else odd though. On my “about” page which uses a redirect template the isChildOf works just fine, but on my other pages it doesn’t. Here’s my menu as it stands right now:

This was all working as expected before the Kirby 3 update:

<?php if ($page->id() != 'home'): ?>
	<ul itemscope itemtype="https://schema.org/SiteNavigationElement" class="menu">
    <?php foreach($pages->listed() as $p): ?>
		<li>
			<?php if ($p->id() == 'about'): ?><br><?php endif ?>
			<h4 itemprop="name">
			<?php if(($p->isActive()) or ($page->isChildOf($p))): ?><span class="dark"><?php endif ?><a href="<?php echo $p->url() ?>"><?php echo $p->title()->html() ?></a></span>
			</h4>
			
	      <?php

    		  // get all children for the current menu item
    		  $children = $p->children()->listed();

		      // display the submenu if children are available
		      if($children->count() > 0):

	      ?>
	      
	    <?php if($page->template() == 'redirect' || $page->parent()->template() == 'redirect'): ?>
   	   		<?php if(($p->isActive()) or ($page->isChildOf($p))): ?>
     	 		<ul class="indent">
        			<?php foreach($children as $child): ?>
        			<li itemprop="name"><a<?php e($child->isOpen(), ' class="dark"') ?> href="<?= $child->url() ?>"><?= $child->title()->html() ?></a></li>
        			<?php endforeach ?>
      			</ul>
      		<?php endif ?>
     	 <?php endif ?>
     	 
     	 <?php if($page->template() == 'work_portal' || $page->parent()->template() == 'work_portal' || $page->parent()->template() == 'series_visual'): ?>
	      	
   	   		<?php if(($p->isActive()) or ($page->isChildOf($p))): ?>
     	 		<ul class="indent">
        			<li itemprop="name">
        			<a <?php
    				$urllinked = (page('work')->url() . '/type:sonic');
   					if ($urlactive == $urllinked)
   					echo 'class="dark"';
   					?>
        			href="<?= page('work')->url() ?>/type:sonic">Sonic Work</a></li>
        			<li itemprop="name">
        			<a <?php
    				$urllinked = (page('work')->url() . '/type:visual');
   					if ($urlactive == $urllinked)
   					echo 'class="dark"';
   					?>
        			href="<?= page('work')->url() ?>/type:visual">Visual Work</a></li>
        			<li itemprop="name">
        			<a <?php
    				$urllinked = (page('work')->url() . '/type:constellation');
   					if ($urlactive == $urllinked)
   					echo 'class="dark"';
   					?>
        			href="<?= page('work')->url() ?>/type:constellation">Constellations</a></li>
      			</ul>
      		<?php endif ?>
     	 <?php endif ?>
      
    <?php endif ?>
		
    <?php endforeach ?>
    </li>
  </ul>
<?php endif ?>

Ok, you are calling this on a first level first, and $page->parent() for a first level page returns null, hence the error.

And since you are looping through all first level pages here, checking of the page is a child/grandchild of another page, doesn’t really make sense.

But maybe Kirby should check this and return the site object or something like this.

I see.

I think that all makes sense.

What this was doing in Kirby 2 was, if there were children for the menu item it would show them if you were on the page or one of its children or grandchildren, and if the child was active or one of its ancestors was active then it would highlight the child in the menu, which seemed to work with just parent-> in the old version.

I’ll take a stab at reworking this then and try to figure out a fix.

Thanks so much!

I’ll compare the source code of both Kirby versions to find out why this is happening and if this might in fact be a bug.

1 Like

The difference is that Kirby 3 returns null for $page->parent() if the page is a first level page, whereas Kirby 2 returns the Site object.

In Kirby 3 you would have to call $page->parentModel() which will return the Site object if no other parent exists.

I see. So I should change that in the Kirby core? I changed it on the template where I was calling for the parent template and that didn’t fix anything.

No, never change anything in the Kirby core.

Right.

Okay, well if I just remove the isChildOf($p) statements then the menu works again. The parentModel() seems to work for testing which templates the pages belong to, but this is still persisting.

I can change the color of the menu item if the menu’s first-level page with isOpen() , but I’d like to keep the color of the menu item changed if the visitor then navigates to one of the children of the menu item.

Or you need a check like this:

<?php if($p->isActive() || ($page->parents()->count() && $page->isChildOf($p))): ?>

This makes sure the page has parents before trying to use the isChildOf() method.

However, in your use case, where you are just looping through the first level pages, this code is pretty useless.

Okay. I’ll give this a try, thanks so much for your help.

For most of the site I only want first-level pages as the menu items, but each of the first-level pages has many children, basically I have several different article sets in different sections and I still want the visitor to know which section they’re in.

I created an issue on GitHub: https://github.com/getkirby/kirby/issues/1394

Great, thanks again! Your fix seems to be working for now at least, just digging through the rest of the changes, but excited for the new Kirby!

This issue with isChildOf() is now fixed on RC 3.0.1 and will be in the next release.