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

one-to-many containing one-to-many relationship


Go to End


4 Posts   4331 Views

Avatar
david_nash

Community Member, 55 Posts

6 May 2009 at 6:08pm

Hi, I think I'm thinking about this the wrong way and I'm wondering if someone could help or make a suggestion.

I want to model some educational 'courses'.

Each Course has a title and description (I'm using CoursePage [extends Page] for this)
Each Course also has many Location/Fee pairs (I'll call this CourseLocation).
Each of those CourseLocations will have many Dates. These are the days the courses will run.

I think I've got the relationships correct but I'm not sure about how the form fields should work.

For CoursePage I use a ComplexTableField that shows the Location/Fee pairs.

But how do I list the multiple dates for each of those pairs?

I tried using another ComplexTableField inside the popup but it the first CTF says 'unknown column `0` in database'.

I have a feeling that I've made this way more complicated than it needs to be. Please help!

Avatar
david_nash

Community Member, 55 Posts

6 May 2009 at 8:17pm

Edited: 06/05/2009 8:18pm

I think I was on the right track, and I reckon I have a pretty good solution. I hope someone can have a look at this and give me some pointers.

I created a third class called CourseDate that just contains a date. The reason I did this was so that I could use it in my $has_many array, which needs to have the $has_one relation in the other direction.

Instead of using a ComplexFieldTable on the second one-to-many relation, I used a TableField.

Here's my code:

<?php
//file mysite/code/CoursePage.php
class CoursePage extends Page {
	static $db = array();
   	static $has_one = array();

	static $has_many = array(
		'Courses' => 'Course'	
	);
	
	//list the location and fee entries in a table
	function getCMSFields() {
		$fields = parent::getCMSFields();
		$tablefield = new ComplexTableField(
			$this,
			'Courses',
			'Course',
			array(
				'Location' => 'Location',
				'Fee' => 'Fee'
			),
			'getCMSFields_forPopup'
		);

		$tablefield->setAddTitle( 'A Course' );
 		$fields->addFieldToTab( 'Root.Content.Courses', $tablefield );
 		return $fields;

	}

}
 
class CoursePage_Controller extends Page_Controller {
}
?>

Next I have a 'Course' which is made up of multiple fee/location pairings. The reason for this is that courses in different locations may have different costs. I extend DataObject because I don't need all the Page stuff.

<?php
class Course extends DataObject {

	public static $db = array(
		'Location' => 'Text',
		'Fee' => 'Currency'
	);

	public static $has_one = array(
		'CoursePage' => 'CoursePage'
	);

	public static $has_many = array(
		'CourseDates' => 'CourseDate'
	);

	//when an entry in the 'Courses' tab is clicked, I want to show a table of dates
	function getCMSFields_forPopup() {
		$fields = new FieldSet();
		$fields->push( new TextField( 'Location' ) );
		$fields->push( new CurrencyField( 'Fee' ) );

		$ctf = new TableField(
			'CourseDates',
			'CourseDate',
			array( 'Date' => 'Date' ),
			array( 'Date' => 'DateField' )
			//array( 'Date' => 'CalendarDateField' ) //the bottom of this gets cut off obscuring the dates!
		);

		$fields->push( $ctf );
		return $fields;
	}
} //end class

Now this bit I'm not 100% on but it works:

<?php
class CourseDate extends DataObject {

	//it's just a 'Date' named 'Date'
	public static $db = array(
		'Date' => 'Date'
	);

	//make the link back to the 'parent'
	public static $has_one = array(
		'Course' => 'Course'
	);

} //end class

Now this all seems pretty good except that when I use CalendarDateField in my Course object the bottom of the calendar gets cut off.

My second choice would be the composite date field, but I can't get that to load the date correctly. So I fall back on DateField.

Avatar
Ingo

Forum Moderator, 801 Posts

11 May 2009 at 8:39pm

Nesting ComplexTableField popups is not really supported. You could try to use a ModelAdmin interface to link each CTF row into a new ModelAdmin edit form instead of a popup.

But yeah, a TableField seems a good option to. We've had troubles with DateField calendar popup styling in the past, here's a custom class that uses jQuery instead, which has a bit more robust CSS.

<?php
/**
 * @todo Hardcoded to NZ date format
 */
class JQueryCalendarDateField extends DateField {
	
	protected $extraClasses = array('jquery-datepicker');
	
	function Field() {
		Requirements::javascript(THIRDPARTY_DIR . "/jquery/jquery.js");
		Requirements::javascript(THIRDPARTY_DIR . "/jquery/jquery_improvements.js");
		Requirements::javascript(THIRDPARTY_DIR . "/jquery/plugins/livequery/jquery.livequery.js");
		Requirements::javascript(THIRDPARTY_DIR . "/jquery/ui/ui.datepicker.js");
		Requirements::javascript(THIRDPARTY_DIR . "/jquery/ui/ui.core.js");
		Requirements::css(THIRDPARTY_DIR . "/jquery/themes/default/ui.all.css");

		Requirements::customScript("
			jQuery('input.jquery-datepicker').livequery(
				function() {
					jQuery(this).datepicker('destroy');
					jQuery(this).datepicker({
						dateFormat: 'dd/mm/yy',
					});				
				}
			);
			", 
			'JQueryCalendarDateField' // unique identifier makes sure rule is only called once
		); 

		return parent::Field();
	}
	
}
?>

Careful, its fairly hacky (re-applying livequery and re-assigning plugins), but it does the job here.

Avatar
david_nash

Community Member, 55 Posts

12 May 2009 at 12:03pm

Thanks Ingo, I really appreciate it!