2
votes

Using SilverStripe 3 when in the CMS looking at draft preview mode I get the following error:

[User Error] Uncaught Exception: Object->__call(): the method 'featuredimage' does not exist on 'PortfolioPage'

enter image description here

This only happens in draft mode, not in published mode. I'm confused as to why. It is fine on the front end and does what it is supposed to but throws the error in draft.

PortfolioPage.ss

<div class="content-container unit size3of4 lastUnit">
    <article>
        <div class="content">
            $Content
            <div id="container">
                <% loop $DescendantFeaturedImages %>
                <% if $FeaturedImage %>
                <div class="item $Parent.URLSegment">
                    <a href="$Link" title="$Parent.Title - $Title">
                        <img src="$FeaturedImage.URL" width="100%" />
                        <div id="mask">
                            <span class="TitleContainer">
                                <h4>$Parent.Title</h4>
                                <p>$Title</p>
                            </span>
                        </div>
                    </a>
                </div>
                <% end_if %>
                <% end_loop %>
            <div>
        </div>
    </article>
    $Form
    $PageComments
</div>

PortfolioPage.php

class PortfolioPage extends Page {
}

class PortfolioPage_Controller extends Page_Controller {

    public function init() {
        parent::init();
        Requirements::css( 'gallery/css/gallerystyles.css' );
    }

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

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

        shuffle( $featuredImages );

        return ArrayList::create( $featuredImages );
    }
}
1
Where does FeaturedImage come from? Is this in Page.php? - 3dgoo
Just to answer your question @3dgoo FeaturedImage is on its on page type in this case GalleryPage.php. The idea was PortfolioPage was a holder and displayed the featured image for all its grand child pages (GalleryPages). - pinkp

1 Answers

2
votes

@3dgoo is on point, the error is being thrown because of $grandChild not having a function called FeaturedImage. If it isn't defined on any of the classes in the ancestry of your page type (as either your own function on one generated from a has_one relationship), that error will be thrown. Looking at the call stack, you can see it is a PortfolioPage object that is the $grandChild.

One reason this would happen on draft only is that the site tree on your draft site isn't the same as the published tree.

To avoid this error on this particular page type, you will need to define a function (or has_one relationship) called FeaturedImage on your PortfolioPage class.

To avoid this error on all page types, you could do either of the following:

  1. Check if the FeaturedImage function exists
  2. Make the FeaturedImage function exist on a common parent class

Check if the function exists

To check if a function exists, it is best to use the built-in hasMethod function. This function doesn't just check for manually defined methods, it also can correctly handle methods added via a DataExtension.

Your code would look something closer to this:

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

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

    shuffle( $featuredImages );

    return ArrayList::create( $featuredImages );
}

Define function on a common parent class

Majority of the time, page types you would write and ones from various modules will inherit from the Page class.

Under the presumption that you intend your FeaturedImage to be a has_one to an image directly, it is just this:

class Page extends SiteTree {

    // ... any other statics you defined here ...

    private static $has_one = array(
        'FeaturedImage' => 'Image'
    );

    // ... any other functions or bits of code below here ...
}

If your PortfolioPage is intended to be a module for others to use, you don't want to have to make them define it in their own Page classes manually, you would want to use a DataExtension.

class PageExtension extends DataExtension {

    // ... any other statics you defined here ...

    private static $has_one = array(
        'FeaturedImage' => 'Image'
    );

    // ... any other functions or bits of code below here ...
}

And then just add the extension by including this in your YML file:

Page:
  extensions:
    - PageExtension