Skip to content
This repository
Browse code

NEW Rewritten tutorial 5 to GridField API

- Removed redundant "module" relationship, has_one is already present in Student->Project relationship
- Simplified FirstName/LastName to Name on Student and Mentor models, makes for less sample code
- Removed detail views for Student and Mentor, as they don't add much educational value
- Fixed indentation in code examples
- Using GridField relation editor, listing students on relation instead of holder
- Added optional exercises for the readers
- Added conclusion, explaining when to use Page vs. DataObject
- Updated sample data to GSOC '12 rather than '07, anonymized and randomised a bit
- Thanks to Naomi Guyer for contributing!
  • Loading branch information...
commit 0c0bcc9aec108b9f2ecd2007d309ab0c921561f9 1 parent 0308cc2
Ingo Schommer authored

Showing 22 changed files with 317 additions and 659 deletions. Show diff stats Hide diff stats

  1. 976  docs/en/tutorials/5-dataobject-relationship-management.md
  2. BIN  docs/en/tutorials/_images/gsoc-mentor-creation.jpg
  3. BIN  docs/en/tutorials/_images/gsoc-mentor-student-selection.jpg
  4. BIN  docs/en/tutorials/_images/gsoc-mentor.jpg
  5. BIN  docs/en/tutorials/_images/gsoc-module-creation.jpg
  6. BIN  docs/en/tutorials/_images/gsoc-project-creation.jpg
  7. BIN  docs/en/tutorials/_images/gsoc-project-module-selection.jpg
  8. BIN  docs/en/tutorials/_images/gsoc-project-student-selection.jpg
  9. BIN  docs/en/tutorials/_images/gsoc-project.jpg
  10. BIN  docs/en/tutorials/_images/gsoc-projects-table.jpg
  11. BIN  docs/en/tutorials/_images/gsoc-student-creation.jpg
  12. BIN  docs/en/tutorials/_images/tutorial5_addNew.jpg
  13. BIN  docs/en/tutorials/_images/tutorial5_mentor.jpg
  14. BIN  docs/en/tutorials/_images/tutorial5_mentor_creation.jpg
  15. BIN  docs/en/tutorials/_images/tutorial5_mentor_students.jpg
  16. BIN  docs/en/tutorials/_images/tutorial5_module_creation.jpg
  17. BIN  docs/en/tutorials/_images/tutorial5_module_selection.jpg
  18. BIN  docs/en/tutorials/_images/tutorial5_project.jpg
  19. BIN  docs/en/tutorials/_images/tutorial5_project_creation.jpg
  20. BIN  docs/en/tutorials/_images/tutorial5_projects_table.jpg
  21. BIN  docs/en/tutorials/_images/tutorial5_student_tab.jpg
  22. BIN  docs/en/tutorials/_images/tutorial5_students.jpg
976  docs/en/tutorials/5-dataobject-relationship-management.md
Source Rendered
@@ -2,774 +2,432 @@
2 2
 
3 3
 ## Overview
4 4
 
