Web and application development

Twitter

Just discovered dragging a folder from the PHPStorm project explorer into the built in terminal, a new tab opens in that folder. Handy

Follow me
29thSeptember 2014

Using namespaces with Silverstripe

Posted at 9:01pm by Jason in Development,Web development

Tags: , , ,

I’ve been trying to use namespaces more in some of my work recently. Not because it was necessarily required, but so I can get into the swing of using namespaces for when it may be required. A few recent projects have involved using Laravel, and that makes use of namespaces, so I went with it.

Moving back to SilverStripe. With version 3.0, SilverStripe moved to requiring at least PHP 5.3 – yeah, that was a while ago and at the time of writing, the current stable version is 3.1.6. Anyway, with PHP 5.3 came native support for namespaces and with the 3.0 release, one thing that got a small mention was that namespaces would start to feature at some point.

I’m not going to get into the ins and outs of namespaces, why you should be using them, why aren’t you using them, PSR-0 and PSR-4 autoloading schemes, I just wanted to see if there would be any problems using namespaces when working with Silverstripe (which is yet to actually start using them).

TL;DR It’s all straight forward and I didn’t have any problems, except for the odd misplaced backslash.

Ok, I’m not going to leave it there, let’s have a look and start with a simple DataObject.

namespace EOS\models
class MyObject extends \DataObject {
private static $db = array(
'Title' => 'Varchar(100)',
'Description => 'HTMLText',
'Enabled' => 'Boolean',
);
}

Well, that all looks pretty familiar. The only real addition is the namespace keyword and the backslash in front of DataObject. That is because DataObject is currently in the global namespace. The backslashes don’t appear to be required in the quoted types for the database fields in the $db array. I’ve tried with and without and it didn’t make any difference – everything worked.

Running /dev/build runs as you would expect and the database is updated as you would expect. The database table name? Well, as usual, it is the name of the class – the fully namespaced class: EOS\models\MyObject.

So, what about extending our basic model with some relations? Again, all really straightforward:

namespace EOS\models
class MyObject extends \DataObject {
private static $db = array(
'Title' => 'Varchar(100)',
'Description => 'HTMLText',
'Enabled' => 'Boolean',
);
private static $has_one = array(
'Creator' => 'Member',
'OtherObject => 'EOS\models\OtherObject'
);
}

Now, here in the $has_one array, we don’t want to use leading backslashes. It just doesn’t work. Using \Member, or indeed \EOS\models\OtherObject would result in errors, either in the build phase or at runtime.

One area worth mentioning is using the YAML configuration system. Namespaces can be used here too, you just need to quote everything where a namespace is used, as per the Yaml spec, because the backslash character is being used:
---
Name: MyProject
After:
- 'framework/*'
- 'cms/*'
---
SSViewer:
theme: 'MyTheme'
SiteConfig:
extensions:
- 'EOS\extensions\SiteConfig'
'EOS\models\MyObject':
extensions:
- 'EOS\extensions\SortOrder'

Namespaced classes can be single quoted or double quoted. This is YAML, not PHP, so there is no string interpolation going on.

This shows the advantage of using namespaces. I always extend the main SiteConfig object. Usually calling the extension something like ProjectSiteConfig or SiteConfigExtension. Now I can just call it SiteConfig. Similarly with the main LeftAndMain class. I can just call my extension LeftAndMain, safe in the knowledge that due to the namespace, there is no naming collision. To me it makes it nice and clear without faffing about with long class names just to avoid the collision. It doesn’t necessarily cut down on typing, but hey, your IDE is providing autocompletion right?

Update

There are a few issues with namespacing page types (inheriting from either Page or even SiteTree). So far the issues only relate to SilverStripe automatically loading templates.

Using the automatic system, SilverStripe expects templates to be named after the class, e.g. Page.ss or the class and the action, separated with an underscore, e.g. Page_index.ss.

Whilst you could name your templates after the class, say you were using the a namespaced class EOS\pages\HomePage, then you would be looking at having a template called EOS\pages\HomePage.ss. This is perfectly possible – and it does work, just some IDEs and editors may have a bit of a problem with that (I know PHPStorm does).

As then automatic approach tries to split classes by the underscore character, I assume that when the SilverStripe core starts to use namespaces, the backslash character may also be included. Therefore it would be looking for a template called EOS_pages_HomePage.ss.

An alternative approach would be to explicitly specify your templates in a $templates array. e.g.
namespace EOS\pages
class HomePage extends \Page {
//put your custom code here...
}
class HomePage_Controller extends \Page_Controller {
protected $templates = array(
'index' => 'MyIndexTemplate',
'thanks' => 'MyThanksTemplate'
);
}

However, that isn’t the end. If you have a customised top level template, then you need to specify that too:
protected $template = array(
'index' => array('MyIndexTemplate', 'MyTopLevelTemplate'),
'thanks' => array('MyThanksTemplate', 'MyTopLevelTemplate')
);

An exception to this is if your top level template and layout template share the same name.

This is great, but your specified templates are only going to get picked up if you return something from your action. Well, in my trials, that seems to be the case. Not only return something, but return an array. The array would then be added to the template so items in the array can be accessed from within the template. Otherwise, anything else you return will be assumed to be what should be rendered.

Another approach would be to call renderWith() at the end of your actions. Here you can specify your template hierarchy, whether or not you want to add anything into the template:
public function thanks(){
//Do you thanks processing
return $this->renderWith(array(
'MyThanksTemplate',
'MyTopLevelTemplate'
));
}

The latter approach is probably the one I will take, should I actually decide I need to namespace my custom page types.

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment