Jump to content
SamC

Can anyone tell me the difference between the following pieces of code

Recommended Posts

I was looking at the uikit menu issues here:

...when I stumbled across something weird that I can't get my head around. I'll paste in the code I used again to save checking that link out and the two commented parts of code I'm talking about:

<?php
  /*
  // when this is used, dropdown under 'Home' has NO home link
  $root = $pages->get("/");
  $children = $root->and($root->children);
  // echo $children: 1|1086|1069|1081|1022
  */
?>

<?php
  /*
  // when this is used, dropdown under 'Home' HAS home link
  $root = $pages->get("/");
  $children = $root->children();
  // insert the following line
  $children->prepend($root);
  // echo $children: 1|1086|1069|1081|1022
  */
?>

<nav class="uk-navbar-container" uk-navbar>
  <div class="uk-navbar-left">

    <ul class="uk-navbar-nav">

      <?php foreach($children as $c):?>
      <li>
        <a href="#"><?= $c->title; ?></a>

          <div class="uk-navbar-dropdown uk-navbar-dropdown-width-2 g8-theme-mkred">
            <div class="uk-navbar-dropdown-grid uk-child-width-1-2" uk-grid>
              <ul class="uk-nav uk-navbar-dropdown-nav">
                <?php foreach($c->children as $cc):?>
                <li><a href="<?= $cc->url ?>"><?= $cc->title ?></a></li>
                <li class="uk-nav-divider"></li>
                <?php endforeach; ?>
              </ul>
            </div>
          </div>
      </li>
      <?php endforeach; ?>

    </ul>
    
  </div>
</nav>

Can anyone tell me what the difference is between the two commented parts at the top? Both $children variables appear to have the same pages but one results with no home link in the dropdown.

However, using either of the versions, the top level menu is the same.

Home | About | Services | Portfolio | Contact

Can't get my head around this. I would expect both dropdowns not to have the 'Home' link.

Any ideas?

Share this post


Link to post
Share on other sites

I can't comment about the first because I don't fancy that pattern but the second think about it as a stack by prepending, you're pushing the items from the top when adding compared than from the bottom. 

Share this post


Link to post
Share on other sites
2 hours ago, SamC said:

Can anyone tell me what the difference is between the two commented parts at the top?

I've tested your code and the outputs are identical. Must be something else you are doing?

 

2 hours ago, SamC said:

Can't get my head around this. I would expect both dropdowns not to have the 'Home' link.

Why? In the first (using and()), you are adding children to the existing item (Home). In the second, you get the children and prepend their root (Home) to them.

Side note:

What I don't understand is why and() has worked with the single page object ($root). I thought (as per docs) it should only work with WireArrays (also PageArrays). $root is a Page not a PageArray, or am I missing something?

Share this post


Link to post
Share on other sites
12 minutes ago, kongondo said:

Why? In the first (using and()), you are adding children to the existing item (Home). In the second, you get the children and prepend their root to them.

It's the dropdowns in the inner loop that are different:

<?php
  // Home, About, Services, Portfolio, Contact
  foreach($children as $c):
?>
<li>
  <a href="#"><?= $c->title; ?></a>

    <div class="uk-navbar-dropdown uk-navbar-dropdown-width-2 g8-theme-mkred">
      <div class="uk-navbar-dropdown-grid uk-child-width-1-2" uk-grid>
        <ul class="uk-nav uk-navbar-dropdown-nav">
          <?php
            // Children of home, Children of about, children of services...
            foreach($c->children as $cc): 
          ?>
          <li><a href="<?= $cc->url ?>"><?= $cc->title ?></a></li>
          <li class="uk-nav-divider"></li>
          <?php endforeach; ?>
        </ul>
      </div>
    </div>
</li>
<?php endforeach; ?>

I have no idea why one version (with the prepend), the dropdown under home is:

Home
----
Home
About
Services
Portfolio
Contact

...and the other version (using and()):

Home
----
About
Services
Portfolio
Contact

The dropdowns under 'Services' however are the same with both versions. The differences only affect the 'Home' dropdown.

12 minutes ago, kongondo said:

What I don't understand is why and() has worked with the single page object ($root). I thought (as per docs) it should only work with WireArrays (also PageArrays). $root is a Page not a PageArray, or am I missing something?

Beats me, but I've used this a few times.

Share this post


Link to post
Share on other sites
1 hour ago, kongondo said:

What I don't understand is why and() has worked with the single page object ($root). I thought (as per docs) it should only work with WireArrays (also PageArrays). $root is a Page not a PageArray, or am I missing something?

It's the WireData::and() method. Was a new one to me too. :-)

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, SamC said:

I have no idea why one version (with the prepend), the dropdown under home is:


Home
----
Home
About
Services
Portfolio
Contact

...and the other version (using and()):


Home
----
About
Services
Portfolio
Contact

Because you are calling both code at the same time :). Comment out the first (the one using and() AND it will work). An object is already in memory and PHP has a reference to it, so the items are added to the original object....or something like that...I am a bit knackered, sorry, can't give a better explanation :P.

Share this post


Link to post
Share on other sites
23 hours ago, kongondo said:

Because you are calling both code at the same time :)

Lol, I wish. I just stuck them in that example above to show the two differences. Running them separately causes the issue.

<?php
  $root = $pages->get("/");
  $children = $root->children();
  // insert the following line
  $children->prepend($root);
?>

...gives me:

Home
----
Home
About
Services
Portfolio
Contact

and (even with changing the variable name in case of it being in memory already):

<?php
  $root = $pages->get("/");
  $children2 = $root->and($root->children);
?>

...gives me:

Home
----
About
Services
Portfolio
Contact

The only way I can get 'Home' into the dropdown in this example is to add it in the inner loop:

<?php foreach($homepage->and($c->children) as $cc):?>

...gives me:

Home
----
Home
About
Services
Portfolio
Contact

Is the weirdest thing.

== EDIT ==

Been looking at these again tonight, tried the following on my demo site. The WireArrays are different (one is larger) for each $topLevels variable (depending on which one you uncomment).

<?php
  $root = $pages->get("/");
  // Home link in inner loop
  // $topLevels = $root->children->prepend($root);

  // NO Home link in inner loop
  // $topLevels = $root->and($root->children);
?>

<ul>

  <?php foreach($topLevels as $topLevel):?>
  <li>
    <a href="#"><?= $topLevel->title; ?></a>

          <ul>
            <?php foreach($topLevel->children as $topLevelChild): ?>
            <li><a href="<?= $topLevelChild->url ?>"><?= $topLevelChild->title ?></a></li>
            <?php endforeach; ?>
          </ul>

  </li>
  <?php endforeach; ?>

</ul>

I get (top option):

Home
 - Home
 - About
 - Services
 - Contact
 - Cats
 - Type

...and (second option)::

Home
 - About
 - Services
 - Contact
 - Cats
 - Type

I would expect no home link to be in either and the resulting WireArrays in '$topLevels' to be the same. The inner loop, loops through the children of the top level page, 'Home' should not be in there twice

It's something to do with the resulting WireArrays of those two lines. Am I just being super slow here or what? :undecided:

Share this post


Link to post
Share on other sites

In both cases you add $root (home or page 1) to the array. That is why they appear.

You could hardcode the link to home and after that make a foreach for the children of $pages->get("/"). You don't have to do fancy tricks to add home  like and or prepend.

Share this post


Link to post
Share on other sites

Something like this

<nav class="uk-navbar-container" uk-navbar>
    <div class="uk-navbar-left">

        <ul class="uk-navbar-nav">
            <?php
            $root = $pages->get("/");
            echo "<li><a href="/">Home</a></li>";
                foreach($root->children as $child){
                echo "<li><a href=" . $child->link . ">" . $child->title . "</a>"
                    if(count($child->children>0)){
                        echo "<div class="uk-navbar-dropdown uk-navbar-dropdown-width-2 g8-theme-mkred"> <div class="uk-navbar-dropdown-grid uk-child-width-1-2" uk-grid> <ul class="uk-nav uk-navbar-dropdown-nav">";
                            foreach($child as $c)
                            {
                            echo "<li><a href=" . $c->url . ">" . $c->title . "</a></li>";
                            }
                            echo "</ul>";
                    }
                    
                } ?>
            </ul>

    </div>
