Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

Template Questions /

Nested control blocks


Reply


8 Posts   5322 Views

Avatar
Romeo

Community Member, 67 Posts

22 August 2009 at 5:24am

I have a Links page which contains a list of Link objects each consisting of a Title, URL and Region. I want to display the links grouped by region. What I had in mind for this was to have a Control block backed by a Regions function in the LinkPage class which gives me the list of regions (in the order I want them), and within this a nested Control block listing the links for each region.

<% control Regions %>
<% control Links($region) %>
#Do something here
<% end_control %>
<% end_control %>

There are two problems here. Firstly, I can't work out how to grab the value I require within the Regions control block - it's just a string, from a string array, but I can't find any documentation which tells me how to get this value. Secondly, assuming I have got it, I don't believe I can pass it as a variable to the inner control block anyway - that's not allowed, is it?

Which means, of course, that I'm doing it wrong...But how should I do this? It seems a simple enough requirement.

Avatar
banal

Community Member, 901 Posts

22 August 2009 at 6:28am

Edited: 24/08/2009 9:48pm

Hi

To pass custom array-like structures to the template engine, you should usually use the [url=http://doc.silverstripe.com/doku.php?id=dataobjectset]DataObjectSet[/url].
And you're right about that variable ($region) in your template. That's not going to work.

In cases like yours, I usually wrap all my data in a DataObjectSet. For your Regions it could look like this:

// static definition of regions
protected static $regionStrings = array('Region A', 'Region B', 'Region C');

public function Regions(){
   $regions = array();
   foreach(self::$regionStrings as $str){
      $regions[] = array(
         'RegionTitle' => $str,
         'RegionLinks' => $this->Links($str)
      );
   }
   
   return new DataObjectSet($regions);
}

The above code simply walks through the regions (which are defined as static array) and adds the String from the Array as "RegionTitle" and the links as "RegionLinks" to an array, which is then returned as DataObjectSet.
Of course, you'll also have to implement the Links method, so that it takes a string as argument and returns the desired collection of links.
This could look like this:

public function Links($region){
   // check if region is one of the defined regions,
   // otherwise return false
   if(!in_array($region, self::$regionStrings))
      return false;
      
   // be sure to replace 'LinkClass' with the actual class
   // name of your Link DataObject. "Region = has to be replaced
   // with the name of the region field you would like to check
   // for a match.
   return DataObject::get('LinkClass', "Region = '$region'");
}

In the template, you can then write something along these lines:

<% control Regions %>
   <h2>$RegionTitle</h2>
   <% control RegionLinks %>
      <a href="$LinkUrl">$LinkTitle</a>
   <% end_control %>
<% end_control %>

The RegionTitle and RegionLinks variables are coming from the DataObjectSet that we built in the "Regions" function. If everything went fine, you should be able to access your Link-DataObject within the "RegionLinks" control block. I assumed the members of the Link class are named $LinkUrl and $LinkTitle, this could be different of course.

Edit: spelling

Avatar
Romeo

Community Member, 67 Posts

22 August 2009 at 6:38am

Fantastic, thanks! I'll give it a go and report back.

Avatar
Romeo

Community Member, 67 Posts

22 August 2009 at 9:02pm

Yes, it works a treat! Thanks ever so much. I'm hoping all of this will come much more easily to me soon. It's all slightly frustrating for me because I'm an experienced Java guy, used to knowing exactly how to do what I want to do with web applications in Java, but I'm new to PHP. Still, a lot of it is similar and I feel I'm making good progress.

Avatar
blueskies

Community Member, 42 Posts

25 March 2010 at 3:20am

Thank you! This was exactly what I was looking for :-)

Avatar
Xurk

Community Member, 50 Posts

15 June 2010 at 2:24am

Thanks a lot for your detailed post, banal :) It really opened my eyes as to how to manipulate data from inside a control, without having to attempt to nest another control. Pointed me in the right direction with instant success!

Avatar
Xurk

Community Member, 50 Posts

