Freemium
Freemium

Reputation: 460

SilverStripe How to get Children from multiple page types (back-end and output)

I didn't know how to write the question using just words so here's a nifty little ascii diagram of what I got going on for your reference before I begin explaining...

                                                +-----+
  Department             Department             |Shows |
       +                     +                  +--+--+
       |                     |                     ^
       +->ShowAssignment     +>ShowAssignment       |
                +                                  |
      +------------------------+                   |
      |         |              |                   |
      v         v              v                   |
AssignShow   AssignShow   AssignShow                |
 +                                                 |
 |   +------+                                      |
 +-->Content|                                      |
 |          |                                      |
 +-->Content+--------------------------------------+
 |          |
 +-->Content|
     +------+

BTW - moving forward lets pretend that we're trying to get the first AssignShow with it's 3 child articles.

Alrighty, so now you've got a visual of what's up let's get to it.

In every Department there is a ShowAssignment page and under it every child page is an AssignShow page that can have Content pages relating to the work a particular department is doing on a show.

In the Shows pages there is a section which finds all the departments that have been assigned to it and lists it out on the page. I have been able to get as far as retrieving all the assigned departments but cannot seem to be able to get down to getting the all the content of what each department is doing for that show...

More visuals :) I have the departments in big blue and where 'Static fo da mo' is I need the title of those articles and a link to its page

More visuals :)

Here is the code I currently have :

Shows.php

 # Get Departments assigned to this show
 public function getAssignedDepartments(){

    $result = new ArrayList();

    # use this shows ID to find out what shows have been selected by departments
    $assignedShowID = AssignShow::get()->filter('ShowsID', $this->ID);

    if(count($assignedShowID) > 0){

        foreach($assignedShowID as $dept){

            $department = Department::get()->byID($dept->DepartmentID);

            $result->add(new ArrayData(array(
                'DepartmentTitle' => $department->Title
                )
            ));

        # This here is where i'm super stuck... I've managed to drill down
        # this far but dont know how to get those darn kids!
        $x = $department
            ->Children()
            ->find('ClassName', 'ShowAssignments')
            ->Children();
        }

        # Title (on the first echo) returns the
        # show title so I know it's targeting correctly...
        foreach ($x as $key) {
            echo $key->Title . '<br>';
            echo $key->Children()->Title . '<br>';
        }

        return $result;
    }
    else
        return null;
 }

So all in all my latest attempt of getting the children got me as far as getting the Title of the AssignShow page but using something like that $key->Children() doesn't let me go 1 step deeper... What do I need to do?


Edit, new info

Righty-o, so I've managed to get the children and able to access their info from the help given by bummzack

Update

If you just need to traverse the hierarchy in code, you should be aware that Children is a List, so something like $this->Children()->Title won't work. You'll need something like:

 $children2LevelsBelow = array();
 foreach ($this->Children() as $child) {
    // Go one level deeper…
    foreach ($child->Children() as $subChild) {
        $children2LevelsBelow[] = $subChild;
    }
 }

I think this is the key part that was missing from the code posted with your question.


The code I have at the moment is like this (still in development so its a bit incomplete, but the answer above has assisted me in getting closer)

Show.php

 # Get Departments assigned to this show
 public function getAssignedDepartments(){

     $result = new ArrayList();

     # use this shows ID to find out what shows have been selected in departments
     $assignedShowID = AssignShow::get()->filter('ShowsID', $this->ID);

     if(count($assignedShowID) > 0){

         foreach($assignedShowID as $dept){

             $department = Department::get()->byID($dept->DepartmentID);

             $result->add(new ArrayData(array(
                 'DepartmentTitle' => $department->Title,
                 'Link' => '/film/departments/' . $department->URLSegment
                 )
             ));

             # this gets me what I need... ALMOST -_-
             foreach($dept->Children() as $x) {
                 echo '<br>' . $x->Title . '<br>'; # get AssignShow child Title
                 echo $x->Content; # gets AssignShow child content (not that i need it)
             }
         }
         return $result;
     }
     else
         return null;
 }  

What I don't understand is how do I output a list of content under every department...
So for every grid-listing in the HTML below it'll need 1 or more <li>'s to go with it before the next block runs...

Here is a snippet of the HTML / template I'm using this for

Shows.ss

 <% loop AssignedDepartments %>

    <div class="grid-listing">

        <h2><a href="$Link">$DepartmentTitle</a></h2>

        <ul>
            <%-- How do I loop in a loop to get a list of ALL li --%>
            <%-- before moving onto the next department in the main loop? --%>
            <li>&rsaquo; <a href="#NoLink">$ContentTitle</a></li>
            <%-- end_loop --%>
        </ul>

    </div><!-- . grid-listing -->

 <% end_loop %>

Upvotes: 2

Views: 1044

Answers (2)

Freemium
Freemium

Reputation: 460

I done did it! It was the querying that was a bit complicated and I was going to resort to using SQL but managed to stick with SilverStripes ORM. Additionally I was trying to get the data within the same function and using the same $result->add() which is probably why I found it so difficult...

