Jump to content

Why does $page->rootParent identify current section?


isellsoap
 Share

Recommended Posts

I first asked this question on Twitter but I guess the forum is a better place to get a good answer. The following code is taken from the head.inc file of the default PW theme:

// Create the top navigation list by listing the children of the homepage.
// If the section we are in is the current (identified by $page->rootParent)
// then note it with <a class='on'> so we can style it differently in our CSS.
// In this case we also want the homepage to be part of our top navigation,
// so we prepend it to the pages we cycle through:

$homepage = $pages->get("/");
$children = $homepage->children;
$children->prepend($homepage);

foreach($children as $child) {
	$class = $child === $page->rootParent ? " class='on'" : '';
	echo "<li><a$class href='{$child->url}'>{$child->title}</a></li>";
}

The definition of $page->rootParent from the cheatsheet:

 The parent page closest to the homepage (typically used for identifying a section)

I understand, that if you have the following page structure …

  • Home
  • About
  • Projects
    • Project A
      • Subproject 1
    • Project B

… and calling $page->rootParent->title on »Subproject 1« outputs »Projects«. But why can I also check with it that a page is the »current« one like written in the above code ($child === $page->rootParent)? I guess the problem I have with this is that one method implies two different semantics (which confuses me). Just wanted to share my thoughts about this as I’m trying to truly »learn« PW form the inside out. :-)

  • Like 2
Link to comment
Share on other sites

Hi there, and welcome to the forums.

The code snippet above grabs all the children of Home (/). If 'Home' is level 0, it's children are level 1. The code then adds Home to the foreach of level 1 with $children->prepend($homepage); This level 1 pages are rootParents.

So it does: for each level 1(rootParent) page that is found output this list element in the navigation:

  • Home
  • About (let's say you are on the about page, then: current page equals (===) rootParent, so mark me with class='on'
  • Projects

$homepage = $pages->get("/");
$rootParents = $homepage->children;
$rootParents->prepend($homepage);

foreach($rootParents as $rp) {
    $class = $rp === $page->rootParent ? " class='on'" : '';
    echo "<li><a$class href='{$rp->url}'>{$rp->title}</a></li>";
}

Link to comment
Share on other sites

Hey, thanks for the welcome.

Now I get it. $page->rootParent by itself outputs an ID and therefor you can check if a page is the current one. Rather trivial, but I really didn’t see it. :-) Thanks.

Darned, i could have fitted that in <140 characters

But still, i managed to lure someone to the forums ;)

  • Like 3
Link to comment
Share on other sites

Thats kinda wrong it doesnt highlight current page but the root parent of the current page here used for subpages under a root parent. Like a trail highlighting. It does also work for the root parents itself but could be missunderstand. $child === $page would be for subpages too in case the code also goes further. === is to compare objects that are the same in memory. So its not the id but the object.

  • Like 1
Link to comment
Share on other sites

Thanks for the clarification. In this case a == comparison would also work, right? If I remember correctly its just a bit slower than === comparison?

From this page on php.net:

When using the comparison operator (==), object variables are compared in a simple manner, namely: Two object instances are equal if they have the same attributes and values, and are instances of the same class.

On the other hand, when using the identity operator (===), object variables are identical if and only if they refer to the same instance of the same class.

So for objects === has got to be faster because it only has to compare one thing (memory location). Whereas, == compares lots of things. So I think that === is what you want when comparing two pages to see if they are the same. Most of the time this works perfectly. But there are some situations where that could fail. An example would be if you loaded a page $a, saved page $b, then loaded another copy of page $a, which we will call $c. If you compared $a === $c, it would fail because you'd saved $b. Why? Any time you save a page it clears the memory cache. So while this seems like an unlikely situation, it can happen. As a result, it's technically more reliable to compare pages by their ID property, like: $a->id == $c->id would still work in situations when "$a === $c" wouldn't. You can also do "$a" == "$c", which is the same as comparing the id property. This works in ProcessWire since the string value of any page is its ID. 

  • Like 3
Link to comment
Share on other sites

Funny that this was exactly my first question too =)

I wondered why in the example template 

$child === $page

would not be sufficient. Now this post helped to understand that you want to highlight the parent as well. Tat part in the template might be confusing for starters.

Maybe explain there in a comment that you could and also would want to highlight both the current page and the parent page.

Link to comment
Share on other sites

  • 3 months later...

Hey Guys,

seems to be the right thread for my problem.

This is my code for two tabs that should show or hide the subpages.

<?php
     $tabs = $pages->find("id=1006|1007");
     foreach($tabs as $t){
         $active_tab = $page->belongs_to;
         $class_tab = $t->id;
         $a_class = $active_tab == $class_tab ? " class='active'" : " class='none'";
         echo "<li$a_class><a href='#{$t->tab_value}'>{$t->title}</a></li>";
         }
 ?>
 

The comparison and the output are correct but the class of the "li" is allways "none"... Anyone an idea?

Link to comment
Share on other sites

This might work:

<?php
     $tabs = $pages->find("id=1006|1007");
     foreach($tabs as $t){
         $a_class = $t == $page->belongs_to ? " class='active'" : " class='none'";
         echo "<li$a_class><a href='#{$t->tab_value}'>{$t->title}</a></li>";
         }
 ?>

But if it doesn't then what is $page->belongs_to? Is it a page ID or a pagearay? I guess what I'm really asking is what type of field is it?

Link to comment
Share on other sites

Hi Ryan,

thank you but it still simply dont work. It takes always the last value (in this case "none").

The Problem are not the IDs. I always get the right IDs if I let them show in the Frontend.

Tab #1 = 1006/1007

Tab #2 = 1007/1007

But the class is still "none" ...

Link to comment
Share on other sites

Well obviously the condition isn't met so it's always false.

For me I'm still trying to figure out what the heck you're doing with belongs_to.. seems redundant to me but maybe because I don't get it. :-)

Link to comment
Share on other sites

I would switch from utilizing the ternary operators "?:" to an IF/ELSE statement. 

Also output your variables at that time, so you can see what they are.

You are black-boxing the thing and wondering why it's not working...

If the if/else works then you probably need some brackets around your ternary-statement:

$foo = ($bar == "foo") ? "Foo is Foo" : "No Foo here."; // OR
$foo = (($bar == "foo") ? "Foo is Foo" : "No Foo here."); // This

If I recall, when I've used ternary operators in PHP, it doesn't seem to evaluate quite like I would expect with C, AHK, or others.

Link to comment
Share on other sites

I don't think parenthesis affect the logic here. I like to wrap ternary blocks in parenthesis just for readability, containing it to a visual open "(" and close ")" to the statement. But I don't think ternary operators and/or parenthesis aren't the issue here. The original issue is that the poster was comparing an object to an ID, which was probably failing. Later it was followed up with one where "==" are being used to compare two objects. That's not an ideal way to compare page objects because that double equals goes in and compares all the properties of both objects. If they are two different copies of the same page, it will fail. The best way to compare pages is to compare the ID (per my post above). Though if one literally wants to compare that two objects are the same instance then 3 equals "===" should be used, since that will compare the memory pointer (efficient) rather than every single variable in the object (not efficient). But I recommend comparing only the 'id' property, just on the off chance that you've got two different instances of the same page. It's also easy to remember, always works, and adds clarity that you are dealing with page objects.

  • Like 1
Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...