Jump to content

How to get grand childs


wmushtaq
 Share

Recommended Posts

PW is extremely useful for developers. Loving it and learning alot from it.

I have a question. Lets say I have a site with a page 'Videos' that has 'Channels' as child pages and each channel has videos in them. I know the 'Videos' page id. So how can I get all videos through it? In other words how can I get all grand children of 'Videos' page?

I appologize if I missed something already mentioned in docs.

Link to comment
Share on other sites

this didn't work for me

$grandchildren = $page->children->children;

neither did this, or any other variations of this kind:

$grandchildren = $page->children()->children();

what worked for me was this:

$gp = $pages->get(5766);
$children = $gp->children;
foreach($children as $child){ // there was a ")" missing
$gchildren=$child->children; // corrected from $children to $child
foreach($gchildren as $gchild) echo $gchild->title;
}

you can also give all of the videos the same template and call them simply like this:

$pages->find("template=video") // corrected from get() to find() after soma's reply

or if you need the videos to have different templates:

$pages->find("template=video1|video2|video3") // also corrected from get() to find()

edit: Ryan, I also assumed parent->parent children->children would work. Why doesn't it? I'm sure there is a very interesting explanation involving methods and classes and lots of oop stuff ;)

Edited by diogo
  • Like 1
Link to comment
Share on other sites

You mean why "$page->children()->children()" didn't work? ("$page->parent->parent" actually works to get the grandparent of a page.)

Well it's simply that "children()" returns a 1 dimensional page array, so it works only with 1 explicit parent. As it would have to do a multidimensional page array which doesn't exists as far as I know.

So a simple loop is needed here like diogo provided. Though I would do it with some check and a little simpler. Also there's a typo in the example of diogo.

$channels = $pages->get(5766)->children();
foreach($channels as $channel) {
if($channel->numChildren){ // has any children?
	echo "<h3>" . $channel->title . "</h3>";
	foreach($channel->children() as $videopage) {
		echo "<p>" . $videopage->title . "</p>";
		echo "<p>" . $videopage->videourl . "</p>"; // or whatever you need
	}
}
}

Remember you could also add selectors to children("string").

Or if you want just to get all videos, you can use the ones diogo showed, but I'm not sure it will work or return a page array with only using "$pages->get("template=video")". I think something is missing, usually "get()" returns a single page.

Following is the best method to use. You could limit it to the "Video" branch, so it doesn't need to scan whole site.

