SSbits - Home page
site by aabweb
Submit a Post >

Tutorials - Big bits of code to help you do more

DataObject as Pages: The Module

SSBdataobjectsaspagesmodule

 Source files  Demo (admin/pass)

It's been a while since we published the DataObject as Pages series of tutorials, which have proved to be particularly popular. At Aab Web we use those techniques regularly as a way to allow our clients to mange large amounts of flat content or content that is distributed in multiple places throughout a site. Although the final implementations in each project are often varied and bespoke, they all start with the basics outlined in those tutorials, so it made sense for us to build a starting point to work from (and include some more complex features at the same time). That starting point is this module, catchily entitled: DataObject as Pages.

About the Module

Get the Module on Github: DataObjects-as-Pages: The Module

So first things you are probably wondering is 'what can I do with this module'. Well once you have extended the classes in the Module you will have a fully functioning DataObject as Page system with

Features

  • Basic Versioning, with Stage & Live versions of each Data Object, managed in Model Admin.
  • Searchable DataObjects, allowing instant implementation into a search dependent site using the DoapSearchForm and Controller.
  • Fully extendable listing page type, allowing implementation of filtering systems and tags/category models
  • Easy implementation: Simply extend the 3 classes, add a couple of static values and you are ready to go!
  • URLSegment based DataObjects with MetaTitle and Description built in.

So without further ado, you can grab the code from the module here: https://github.com/arambalakjian/DataObjects-as-Pages and then follow the instructions below, where we'll first take you through the most basic implementation, then show you how you can extend it to do some really cood things, like we did on www.mymuswell.com, where all the Articles, Places, events and Deals are based on DataObjects.

Basic Setup

Ok, so you probably want to just get going with this so you can have a play around. We'll here are the 3 files you need to create, just like when doing the Part 2 tutorial, only this time without any actual logic!