5  
-In the [second tutorial](2-extending-a-basic-site) we have learned how to add extrafields to a page type thanks
6  
-to the *$db* array and how to add an image using the *$has_one* array and so create a relationship between a table and
7  
-the *Image* table by storing the id of the respective *Image* in the first table. This tutorial explores all this
8  
-relations between [DataObjects](/topics/datamodel#relations) and the way to manage them easily.
9  
-
10  
-<div class="notice" markdown='1'>
11  
-	I'm using the default tutorial theme in the following examples so the templates may vary or you may need to change
12  
-	the template code in this example to fit your theme
13  
-</div>
  5
+This tutorial explores the relationship and management of [DataObjects](/topics/datamodel#relations). It builds on the [second tutorial](2-extending-a-basic-site) where we learnt how to define
  6
+additional fields on our models, and attach images to them.
14 7
 
15 8
 ## What are we working towards?
16 9
 
17  
-To simulate these relations between objects, we are going to simulate the management via the CMS of the **[Google Summer
18  
-Of Code 2007](http://www.silverstripe.com/google-summer-of-code-2007-we-are-in/)** that SilverStripe was part of.
19  
-
20  
-To do this, we are gonna use the following objects :
21  
-
22  
-*  Project : Project on SilverStripe system for the GSOC 
23  
-*  Student : Student involved in the project
24  
-*  Mentor : SilverStripe developer
25  
-*  Module : Module used for the project
  10
+To demonstrate relationships between objects, 
  11
+we are going to use the example of students working on various projects.
  12
+Each student has a single project, and each project has one or more
  13
+mentors supervising their progress. We'll create these objects,
  14
+make them editable in the CMS, and render them on the website.
26 15
 
  16
+This table shows some example data we'll be using:
27 17
 
28  
-This is a table which sums up the relations between them :
29  
- | Project             | Student             | Mentor        | Modules          |                           
30  
- | -------             | -------             | ------        | ------------------                           
31  
- | i18n Multi-Language | Bernat Foj Capell   | Ingo Schommer | Cms, Framework, i18n, Translation              | 
32  
- | Image Manipulation  | Mateusz Ujma        | Sam Minnee    | Cms, Framework, ImageManipulation              | 
33  
- | Google Maps         | Ofir Picazo Navarro | Hayden Smith  | Cms, Framework, Maps                           | 
34  
- | Mashups             | Lakshan Perera      | Matt Peel     | Cms, Framework, MashUps                        | 
35  
- | Multiple Databases  | Philipp Krenn       | Brian Calhoun | Cms, Framework, MultipleDatabases              | 
36  
- | Reporting           | Quin Hoxie          | Sam Minnee    | Cms, Framework, Reporting                      | 
37  
- | Security & OpenID   | Markus Lanthaler    | Hayden Smith  | Cms, Framework, auth_openid                    | 
38  
- | SEO                 | Will Scott          | Brian Calhoun | Cms, Framework, googleadwords, googleanalytics | 
39  
- | Usability           | Elijah Lofgren      | Sean Harvey   | Cms, Framework, UsabilityElijah                | 
40  
- | Safari 3 Support    | Meg Risen           | Sean Harvey   | Cms, Framework, UsabilityMeg                   | 
  18
+ | Project             | Student             | Mentor   	    | 
  19
+ | -------             | -------             | -------			  |
  20
+ | Developer Toolbar | Jakob,Ofir   | Mark,Sean	|
  21
+ | Behaviour Testing  | Michal,Wojtek | Ingo, Sean |
  22
+ | Content Personalization | Yuki | Philipp	| 
  23
+ | Module Management | Andrew | Marcus,Sam | 
41 24
 
42  
-## GSOC Projects
  25
+### Has-One and Has-Many Relationships: Project and Student
43 26
 
44  
-Before starting the relations management, we need to create a *ProjectsHolder* class where we will save the GSOC Project
45  
-pages.
  27
+A student can have only one project, it'll keep them busy enough.
  28
+But each project can be done by one or more students.
  29
+This is called a **one-to-many** relationship.
  30
+Let's create the `Student` and `Project` objects.
46 31
 
47  
-*tutorial/code/ProjectsHolder.php*
  32
+**mysite/code/Student.php**
48 33
 
49 34
 	:::php
50 35
 	<?php
51  
-	
52  
-	class ProjectsHolder extends Page {
53  
-	
54  
-		static $allowed_children = array( 'Project' );
55  
-	
56  
-	}
57  
-	
58  
-	class ProjectsHolder_Controller extends Page_Controller {
59  
-	
60  
-	}
61  
-
62  
-## Project - Student relation
63  
-
64  
-**A project can only be done by one student.**
65  
-
66  
-**A student has only one project.**
67  
-
68  
-This relation is called a **1-to-1** relation.
69  
-
70  
-The first step is to create the student and project objects.
71  
-
72  
-*tutorial/code/Student.php*
73  
-
74  
-	:::php
75  
-	<?php
76  
-	
77 36
 	class Student extends DataObject {
78  
-	
79 37
 		static $db = array(
80  
-			'FirstName' => 'Text',
81  
-			'Lastname' => 'Text',
82  
-			'Nationality' => 'Text'
  38
+			'Name' => 'Varchar',
  39
+			'University' => 'Varchar',
83 40
 		);
84  
-	
85  
-		public function getCMSFields_forPopup() {
86  
-			$fields = new FieldList();
87  
-			
88  
-			$fields->push( new TextField( 'FirstName', 'First Name' ) );
89  
-			$fields->push( new TextField( 'Lastname' ) );
90  
-			$fields->push( new TextField( 'Nationality' ) );
91  
-			
92  
-			return $fields;
93  
-		}
94  
-	
  41
+		static $has_one = array(
  42
+			'Project' => 'Project'
  43
+		);	
95 44
 	}
96 45
 
97  
-
98  
-*tutorial/code/Project.php*
  46
+**mysite/code/Project.php**
99 47
 
100 48
 	:::php
101 49
 	<?php
102  
-	
103 50
 	class Project extends Page {
104  
-	
105  
-		static $has_one = array(
106  
-			'MyStudent' => 'Student'
  51
+		static $has_many = array(
  52
+			'Students' => 'Student'
107 53
 		);
108  
-	
109 54
 	}
110  
-	class Project_Controller extends Page_Controller {}
111  
-
112  
-This code will create a relationship between the *Project* table and the *Student* table by storing the id of the
113  
-respective *Student* in the *Project* table.
114  
-
115  
-The second step is to add the table in the method *getCMSFields* which will allow you to manage the *has_one* relation.
116  
-
117  
-	:::php
118  
-	class Project extends Page {
119  
-	
120  
-		...
121  
-	
122  
-		public function getCMSFields() {
123  
-			$fields = parent::getCMSFields();
124  
-			
125  
-			$tablefield = new HasOneComplexTableField(
126  
-				$this,
127  
-				'MyStudent',
128  
-				'Student',
129  
-				array(
130  
-					'FirstName' => 'First Name',
131  
-					'Lastname' => 'Family Name',
132  
-					'Nationality' => 'Nationality'
133  
-				),
134  
-				'getCMSFields_forPopup'
135  
-			);
136  
-			$tablefield->setParentClass('Project');
137  
-			
138  
-			$fields->addFieldToTab( 'Root.Student', $tablefield );
139  
-			
140  
-			return $fields;
141  
-		}
142  
-	
  55
+	class Project_Controller extends Page_Controller {
143 56
 	}
144 57
 
145  
-Let’s walk through the parameters of the *HasOneComplexTableField* constructor.
146  
-
147  
-1.  **$this** : The first object concerned by the relation
148  
-2.  **'MyStudent'** : The name of the second object of the relation
149  
-3.  **'Student'** : The type of the second object of the relation
150  
-4.  **array(...)** : The fields of the second object which will be in the table
151  
-5.  **'getCMSFields_forPopup'** : The method which will be called to add, edit or only show a second object
152  
-
153  
-You can also directly replace the last parameter by this code :
  58
+The relationships are defined through the `$has_one`
  59
+and `$has_many` properties on the objects.
  60
+The array keys declares the name of the relationship,
  61
+the array values contain the class name (see the ["database structure"](/reference/database-structure)
  62
+and ["datamodel"](/topics/datamodel) topics for more information).
  63
+
  64
+As you can see, only the `Project` model extends `Page`,
  65
+while `Student` is a plain `DataObject` subclass.
  66
+This allows us to view projects through the standard
  67
+theme setup, just like any other page. 
  68
+It would be possible to render students separately as well,
  69
+but for now we'll assume they're just listed as part of their `Project` page.
  70
+Since `Project` inherits all properties (e.g. a title) from its parent class,
  71
+we don't need to define any additional ones for our purposes.
  72
+
  73
+Now that we have our models defined in PHP code,
  74
+we need to tell the database to create the related tables. 
  75
+Trigger a rebuild through *dev/build?flush=all* before you 
  76
+proceed to the next part of this tutorial.
  77
+
  78
+### Organizing pages: ProjectHolder
  79
+
  80
+A `Project` is just a page, so we could create it anywhere in the CMS.
  81
+In order to list and organize them, it makes sense to collect them under a common parent page.
  82
+We'll create a new page type called `ProjectsHolder` for this purpose,
  83
+which is a common pattern in SilverStripe's page types. Holders
  84
+are useful for listing their children, and usually restrict these children to a specific class,
  85
+in our case pages of type `Project`.
  86
+The restriction is enforced through the `$allowed_children` directive.
  87
+
  88
+**mysite/code/ProjectsHolder.php**
154 89
 
155 90
 	:::php
156  
-	   new FieldList(
157  
-	      new TextField( 'FirstName', 'First Name' ),
158  
-	      new TextField( 'Lastname' ),
159  
-	      new TextField( 'Nationality' )
160  
-	   );
161  
-
162  
-
163  
-<div class="tip" markdown='1'>
164  
-	Don't forget to rebuild the database using *dev/build?flush=1* before you 
165  
-	proceed to the next part of this tutorial.
166  
-</div>
167  
-
168  
-Now that we have created our *Project* page type and *Student* data object, let’s add some content.
169  
-
170  
-Go into the CMS and create one *Project* page for each project listed [above](#what-are-we-working-towards) under a 
171  
-*ProjectsHolder* page named **GSOC Projects** for instance.
172  
-
173  
-![tutorial:gsoc-project-creation.png](_images/gsoc-project-creation.jpg)
174  
-
175  
-As you can see in the tab panel *Student*, the adding functionality is titled *Add Student*. However, if you want to
176  
-modify this title, you have to add this code in the *getCMSFields* method of the *Project* class :
177  
-
178  
-	:::php
179  
-	$tablefield->setAddTitle( 'A Student' );
180  
-
181  
-
182  
-Select now one of the *Project* page that you have created, go in the tab panel *Student* and add all the students
183  
-listed [above](#what-are-we-working-towards) by clicking on the link **Add A Student** of your 
184  
-*HasOneComplexTableField* table.
185  
-
186  
-![tutorial:gsoc-student-creation.png](_images/gsoc-student-creation.jpg)
  91
+	<?php
  92
+	class ProjectsHolder extends Page {
  93
+		static $allowed_children = array(
  94
+			'Project'
  95
+		);
  96
+	}
  97
+	class ProjectsHolder_Controller extends Page_Controller {
  98
+	}
187 99
 
188  
-After having added all the students, you will see that, in the tab panel *Student* of all the *Project* pages, the
189  
-*HasOneComplexTableField* tables have the same content.
  100
+You might have noticed that we don't specify the relationship
  101
+to a project. That's because its already inherited from the parent implementation,
  102
+as part of the normal page hierarchy in the CMS.
190 103
 
191  
-For each *Project* page, you can now affect **one and only one** student to it ( see the
192  
-[list](#What_are_we_working_towards?) ).
  104
+Now that we have created our `ProjectsHolder` and `Project` page types,  we'll add some content.
  105
+Go into the CMS and create a `ProjectsHolder` page named **Projects**. 
  106
+Then create one `Project` page for each project listed [above](#what-are-we-working-towards).
193 107
 
194  
-![tutorial:gsoc-project-student-selection.png](_images/gsoc-project-student-selection.jpg)
  108
+### Data Management Interface: GridField
195 109
 
196  
-You will also notice, that you have the possibility to **unselect** a student which will make your *Project* page
197  
-without any student affected to it.
  110
+So we have our models, and can create pages of type
  111
+`Project` through the standard CMS interface,
  112
+and collect those within a `ProjectsHolder`.
  113
+But what about creating `Student` records?
198 114
 
199  
-**At the moment, the *HasOneComplexTableField* table doesn't manage totally the *1-to-1* relation because you can easily
200  
-select the same student for two ( or more ) differents *Project* pages which corresponds to a *1-to-many* relation.**
  115
+Since students are related to a single project, we will
  116
+allow editing them right the on the CMS interface in the `Project` page type.
  117
+We do this through a powerful field called `[GridField](/topics/grid-field)`.
  118
+All customization to fields for a page type are managed through a method called
  119
+`getCMSFields()`, so let's add it there:
201 120
 
202  
-To use your *HasOneComplexTableField* table for a **1-to-1** relation, make this modification in the class *Project* :
  121
+**mysite/code/Project.php**
203 122
 
204 123
 	:::php
  124
+	<?php
205 125
 	class Project extends Page {
206  
-	
207  
-		...
208  
-	
  126
+		// ...
209 127
 		public function getCMSFields() {
210  
-		
211  
-			...
212  
-			
213  
-			$tablefield->setParentClass('Project');
214  
-			
215  
-			$tablefield->setOneToOne();
216  
-			
217  
-			$fields->addFieldToTab( 'Root.Student', $tablefield );
218  
-			
  128
+			// Get the fields from the parent implementation
  129
+			$fields = parent::getCMSFields();	
  130
+
  131
+			// Create a default configuration for the new GridField, allowing record editing
  132
+			$config = GridFieldConfig_RelationEditor::create();
  133
+
  134
+			// Set the names and data for our gridfield columns
  135
+			$config->getComponentByType('GridFieldDataColumns')->setDisplayFields(array(
  136
+				'Name' => 'Name',
  137
+				'Project.Title'=> 'Project' // Retrieve from a has-one relationship
  138
+			));	
  139
+						
  140
+			// Create a gridfield to hold the student relationship    
  141
+			$studentsField = new GridField(
  142
+				'Students', // Field name
  143
+				'Student', // Field title
  144
+				$this->Students(), // List of all related students
  145
+				$config
  146
+			);		
  147
+			 
  148
+			// Create a tab named "Students" and add our field to it
  149
+			$fields->addFieldToTab('Root.Students', $studentsField); 
  150
+						
219 151
 			return $fields;
220 152
 		}
221  
-	
222 153
 	}
223 154
 
224  
-Now, you will notice that by checking a student in a *Project* page, you will be unable to select him again in any other
225  
-*Project* page which is the definition of a **1-to-1** relation.
226  
-
227  
-
  155
+This creates a tabular field, which lists related student records, one row at a time.
  156
+Its empty by default, but you can add new students as required,
  157
+or relate them to the project by typing in the box above the table.
  158
+
  159
+In our case, want to manage those records, edit their details, and add new ones. 
  160
+To accomplish this, we have added a specific `[api:GridFieldConfig]`.
  161
+While we could've built the config from scratch, there's several
  162
+preconfigured instances. The `GridFieldConfig_RecordEditor` default configures
  163
+the field to edit records, rather than just viewing them.
  164
+The GridField API is composed of "components", which makes it very flexible.
  165
+One example of this is the configuration of column names on our table:
  166
+We call `setDisplayFields()` directly on the component responsible for their rendering.
  167
+
  168
+<div class="note" markdown="1">
  169
+	Adding a `GridField` to a page type is a popular way to manage data,
  170
+	but not the only one. If your data requires a dedicated interface
  171
+	with more sophisticated search and management logic, consider
  172
+	using the `[ModelAdmin](reference/modeladmin)` interface instead.
  173
+</div>
228 174
 
  175
+![tutorial:tutorial5_project_creation.jpg](_images/tutorial5_project_creation.jpg)
229 176
 
  177
+Select each `Project` page you have created before, 
  178
+go in the tab panel called "Students", and add all students as required,
  179
+by clicking on the link **Add Student** of your *GridField* table.
230 180
 
  181
+![tutorial:tutorial5_addNew.jpg](_images/tutorial5_addNew.jpg)
231 182
 
232  
-## Student - Mentor relation
  183
+Once you have added all the students, and selected their projects, it should look a little like this:
233 184
 
234  
-**A student has one mentor.**
  185
+![tutorial:tutorial5_students.jpg](_images/tutorial5_students.jpg)
235 186
 
236  
-**A mentor has several students.**
  187
+### Many-many relationships: Mentor
237 188
 
238  
-This relation is called a **1-to-many** relation.
  189
+Now we have a fairly good picture of how students relate to their projects.
  190
+But students generally have somebody looking them over the shoulder.
  191
+In our case, that's the "mentor". Each project can have many of them,
  192
+and each mentor can be have one or more projects. They're busy guys!
  193
+This is called a *many-many* relationship.
239 194
 
240  
-The first step is to create the mentor object and set the relation with the *Student* data object.
  195
+The first step is to create the `Mentor` object and set the relation with the `Project` page type.
241 196
 
242  
-*tutorial/code/Mentor.php*
  197
+**mysite/code/Mentor.php**
243 198
 
244 199
 	:::php
245 200
 	<?php
246  
-	
247  
-	class Mentor extends Page {
248  
-	
  201
+	class Mentor extends DataObject {
249 202
 		static $db = array(
250  
-			'FirstName' => 'Text',
251  
-			'Lastname' => 'Text',
252  
-			'Nationality' => 'Text'
  203
+			'Name' => 'Varchar',
253 204
 		);
254  
-		
255  
-		static $has_many = array(
256  
-			'Students' => 'Student'
  205
+		static $belongs_many_many = array(
  206
+			'Projects' => 'Project'
257 207
 		);
258  
-		
259  
-		public function getCMSFields() {
260  
-			$fields = parent::getCMSFields();
261  
-			
262  
-			$fields->addFieldToTab( 'Root.Content', new TextField( 'FirstName' ) );
263  
-			$fields->addFieldToTab( 'Root.Content', new TextField( 'Lastname' ) );
264  
-			$fields->addFieldToTab( 'Root.Content', new TextField( 'Nationality' ) );
265  
-		
266  
-			return $fields;
267  
-		}
268  
-	
269 208
 	}
270  
-	class Mentor_Controller extends Page_Controller {}
271 209
 
272  
-*tutorial/code/Student.php*
  210
+**mysite/code/Project.php**
273 211
 
274 212
 	:::php
275  
-	class Student extends DataObject {
276  
-	
277  
-		...
278  
-	
279  
-		static $has_one = array(
280  
-			'MyMentor' => 'Mentor'
  213
+	class Project extends Page {
  214
+		// ...
  215
+		static $many_many = array(
  216
+			'Mentors' => 'Mentor'
281 217
 		);
282  
-	
283  
-	}
284  
-
285  
-
286  
-This code will create a relationship between the *Student* table and the *Mentor* table by storing the id of the
287  
-respective *Mentor* in the *Student* table.
288  
-
289  
-The second step is to add the table in the method *getCMSFields* which will allow you to manage the *has_many* relation.
290  
-
291  
-*tutorial/code/Mentor.php*
292  
-
293  
-	:::php
294  
-	class Mentor extends Page {
295  
-	
296  
-		...
297  
-	
298  
-		public function getCMSFields() {
299  
-			$fields = parent::getCMSFields();
300  
-		
301  
-			...
302  
-		
303  
-			$tablefield = new HasManyComplexTableField(
304  
-				$this,
305  
-				'Students',
306  
-				'Student',
307  
-				array(
308  
-					'FirstName' => 'FirstName',
309  
-					'Lastname' => 'Family Name',
310  
-					'Nationality' => 'Nationality'
311  
-				),
312  
-				'getCMSFields_forPopup'
313  
-			);
314  
-			$tablefield->setAddTitle( 'A Student' );
315  
-		
316  
-			$fields->addFieldToTab( 'Root.Students', $tablefield );
317  
-		
318  
-			return $fields;
319  
-		}
320  
-	
321 218
 	}
322  
-	class Mentor_Controller extends Page_Controller {}
323  
-
324  
-To know more about the parameters of the *HasManyComplexTableField* constructor, [check](#project_-_student_relation)
325  
-those of the *HasOneComplexTableField* constructor.
326  
-
327  
-<div class="tip" markdown='1'>
328  
-	Don't forget to rebuild the database using *dev/build?flush=1* before you 
329  
-	proceed to the next part of this tutorial.
330  
-</div>
331  
-
332  
-Now that we have created our *Mentor* page type, go into the CMS and create one *Mentor* page for each mentor listed
333  
-[above](#what-are-we-working-towards) under a simple *Page* named
334  
-**Mentors** for instance.
335  
-
336  
-![tutorial:gsoc-mentor-creation.png](_images/gsoc-mentor-creation.jpg)
337  
-
338  
-For each *Mentor* page, you can now affect **many** students created previously ( see the
339  
-[list](#What_are_we_working_towards?) ) by going in the tab panel *Students*.
340  
-
341  
-![tutorial:gsoc-mentor-student-selection.png](_images/gsoc-mentor-student-selection.jpg)
342  
-
343  
-You will also notice, that by checking a student in a *Mentor* page, you will be unable to select him again in any other
344  
-*Mentor* page which is the definition of a **1-to-many** relation.
345  
-
346  
-As the *HasOneComplexTableField* table, you also have the possibility not to select any student which will make your
347  
-*Mentor* page without any student affected to it.
348  
-
349  
-
350  
-
351  
-
352  
-
353  
-
354  
-
355  
-
356  
-
357  
-
358  
-## Project - Module relation
359  
-
360  
-**A project uses several modules.**
361  
-
362  
-**A module is used by several projects.**
363 219
 
364  
-This relation is called a **many-to-many** relation.
  220
+This code will create a relationship between the `Project` table and the `Mentor` table by storing the ids of the respective `Project` and `Mentor` in a another table named "Project_Mentors"
  221
+(after you've performed a `dev/build` command, of course).
365 222
 
366  
-The first step is to create the module object and set the relation with the *Project* page type.
  223
+The second step is to add the table in the method `getCMSFields()`,
  224
+which will allow you to manage the *many_many* relation.
  225
+Again, GridField will come in handy here, we just have
  226
+to configure it a bit differently.
367 227
 
368  
-*tutorial/code/Module.php*
369  
-
370  
-	:::php
371  
-	<?php
372  
-	
373  
-	class Module extends DataObject {
374  
-	
375  
-	   static $db = array(
376  
-	      'Name' => 'Text'
377  
-	   );
378  
-	
379  
-	   static $belongs_many_many = array(
380  
-	      'Projects' => 'Project'
381  
-	   );
382  
-	
383  
-	   public function getCMSFields_forPopup() {
384  
-	      $fields = new FieldList();
385  
-	      $fields->push( new TextField( 'Name' ) );
386  
-	      return $fields;
387  
-	   }
388  
-	
389  
-	}
390  
-
391  
-*tutorial/code/Project.php*
  228
+**mysite/code/Project.php**
392 229
 
393 230
 	:::php
394 231
 	class Project extends Page {
395  
-	
396  
-	   ...
397  
-	
398  
-	   static $many_many = array(
399  
-	      'Modules' => 'Module'
400  
-	   );
401  
-	
402  
-	}
403  
-
404  
-
405  
-This code will create a relationship between the *Project* table and the *Module* table by storing the ids of the
406  
-respective *Project* and *Module* in a another table named **Project_Modules**.
  232
+		// ...
  233
+		public function getCMSFields() {
  234
+			// ...
407 235
 
408  
-The second step is to add the table in the method *getCMSFields* which will allow you to manage the *many_many*
409  
-relation.
  236
+			// Same setup, but for mentors
  237
+			$mentorsField = new GridField(
  238
+				'Mentors',
  239
+				'Mentors',
  240
+				$this->Mentors(),
  241
+				GridFieldConfig_RelationEditor::create()
  242
+			);		      
  243
+			$fields->addFieldToTab('Root.Mentors', $mentorsField);
410 244
 
411  
-	:::php
412  
-	class Project extends Page {
413  
-	
414  
-	   ...
415  
-	
416  
-	   public function getCMSFields() {
417  
-	      $fields = parent::getCMSFields();
418  
-	
419  
-	      ...
420  
-	
421  
-	      $modulesTablefield = new ManyManyComplexTableField(
422  
-	         $this,
423  
-	         'Modules',
424  
-	         'Module',
425  
-	         array(
426  
-		    'Name' => 'Name'
427  
-	         ),
428  
-	         'getCMSFields_forPopup'
429  
-	      );
430  
-	      $modulesTablefield->setAddTitle( 'A Module' );
431  
-	
432  
-	      $fields->addFieldToTab( 'Root.Modules', $modulesTablefield );
433  
-	
434  
-	      return $fields;
435  
-	   }
436  
-	
  245
+			return $fields;
  246
+		}
437 247
 	}
438 248
 
439  
-To know more about the parameters of the *ManyManyComplexTableField* constructor,
440  
-[check](#project_-_student_relation) those of the *HasOneComplexTableField*
441  
-constructor.
442  
-
443  
-<div class="tip" markdown='1'>
444  
-	Don't forget to rebuild the database using *dev/build?flush=1* before you 
445  
-	proceed to the next part of this tutorial.
446  
-</div>
447  
-
448  
-Select now one of the *Project* page, go in the tab panel *Modules* and add all the modules listed
449  
-[above](#what-are-we-working-towards) by clicking on the link **Add A
450  
-Module** of your *ManyManyComplexTableField* table.
451  
-
452  
-![tutorial:gsoc-module-creation.png](_images/gsoc-module-creation.jpg)
453  
-
454  
-For each *Project* page, you can now affect **many** modules created previously ( see the
455  
-[list](#What_are_we_working_towards?) ) by going in the tab panel
456  
-*Modules*.
457  
-
458  
-![tutorial:gsoc-project-module-selection.png](_images/gsoc-project-module-selection.jpg)
  249
+The important difference to our student management UI is the usage
  250
+of `$this->Mentor()` (rather than `Mentor::get()`). It will limit
  251
+the list of records to those related through the many-many relationship.
459 252
 
460  
-You will also notice, that you are able to select several times a *Module* on different *Project* pages which is the
461  
-definition of a **many-to-many** relation.
  253
+In the CMS, open one of your `Project` pages and select the "Mentors" tab. 
  254
+Add all the mentors listed [above](#what-are-we-working-towards) 
  255
+by clicking on the **Add Mentor** button. 
462 256
 
463  
-As the *HasOneComplexTableField* and *HasManyComplexTableField* table, you also have the possibility not to select any
464  
-module which will make your *Project* page without any module affected to it.
  257
+![tutorial:tutorial5_module_creation.jpg](_images/tutorial5_module_creation.jpg)
465 258
 
  259
+To associate the mentor with a project, select one the the mentors, and click on the projects tab. Add all the projects a mentor is associated with (see the [list](#What_are_we_working_towards?)), by typing the name in "Find Projects by Page name" and clicking the "Link Existing" button.
  260
+You will notice that you are able to select the same `Project` for multiple mentors. 
  261
+This is the definition of a **many-to-many** relation.
466 262
 
  263
+![tutorial:tutorial5_module_selection.jpg](_images/tutorial5_module_selection.jpg)
467 264
 
468 265
 
  266
+## Website Display
469 267
 
470  
-## Displaying the data on your website
471  
-
472  
-Now that we have created all the *Page* and *DataObject* classes necessary and the relational tables to
473  
-manage the [relations](../topics/datamodel#relations) between them, we would like to see these relations on the website.
474  
-
475  
-We will see in this section how to display all these relations but also how to create a template for a *DataObject*.
  268
+Now that we have created all the *Page* and *DataObject* classes necessary and the relational tables to manage the [relations](/topics/datamodel#relations) between them, we would like to see these relations on the website. We will see in this section how to display all these relations, 
  269
+but also how to create a template for a *DataObject*.
476 270
 
477 271
 For every kind of *Page* or *DataObject*, you can access to their relations thanks to the **control** loop.
478 272
 
479  
-**1. GSOC Projects**
  273
+### Projects Overview Template
480 274
 
481  
-Let's start with the *ProjectsHolder* page created before. For this template, we are will display the same table than
482  
-[above](#what-are-we-working-towards).
  275
+We'll start by creating a `ProjectsHolder` template,
  276
+which lists all projects, and condenses their
  277
+student and mentor relationships into a single line.
  278
+You'll notice that there's no difference between
  279
+accessing a "has-many" and "many-many" relationship
  280
+in the template loops: To the template, its just
  281
+a named list of object.
483 282
 
484  
-![tutorial:gsoc-projects-table.png](_images/gsoc-projects-table.jpg)
  283
+![tutorial:tutorial5_projects_table.jpg](_images/tutorial5_projects_table.jpg)
485 284
 
486  
-*tutorial/templates/Layout/ProjectsHolder.ss*
  285
+**themes/simple/templates/Layout/ProjectsHolder.ss**
487 286
 
488 287
 	:::ss
489  
-	<% include Menu2 %>
490  
-
491  
-	<div id="Content" class="typography">
492  
-		<% if Level(2) %>
493  
-	        <% include BreadCrumbs %>
494  
-	    <% end_if %>
495  
-
496  
-		$Content
497  
-
498  
-		<table>
499  
-	        <thead>
500  
-	            <tr>
501  
-	                <th>Project</th>
502  
-	                <th>Student</th>
503  
-	                <th>Mentor</th>
504  
-	                <th>Modules</th>
505  
-	            </tr>
506  
-	        </thead>
507  
-	        <tbody>
508  
-	            <% loop Children %>
509  
-	                <tr>
510  
-	                    <td>$Title</td>
511  
-	                    <td>
512  
-	                        <% if MyStudent %>
513  
-	                            <% loop MyStudent %>
514  
-	                                $FirstName $Lastname
515  
-	                            <% end_loop %>
516  
-	                        <% else %>
517  
-	                            No Student
518  
-	                        <% end_if %>
519  
-	                    </td>
520  
-	                    <td>
521  
-	                        <% if MyStudent %>
522  
-	                            <% loop MyStudent %>
523  
-	                                <% if MyMentor %>
524  
-	                                    <% loop MyMentor %>
525  
-	                                        $FirstName $Lastname
526  
-	                                    <% end_loop %>
527  
-	                                <% else %>
528  
-	                                    No Mentor
529  
-	                                <% end_if %>
530  
-	                            <% end_loop %>
531  
-	                        <% else %>
532  
-	                            No Mentor
533  
-	                        <% end_if %>
534  
-	                    </td>
535  
-	                    <td>
536  
-	                        <% if Modules %>
537  
-	                            <% loop Modules %>
538  
-	                                $Name &nbsp;
539  
-	                            <% end_loop %>
540  
-	                        <% else %>
541  
-	                            No Modules
542  
-	                        <% end_if %>
543  
-	                    </td>
544  
-	                </tr>
545  
-	            <% end_loop %>
546  
-	        </tbody>
547  
-	    </table>
548  
-
549  
-		$Form
550  
-
  288
+	<div class="content-container typography">	
  289
+		<article>
  290
+			<h1>$Title</h1>
  291
+			<div class="content">
  292
+				$Content
  293
+				
  294
+				<table>
  295
+					<thead>
  296
+						<tr>
  297
+							<th>Project</th>
  298
+							<th>Students</th>
  299
+							<th>Mentors</th>
  300
+						</tr>
  301
+					</thead>
  302
+					<tbody>
  303
+					<% loop Children %>
  304
+						<tr>
  305
+							<td>
  306
+								<a href="$Link">$Title</a>
  307
+							</td>	
  308
+							<td>
  309
+								<% loop Students %>	                            
  310
+									$Name ($University)<% if Last !=1 %>,<% end_if %>
  311
+								<% end_loop %>
  312
+							</td>    
  313
+							<td>
  314
+								<% loop Mentor %>
  315
+									$Name<% if Last !=1 %>,<% end_if %>
  316
+								<% end_loop %>
  317
+							</td>
  318
+						</tr>
  319
+					<% end_loop %>
  320
+					</tbody>
  321
+					</table>
  322
+			</div>
  323
+		</article>
551 324
 	</div>
  325
+	<% include SideBar %>
552 326
 
  327
+Navigate to the holder page through your website navigation,
  328
+or the "Preview" feature in the CMS. You should see a list of all projects now.
  329
+Add `?flush=all` to the page URL to force a refresh of the template cache.
553 330
 
554  
-<div class="notice" markdown='1'>
555  
-	If you are using the blackcandy template: You might want to move the `<% include Sidebar %>`
556  
-	(tutorial/templates/Includes/SideBar.ss) include in the *tutorial/templates/Layout/Page.ss* template above
557  
-	the typography div to get rid of the bullets
558  
-</div>
559  
-
560  
-
561  
-**2. Project**
  331
+To get a list of all projects, we've looped through the "Children" list,
  332
+which is a relationship we didn't define explictly.
  333
+It is provided to us by the parent implementation,
  334
+since projects are nothing other than children pages in the standard page hierarchy.
562 335
 
563  
-We know now how to easily access and show [relations](../topics/datamodel#relations) between *DataObject* in a template.
  336
+### Project Detail Template
564 337
 
565  
-We can now do the same for every *Project* page by creating its own template.
  338
+Creating the detail view for each `Project` page works in a very similar way.
  339
+Given that we're in the context of a single project,
  340
+we can access the "Students" and "Mentors" relationships directly in the template.
566 341
 
567  
-![tutorial:gsoc-project.png](_images/gsoc-project.jpg)
  342
+![tutorial:tutorial5_project.jpg](_images/tutorial5_project.jpg)
568 343
 
569  
-*tutorial/templates/Layout/Project.ss*
  344
+**themes/simple/templates/Layout/Project.ss**
570 345
 
571 346
 	:::ss
572  
-	<% include Menu2 %>
573  
-
574  
-	<div id="Content" class="typography">
575  
-		<% if Level(2) %>
576  
-	        <% include BreadCrumbs %>
577  
-	    <% end_if %>
578  
-
579  
-		$Content
580  
-
581  
-		<% if MyStudent %>
582  
-	        <% loop MyStudent %>
583  
-	            <p>First Name: <strong>$FirstName</strong></p>
584  
-	            <p>Lastname: <strong>$Lastname</strong></p>
585  
-	            <p>Nationality: <strong>$Nationality</strong></p>
586  
-
587  
-	            <h3>Mentor</h3>
588  
-
589  
-	            <% if MyMentor %>
590  
-	                <% loop MyMentor %>
591  
-	                    <p>First Name: <strong>$FirstName</strong></p>
592  
-	                    <p>Lastname: <strong>$Lastname</strong></p>
593  
-	                    <p>Nationality: <strong>$Nationality</strong></p>
594  
-	                <% end_loop %>
595  
-	            <% else %>
596  
-	                <p>This student doesn't have any mentor.</p>
597  
-	            <% end_if %>
598  
-	        <% end_loop %>
599  
-	    <% else %>
600  
-	        <p>There is no any student working on this project.</p>
601  
-	    <% end_if %>
602  
-
603  
-	    <h3>Modules</h3>
604  
-
605  
-	    <% if Modules %>
606  
-	        <ul>
607  
-	            <% loop Modules %>
608  
-	                <li>$Name</li>
609  
-	            <% end_loop %>
610  
-	        </ul>
611  
-	    <% else %>
612  
-	        <p>This project has not used any modules.</p>
613  
-	    <% end_if %>
614  
-
615  
-		$Form
616  
-
  347
+	<div class="content-container typography">	
  348
+		<article>
  349
+			<h1>$Title</h1>
  350
+			<div class="content">
  351
+				$Content
  352
+
  353
+				<h2>Students</h2>
  354
+				<% if Students %>
  355
+					<ul>
  356
+					<% loop Students %>
  357
+						<li>$Name ($University)</li>
  358
+					<% end_loop %>
  359
+					</ul>
  360
+				<% else %>
  361
+					<p>No students found</p>
  362
+				<% end_if %>
  363
+
  364
+				<h2>Mentors</h2>
  365
+				<% if Mentors %>
  366
+					<ul>
  367
+					<% loop Mentors %>
  368
+						<li>$Name</li>
  369
+					<% end_loop %>
  370
+					</ul>
  371
+				<% else %>
  372
+					<p>No mentors found</p>
  373
+				<% end_if %>
  374
+
  375
+			</div>
  376
+		</article>
617 377
 	</div>
  378
+	<% include SideBar %>
618 379
 
  380
+Follow the link to a project detail from from your holder page,
  381
+or navigate to it through the submenu provided by the theme.
619 382
 
620  
-What we would like now is to create a special template for the *DataObject* *Student* and the *Page* *Mentor* which will
621  
-be used when we will call directly the variable in the *Project* template. In our case, we will use the same template
622  
-because these two classes have the same fields ( FirstName, Surname and Nationality ).
623  
-
624  
-*tutorial/templates/Includes/GSOCPerson.ss*
625  
-
626  
-	:::ss
627  
-	<p>First Name: <strong>$FirstName</strong></p>
628  
-	<p>Lastname: <strong>$Lastname</strong></p>
629  
-	<p>Nationality: <strong>$Nationality</strong></p>
  383
+### Student Detail Template
630 384
 
  385
+You might have noticed that we duplicate the same template code
  386
+between both views when it comes to displaying the details
  387
+on students and mentors. We'll fix this for students,
  388
+by introducing a new template for them.
631 389
 
632  
-Now the template is created, we need to establish the link between the *Student* and *Mentor* classes with their common
633  
-template.
634  
-
635  
-To do so, add this code in the two classes.  This will create a control on each of those objects which can be called 
636  
-from templates either within a control block or dot notation.
637  
-
638  
-*tutorial/code/Student.php, tutorial/code/Mentor.php*
639  
-
640  
-	:::php
641  
-	public function PersonalInfo() {
642  
-		$template = 'GSOCPerson';
643  
-		return $this->renderWith( $template );
644  
-	}
645  
-
646  
-
647  
-We can now modify the *Project.ss* template.
  390
+**themes/simple/templates/Includes/StudentInfo.ss**
648 391
 
649 392
 	:::ss
650  
-	
651  
-	...
652  
-	
653  
-	<% if MyStudent %>
654  
-		$MyStudent.PersonalInfo
655  
-	
656  
-		<h3>Mentor</h3>
657  
-		
658  
-		<% loop MyStudent %>
659  
-			<% if MyMentor %>
660  
-				$MyMentor.PersonalInfo
661  
-			<% else %>
662  
-				<p>This student doesn't have any mentor.</p>
663  
-			<% end_if %>
664  
-		<% end_loop %>
665  
-	<% else %>
666  
-		<p>There is no any student working on this project.</p>
667  
-	<% end_if %>
668  
-	
669  
-	...
670  
-
671  
-<div class="notice" markdown='1'>
672  
-	Remember to add `?flush=1` to the url when refreshing the project page or otherwise you will get a template error
673  
-</div>
  393
+	$Name ($University)
674 394
 
675  
-In the *Project* template, it has been really easy to display the **1-to-1** relation with a *Student* object just by
676  
-calling the variable **$MyStudent**. This has been made possible thanks to the code below present in the *Project*
677  
-class.
678  
-
679  
-	:::php
680  
-	static $has_one = array(
681  
-	    'MyStudent' => 'Student'
682  
-	);
683  
-
684  
-
685  
-However, in the *Student* class, there is no any code relating to the **1-to-1** relation with a *Project* *Page*. So
686  
-how to access it from a *Student* *DataObject* ?
687  
-
688  
-**3. Mentor**
689  
-
690  
-In this template, we are gonna try to access the *Project* details from a *Student* *DataObject*.
691  
-
692  
-What we want to do is to access to the *Project* page in the same way than we have done for the other relations
693  
-**without modifying the relations between *Page* and *DataObject* and the database structure**.
694  
-
695  
-![tutorial:gsoc-mentor.png](_images/gsoc-mentor.jpg)
696  
-
697  
-To do so, we have to create a function in the *Student* class which will return the *Project* linked with it. Let's call
698  
-it *MyProject* for instance.
  395
+To use this template, we need to add a new method to our student class:
699 396
 
700 397
 	:::php
701 398
 	class Student extends DataObject {
702  
-	
703  
-		...
704  
-	
705  
-		public function MyProject() {
706  
-			return Project::get()->filter("MyStudentID", $this->ID);
  399
+		function getInfo() {
  400
+			return $this->renderWith('StudentInfo');
707 401
 		}
708  
-	
709 402
 	}
710 403
 
711  
-
712  
-We can now use this value in the same way that we have used the other relations.
713  
-That's how we can use this function in the *Mentor* template.
714  
-
715  
-*tutorial/templates/Layout/Mentor.ss*
716  
-
717  
-	:::ss
718  
-	<% include Menu2 %>
719  
-
720  
-	<div id="Content" class="typography">
721  
-		<% include BreadCrumbs %>
722  
-		$Content
723  
-
724  
-	    <h3>Personal Details</h3>
725  
-
726  
-	    <p>First Name: <strong>$FirstName</strong></p>
727  
-	    <p>Lastname: <strong>$Lastname</strong></p>