$videos = $pages->get("/videos/")->find("template=video");
foreach($videos as $vid){ ...

Or what I think diogo wanted to write is something like this.

$videos = $pages->find("template=video");

But this will search through whole site structure. Something you might want to limit.

  • Like 1
Link to comment
Share on other sites

$page->children()->children()

This doesn't work because $page->children is an array of pages (PageArray), not a Page. Think of it like a boat carrying a bunch of people. The boat can't have children, but the people in it can.

I'm also not sure $page->children->children would be all that useful, because they would have lost all sorting context. However, I'm only thinking in context of the sites I make and know everyone has different needs. So if you want to have something equivalent to $page->children->children, it would be $page->find('id>0'). But I would suggest using Soma's examples as they are more specific to a template, and it's better to be specific.

You could limit it to the "Video" branch, so it doesn't need to scan whole site.

Nearly any search that gets passed through a selector is fully indexed, so it doesn't have to scan the whole site. If you put in "template=video" it [mysql] heads straight to an index of pages with the video template, so it's not scanning or considering other pages.

But this will search through whole site structure. Something you might want to limit.

You'd want to limit that if you were using the video template elsewhere in the site and didn't want videos from one branch getting mixed in with videos from another. However, if you know where you are using the video template and aren't going to be using it on other branches in the site, there's no speed advantage to performing the search within a parent $page rather than just using $pages->find() to do it. Technically, there's slightly more [minor] overhead doing a $page->find() than a $pages->find() because it's adding a lookup table to the search that may or may not need to be there.

  • Like 2
Link to comment
Share on other sites

Oh, sorry for the typo and errors... next time I will be sure I'm awaken when I post

edit: ok, now I understand why children->children could never work. But I think in some situations it can be useful to target pages like @wmushtaq asked. Maybe a parameter like this "deep=2" passed to children(), or even a method descendants() with this same parameter...

Link to comment
Share on other sites

Maybe a parameter like this "deep=2" passed to children(), or even a method descendants() with this same parameter...

If you wanted all grandchildren, and nothing deeper, you could do this:

$page->find("parent=$page->children"); 

If you wanted grandchildren and anything deeper, you could do this:

$page->find("parent!=$page");  

If you wanted children, grandchildren and anything deeper, you could do this:

$page->find(''); 
  • Like 6
Link to comment
Share on other sites

Thanks for this ryan - this one in particular makes sense in terms of the original post I think (assuming this is being called from the "Videos" page itself):

$page->find("parent=$page->children");
Link to comment
Share on other sites

(assuming this is being called from the "Videos" page itself)

If not, it can be called like this:

$gp = $pages->get(5766);
$gc = $pages->find("parent=$gp->children");
foreach($gc as $a) {
 echo $a->title;
 etc...
}
Link to comment
Share on other sites

  • 1 year later...

If you wanted all grandchildren, and nothing deeper, you could do this:

$page->find("parent=$page->children");

If you wanted grandchildren and anything deeper, you could do this:

$page->find("parent!=$page");

If you wanted children, grandchildren and anything deeper, you could do this:

$page->find('');

If not, it can be called like this:

$gp = $pages->get(5766);
$gc = $pages->find("parent=$gp->children");
foreach($gc as $a) {
  echo $a->title;
  etc...
}

Could someone please give me examples of using the selector "include=hidden" in the above examples by Ryan and Diogo? I tried this and wasn't able to fetch hidden grandchildren. How do I get "hidden" children? What about both "hidden" parent and grandchildren? Thanks.

Btw, in the API docs it says.

Check the box for the 'hidden' status when editing a page, which will hide it from find(), children() and siblings() methods.

Is this true for get() as well?

Thanks

/k

Link to comment
Share on other sites

include hidden works as any other selector. In the examples above:

$page->find("parent=$page->children, include=hidden");

$page->find("parent!=$page, include=hidden");

$page->find('include=hidden');

$gc = $pages->find("parent=$gp->children, include=hidden");

Is this true for get() as well?

I thought so, but just tried it, and surprisingly it returns the first page that is not hidden. So. I guess not.

But you can have the same result by doing:

$page->find('include=hidden')->first();

although it's more expensive

  • Like 1
Link to comment
Share on other sites

get() will get the page regardless of its status. It's a explicit call.

$pages->get(5766) will always get the page with id 5766 whether it's hidden unpublished or red or blue. So include=hidden won't do anything here.

  • Like 1
Link to comment
Share on other sites

actually, include=hidden does sort of work with get:

// 7|2|27|1000 are hidden

echo $page->find('parent=/'); // 1001|1003|1005

echo $page->get('parent=/'); // 1001

echo $page->find('parent=/, include=hidden'); // 2|7|27|1000|1001|1003|1005

echo $page->get('parent=/, include=hidden'); // 1001

//but

echo $page->find('parent=/, sort=-sort, include=hidden');  // 7|2|27|1000|1005|1003|1001

echo $page->get('parent=/, sort=-sort, include=hidden'); // 7
 

confused? Ya, me too...

edit: I guess get() gives priority to non hidden pages unless you explicitly sort them.

  • Like 1
Link to comment
Share on other sites

include hidden works as any other selector. In the examples above:

$page->find("parent=$page->children, include=hidden");

$page->find("parent!=$page, include=hidden");

$page->find('include=hidden');

$gc = $pages->find("parent=$gp->children, include=hidden");

Is this true for get() as well?

I thought so, but just tried it, and surprisingly it returns the first page that is not hidden. So. I guess not.

But you can have the same result by doing:

$page->find('include=hidden')->first();

although it's more expensive

Thanks Diogo. $pages-> confused me too in my tests. 

get() will get the page regardless of its status. It's a explicit call.

$pages->get(5766) will always get the page with id 5766 whether it's hidden unpublished or red or blue. So include=hidden won't do anything here.

Thanks Soma. Maybe this (@Ryan) should be exclusively stated in the docs, thanks)

Link to comment
Share on other sites

actually, include=hidden does sort of work with get:

// 7|2|27|1000 are hidden

echo $page->find('parent=/'); // 1001|1003|1005

echo $page->get('parent=/'); // 1001

echo $page->find('parent=/, include=hidden'); // 2|7|27|1000|1001|1003|1005

echo $page->get('parent=/, include=hidden'); // 1001

//but

echo $page->find('parent=/, sort=-sort, include=hidden');  // 7|2|27|1000|1005|1003|1001

echo $page->get('parent=/, sort=-sort, include=hidden'); // 7
 

confused? Ya, me too...

edit: I guess get() gives priority to non hidden pages unless you explicitly sort them.

OK.....seems there is something going on there. Strange...Did you test this on PW 2.3? 

EDIT: OK, but why?  :unsure:

Edited by kongondo
Link to comment
Share on other sites

It is 2.3,  but it's not that strange if this is the intention

edit: I guess get() gives priority to non hidden pages unless you explicitly sort them.
 
EDIT: i don't know :)
Edited by diogo
Link to comment
Share on other sites

$page->find() or $page->get() does nothing...

however if you use a selector in get(selector) it behave different and return first found entry and it's not specific, so include=all or include=hidden will have a effect.