21 June 2010 at 8:42pm

Edited: 21/06/2010 8:47pm

Hmm, now that I'm actually trying to apply this example to a DataObject which has a nested DataObject inside it, I can't manage to get to the finish line. I'm stuck on the final lap, I think :)

I've got the call to another function (QuestionAuthor) setting the value of a property in the DataObject being returned and it looks like this:

function LatestQuestions($iNum = 10) {
   $oQuestions = DataObject::get("Question", "", "Created DESC", "", $iNum);

   foreach ($oQuestions as $oQuestion => $data) {
      $data->Author = array($this->QuestionAuthor($data->Author));
      $data->Answers = $this->NumAnswers($data->ID);
   }

   return $oQuestions;
}


function QuestionAuthor($iAuthorID) {
   $oAuthor = DataObject::get_by_id("Member", (int) $iAuthorID);

   $dsoAuthor = new DataObjectSet();
   $dsoAuthor->push(new DataObject(array(
      'Avatar' => $oAuthor->data()->AvatarPath,
      'Name' => $oAuthor->data()->FirstName." ".$oAuthor->data()->Surname,
      'Occupation' => $oAuthor->data()->Occupation
   )));

   return $dsoAuthor;
}

And then this is what I have in my template:

<ul id="questionsList">
<% control LatestQuestions(5) %>
   <li>
      <div class="author">
      <% control Author %>
         <img src="$Avatar" width="35" height="35" alt="" />
         <p>
            <span class="name">$Name</span><br />
            <span class="title">$Occupation</span>
         </p>
      <% end_control %>
      </div>

      <p class="question">
         <a href="questions/$ID/$Title" class="title">$Title</a><br />
         <span>posted on $Created.format(d F Y) ($Answers responses)</span>
      </p>

      <a href="questions/$ID/$Title#AnswerForm" class="button">respond &raquo;</a>
   </li>
<% end_control %>
</ul>


So what my problem is, is that I can't seem to access the variables set inside the nested control ($Name, $Occupation and $Avatar). I've been var_dump'ing the complete DataObjectSet returned by the parent control and everything seems to be in place (here is an excerpt with some irrelevant stuff filtered out for readability):

object(DataObjectSet)#70 (16) {
   array(5) {
      [0]=>
      object(Question)#71 (15) {
         array(11) {
            ["ClassName"]=> string(8) "Question"
            ["Created"]=> string(19) "2010-06-18 14:22:35"
            ["LastEdited"]=> string(19) "2010-06-18 14:22:35"
            ["Title"]=> string(12) "A test entry"
            ["Author"]=> array(1) {
               [0]=>
               object(DataObject)#79 (15) {
                  array(3) {
                     ["Avatar"]=> string(32) "assets/Uploads/av/testavatar.jpg"
                     ["Name"]=> string(20) "Lonneke Ceelie"
                     ["Occupation"]=> string(12) "Editor"
                  }
               }
            }
            ["Content"]=> string(50) "Lorem ipsum test test test"
            ["QuestionHolderID"]=> string(2) "30"
            ["DocumentID"]=> string(1) "0"
            ["ID"]=> int(6)
            ["RecordClassName"]=> string(8) "Question"
            ["Answers"]=> int(5)
         }
      }
   }
}

Yet in my nested control, I can't seem to access the three variables I want to. I've tried a few different things, like the second function returning (instead of a DataObject as above) a plain Array, an ArrayData object and a DataObjectSet, but the results are similar to the above each time. I also tried i.e. $Author.Occupation in the nested control, but that accomplishes nothing either.

The only time I can get anything to print onscreen is if I remove the <% control Author %> and replace it with $Author, which prints out "Array" onscreen for me.

Am I missing something completely obvious here? Any pointers would be much appreciated :)

Avatar
inCharge

Community Member, 102 Posts

8 July 2010 at 11:33pm

Maybe you need to use Top.RegionLinks to put it into the parent context.

For a full explanation see http://silverstripe.org/template-questions/show/287958#post287958