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.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

Data Model Questions /

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

Looping many to many in the controller


Go to End


5 Posts   1976 Views

Avatar
3geeks

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 ...
etc.

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!

Cheers,
Duncan

Avatar
3geeks

Community Member, 9 Posts

21 January 2013 at 8:14pm

Anyone?

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! :)

Avatar
Izeroy

Community Member, 3 Posts

23 January 2013 at 2:37am

Hello,
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 %> 
         $LongTitle 
      <% end_control %> 
   <% end_if %>
<% end_control %>

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

Greetz
Izeroy

Avatar
Willr

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 (http://doc.silverstripe.com/framework/en/topics/data-types#casting-arbitrary-values)

Avatar
3geeks

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.

Cheers.