But I meant $pages->get(id) or $pages->get("/path/") is explicit and will get the page regardless of it's state, it's a explicit API call.

  • Like 1
Link to comment
Share on other sites

Soma, I understand that $pages->get(id) is an explicit call, but get() is also useful to get what would be the first result of a find() without pulling everything from the database, just like doing $pages->find("limit=1"). Or, at least this is what I thought...

Link to comment
Share on other sites

Yeah nothing special here with get and selector that is not explicit calling a page, it behaves like a find with limit=1. 

The sorting is something I wondered too, sometimes you have to define sort=sort to get the manually sorted (also with find()), but then it's all normal and behaves as it should.

Behind the scenes $pages->get(selector) will call $pages->find() at the end.

You can also use $pages->findOne(selector) and will return the first matches page and return a Page not PageArray.

Link to comment
Share on other sites

confused? Ya, me too...

edit: I guess get() gives priority to non hidden pages unless you explicitly sort them.

I'm assuming those $page variables were actually supposed to be $pages? :) That first set of functions you called in your example have no sort defined. if you don't specify a "sort" in your selector to these functions, the order is either undefined, or by relevance. Functions where sort is inclusive are family functions connected with a page, like $page->children(), $page->child(), $page->siblings(), etc. You can expect those to use a predictable order. But a $pages->find() or $pages->get() with no "sort=" in it means you can't expect any particular order. Except when you are trying to partial match a string, it would attempt to sort them by relevance. 

If someone had a selector like yours, I would guess they probably wanted this instead:

echo $pages->get('/')->children("include=hidden"); // all 
// or
echo $pages->get('/')->child("include=hidden"); // first
Link to comment
Share on other sites

  • 1 year later...

Hi,

I would highly appreciate some help with selecting/ displaying grandchildren or nested pages

My page structure is:

Home

  |- Projects

     |- Nature

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

     |- People

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

     |- Animals

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

   ......

   and so on ...

   ......

   |- About

   |- Contact

I am having problems displaying recent 3 projects' Featured Images on the home page.

<?php     
$parent_page = wire('pages')->get('/projects/'); // Have used $pages->get('/projects/'); too
$parent_page_children = $parent_page->children(); 
Debugger::dump($parent_page_children); // Debugger::dump($parent_page_children); (Using Tracy) dumps 1 PageArray
foreach ($parent_page_children as $children) { 
Debugger::dump($children); // Debugger::dump($children); (Using Tracy) dumps 6 pages
if ($children->numChildren) {
    foreach ($children->children("is_featured=1, include=all, sort=-date, limit=3") as $child) { 
	Debugger::dump($child); // Debugger::dump($child); (Using Tracy) dumps 6 pages, currently 6 checked as Featured (Need only three) ?>
	<a href="<?php echo $child->link_to_page->url; ?>">
	    <img src="<?php echo $child->my_image->url; ?>" alt="<?php echo $child->image_desc; ?>"/>
	</a>
<?php	}; }; }; ?>

Currently I have 6 pages with featured checkbox checked for testing. The code above displays all 6 instead of limiting to recent 3 sorted by -date..

How can I get recent three to display on my homepage?

Again, any help would be highly appreciated. Thanks.

Link to comment
Share on other sites

if i was using pages for images, I would keep the images template published and not hidden, but i wouldn't make a php file for it.

you can easily find your featured images by using this:

$featured_images = $pages->find("template=image, featured=1");
  • Like 2
Link to comment
Share on other sites

@Kay Lohn: Your "...limit=3" selector fires several times - as many, as there are children under "Projects". And each time it limits maximum number of featured photoes under that category. So you can output a maximum of (3 x NumberOfProjectsChildrenPages) featured photo pages.

As Macrura suggested there are better/simplier ways to do what you intend to, like:

<?php foreach ($pages->find("template=photo_page_template, include=hidden, sort=-date, limit=3")) { ?>
	<a href="<?php echo $child->link_to_page->url; ?>">
		<img src="<?php echo $child->my_image->url; ?>" alt="<?php echo $child->image_desc; ?>"/>
	</a>
<?php }; ?>

Where photo_page_template is the template of you photo pages.

  • Like 1
Link to comment
Share on other sites

...and just to contribute to the original topic. I use this function to get all subpages of a page:

function getAllDescendants ($root, $prependRoot) {
	$descentants = new PageArray();
	foreach ($root->children() as $child) {
		$descentants->add($child);
		if ($child->numChildren()) {
			$descentants->add(getAllDescendants($child));
		}
	}
	if ($prependRoot) $descentants->prepend($root);
	return $descentants;
}

I include this function in _functions.php file which prepends every template file and use it like this:

<?php
$listingPages = getAllDescendants($pages->get('some selector'))->find("some selector");
foreach($listingPages as $listingItem) {
	...
};
?>
  • 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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...