SilverStripe

Post image for SilverStripe

SilverStripe is now my CMS of choice, I have a few SilverStripe projects under my belt I’m enjoying developing CMS driven websites and unlocking some of the power of Sapphire, the framework underpinning the SilverStripe CMS.

There is a learning curve attached however, which led me to start documenting little snippets that make life a little easier.

SilverStripe Tutorials

I have written a few posts detailing how to accomplish different tasks on SilverStripe. Some of the more popular tutorials are:
How to create a basic image gallery in SilverStripe and
Installing the SilverStripe membership module.

Other articles include:
Set up PayPal express module with SilverStripe
Getting started with PHPUnit and SilverStripe Unit testing
HTML emails with inline CSS in SilverStripe
Add google maps to SilverStripe pages
Submit UserForms module forms via AJAX
How to change the breadcrumb seperator on SilverStripe
How to access SilverStripe session data
How to install the SilverStripe recaptcha module

SilverStripe Templates

Resize an image and get URL on the fly in template file:

<% control Image %>
<% control SetSize(blah, blah) %>
$FileName
<% end_control %>
<% end_control %>

Using GroupedBy to organise a listing of Pages or DataObjects:

<% control AllChildren.GroupedBy(Province) %>
	<h2>$Province</h2>
 
	<% control Children %>
	$Title
        $Content
	<% end_control %>
 
<% end_control %>

Check page class in the template:

If on the home page do not show the SilverStripe navigator.
<% if ClassName != HomePage %>
$SilverStripeNavigator
<% end_if %>

Using renderWith() from a DataObject to give the view access to the data and methods of that object:

//Fields and methods in the someObject class will be accessible to the view
$content = $someObject->renderWith("SomePage");

You can also pass data to the template by using customise():

$someViewableDataObject->customise(array(
	"Data" => $someData
))->renderWith('SomeTemplate');

Return an array of data in a DataObjectSet for access in the template:

  public function getAllProvinces() {
        //Get all the children for this page then pull out the provinces
 
        $currentPage = $this->data();
        $pages = $currentPage->AllChildren();
 
        $allProvinces = array();
 
        $i = 0;
        foreach ($pages as $page) {
            $allProvinces[$i]['provinceID'] = $page->Province;
            $allProvinces[$i++]['provinceName'] = $page->strProvince;
        }
 
        return new DataObjectSet($allProvinces);
   }

Adding comments to template files that will not be rendered:

<%-- Comments marked like this will be excluded from the HTML output --%>

If you ever have the problem of sitetree_link ids appearing in your anchor tags like:

<a href="[sitetree_link id=x]"></a>

it is likely because the content containing the link is passed as plain text rather than as an instance of HTMLText.

$content = new HTMLText(); 
$content->setValue('html with the anchor tag in it here');

SilverStripe DataObjects

Setting default values for database fields, the $defaults array in SilverStripe DataObjects makes all new objects created have the default, it does not alter already saved objects (rows in the database).

static $db = array(
        'Mailto' => 'Varchar(100)'
);
static $defaults = array(
        'Mailto' => 'info@example.com'
);

Accessing the model (Page) from the controller, in Page_Controller this will return the Page object.

$this->data();

Accessing a field in a DataObject

$this->getField("Field");
//or
$this->Field;
//or
$this->dataRecord->MyField;

Sometimes its useful to access a field from a DataObject and return the object that represents this field

public static $db = array(
  'Amount' => 'Money'
);
 
//Elsewhere in DataObject:
$this->dbObject("Amount");

Accessing a component in a DataObject such as a $has_one or $has_many

$this->Field(); //has_one relationship
$this->Fields(); //has_many relationship

Require a field in the CMS area

getCMSValidator() {
    return new RequiredFields('FieldName');
}

Accessing getFunctionName() as $FunctionName in the view, you can drop the ‘get’ prefix of a function name to call the function in views. So if you have a scenario like:

class Page extends SiteTree {
    public static $db = array(
        "Foo" => "Text",
    );
}
class Page_Controller extends ContentController {
    public function getFoo() {
        return 'Some value';
    }
}

Calling $Foo in your view will result in ‘Some value’. If you want to access $Foo of the Page class rename the function getFoo(). You don’t need to write getters for the db fields in the model (Page in this case) because $db fields become variables in the view and $has_x relationships become methods.

Creating a multiple or 2 column key index:

static $indexes = array( 
  'KeyName' => array( 
    'type' => 'unique', 
    'value' => 'ColumnOne,ColumnTwo' 
  ) 
);

Checking if writing a DataObject for the first time in the onAfterWrite() method:

class Something extends Page {
  private $firstWrite = false;
 
  function onBeforeWrite() {
    parent::onBeforeWrite();
    if (!$this->ID) $this->firstWrite = true;
  }
 
  function onAfterWrite() {
    parent::onAfterWrite();
    if ($this->firstWrite) echo 'New object';
  }
}

