SSbits - Home page
Site by Carbon Crayon
Submit a Post >

Tutorials - Big bits of code to help you do more

DataObjects as Pages - Part 1: Keeping it Simple

SSBdoaspagesp1

 Source files (3.4 KB)  Demo (admin/pass)

In this two part tutorial, I am going to show you how to display DataObjects as if they were pages. This is often very useful for times when you have lots of items that don't warrant full site tree objects, but do need to be displayed on their own on the site. In this first part displaying Staff Members we are going to keep it simple, using a Data Object Manager to manage our Staff Members and referring to their ID in the URL. Later in part 2, we will see how to use ModelAdmin to manage a Search Engine friendly product catalogue.

Want a quick solution? Check out the DataObjectAsPages Module

Note. We will be using the DataObjectManager module in this tutorial, which you should install if you don't already have it. You can also use a ComplextTableField in place of the DataObjectManger if you prefer not to use any external modules.

Preperation

We will be creating 4 files in this tutorial, StaffMember.php, StaffPage.php, StaffPage.ss and StaffPage_show.ss. Lets create these files and their initial code (you can also get them from the STARTING_FILES folder in the SourceFiles Zip attached to this page.

mysite/code/StaffMember.php

<?php
class StaffMember extends DataObject
{

}

mysite/code/StaffPage.php

<?php 

class StaffPage extends Page 
{
	public function getCMSFields()
	{
		$fields = parent::getCMSFields();
		
		return $fields;
	}
}

class StaffPage_Controller extends Page_Controller
{

}

themes/blackcandy/templates/Layout/StaffPage.ss

<div class="typography">
	
	<h2>$Title</h2>

	$Content

</div>

themes/blackcandy/templates/Layout/StaffPage_show.ss

<div class="typography">

	<% include SideBar %>

	<div id="Content">
	
	</div>

</div>

The StaffMember DataObject

Now that we have our basic staff member DataObject, lets's add some meat to it. A few db fields & relations, the summary_fields static, CMS fields for the popup and a function to generate our thumbnail in the DataObjectManger (see this post for more info on DOM/CTF thumbnail generation).

<?php
class StaffMember extends DataObject
{
	//db fields
	static $db = array (
		'Name' => 'Varchar(255)',
		'Role' => 'Varchar(100)',
		'Description' => 'Text'
	);
	
	//Relations
	static $has_one = array (
		'StaffPage' => 'StaffPage',
		'Photo' => 'Image'
	);
	
	//Fields to show in the DOM table
	static $summary_fields = array(
		'Thumb' => 'Photo',  
		'Name' => 'Name',
		'Role' => 'Role'
	);
	
	//Fields for the DOM Popup
	public function getCMSFields()
	{
		return new FieldSet(
			new TextField('Name'),
			new TextField('Role'),
			new TextareaField('Description', 'Description'),
			new ImageField('Photo', 'Photo', Null, Null, Null, 'Uploads/staff-photos/')
		);
	}

	//Generate our thumbnail for the DOM
	public function getThumb()
	{
		if($this->PhotoID)
			return $this->Photo()->CMSThumbnail();
		else	
			return '(No Image)';
	}
}

It's all pretty standard here, the only things to note are that we are adding a has_one relation to the StaffPage page type and that we define $summary_fields and use the function name getCMSFields() (not getCMSFields_forPopup()). This is so that when we define our DOM in the next step, we don't need to supply these as arguments as DOM knows to look for these automatically.

The StaffPage Page Type

Now that we have our DataObject, we need a page that allows us to create them. We will be doing that through the StaffPage which will have a has_many relation to StaffMember. Remember that StaffMember has a has_one relation to StaffPage, Meaning that each StaffMember can only be accociated to a single StaffPage while each StaffPage can have lots of StaffMembers accociated with it. If this is confusing don't worry, you can leave understanding relations to another day :)

So inside StaffPage.php add the following:

<?php 

class StaffPage extends Page {
	
	static $has_many = array(
		'StaffMembers' => 'StaffMember'
	);
	
	public function getCMSFields()
	{
		$fields = parent::getCMSFields();
		
		$manager = new DataObjectManager(
			$this,
			'StaffMembers',
			'StaffMember'
		);	
		$fields->addFieldToTab("Root.Content.StaffMembers", $manager);
		
		return $fields;
	}

}

class StaffPage_Controller extends Page_Controller {
	

}

Again, this stuff is pretty standard. Notice that our DOM definition on line 13-17 has far fewer arguments than many examples you will see. That is because as mentioned we used $summary_fields and getCMSFields() in our StaffMember DataObject so the DOM will pick these up automatically.

Here are some pics of what your StaffPage should look like in the CMS:

The DataObjectManager tableThe DataObjectManager popup

Now that we can create StaffMember objects, lets get a basic template together to display them in a list.

The StaffPage listing Template

So here is our really basic StaffPage template to list our StaffMembers attached to that page, inside StaffPage.ss add the following:

<div class="typography">
	
	<h2>$Title</h2>

	$Content
	
	<% if StaffMembers %>
	<ul id="staff-list">
		<% control StaffMembers %>
		<li>
			<a href="$Link">$Photo.CroppedImage(200,100)</a>
			<h2><a href="$Link">$Name</a></h2>
			<p>$Role</p>
		</li>
		<% end_control %>
	</ul>
	<% end_if %>

</div>

You may have noticed something strange here. As we loop through the <% control StaffMembers %> we are using $Link as if each individual StaffMember  were a page. Of course this is works fine when using SiteTree objects, as their links are generated automatically, however our StaffMembers are DataObjects and so don't have a URL at all and we don't yet have a way of displaying each staff member on it's own, nevermind linking to it! We will be adding a way to generate the correct link in a bit, but before I confuse the matter too much, let's create a way to view our StaffMembers as actual pages.

The StaffMember Detail Page

Here is where things start to get interesting. Up to now we have been doing pretty standard stuff, but now we are going to use SilverStripe to create 'virtual' pages on the front end of our site for each StaffMember. To do this we are going to use URL paramaters.  These are values passed to the Controller as an array via the URL. They are stuctured /action/ID/OtherID so the URL www.mysite.com/somepage/somefunction/value/anothervalue would create the following array: ('Action' => 'somefunction', 'ID' => 'value', 'OtherID' => 'anothervalue'). It would also try to run the function somefunction() if it existed in the controller and had been added to the $allowed_actions static array. Although not a requirement, it is good practice to use the $allowed_actions to prevent unauthorized function calls.

So in our particluar example the URL will look something like this www.mysite.com/staff/show/23. where show is our action which calls the show() function on our pages controller and 23 is the ID of the StaffMember object we want to view. This is of course not Search Engine friendly but it is quicker and simpler than using a URL segment. We will be delving into a more complex example using proper URL segments in part 2.

Here is what your StaffPage_Controller class inside StaffPage.php should look like:

class StaffPage_Controller extends Page_Controller {
	
	//Allow our 'show' function as a URL action
	static $allowed_actions = array(
		'show'
	);
	
	//Get the current staffMember from the URL, if any
	public function getStaffMember()
	{
		$Params = $this->getURLParams();
		
		if(is_numeric($Params['ID']) && $StaffMember = DataObject::get_by_id('StaffMember', (int)$Params['ID']))
		{		
			return $StaffMember;
		}
	}
	
	//Displays the StaffMember detail page, using StaffPage_show.ss template
	function show() 
	{		
		if($StaffMember = $this->getStaffMember())
		{
			$Data = array(
				'StaffMember' => $StaffMember
			);
			
			//return our $Data array to use on the page
			return $this->Customise($Data);
		}
		else
		{
			//Staff member not found
			return $this->httpError(404, 'Sorry that member of staff could not be found');
		}
	}
}

Now there's quite a lot to take in here so let's go through it carefully. First thing we do is define our show function as allowed in the $allowed_actions static array so that SilverStripe lets us use it to run the show() function. Then we create a function getStaffMember() to fetch the current StaffMember object from the ID in the URL, if there is one. So the first thing we do here is fetch all of the URL parameters. These will be returned as an associative array of 2 items (with keys of 'Action' and 'ID') which is fetched by calling $this->getURLParams(). Of these 2 values we actually only need the ID which represents the ID of the StaffMember we want to display, as the action has already been used to call the function we are in. Next we check whether the ID is numeric and if it is we get the StaffMember using a DataObject::get_by_id(). We check that the value is numeric to prevent SS erroring by passing a non-numeric value to the get_by_id() function. Generally it is a really good idea to do checks like this (and/or use Convert::raw2sql($Value)) anyway to ensure you are passing in a sanitized value just to be on the safe side (see this post for more info on sanitization).

Now we create the actual show() function which will display our StaffMember DataObject as a page. We first test to see if there is a StaffMember to show by trying to assing the result of our getStaffMember() function to $StaffMember. If this fails then we pass the user to a 404 page, otherwise we then define an array with our $StaffMember object in it. This array is then returned using the customise(). This is really the heart of this whole process. What we are doing here is saying when you render the page again, include our custom $Data in the template. Although we are only defining a single value here, you can add as many as you like, for example setting 'Title' or 'Content' to something other than that in StaffPage, which is effectively still the page we are on.

Note. We could have easily combined the getStaffMember() function into the  show() function, but as you will see later it is very useful to be able to fetch the current StaffMember for use in other functions, in this instance we will be using it in our custom Breadcrumbs trail.

The clever bit in all this comes with the temeplate that SilverStripe uses. Because we have called a URL action SilverStripe knows to look for a template that follows the convention PageType_action. In this case our page type is StaffPage and our action is show, so SilverStripe will look for the template StaffPage_show.ss, which should now look like this: 

<div class="typography">

	<% include SideBar %>

	<div id="Content">
	
	<% control StaffMember %>
	
		<h2>$Name</h2>
		<p>Role: $Role</p>
		
		$Photo.CroppedImage(250,250)

		<p>$Description</p>
	
	<% end_control %>
	
	</div>

</div>

As you can see, we use <% control StaffMember %> to access the 'StaffMember' value from our $Data array. Once inside StaffMember we have access to all the attributes of that Object.

So now we have a page that displays a list of our StaffMembers and we have a way to access each StaffMember on their own page using the URL www.mysite.com/{staffpage}/show/{ID}. What we now need to do is go back to our StaffMember and create a function Link() which generates a Link to that StaffMembers corresponding detail page so that our StaffPage.ss template starts correctly linking to them.

Inside the StaffMember class in StaffMember.php add this function:

<?php
class StaffMember extends DataObject
{
	.
	.
	.
	//Return the link to view this staff member
	public function Link()
	{
		if($StaffPage = $this->StaffPage())
		{
			$Action = 'show/' . $this->ID;
			return $StaffPage->Link($Action);	
		}
	}
}

All we do here is grab the related StaffPage then construct the link by joining the StaffPage's Link to 'show/' and finally the ID of the current StaffMember. So now when $Link is called from within the <% control staffMembers %> loop it will correctly output the link to that StaffMember! Your page will look something like this (with a bit of extra styling, included in the source files):

The Finishing Touches : Menus and BreadCrumbs

At this point you have a way to display DataObjects as pages and link to them. However there are a couple of things that are missing that make them less useful, namely the ability to include our DataObjects in the SideMenu as well as being able to correctly output breadcrumbs which include our StaffMembers.

Sidbear Menu

So firstly lets make it so that our DataObjects show up in the Sidebar menu of the Blackcandy theme. This is actually easier than you would imagine. All you need to think about is what the Sidebar menu is doing: 1st it grabs the children of the current page by calling Children(), then it lists these children by referring to each of their $MenuTitle's and $Links'. We already have a $Link function on our StaffMember, so to allow our them to be shown like this we simply have to tell the StaffPage to return all of it's StaffMembers when SilverStripe asks it for its children() and then we need to ensure that our StaffMember returns something useful when SilverStripe asks it for it's $MenuTitle. Here's what we do:

Inside the StaffPage class in StaffPage.php add the following function

class StaffPage extends Page 
{
	.
	.
	.
	public function onBeforeDelete()
	{
		$CurrentVal = $this->get_enforce_strict_hierarchy();
		$this->set_enforce_strict_hierarchy(false);
		
		parent::onBeforeDelete();
		
		$this->set_enforce_strict_hierarchy($CurrentVal);
	}	
	
	public function Children(){
		return $this->StaffMembers();
	}
}
.
.
.

This function simply returns all of the StaffMembers attached to that StaffPage when SilverStripe asks for it's children. We also add in an onBeforeDelete() function. This is to prevent Silvertripe deleting the attached StaffMembers when we unpublish the page which would normally happen because it expects the objects returned in Children() to be SiteTree obejcts and therefore should be removed from the Live site too. The difference here is that our StaffMember objects are not versioned, so it ends up deleting them completely!

Then we just add these two functions to our StaffMember class in StaffMember.php:

class StaffMember extends DataObject
{
	.
	.
	.
	//Return the Name as a menu title
	public function MenuTitle()
	{
		return $this->Name;
	}	

	//Ensure that the DO shows up in menu (unclear whether this is needed or not)
	function canView()
	{
		return $this->StaffPage()->canView();
	}
}

The first function MenuTitle() just returns the StaffMembers name when SilverStripe asks for it's $MenuTitle.The second function canView() is to fix an issue some people were having whereby non logged in users could not see the items in the menu. All it does is return true or false based on the parent StaffPage() access permissions, so if the StaffPage() is allowed to be viewed by anyone, so will this StaffMember object. This is no necessarily needed, but fixes the issue if you are having it.  That's it! Your StaffMembers will now be listed and linked from your Sidebar menu. However there is one little detail still missing: the current page is not highlighted. To do this we just need to add a function to our StaffMember class which returns 'current' if we are viewing it. Here is what it would look like, placed in the StaffMember class inside StaffMember.php

class StaffMember extends DataObject
{
.
.
.
	
	public function LinkingMode()
	{
		//Check that we have a controller to work with and that it is a StaffPage
		if(Controller::CurrentPage() && Controller::CurrentPage()->ClassName == 'StaffPage')
		{
			//check that the action is 'show' and that we have a StaffMember to work with
			if(Controller::CurrentPage()->getAction() == 'show' && $StaffMember = Controller::CurrentPage()->getStaffMember())
			{
				//If the current StaffMember is the same as this return 'current' class
				return ($StaffMember->ID == $this->ID) ? 'current' : 'link';
			}
		}
	}
}

So what we are doing here is first checking that we have a page controller to work with and that it belongs to a StaffPage class. Then we check that there is an action 'show' being called on that page using the function getAction() and that we can assign the current StaffMember. It's important to check these as only when these conditions are all true do we actually want to perform the comparison in the next line. This comparison returns 'current' if the currently viewing StaffMember (assigned to $StaffMember) is the same as this staff member (i.e. the instance of StaffMember that the function is currently running from).

Note. Strangely it doesn't seem possible to assign Controller::CurrentPage() to a variable like $Controller, which is why we are calling it so many times.

Breadcrumbs

Finally we are going to ensure that when the breadcrumbs are drawn on our StaffPage_show, they correctly include the StaffMember and link to the StaffPage. Add this function to your StaffPage class in StaffPage.php:

.
.
.
class StaffPage_Controller extends Page_Controller {
	.
	.
	.
	//Return our custom breadcrumbs
	public function Breadcrumbs() {
		
		//Get the default breadcrumbs
		$Breadcrumbs = parent::Breadcrumbs();
		
		if($StaffMember = $this->getStaffMember())
		{
			//Explode them into their individual parts
			$Parts = explode(SiteTree::$breadcrumbs_delimiter, $Breadcrumbs);
	
			//Count the parts
			$NumOfParts = count($Parts);
			
			//Change the last item to a link instead of just text
			$Parts[$NumOfParts-1] = ('<a href="' . $this->Link() . '">' . $Parts[$NumOfParts-1] . '</a>');
			
			//Add our extra piece on the end
			$Parts[$NumOfParts] = $StaffMember->Name; 
	
			//Return the imploded array
			$Breadcrumbs = implode(SiteTree::$breadcrumbs_delimiter, $Parts);			
		}

		return $Breadcrumbs;
	}		
}

This function looks quite complex but is actually pretty strait forward. We grab the default Breadcrumbs, then if our getStaffMember() function returns a StaffMember (i.e. we are on a detail page) we break it up into an array of each section, make the last item into a link to the main StaffPage (it was just a string previously as SilverStripe thought that was the page we were on) then finally add another item onto the array with the value of the staff members name. Then we implode it all using the Breadcumb delimiter (&raquo; by default) and return it to the template. The clever thing about this function is that if we are simply just on the StaffPage rather than the detail page, then it just returns the default Breadcrumbs. However because these Breadcrumbs are being rendered from the main StaffPage, if we are on Level 1, then they won't show up due to the <% if Level(2) %> in the Breadcrumbs.ss include, so we need to insert the code directly into our StaffPage_show.ss template like so:

.
.
.
	<div id="Content">
		
		<div id="Breadcrumbs">
		   	<p>$Breadcrumbs</p>
		</div>
	
		<% control StaffMember %>
.
.
.

And that's it! You now have a complete System for displaying DataObjects as their own pages. Your final Staff detail page should look something like this:

In the next part of this tutorial, we will look at how to use ModelAdmin to create a complete SearchEngine friendly Product manager using DataObjects for each Product.

Special Thanks

Special thanks go to Daniel Hensby for their contributions to this post.

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.

  • Colin Burns
    07/10/2010 3:44am (4 years ago)

    Hi Aram,

    It looks as though you can only have a limited number of URL params? Is this correct?

    http://domain.com/staff/show/ID/OtherID to add in additional parameters I would need to use a GET request string...

    i.e.
    Director::urlParam('ID')
    Director::urlParam('OtherID')

    Can you confirm this or is it just my bad coding that is causing this to fail...

    What I need is:
    http://domain.com/staff/show/ID1/ID2/ID3, but it would be good to be able to go to a decent level like 5 or 6. Sometime you want to pass information and other times you want to add elements for URL friendliness...

    Cheers,
    Colin

  • Marcus Dalgren
    07/10/2010 10:21am (4 years ago)

    @Aram/Dario - That's really weird, I've never had to define a canView() method just to make DataObjects visible for non logged in users. I can still see the staff listing when using the old code without the canView(), what SS versions are you guys running?

  • dario
    07/10/2010 11:41am (4 years ago)

    @Marcus... I'm running 2.4.2

  • Aram Balakjian
    07/10/2010 12:01pm (4 years ago)

    @ MArcus, Dario - I am on 2.4.1 with that code. It does seem bizzare, but it's definaly the case on my local env. Anyone else having this issue?

    @Colin - These are the just the default URL Params, but you can define your own using rewrite rules - see: http://doc.silverstripe.org/controller

  • Marcus Dalgren
    10/10/2010 2:51pm (4 years ago)

    @Dario, Aram - It's even more bizzarre that I'm running 2.4.2 (the same as Dario) and not experiencing the issue. I'll download 2.4.1 and see if I can replicate this in some way. If it's a bug it would be nice if we could track it down and get it squished.

  • Daniel Hensby
    11/10/2010 1:28pm (4 years ago)

    Hi Aram,

    I don't know if you will keep the canView() func, but if you do, I'd code it so that it inherits from the parent page:

    function canView() {
    return $this->StaffPage()->canView();
    }

    :D

  • Aram Balakjian
    14/10/2010 2:42pm (4 years ago)

    Thanks (again!) Dan, added to the code :)

  • Jeroen Marechal
    18/10/2010 8:07am (4 years ago)

    Very useful article, looking forward to Part II when it comes to URLSegments

  • Shea
    20/10/2010 2:25am (4 years ago)

    Thanks for this tutorial Aram. I'm just starting with SilverStripe and this has been very helpful.

    just a small note - the code in the zip is fine but on this page it looks like public function Breadcrumbs() is in the model rather than the controller?

    Loving the site and looking forward to reading more tutorials =D

  • Aram Balakjian
    21/10/2010 12:04pm (4 years ago)

    @Mr Squatch - Thanks for catching that, I have moved it back to the controller. The reason this got half changed was that to be completely 'correct' the Breadcrumbs function should actually be in the Model, as we are overriding the same function in the SiteTree class. However, we then lose access to our getStaffMember() function, which requires URL paramaters, not accessible from the model.

    So inorder to make it all work, it is back in the controller, but I am going to investigate how to do this to ensure cpnsistency with core classes. To be honest I am not entirely sure why it is in the model, as in my view it as a controller function.....

  • alex li
    29/10/2010 3:50am (3 years ago)

    very good tutorial, thanks for sharing ..

  • Ty Barho
    08/11/2010 11:41pm (3 years ago)

    Waiting patiently (or not so much . . . ) for Part 2! Great work!

  • dendeffe
    22/11/2010 1:22pm (3 years ago)

    Hi Aram, nice tutorials. I have spooted an error, though. When I click on next in the DOM (https://skitch.com/dendeffe/rbptp/window), I get this error: https://skitch.com/dendeffe/rbpts/window

    Might be a (known) DOM problem?

  • Aram Balakjian
    22/11/2010 1:30pm (3 years ago)

    @ Dendeff - Yea that looks like a DOM error, probably caused by 2.4.3, I know there have been problems recently. I suspect UC will fix it shortly. Otherwise try it in 2.4.2 and see if you still get the error?

    Aram

  • Nicolas
    21/01/2011 7:52am (3 years ago)

    Thank you very much for this well written, educative tutorial with a real-life purpose. If part 2 is as good as this one, it'll answer the questions I actually came here for in an instant :-)

    Small detail: In the Link()-function on the StaffMember Class I had to append a slash to 'show' for the link to work.

  • Thomas Apfelbacher
    21/02/2011 4:46pm (3 years ago)

    Hi Aram, thanks again for your nice tutorials. I'm a noob at the whole silverstripe-ajax-thingy. (Yes, I#ve read the ajax-recipe) So i thougt i learn this topic by 'playing' with this tutorial-code.My Goal is to show the StaffMember in a ajaxpopup instead to show a virtual page. It is'nt that heard to use a lightboxclone. But i wonder, where to put the Director::is_ajax-code. Or better which part of your code is need to be changed. I guess it is the function show() in StaffPage.php, right? Can you give me some guidance, please. Thanks pipifix

  • Thomas Apfelbacher
    21/02/2011 10:57pm (3 years ago)

    Hi Aram, its me again. It seems im a total beginner. I just simply want to sort the staff by the names of the staffmembers. Like my question above i dont know, where to put: return $StaffMembers->sort('Title',ASC);.
    In your tutorial the Staffmembers are rendern in order of creation (ID).
    Thanks again. Thomas

  • Aram Balakjian
    22/02/2011 9:39am (3 years ago)

    Hi Thomas,

    If you want a custom order for your staffmembers, you need to write your own function to return them in that order. So if you look at the code under 'The StaffPage listing Template' you'll see we simply <% control StaffMembers %> to loop over theStaffMembers attached to the page.

    So instead change this to <% control OrderedStaffMembers %> and then add a function like this to your StaffPage_Controller:

    function getOrderedStaffMembers()
    {
    return $this->StaffMembers(Null, 'FirstName DESC');
    }

    Hope that helps :)

    Aram

  • Thomas Apfelbacher
    22/02/2011 5:33pm (3 years ago)

    Hi Aram, thanks for your help. It works fine. Is there a reason why sort wont work and $this->StaffMembers(Null, ..) will work?

    Is it possible to have a short look at the question above (ajaxlink)? Thanks a lot. Thomas

  • Aram Balakjian
    22/02/2011 5:39pm (3 years ago)

    Hi Thomas,

    the only reason you can't do that in the template (<% control StaffMembers(Null, 'FirstName ASC') %> is because the parser is a little limited. This will be improved for SS3 so hopefully this sort of workaround won't be needed.

    Unfortunately I don't have a lot of time right now to look into the other question, but I know that in the past I would either use a hidden div and load that into the modal window or just load a page into the modal window with a stripped out template (e.g. http://www.severndeanery.nhs.uk/contacts/staff-contacts/). I'll let you know if I get some time to have a look :)

    Aram

  • Thomas Apfelbacher
    23/02/2011 7:13pm (3 years ago)

    Thanks again for your reply. I choosed the stripped-out-template-trick. Everything works fine.

    Maybe a dataobject-ajax-tutorial in the future... ;-)

    Thomas

  • Simon Erkelens
    06/04/2011 4:06pm (3 years ago)

    Cool trick, if you don't want the /show/{number_here}, add the following lines to your page-controller:
    [code]
    public static $url_handlers = array(
    '$ID!' => 'show',
    );
    [/code]
    This way, when you go to news/2, Silverstripe processes it as going to news/show/2

    Cleans up your URL.

    What I'd like to see is the slugged-URL. Have you got anything for that too?

  • Simon Erkelens
    08/04/2011 8:41am (3 years ago)

    Here's a brilliantly strange thing:

    The moment I add a staffmember, the staffpage disappears from the menu.

    Can someone please explain why?

  • Simon Erkelens
    08/04/2011 9:05am (3 years ago)

    Ah, nevermind, I had a control Children in the menu... that kinda did it...

  • ModernDegree
    09/04/2011 3:10am (3 years ago)

    Is it possible to use [code]static $permissions = array( 'view' );[/code] instead of the parent's canView function? I've it in the past to solve the guest user read issue.

  • ModernDegree
    09/04/2011 3:10am (3 years ago)

    Great post, btw

  • Gordon Anderson
    18/04/2011 5:30am (3 years ago)

    Thanks for all the hints. However I am stuck on a couple of things:

    1) I am trying to gracefully degrade the imagegallery plugin by unclecheese to be non JS friendly. I now have an ImageGalleryPage showing ImageGalleryAlbums correctly in the menu (ImageGalleryAlbums being DataObjects, ImageGalleryPage extending Page). However when viewing a single phone (class is ImageGalleryItem) the menu disappears, I assume due to the hierarchy being DataObject -> DataObject instead of Page -> DataObject.

    2) I've had to revert to URLs for the form /images/35 for the mean time just to get things working. Is it possible to keep the URL structure intact, e.g. /galleries/gordons-travels/bangkok/image/35 instead of /image/35 ?

    Cheers

    Gordon

  • edwardlewis
    25/05/2011 12:43pm (3 years ago)

    Is there a way to make the fields only display when there is information in there?

    I have adapted the code to make more fields, I have fields to enter peoples facebook, twitter, website url etc and want it to be displayed on the site using icons. At the moment, the icons are still displayed, even when there is no links specified..

  • Aram Balakjian
    25/05/2011 12:53pm (3 years ago)

    Hi Edward,

    This is very simple. Lets say your field is 'TwitterLink' then you would do this in the template:

    <% if TwitterLink %>
    <a href="$TwitterLink">Twitter</a>
    <% end_if %>

    :)

    Aram

  • edwardlewis
    08/06/2011 12:16am (3 years ago)

    thanks for the tutorial and extra help.. pretty much followed it line by line on my website: http://weareword.com/we-are-word/show/4

  • Mom Bunheng
    10/06/2011 3:22am (3 years ago)

    Hi,
    First of all thanks for your contribution, it is really important resource for me, I use this for almost my website especially charity organisation. With this tutorial I would like to know is there any possible way to hide the StaffMemer name from Menu title?

    Thanks
    Bunheng

  • Aram Balakjian
    10/06/2011 9:19am (3 years ago)

    Hi Mom.

    Could you explain what you mean by 'Hide the staff member from MenuTitle"? I am confused :)

    Aram

  • Mom Bunheng
    11/06/2011 4:21am (3 years ago)

    Hi,

    I would like to hide StaffMember Name from menu, I fixed it deleting the following function:

    public function Children(){
    return $this->StaffMembers();
    }

    Once again thanks for your value contribution.

    Thanks
    Bunheng

  • Mom Bunheng
    12/06/2011 7:53am (3 years ago)

    Hi,

    I would like to know if it is possible to use URLSegment instead of ID within URL, hope you have trick.

    Thanks
    Bunheng

  • saffron
    29/07/2011 5:44pm (3 years ago)

    Hello Aram

    how would you realise a pagination function and limitation of the results.

    really need help here. i am limiting the results (news articles) and can not figure out the pagination.

    thanks!

    tobias

  • edwardlewis
    17/08/2011 9:15am (3 years ago)

    is it possible to add in an additional field which would be used for the url of the page?

  • Aram Balakjian
    17/08/2011 9:28am (3 years ago)

    Hi Edward, check out the second part of this tutorial that covers using URLSegments instead of IDs :)

    http://www.ssbits.com/tutorials/2010/dataobjects-as-pages-part-2-using-model-admin-and-url-segments-to-create-a-product-catalogue/

  • edwardlewis
    18/08/2011 9:29am (3 years ago)

    Thanks for your reply :) I have had a look through both tutorials, they are both really useful!

    When I used this one on a site, none of the items appear in the xml sitemap. Is this a limitation of the method or just extra functionality which hasnt been added in this case?

  • Aram Balakjian
    18/08/2011 9:31am (3 years ago)

    Hi Edward, yes it's just a case of not having extended the functionality to support the Site map. It's not too much work to do, have a look in the googlesitemaps module to see how to do it, basically you will need to extend that to add the DO to the results and adapt the xml template accordingly.

    Aram

  • edwardlewis
    18/08/2011 9:35am (3 years ago)

    wow.. you must be online as much as me. Thanks for the info and speedy reply! :))

  • kBits
    25/08/2011 5:29am (3 years ago)

    Hi Aram,

    This is super cool thanks for the tutorial! I am using this code to create a wallpaper gallery to offer downloads to my visitors. I ran into a problem however: if I 'unpublish' the main page from the site, I lose all the data I uploaded to the page. So I tried with the demo and same thing, if you publish the page you lose all the Staff Members. Any ideas on what to do about this? Thanks!

  • kBits
    25/08/2011 5:30am (3 years ago)

    Sorry, I meant 'unpublish' the page, all the Staff Members are gone.

  • Aram Balakjian
    26/08/2011 5:11pm (3 years ago)

    Hi Kbits, I have found a solution to this issue and changed the post accordingly :)

    Thanks for pointing it out!

    Aram

  • kBits
    26/08/2011 5:42pm (3 years ago)

    Thanks Aram! I really appreciate it. Also, thanks for providing all of your excellent tutorials, they have been a great benefit to me in helping create my site and learn more about SS. Great stuff!

  • Catcher
    26/09/2011 7:45pm (3 years ago)

    Really useful post, and a nice introduction to under-the-hood MVC operations in SS, thanks Aram!

    A question though, how does the StaffMember get associated with its StaffPage?
    I'm following the example closely, but my StaffMember's associated StaffPageID always comes up 0 since it never gets written, and then the Link() function comes up with an uninitialized page with no URLSegment.

    Thanks again!

  • Aram Balakjian
    27/09/2011 9:43am (3 years ago)

    Hi Catcher,

    The StaffMember object gets accociated by the DataObjectManager, when you add it and save it should set the StaffPageID field to the current page. Make sure your DOM definition is correct.

    Aram

  • theRoom
    01/11/2011 10:16am (2 years ago)

    Hi Aram, thanks for this great post!

    I want to ask you if you could point me (us) in the right direction to implement pagination to the Staff Page.

    Thanks again! :)

  • priithansen
    11/12/2011 3:41pm (2 years ago)

    Very usefull tutorial thanks!

    One little question. Is it possible to show the product page using just the Layout template without rest of the page structure? So I could use it inside a modal window with ajax requests.

  • priithansen
    11/12/2011 10:35pm (2 years ago)

    Nevermind got it from the Part2.

    And again Thanks for the supper useful tutorial.

  • VRoxane
    08/01/2012 12:24pm (2 years ago)

    Hi Aram ! Thank you for those amazing SSbits !

    I'm trying to insert a contact form on these "DataObjects-Pages".
    But I didn't manage to do this. On submit, I'm redirected to the parent page plus "/ContactForm".

    Is there a way to do this ? Can you help me ?
    (My searchform does the same on those DO Pages...)

  • Aram Balakjian
    08/01/2012 12:37pm (2 years ago)

    Hi Roxane,

    What you need to do is put the form function on the CategoryPage_Controller and then pass in the ID of the DataObject you are on in a hidden field in the form itself. So you effectively call the form on the parent page, but you can work out which DO you were on in the submission controller by grabbing the value from the hidden field.

    Hope that makes sense!

    Aram

  • VRoxane
    08/01/2012 3:09pm (2 years ago)

    It worked !
    I had to add "ContactForm" to the allowed actions as well, otherwise I ended on the 404.

    Thank you very much, again :) !

  • Vincent_vega
    23/01/2012 9:01am (2 years ago)

    I've created a page that lists all the staff etc on one page (based on this tutorial).

    The staff page is all good but i would like each staff member to be listed under their role, basically like how Silverstripe have done it with their team page http://www.silverstripe.com/about-us/team/

    So CEO, Development, Design etc etc

    Can someone point me in the right direction on what documentation i have to read up on?

    Cheers

    V

  • Aram Balakjian
    23/01/2012 9:29am (2 years ago)

    Hi Vincent,

    you can do <% control StaffMembers.groupBy(Role) %> which will group your items by the field Role.

    Then in the loop use $Role and <% control Children %> to loop through each role and staff memebrs.

    Cheers,

    Aram

  • Vincent_vega
    23/01/2012 11:09am (2 years ago)

    <% if StaffMembers %>

    <div class="staff-wrapper">

    <% control StaffMembers %>

    <div class="staff-container">

    <div class="staff-thumb">$Photo.CroppedImage(100,150)</div>

    <div class="staff-copy">

    <h4>$Name</h4>

    <p>Role: $Role</p>

    <p>$Description</p>


    </div>
    </div>
    <% end_control %>

    </div><!-- end of Staff Wrapper -->

    <% end_if %>



    Sorry Aram, where would the new code go? bit lost at the moment as when i replace the <% control StaffMembers %> with <% control StaffMembers.groupBy(Role) %> i lose the thumbnail etc

  • Aram Balakjian
    23/01/2012 11:14am (2 years ago)

    You would do

    <% control StaffMembers.groupBy(Role) %>

    <h2>$Role</h2>
    <% control Children %>
    //Normal Staff member Markup
    <% end_control %>
    <% end_control %>

  • Vincent_vega
    23/01/2012 11:40am (2 years ago)

    yeah i tried that but didn't work? weird

  • Vincent_vega
    23/01/2012 8:57pm (2 years ago)

    Got it to work, by changing <% control StaffMembers.groupBy(Role) %>
    to <% control StaffMembers.GroupedBy(Role) %>

  • Thomas Apfelbacher
    13/02/2012 6:19pm (2 years ago)

    Hi Aram,

    i simple want to provide a contactform on the staffdetailspage (staff_show.ss). but i dont get a clue how to stick togehter the userform/form and this fine piece of code. I guess i need to build the form in the StaffMember.php and not in the StaffPage.php. Or am i wrong? And how to pass the mailvariable to the form?

    Can you give me a tip or point to a tutorial/snippet, please. Thanks a lot.

  • Aram Balakjian
    14/02/2012 9:11am (2 years ago)

    Hi Thomas, that is going to be pretty tricky to achieve. The best way to do this would be to build the form in the Controller rather than trying to use the UserForms module. You will need to create a hidden field with the ID of the staff member you are currently viewing so that when you process the form submission you know who to send it to.

    Have a look at the Contact Form Tutorial to get you started: http://www.ssbits.com/newbies/2010/creating-a-simple-contact-form/

  • Elie Andraos
    10/04/2012 9:50am (2 years ago)

    Hi Aram,
    What if the staffmember should have many images ? like mulitple uploads, what should i do ?

    use ImageField with hasmany photos ?
    Please advise :)

  • Neil Creagh
    18/04/2012 4:39pm (2 years ago)

    Any tips on getting this to work with many_many files?
    I'm using Uncle Cheese's KickAssets module which makes adding multiple files (photos) to each page in the CMS nice and easy... but I'd like to be able to show these on the front end on individual page if clicked. eg. /show/ID

    But can't get this to work and I think it's because my dataobjects (Files) are many_many instead of has_many.

    My Page.php uses:

    static $many_many = array (
    'Photos' => 'File'
    );

    public function ShowPhotos($limit = null) {
    $data = $this->getManyManyComponents('Photos', '', 'ManyManySort', '', $limit);
    return $data;
    }

    Any ideas or tips would be appreciated - thanks!

  • Neil Creagh
    24/04/2012 3:20pm (2 years ago)

    Ok ignore the above - I have this working using Files as pages instead of DataObjects (because I'm using Uncle Cheese's KickAssets) - the details are on this post:
    http://www.silverstripe.org/general-questions/show/19748

    Along with a couple of questions that I think experienced Silverstripers might be able to help me with.

  • Dredd
    10/05/2012 12:24pm (2 years ago)

    You can move Breadcrumbs() into model if you replace $this->getStaffMember() with $this->CurrentPage()->getStaffMember() .

  • theRoom
    19/08/2012 8:30am (2 years ago)

    Hi Aram, thanks for the great tutorial, I've used it many times already!
    Now I'm trying to use it again having 2 images per staff member, how do I do it? I've tried this but doesn't really work:

    //Relations
    static $has_one = array (
    'ProductPage' => 'ProductPage',
    );

    static $has_many = array (
    'Photo_1' => 'Image',
    'Photo_2' => 'Image'
    );

    Any advice please?
    Thanks a lot

  • Aram Balakjian
    20/08/2012 5:02pm (2 years ago)

    Hi theRoom

    Add your images to the $has_one relation, rather than the has_many, as technically it has one 'Photo_1' and one 'Photo_2'.

    Aram

  • pinkp
    17/05/2013 7:51pm (11 months ago)

    Is there an SS3 tutorial version of this? Or similar?

  • Aram Balakjian
    21/05/2013 9:06am (11 months ago)

    Hi Pinkp,

    The principles will be the same for SS3, you will just need to change a few bits here and there as per the upgrading docs.

    Alternatively you can try the module here: https://github.com/arambalakjian/DataObjects-as-Pages

    Cheers,

    Aram

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 njorndare
7 article image Martijn van Nieuwenhoven
8 article image Darren-Lee
9 article image Roman Schmid
10 article image Matt Clegg

View full leaderboard


Advertisement