Jump to content

How to add children to an instance of Page using the API?


bytesource
 Share

Recommended Posts

 I created some product categories in the page tree that are connected to a page reference field product_category:

Category pages
Categories (template: categories_top_level)
---- Drilling Equipment (template: categories_first_level)
--------  abc (template: categories_second_level)
--------  def  
---- Titanium Products (template: categories_second_level)
--------- ghi (template: categories_second_level)
--------- jkl
Field
Name: product_category
Type: Page
Select: Single select (radio button)
Choose page from: template categories_second_level)


I then added the reference field to the template offer.

Special offer pages (template: offer)
-- product 1 (product_category field, selected: def)
-- product 2 (product_category field, selected: abc)
-- product 3 (product_category field, selected: ghi)
-- product 4 (product_category field, selected: abc)

Each product page has a side navigation that lists all products sorted by the PARENT of their selected category (in this case ‘drilling equipment’ of ‘titanium products’)

The navigation function expects a page array for the menu categories. The pages children will be output as links:

Menu
Drilling Equipment (category page title, trigger for accordion)
--- abc (link to page abc)
--- def
Titanium Products (category page title, trigger for accordion)
--- ghi


The code I came up with constructs a new Page object for each category. Its children need to be the product pages belonging to that category:


// navigation
$nav = new TemplateFile(wire('config')->paths->templates . 'snippets/sidenav-accordion.php');
$nav->set('subject', "Special Offers");

// Output folders 'drilling equipment', 'titanium products'
$categories = $pages->find('template=categories_first_level, include=hidden');

// Prepare parents and children to be output in sidemenu
// Subject: Special Offers
// Parents' Titles: Drilling Equipment, Titanium Equipment
// Children: Offers of the respective category (drilling equipment of titanium equipemnt)
$parents = new WireArray();
foreach($categories as $category) {
  $p = new Page(); // Will be sent to sidemenu, so we need to prepare a title and its children.
  $p->set('template', 'special-offers'); // We have to attach some template
  $p->title = $category->title;

  // Prepare children
  $children = new WireArray();
  foreach($pages->find('template=offer') as $offer) { // get all offers
    // add offer as a child of $parent if the PARENT
    // of its selected category (a page reference) matches $parent (= same category)
    if ($offer->product_category->parent->url == $category->url) {
      $children->append($offer);   // This does work
    }
  }
  $p->children = $children;  // This does NOT work
  $parents->append($p);
}

$nav->set('items',       $parents);
$nav->set('currentPage', $page);

$sidenav =  $nav->render();

include('./main.inc');

The above code does work - up to the point where I need to add a parent’s children. Therefore I would like to ask if there is a way to add children to a page instance using the API. Also, is constructing page instances outside the page tree an accepted practice or would it be better to approach this problem from another angle?

Cheers,

Stefan

Link to comment
Share on other sites

I just found a typo. The correct assignment of templates for the category pages is as follows:

Category pages

Categories (template: categories_top_level)

---- Drilling Equipment (template: categories_first_level)

--------  abc (template: categories_second_level)

--------  def  

---- Titanium Products (template: categories_first_level)

--------- ghi (template: categories_second_level)

--------- jkl

Link to comment
Share on other sites

You can't have "children" or "parents" in a Page or WireArray. It's a one dimensional thing.

You could create your own 2 dimensional array storing the id's and then iterate that to create the navigation.

You could create the navigation directly from the categories and list pages that belong to the current category.

I'm not sure about your menu example, as I don't see where the products are and such, it's just the categories as is. So without really see all context and needs according to your code I would do it like this.



$navcode = '<ul>';
foreach($categories as $category) {
  $navcode .= "<li>$category->title";
  if($children = $pages->find("template=offer,product_category=$category")){
    $navcode .= "<ul>";
    foreach($children as $offer) {
      $class ($offer === $page) ? " class='active'" : '';
      $navcode .= "<li$class><a href='$offer->url'>$offer->title</a></li>";
    }
    $navcode .= "</ul>";
  }
  $navcode .= "</li>";
}
$navcode .= "</ul>";

$nav->set('navcode', $navcode);
Link to comment
Share on other sites

You can't have "children" or "parents" in a Page or WireArray. It's a one dimensional thing.

What I wanted to say is that each parent page of the WireArray (the category) has children (the product pages belonging to that category). However, I am not sure how to add children to a Page instance. At least the following code did not work:

  $p = new Page();
  $p->set('template', 'special-offers'); 
  $p->title = $category->title;

  $children = new WireArray();
  foreach($pages->find('template=offer') as $offer) {
    if ($offer->product_category->parent->url == $category->url) {
      $children->append($offer);   
    }
  }
  $p->children = $children;  // This does NOT work
  $parents->append($p);

Therefore I would like to know if a page's children cannot be set at all or if I just made a mistake. 

That said, your solution is much cleaner than mine and I love learning from other peoples' code. 

There is just one problem: I want to output the parent of the selected category. For example, if a product page belongs to category 'abc', then in the menu it should be listed under that category's parent, which in this case would be 'drilling equipment'. So the following code does not work, unfortunately (here $category is a PageArray containing the category pages 'drilling equipment' and 'titanium products'):

$children = $pages->find("template=offer,product_category=$category")

$category is either 'drilling equipment' or 'titanium products', but product_category always references a child of $category. So there won't be equality.

Instead of using offer.product_category, I would need to refer to the selected category page's parent. I guess this requirement cannot be expressed using a selector, but I would be glad if I was wrong. 

Cheers, 

Stefan

Link to comment
Share on other sites

No, again you can't add children or have parents on a self created PageArray or WireArray.

For the other thing I think there's some easy solution, just can't make any sense atm with those sub categories... however to get it to work I think you can simply check against all children of the category.

if($children = $pages->find("template=offer,product_category={$category->children}")){ ...
  • Like 1
Link to comment
Share on other sites

No, again you can't add children or have parents on a self created PageArray or WireArray.

I guess I won't forget that anymore  ;)

For the other thing I think there's some easy solution, just can't make any sense atm with those sub categories...

The value of the product_category page reference is a child of the parent category I need to output. That's why the comparison inside the selector is not as straightforward as usual.

if($children = $pages->find("template=offer,product_category={$category->children}")){ ...

You solution is working like a charm! I did not know a selector could compare a field value against an array of values (at least not without imploding the values and placing a '|' in between each one).

This is my final implementation, which essentially is your solution:

$categories = $pages->find('template=categories_first_level, include=hidden');

$sub_categories = $category->children('include=hidden');
if($children = $pages->find("template=offer,product_category={$sub_categories}")){

Thank you so much for your help!

Cheers, 

Stefan

Link to comment
Share on other sites

Ok I thought there maybe a way to construct PageArray's. You can't alter the children built in property I think as it is in fact a method to retrieve the children from the DB on runtime using selectors $page->children($selector), but you could add a custom value on runtime memory and make it another PageArray. Then add some children to it. Since you can't use "children" just have to name it different for example "mychildren" and it works. You can also then use all the PageArray methods on them as ususal. You will just have still the children() and parent(), siblings() etc available but they would be in the original context of where the page originally is and not in your pseudo PageArray.

Here's a test script might useful for many others too.

// note just simple test pseudo code

// base array object
$pa = new PageArray();

// get some pages to work with
$category = $pages->get("/templates/");
$about = $pages->get("/about/");
$home = $pages->get("/");

// add new PageArray property "mychildren"
$category->mychildren = new PageArray();

// add other pages in
$category->mychildren->add($about);
$category->mychildren->add($home);

// you can sort them 
$category->mychildren->sort("-created");

// add the $category to the base array
$pa->add($category);

// output the nested PageArray using mychildren
echo "<ul>";
foreach($pa as $p){
    echo "<li><a href='$p->url'>$p->title</a>";
    echo "<ul>";
        foreach($p->mychildren as $child){
            echo "<li><a href='$child->url'>$child->title</a></li>";
        }
    echo "</ul>";
}
echo "</li></ul>";

Gives a nested list:

Templates

  • About 2
  • Home
  • Like 5
Link to comment
Share on other sites

That is exactly what I was looking for! All of my side menus use the same template, and on most pages I just output parents and their children. Now all I need to do is add an additional test for the existence of a variable like 'mychildren' and process that one instead of the built-in children PageArray. 

So many great tips in just one thread. Once again thank you for your help!

Cheers, 

Stefan

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