</nav>

Share this post


Link to post
Share on other sites
27 minutes ago, webhoes said:

In both cases you add $root (home or page 1) to the array. That is why they appear.

The issue is that for Sam, in the first code, "Home" appears twice, for some reason. I wasn't able to replicate that. I have a strong feeling that some other code is contributing to that anomaly. 

Share this post


Link to post
Share on other sites

Tried same code, two different sites, one of which is nearly a fresh install with exactly the same issue.

If the outer loop, loops through the top level pages (including home) and the inner loop loops through the children of the top level pages, home should never be output inside the inner loop, but it does for some reason. This only happens using:

<?php
  $topLevels = $root->children->prepend($root);
?>

<ul>

  <?php foreach($topLevels as $topLevel):?>
  <li>
    <a href="#"><?= $topLevel->title; ?></a>

          <ul>
            <?php foreach($topLevel->children as $topLevelChild): ?>
            <li><a href="<?= $topLevelChild->url ?>"><?= $topLevelChild->title ?></a></li>
            <?php endforeach; ?>
          </ul>

  </li>
  <?php endforeach; ?>

</ul>

There's literally no other code affecting this. Nothing in '_init.php' (which I do prepend) and this is just direct output. Check out the resulting WireArrays for these two variables ($topLevels and $topLevels2):

<?php
  $root = $pages->get("/");
  $topLevels = $root->children->prepend($root);
  $topLevels2 = $root->and($root->children);
?>

They're not the same. This isn't an important issue, but I'm curious :P

I can't see any errors in the code, or any other code that would affect this throughout the site.

  • Like 1

Share this post


Link to post
Share on other sites

Yes, this is somewhat unexpected behavior and can be replicated with a short piece of code:

$r = $pages->get('/');

$x = $r->children;
$y = $r->children;
$z = $r->children->prepend($r);

echo $x . PHP_EOL;
echo $y . PHP_EOL;
echo $z . PHP_EOL;

This outputs here:

1001|1005|1025
1|1001|1005|1025
1|1001|1005|1025

This illustrates that consecutive calls to children() return a pointer to the original PageArray while the very first call returns an independent copy. That's likely a caching thing, though I can't say if this is expected behavior.

Tested with different versions of PW.

  • Like 4

Share this post


Link to post
Share on other sites
1 hour ago, BitPoet said:

This illustrates that consecutive calls to children() return a pointer to the original PageArray while the very first call returns an independent copy.

And it's not just children() but other (all?) methods that use PageFinder:

2018-01-24_085925.png.06a4e0c42c266b88f23a70babbb06404.png

Weird.

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, BitPoet said:

This outputs here:


1001|1005|1025
1|1001|1005|1025
1|1001|1005|1025

This illustrates that consecutive calls to children() return a pointer to the original PageArray while the very first call returns an independent copy.

Aha! :lol: thanks @BitPoet

Share this post


Link to post
Share on other sites

Hmm. Then I must be going mad. Using the original code by @SamC, (even together/at the same time), I get identical output! Could someone please try the following and let me know if you get different/same output? Even if I comment out the 2nd $pages->get('/'), it makes no difference:) 

 

  // code 1
  $root = $pages->get("/");
  $children = $root->and($root->children);
  echo $children . '<br><hr>';// outputs >>> 1|1001|1005|1137
  
  // code 2
  $root = $pages->get("/");
  $children = $root->children;
  $children->prepend($root);
  echo $children . '<br>';// outputs >>> 1|1001|1005|1137
  exit;

ProcessWire 3.0.85, home.php (at the very top, exit after code)

Share this post


Link to post
Share on other sites

Thanks for testing @adrian! The code is from the OP. However, the OP, @SamC, is getting different results with the very same code

