Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

Data Model Questions /

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

Looping many to many in the controller

Go to End

5 Posts   1183 Views


Community Member, 9 Posts

16 January 2013 at 9:48pm

Hi guys,

I'm sure this is a very simple task but I'm just not sure on the syntax.

Basically I have a Job dataobject that has the usual fields like Title, Description, etc, but it also has a many to many relationship with Locations as each job can have multiple locations assigned.

So Job is (some code stripped out for clarity):

class Job extends DataObject {
	static $db = array(
		'JobID' => "Int",
		'Title' => "Varchar(255)",
		'ShortDescription' => "Text",
		'Description' => 'HTMLText'

	public static $many_many = array(
		'Categories' => 'Category',
		'SubCategories' => 'SubCategory',
		'Locations' => 'Location',
		'JobTypes' => 'JobType'
public function getLongTitle() {
    $data = $this->Title . '  |  ';
    return $data;

And location is (again missing code for clarity):

class Location extends DataObject {
	static $db = array(
		'Title' => "Varchar(255)"
	public static $many_many = array(
		'Jobs' => 'Job'

In my template I have a small block that shows the latest 5 jobs using this bit of code in the pages class.

	function LatestVacancies() {
		return DataObject::get('Job','','ID DESC','','5');	

Which is fine when I was only showing the title.

What I need to do now however is show the locations after the job title. I know I could use another control block in the template to loop through the locations, but I only have a limited width of space so I want to use the ".LimitCharacters(35)".

So what I want to do is return a long title from the php to the template which will be a single string in the format <job title> | <location1>, <location2>, etc...

This way I will be able to use the LimitCharacters on that entire string as some job title's are really short the 35 character cut off will be part way through one of the locations, and some are long so it cuts off part way through the job title.

So I might end up with:
Assessor | Perth, Sydney
Senior Quality Controller | Sydn ...
Group Facilities and Capital Works ...

I started to make a getLongTitle() function in the Job DataObject which works fine (as I use other bits from the Job DataObject in the template) but I just don't know how to access the Locations array from the job and build that string.

Like I said, this should be easy, I'm just not used to PHP or Silver Stripe.

Hopefully someone can fix that one function up for me!



Community Member, 9 Posts

21 January 2013 at 8:14pm


I would have thought this was about 2 lines of code... I just don't have any of my developers available to help me as I have them all on different projects so I'm trying to do this bit myself.

Please help! :)


Community Member, 3 Posts

23 January 2013 at 2:37am

firstly you must used in Job Class or in Location Class for a many_many relationship the array

$belongs_many_many = array();

And then you can use in your template

<% control LatestVacancies %> 
   <% if Locations %> 
      <% control Locations %> 
      <% end_control %> 
   <% end_if %>
<% end_control %>

This is only theory and i have not try this ;)



Forum Moderator, 5523 Posts

24 January 2013 at 8:38pm

Edited: 24/01/2013 10:12pm

As mentioned in the original post, should be in PHP

public function getLongTitle() {
$title = $this->Title . implode(", ", $this->Locations()->column('Title'));

return substr($title, 0, 50);

The implode trick should work, if not you can do the longer - foreach($this->Locations() as $location) .... and append the locations there.

The other thing I would do is add LongTitle to your $casting array to ensure the field is escaped in templates (


Community Member, 9 Posts

24 January 2013 at 8:46pm

Thanks Willr.

That was more what I was after. I was sure I could do it with a foreach loop I just wasn't sure on the syntax.

I'll give the implode trick a bash first.