1

I'm trying to get a specific image ($FeaturedImage) from every grandchild page (GalleryPage.ss) of my Portfolio Page (PortfolioPage.ss) and display them in a random order.

I can get the images using the template easily enough. PortfolioPage.ss

<% loop Children %>
 <% loop Children %>
   <% loop FeaturedImage %>
      <img src="$Url"></>
   <% end_loop %>
 <% end_loop %>
<% end_loop %>

But this will display them in the order of the pages in the menu.

After some research it seems best to create a function in the page controller, but I'm not sure how to write this.. (anyone with a link to documentation / tutorials on these would also be great).

Examples of similar code found so far: get Dataobjects from Children - SilverStripe 3.1 http://www.silverstripe.org/template-questions/show/23296

Silverstripe docs: http://doc.silverstripe.org/framework/en/topics/datamodel

I'm just not sure how to apply this to my code..thanks

Community
  • 1
  • 1
pinkp
  • 445
  • 2
  • 12
  • 30
  • If I understand you correctly, you only want the images from grandchildren, not children or grandgrandchildren? Or doesn't it matter? If not you can recursively loop through Children in your template. Give me a shout if this is sufficient enough for you. – Marius Engen Haugen Dec 12 '13 at 12:41
  • I want an image from grandchildren pages. They have a chosen image per page which I want to display on their grandparent page in a random order. I can loop in the template just fine to get the grandchild images just not sort them randomly.. – pinkp Dec 12 '13 at 23:22

2 Answers2

3

Basically you need to create a function in your Portfolio Page Controller (or in whatever page you need this logic to be).

Here are 2 examples. First one just gets all the existing FeaturedImage from the database and returns then in random order:

function AllFeaturedImages()
{
    return FeaturedImage::get()->sort('RAND()');
}

And this one get all the FeaturedImage from the page's children's children and return them in random order:

function DescendantFeaturedImages()
{
    $featuredImages = array();

    foreach ($this->Children() as $child)
    {
        foreach ($child->Children() as $grandChild)
        {
            $images = $grandChild->FeaturedImage();
            if ( $images )
            {
                $featuredImages = array_merge( $featuredImages, $images->toArray() );
            }
        }
    }

    shuffle($featuredImages);

    return ArrayList::create($featuredImages);
}

If the FeaturedImage relation is just a has_one, this changes a little bit:

function DescendantFeaturedImages()
{
    $featuredImages = array();

    foreach ($this->Children() as $child)
    {
        foreach ($child->Children() as $grandChild)
        {
            $image = $grandChild->FeaturedImage();
            if ( $image )
            {
                array_push( $featuredImages, $image );
            }
        }
    }

    shuffle($featuredImages);

    return ArrayList::create($featuredImages);
}

Then in your Portfolio Page template you can just loop through the FeaturedImage by calling the function name. So here, either $AllFeaturedImages or $DescendantFeaturedImages. In your case you'll get something like:

<% loop $DescendantFeaturedImages %>
    <img src="$URL"/>
<% end_loop %>

I could find one example in the SilverStirpe tutorials using a Controller function: http://doc.silverstripe.org/framework/en/tutorials/2-extending-a-basic-site

Let me know how this goes.

colymba
  • 2,644
  • 15
  • 15
  • Thanks so much for your thorough answer! Unfortunately I'm just getting a "server error" when I refresh the Portfolio page... The page just shows an un-styled version with no content or theme. I've used the second example and the template code you supplied. What more information can I supply to see what I'm doing wrong? – pinkp Dec 12 '13 at 19:55
  • @pinkp did you flush your templates? – Marius Engen Haugen Dec 13 '13 at 09:21
  • You should get more than just "server error", what's the error? Unless your site is not in dev mode... Also, I was just guessing the DataObject relations from your template (post a link to your classes to make sure). I've also fixed a typo in the template code above. – colymba Dec 13 '13 at 10:51
  • @colymba, My class on GalleryPage.php is FeaturedImage & Flushing the template gave me the actual error: [User Error] Uncaught Exception: Object->__call(): the method 'toarray' does not exist on 'Image' default : 742 throw new Exception ( 743 "Object->__call(): extra method $method is invalid on $this->class:" 744 . var_export($config, true) 745 ); 746 } 747 } else { 748 // Please do not change the exception code number below. 749 $class = get_class($this); 750 throw new Exception("Object->__call(): the method '$method' does not exist on '$class'", 2175); – pinkp Dec 15 '13 at 21:09
  • 1
    My example assumed `->FeaturedImage()` returned a `ManyList`, but apparently it is just a `has_one` relation. Updated the example above for this, using `push` on each image object instead. – colymba Dec 16 '13 at 07:58
  • @colymba That's great and is showing the FeaturedImage's now from the grandchild pages. But it is not sorting them randomly. It seems to be in the order of creation...? – pinkp Dec 16 '13 at 12:15
  • 1
    my bad, ArrayList doesn't support RAND for sorting. Updated the example to pass through normal array first. – colymba Dec 16 '13 at 13:04
  • @colymba Awesome! That was it thank you! One more thing.. Do you know how I can now link to the FeaturedImage's page... For example: <% loop $DescendantFeaturedImages %> <% end_loop %> Link & Title only give me the image link and title, how do I get the page information. I've tried $Up and $Top but this gives the current page and not the grandchild page of the FeaturedImage. Thanks – pinkp Dec 16 '13 at 13:13
  • 1
    You'll have to return a list of `Page` not `Image` for that. Basically inside the 2nd foreach just do `array_push( $featuredImages, $grandChild );` which will store the page holding the FeaturedImage. Then in the template you should be able to do something like ``... if this is unclear, best is to open a new question. – colymba Dec 16 '13 at 15:35
  • How would you alter the function to just apply to Child pages? – pinkp Jan 09 '14 at 10:54
2

I tried Colymba's code, and it worked like a champ. I would recommend following his code over the method I'm gonna explain below.

As you say in a comment, you can access grandparent images from template. You can use JavaScript, or as in this example jQuery, to randomly sort your images.

(function($){

    $.fn.shuffle = function() {

        var allElems = this.get(),
            getRandom = function(max) {
                return Math.floor(Math.random() * max);
            },
            shuffled = $.map(allElems, function(){
                var random = getRandom(allElems.length),
                    randEl = $(allElems[random]).clone(true)[0];
                allElems.splice(random, 1);
                return randEl;
           });

        this.each(function(i){
            $(this).replaceWith($(shuffled[i]));
        });

        return $(shuffled);

    };

})(jQuery);

And then call the function on the elements you want to sort randomly:

$('#imgholder img').shuffle();

A more thorough explanation can be found on css-tricks.com