Similar to yours, here's my Tracy console output: The output is identical

pw-mystery-pagearray-output.thumb.png.fb99cb789057a32a08e40a56f86d3aeb.png

Edited by kongondo

Share this post


Link to post
Share on other sites
1 hour ago, kongondo said:

I get identical output!

Yeah that's why it's so weird. I did too! Check my original post, the ids output are:

1|1086|1069|1081|1022

...for both versions. However, it's the returned WireArrays that are different (check them with print_r ()). One version gives me an added 'Home' link but in order for that to be output in the inner loop, it must have been added to the children of home as the outer loop is correct.

If 'Home' was added as an extra parent, then the outer loop would have output :

Home|Home|About|Services|Portfolio|Contact

It's like there's a wirearray inside a wirearray.

Share this post


Link to post
Share on other sites
5 minutes ago, SamC said:

However, it's the returned WireArrays that are different (check them with print_r ()).

I have checked. The WireArrays are identical as well. Each with the same 4 items.

Share this post


Link to post
Share on other sites
3 hours ago, kongondo said:

Hmm. Then I must be going mad. Using the original code by @SamC, (even together/at the same time), I get identical output!

The thing is, that isn't the code from the OP.

To experience the issue I believe you need to have a minimum of three usages of the same PageFinder operation (in this case, getting the children of the root page). In SamC's original code there were two usages where the children of root are retrieved (showing different alternatives for doing the same thing) and then a third usage of the children of root within the foreach that generates the dropdown menu.

An explanation of what I believe is going on (to the extent that I understand it):

2018-01-24_135224.png.d7204c3e2ea0a184056b343d975e6a21.png

Edited by Robin S
corrected screenshot
  • Like 3

Share this post


Link to post
Share on other sites
7 hours ago, kongondo said:

Then I must be going mad

I don't think so. But I believe that you don't have the exactly same setup as @SamC, in which something, somewhere (a module, a prepend file, site/ready.php, whatever), already retrieves the children once before the code we see is executed. It's the only thing that makes sense. If you repeat code 2 in your test script, you'll get an output starting with 1|1|..., I'm pretty sure of that, so you can see the how, just not the where.

  • Like 4

Share this post


Link to post
Share on other sites

Just tried this morning on a completely fresh install, no prepended files, no extra modules etc. Created a basic structure just with basic page and included "_main-menu.php" which shows the issue of running $pages->children twice:

<?php namespace ProcessWire;

  $root = $pages->get("/");
  $topLevelsFirst = $root->children();
  $topLevels = $root->children();
  $topLevels->prepend($root);
?>

<ul>

  <?php foreach($topLevels as $topLevel):?>
  <li>
    <a href="<?= $topLevel->url ?>"><?= $topLevel->title; ?></a>

    <ul>
      <?php foreach($topLevel->children as $topLevelChild): ?>
      <li><a href="<?= $topLevelChild->url ?>"><?= $topLevelChild->title ?></a></li>
      <?php endforeach; ?>
    </ul>

  </li>
  <?php endforeach; ?>

</ul>

..which outputs:

testing_double_home.jpg

...and

<?php namespace ProcessWire;

  $root = $pages->get("/");
  $topLevels = $root->children();
  $topLevelsSecond = $root->children();
  $topLevels->prepend($root);
?>

<ul>

  <?php foreach($topLevels as $topLevel):?>
  <li>
    <a href="<?= $topLevel->url ?>"><?= $topLevel->title; ?></a>

    <ul>
      <?php foreach($topLevel->children as $topLevelChild): ?>
      <li><a href="<?= $topLevelChild->url ?>"><?= $topLevelChild->title ?></a></li>
      <?php endforeach; ?>
    </ul>

  </li>
  <?php endforeach; ?>

</ul>

..which outputs:

testing_single_home.jpg

So I must have $pages->children called earlier on the page somewhere (probably in the main menu above my test one) which would cause the double home link. I think @BitPoet was right earlier about the pointer.

This would also be why @kongondo and myself were getting different output.

  • 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...