SilverStripe Controllers

Because I am always forgetting the syntax, how to include jQuery from Sapphire on SilverStripe 2.4

Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');

If you want to overload the index() method of a controller its often useful to return the Content and Form:

function index() {
 
  //Do something here
 
  return array( 
     'Content' => $this->Content, 
     'Form' => $this->Form 
  );
}

SilverStripe CMS

Adding nested tabs to 3 or more levels deep can be achieved by adding a TabSet and then adding new Tabs to that tab set:

$fields->addFieldToTab("Root", new TabSet('Advertisements', 
  new TabSet('LargeAds', new Tab('One'), new Tab('Two')),
  new Tab('SmallAds')
)); 
 
$fields->addFieldToTab("Root.Advertisements.LargeAds.One", new TextField('LargeAdOneLink', 'URL address for advertisement')); 
 
//Or something like
$fields->addFieldToTab("Root", new TabSet('Advertisements')); 
$fields->addFieldToTab("Root.Advertisements", new Tab('LargeAds'));

SilverStripe Debugging

Javascript files are concatenated and saved in the Assets folder. If you are having problems accessing the CMS due to Javascript errors check the permissions of the assets/ and perhaps also assets/_combinedfiles/ folders.

When SilverStripe is in production mode javascript files will be concatenated and saved. In order to disable this add the following line to mysite/_config.php:

Requirements::set_combined_files_enabled(false);

Debugging SilverStripe issues, the most annoying problem is the blank white page. The blank white page can often be attributed to some syntax error in the template file. If no errors are being shown try adding: ?isDev=1 to the URL. Or looking in apache error logs for any related errors:

tail -f /var/logs/apache2/error.log

Setting a log for the application is also useful, in the mysite/_config.php file:

SS_Log::add_writer(new SS_LogFileWriter('/var/www/silverstripe/mysite/errors.log'));

Then can log errors specifically using something like:

SS_Log::log(new Exception('Some log message here'), SS_Log::NOTICE); 
 
//Or for objects/arrays
SS_Log::log(new Exception(print_r($this, true)), SS_Log::NOTICE);

Admin splash / loading screen stuck? If you have a look in the firebug console and see a bunch of javascript errors its likely due to problems creating combinedfiles for the javascript that needs to be loaded for the CMS. This might be due to a permissions problem of your /assets/_combinedfiles/ folder, if changing permissions of this folder doesn’t work and neither does changing the owner or turning off php_safe_mode then you can stop SilverStripe from combining files in your _config.php file:

Requirements::set_combined_files_enabled(false);

PHP: Warning: Unexpected character in input: ”’ (ASCII=39) – this warning was related to a PHP parse error in one of my class files, (http://www.php.net/manual/en/function.token-get-all.php#79502). In my case it related to a poorly formed Enum string.

If you get the warning: “File is not a valid upload” when trying to upload files to SilverStripe then you might want to view this ticket and apply one of the patches, you can also try turning off open_basedir.

Useful SilverStripe Documentation

I find myself hitting most of these pages often for reference:

  • Page controls – what you can and can’t do in the SilverStripe templates.
  • SilverStripe templates – including template syntax, if and control blocks and Modulus/MultipleOf.
  • Debugging – a must have for any CMS.
  • Environment management – a cool technique to manage development and live environments for a SilverStripe project.
  • Execution pipeline – the process of a page request from SilverStripe.
  • Image controls – all those functions you can use on Images in SilverStripe templates.
  • Text methods – all the functions you can use to manipulate text in templates.
  • URL variable tools – very useful for development and testing a SilverStripe site

SilverStripe text books for reference:

Was this article useful?

rss feed icon

Email this article to yourself or...

rss feed icon

Subscribe to the RSS feed for more useful articles and tips.

Share this article with others

  • del.icio.us
  • Twitter
  • Reddit
  • StumbleUpon
  • Facebook
  • Digg
  • http://heyday.co.nz Shane Garelja

    Great article! Some really nice pointers for people starting out with SilverStripe.

    Another useful one is the “correct” way to access a field on the model from within the controller: $this->dataRecord->MyField;

    Also – you have a typo… under “Accessing a field in a DataObject” it should be “$this->Field;” not “$this->$Field;”

  • frank

    Thanks Shane, I often find myself checking this article for something or other :-) have added your contribution

  • John Black

    RE: your advice about
    a href”=[sitetree_link id=x]”
    … I found a forum post with recommended syntax from the SS team to address this in cases where something like $Content is being overridden by subclasses:
    http://silverstripe.org/general-questions/show/16384

  • http://ninnle.com Sam Jarvis

    Absolutely fantastic article, the silverstripe devs need this level of tutorial quality in their documentation.

    Jolly good show!

  • Todd

    Thanks alot for the snippets list and the membership module tut.

  • Mikhail

    Thanks buddy, this is useful and I’m wondering why I haven’t started a snippets-holder like this myself for a range of things.