mysite/code/ProductListingPage.php (Extends DataObjectAsPageHolder

 

class ProductListingPage extends DataObjectAsPageHolder 
{

}

class ProductListingPage_Controller extends DataObjectAsPageHolder_Controller 
{
	//This needs to know be the Class of the DataObject you want this page to list
	static $item_class = 'Product';
	//Set the sort for the items (defaults to Created DESC)
	static $item_sort = 'Title ASC';
}

mysite/code/Product.php (Extends DataObjectAsPage)

class Product extends DataObjectAsPage 
{
	//The class of the page which will list this DataObject
	static $listing_class = 'ProductListingPage';
	//Class Naming (optional but reccomended)
	static $plural_name = 'Products';
	static $singular_name = 'Product';
}

mysite/code/ProductAdmin.php (extends DataObjectAsPageAdmin)

class ProductAdmin extends DataObjectAsPageAdmin {
   
	public static $managed_models = array(
		'Product'
	);

	static $url_segment = 'products';
	static $menu_title = 'Products';
}

And that's it! You now have a fully functioning, versioned, searchable DataObject as Page implementation! You can now carry on as normal and add all the fields you want to Product, implement your templates (see below) and see where you can take it.

Templates

You will see there are 2 very basic templates provided with the module, DataObjectAsPageHolder.ss and DataObjectAsPageHolder_show.ss. These are just to get you started but should show you how to setup your own. The modules show function makes use of SilverStripes automatic template finder which follows the naming convention [PageClass]_[action].ss. So for this module our [action] is 'show' meaning we just need to add a second template with _show on the end of our standard template for displaying the DataObject detail page.

Following this logic, if we wanted to create a template for our Products in the code above, we would have the following:

themes/mytheme/templates/Layout/ProductListingPage.ss (the page to list all the Products)

<div class="typography">
	
	  	<% include BreadCrumbs %>
		
		<ul>
		<% control Items %>
			<li>
				<h2>$Title</h2>
				<p>$Content.FirstParagraph</p>
				<a href="$Link">View</a>
			</li>
		<% end_control %>
		</ul>
</div>

<% control Items %> is used to cycle through the DataObjects which are being listed on this page. If you check the 'Use Items as children' under the Behavior tab of the Listing Page, the items will also be returned as <% children %>, allowing you to include them in Menu's etc. You can also pass in an arberary limit to Items(), for example <% control Items(5) %> would give you the first 5 products.

themes/mytheme/templates/Layout/ProductListingPage_show.ss (the actual product page)

<div class="typography">

	<div id="Breadcrumbs">
		<p>$Breadcrumbs</p>
	</div>
		
	<% control Item %>
		<h2>$Title</h2>
				
		$Content
	<% end_control %>

</div>

On this page we use <% control Item %> to go into the current DataObject and display it's data, in this case just the default $Title and $Content.

Extending the Listing Page to do Fancy things

The Listing page is really the core of the module. It fetches and displays the relevent DataObjects and it's here that you can start extending in order to gain more control over how those DataObjects are returned by the Items function. We have built the module to allow you to extend the Listing Page functionality without having to re-write the core functions (in most cases). Primrily this section focuses on the functions you can use to manipulate the DO's that are retreived, but in a future tutorial we will look at creating specific functionality such as a URL based filtering system.

Pagination

Pagination is built into the Module, and enabled by default. You can find the options to change the page size and disable it under the Behavior tab of your listing page, along with the switch to return your Items as Children of the listing page.

 

Manipulating the WHERE

	/*
	 * Overload the SQL WHERE builder
	 */
	public function getItemsWhere() 
	{
		//Use this function to return a custom filter to the Items() function
		return "Date > CURDATE";
	}

By adding a getItemsWhere() function to your ListingPage_Controller, you can manipulate the Where statement that is used when collecting all the DataObjects to list on this page. This allows you to build filtering into your page, dynamically adding extra conditions to the SQL. As a basic example you could return Date > CURRDATE(); if your DataObject had a Date field and you only wanted to show DataObjects where this date was after today.

 

Manipulating the Sort Order

As well as using the static $item_sort as detailed above, there is also a function which you can use to set the sort more dynamically:

	/*
	 * Overload the Sorting of Items
	 */	
	function getItemsSort()
	{
		return "MyField DESC";
	}	

Manipulating the Join

	/*
	 * Overload the Join builder
	 */	
	function getItemsJoin()
	{
		return [JOIN STATEMENT];
	}

Pretty self explanitory, but here you can add a join to the SQL in the Items() function, so if you needed to filter on a related objects field, you could add the filter in the getItemsWhere() function then return the required Join here.

 

So there you have it, the first release of what will hopefully prove to be a useful module. Obviously it's in it's early days so if anyone has any suggestions as to how to improve it please let us know!

Aram Balakjian avatar

Aram Balakjian

Aram is a web developer running London based agency Aab Web. He has a strong passion for developing attractive, usable sites around the SilverStripe CMS.

  • FullWebService
    24/01/2012 1:10pm (30 days ago)

    Looks intersting and useful. I'll try it out this week.

  • RoyalPulp
    24/01/2012 2:44pm (30 days ago)

    Hi Aram,
    wonderful, I have been using "Dataobject as a Page" in many projects now and I will test this module next week.

    There's one important piece of code that I use in addition, to write the DataObject-Pages into the sitemap.xml:
    http://www.silvercart.org/blog/dataobjects-and-googlesitemaps/

    Hendrik

  • Aram Balakjian
    24/01/2012 2:50pm (30 days ago)

    RoyalPuly - good point. There is already an absoluteLink() function on the DataObjectAsPage class, so you can use the code on silvercart.com to make it work, but it would be much better to have it out the box, so I will look into the best way to do that.

    Aram

  • Xurk
    25/01/2012 1:02pm (29 days ago)

    Thanks for sharing, this sounds really interesting!

    We regularly use the "DataObjects as Pages" method for our websites and the fact that it is now a module not only facilitates the task of implementing it, but (perhaps the best feature) now also includes DataObjects in the search results... very nice.

    Recently we gave UncleCheese's RemodelAdmin a shot which is actually the reverse of this - "Pages as DataObjects" comes pretty close to describing it. Great to know that we can now pick one of the two methods depending on what the project calls for, and not loose any functionality of the class we're attempting to mirror :-)

  • Matty Balaam
    27/01/2012 9:43am (27 days ago)

    I realise this isn't an issues at the moment and won't be for a while, but I'm guessing this won't be compatible with SS3 as GridField is going to replace DataObjects.

    Do you think you'd have plans to update this module at that point? Do you think it will be an easy enough job to do, or a complete rewrite?

  • Aram Balakjian
    27/01/2012 9:47am (27 days ago)

    Hi Matty,

    As I understand it, Grid Field is going to replace ComplexTableField, so won't effect this module.

    However I will need to update it to accomodate the new DataList class which will supercede the DataObjectSet.

    I'll do that once I get familiar with the new changes, but I don't expect it to be a hugely complex task :)

    Aram

  • Matty Balaam
    27/01/2012 3:03pm (27 days ago)

    Ah, brilliant, great to know this module will be future proof, thanks for your clarification.

  • Richard Rudy
    27/01/2012 7:10pm (26 days ago)

    Great Stuff Aram, just popped on to look over the tutorial and lo and behold its wrapped and ready to go

    Thanks

  • jakmax
    31/01/2012 10:07pm (22 days ago)

    Hi Aram,

    i create product page and product, it's all ok.
    But i need to have comments in all dataobject page.
    I add $Pagecomments in my .ss template, but when i save it posts same comment in all pages.. can you help me? my code is post here
    http://www.silverstripe.org/dataobjectmanager-module-forum/show/19101
    sorry for my english, i'm from italy
    thanks
    bye, max

  • MattIn4D
    31/01/2012 10:49pm (22 days ago)

    Hey Aram, great module!
    I'm creatjng a portfolio with it and it seems the pagination controls are missing from the template? Also I would like to see an example of category/tag filtering if anyone could help me there.
    Thanks

  • Aram Balakjian
    31/01/2012 10:55pm (22 days ago)

    @jakmax - See forum reply, unfortunately not a stright forward task (until we release a module ;).

    @Mattin4D - It looks like it's there, line 20 of DataObjectAsPageHolder.ss: https://github.com/arambalakjian/DataObjects-as-Pages/blob/master/templates/Layout/DataObjectAsPageHolder.ss

    Aram

  • MattIn4D
    31/01/2012 11:03pm (22 days ago)

    ok thanks I should have checked, I downloaded the day you posted this.

  • Aram Balakjian
    31/01/2012 11:04pm (22 days ago)

    Ah yea, I have added it since, keep checking back too as I will be continually updating it :)

    Aram

  • jakmax
    01/02/2012 8:36am (22 days ago)

    Ok Aram,
    thank you very much for your reply.
    ok so if I understand correctly I have to create a form that records on a database (like UserForm) and pass an hidden field with the ID of the DO and then invoke in the template a query with the comments that have the id of the DO...

  • Aram Balakjian
    01/02/2012 9:11am (22 days ago)

    Hi Jakmax,

    Yep pretty much, when you pass the ID of the DO, in the submission function, you can relate that comment to that DO using a has_one on the Comment to a has_many on the DO. Then, say your has_many was called 'Comments' then you just call <% control Comments %> in the template and it will return all the comments accociated with that page.

    Aram

  • jakmax
    01/02/2012 10:08am (22 days ago)

    ok I think I understand ;-)
    I'll try tonight and then I can tell you ..
    Meanwhile, thank you very much

    Jakmax

  • Bart van Irsel
    02/02/2012 8:52pm (20 days ago)

    Hi Aram,

    Thanks a lot for sharing this at exactly the right moment.
    We're going to look into this for our project which can use this tomorrow!

    Cheers,

    Bart

  • Aram Balakjian
    02/02/2012 9:16pm (20 days ago)

    @bart - Great! Let me know how you find it :)

    Aram

  • jakmax
    03/02/2012 12:06am (20 days ago)

    Hi Aram,
    I almost solved, but when I try to pass the hidden field I get the following error:
    [Notice] Trying to get property of non-object
    my hidden field is:
    new HiddenField ('ProductID', 'ProductID', $ this-> getCurrentProduct () -> ID)
    where I'm wrong?
    Thanks

    jakmax

  • Aram Balakjian
    03/02/2012 1:19pm (20 days ago)

    @jakmax - ahh yea, you need to wrap it in an if() because when the form submits, it doesnt actually have a URl ID anymore, try this:

    if($product = $this->getCurrentProduct)
    {
    $fields->push(new HiddenField('ProductID','ProductID',$product->ID));
    }

  • jakmax
    03/02/2012 2:12pm (20 days ago)

    i tried:

    $fields = new FieldSet(
    new TextField('Nome', 'Nome *'),
    new TextareaField('Commento', 'Commento *'),
    /*new HiddenField('ProductID','ProductID','88'),*/

    new PhpCaptchaField('Captcha','')

    );
    if($Product = $this->getCurrentProduct())
    {
    $fields->push(new HiddenField('ProductID','ProductID',$Product->ID));
    }

    but the value in DB is 0

    :-(

  • jakmax
    03/02/2012 2:19pm (20 days ago)

    ok great Aram!

    i change my code and it's work great!!

    function PostComments() {



    // Create fields
    $Params = Director::urlParams();
    $fields = new FieldSet(
    new TextField('Nome', 'Nome *'),
    new TextareaField('Commento', 'Commento *'),
    new HiddenField('ProductID','ProductID',''),

    new PhpCaptchaField('Captcha','')

    );
    if($Product = $this->getCurrentProduct())
    {
    $fields->push(new HiddenField('ProductID','ProductID',$Product->ID));
    }
    // Create action
    $actions = new FieldSet(
    new FormAction('SendComment', 'Invia')
    );

  • jakmax
    03/02/2012 2:38pm (20 days ago)

    Thank you very much Aram!

  • Aram Balakjian
    03/02/2012 2:40pm (20 days ago)

    @ jakmax - no problem! Glad you got it working :)

  • Todd
    04/02/2012 11:35pm (18 days ago)

    Aram --

    Thanks for the great module. It's an awesome point to build from. (An aside: www.mymuswell.com is a fantastic site. Well done.)

    I just noticed that if you try to have a db field in an object with the same name as the object, it breaks the SQL queries the module uses. (I had a Quote object that had an HTML db field called Quote and another field called Author.) Probably a small thing since it's likely not best practice for a field in an object to share the object's name. Changing the field name solved the problem.

  • Thomas Apfelbacher
    09/02/2012 9:42am (14 days ago)

    Hi Aram,

    thanks again for a nice and useful tutorial.
    FYI: The demo login gives an error: [User Warning] Cookie 'alc_enc' can't be set. The site started outputting was content at line 2 in /.../demo_site/local-config.php

    Greetings. Pipifix

  • Aram Balakjian
    09/02/2012 9:52am (14 days ago)

    Thanks Thomas, that's fixed now :)

  • Adam Stead
    11/02/2012 2:48pm (12 days ago)

    This is a great little module, we regularly use DataObjects as pages, now this module will streamline that progress. Hopefully we will be able to contribute more to it as we use it.

  • Daniel Schweiger
    17/02/2012 7:45am (6 days ago)

    hi,
    thx for sharing this module.

    Is it possible to manage the DataObjects from a own tab on the page, not via ModelAdmin?

Post a comment ...

You cannot post comments until you have logged in. Login Here.

Advertisement

Site of the Month

Find SSbits on

Top Contributers

Rank Avatar Name
1 article image Aram Balakjian
2 article image Daniel Hensby
3 article image Marcus Dalgren
4 article image Hamish Campbell
5 article image Ty Barho
6 article image Martijn van Nieuwenhoven
7 article image Darren-Lee
8 article image Roman Schmid
9 article image dalesaurus
10 article image Matt Clegg

View full leaderboard


Advertisement