This is the order of how it works.

Shows.php
We get the departments that are assigned to the show we're viewing. We put in a return value for the ID of the department, we'll be using that later

 # Get Departments assigned to this show
 public function getAssignedDepartments(){

     $result = ArrayList::create();

     # use this shows ID to find out what shows have been selected in departments
     $assignedShowID = AssignShow::get()->filter('ShowsID', $this->ID);

     if(count($assignedShowID) > 0){

         foreach($assignedShowID as $dept){

             $department = Department::get()->byID($dept->DepartmentID);

             $result->add(ArrayData::create(array(
                 'Title' => $department->Title,
                 'Link' => '/film/departments/' . $department->URLSegment,
                 'DepartmentID' => $dept->DepartmentID
                 )
             ));
         }

         return $result;
     }
     else
         return null;
 }

Shows.ss
We use the above to get the departments assigned to the show and loop through them. While looping we need a second loop to get the children of that department, we need it to loop through all the children before moving onto the next department loop. To ensure we're getting the right content we pass in the DepartmentID to use in the getDepartmentContent function

 <% loop AssignedDepartments %>

     <div class="grid-listing">

         <h2><a href="$Link">$Title</a></h2>

         <ul>
             <% loop $Up.getDepartmentContent($DepartmentID) %>
             <li>&rsaquo; <a href="$Link">$Title</a></li>
             <% end_loop %>
         </ul>

     </div><!-- . grid-listing -->

 <% end_loop %>

Shows.php
Now we get the children, this was the part I had quite a lot of trouble with. We use the DepartmentID we passed in to help us filter relevant content, otherwise it'll get all the content assigned to the show and output it for every department, which isn't accurate. We also build the URL using the URLSegments

 public function getDepartmentContent($DepartmentID){

     # filter result to get the AssignShow which matches this show
     # and the department id supplied param
     $assignedShowID = AssignShow::get()->filter(array(
         'ShowsID' => $this->ID,
         'DepartmentID' => $DepartmentID
         )
     );

     $result = ArrayList::create();

     foreach($assignedShowID as $key){

         foreach($key->Children() as $children){

             # AssignShow
             $assignShow = AssignShow::get()->byID($children->ParentID);

             # ShowAssignment
             $showAssignment = ShowAssignments::get()->byID($assignShow->ParentID);

             # Department
             $department = Department::get()->byID($showAssignment->ParentID);

             # full url path
             $link = $department->URLSegment . '/'
                     . $showAssignment->URLSegment . '/'
                     . $assignShow->URLSegment . '/'
                     . $children->URLSegment;

             $result->add(ArrayData::create(array(
                 'Title' => $children->Title,
                 'Link' => 'film/departments/' . $link
                 )
             ));
         }
     }

     return $result;
 }

This way here provides me with exactly what I needed to retrieve in the way I needed it. It also kept everything contained in the Controller to keep it neat.

Upvotes: 0

bummzack
bummzack

Reputation: 5875

I guess you have a relation from Shows to Department, maybe has_many or many_many?

So you should be able to do something like this in your Shows template:

<% loop $Departements %>
<div class="departement">
    <h1>$Title</h1>
    <% loop $Children %>
    <div class="show-assignment">
        <h2>$Title</h2>
        <% loop $Children %>
        <div class="assign-show">
            <h3>$Title</h3>
            <% loop $Children %>
                <div class="content">
                    <h4>$Title</h4>
                </div>
            <% end_loop %>
        </div>
        <% end_loop %>
    </div>
    <% end_loop %>
</div>
<% end_loop %>

While it looks mesmerizing, a template like this is rather ugly and doesn't play well if you ever plan to change the hierarchy…

Instead, you could just have a special method in your Page class that renders it's children recursively.

Eg. create a method like this in your Page class:

public function RecursiveChildren(){
    return $this->renderWith(array('RC' . $this->ClassName, 'RCPage'));
}

This simply renders the current page with an RC<ClassName>, or RCPage template, depending on what is available. The minimal required template would be RCPage.ss and could look like this.

<div class="$ClassName">
    <h1>$Title</h1>
    <% if $Children %><% loop $Children %>
        $RecursiveChildren
    <% end_loop %><% end_if %>
</div>

What you can then do, is replace the complex template above with something like this:

<% loop $Departements %>
$RecursiveChildren
<% end_loop %>

And it will create pretty much the same output as the complex template above.

To design different templates for each page type, you could go ahead and create: RCDepartment.ss, RCShowAssignment.ss etc. each responsible for rendering the fragment of said page-type.

Update

If you just need to traverse the hierarchy in code, you should be aware that Children is a List, so something like $this->Children()->Title won't work. You'll need something like:

$children2LevelsBelow = array();
foreach ($this->Children() as $child) {
    // Go one level deeper…
    foreach ($child->Children() as $subChild) {
        $children2LevelsBelow[] = $subChild;
    }
}

I think this is the key part that was missing from the code posted with your question.

Upvotes: 2

Related Questions