Permalink
Browse files

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...
1 parent 0308cc2 commit 0c0bcc9aec108b9f2ecd2007d309ab0c921561f9 @chillu chillu committed Jul 19, 2012
View
976 docs/en/tutorials/5-dataobject-relationship-management.md
@@ -2,774 +2,432 @@
## Overview
-In the [second tutorial](2-extending-a-basic-site) we have learned how to add extrafields to a page type thanks
-to the *$db* array and how to add an image using the *$has_one* array and so create a relationship between a table and
-the *Image* table by storing the id of the respective *Image* in the first table. This tutorial explores all this
-relations between [DataObjects](/topics/datamodel#relations) and the way to manage them easily.
-
-<div class="notice" markdown='1'>
- I'm using the default tutorial theme in the following examples so the templates may vary or you may need to change
- the template code in this example to fit your theme
-</div>
+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
+additional fields on our models, and attach images to them.
## What are we working towards?
-To simulate these relations between objects, we are going to simulate the management via the CMS of the **[Google Summer
-Of Code 2007](http://www.silverstripe.com/google-summer-of-code-2007-we-are-in/)** that SilverStripe was part of.
-
-To do this, we are gonna use the following objects :
-
-* Project : Project on SilverStripe system for the GSOC
-* Student : Student involved in the project
-* Mentor : SilverStripe developer
-* Module : Module used for the project
+To demonstrate relationships between objects,
+we are going to use the example of students working on various projects.
+Each student has a single project, and each project has one or more
+mentors supervising their progress. We'll create these objects,
+make them editable in the CMS, and render them on the website.
+This table shows some example data we'll be using:
-This is a table which sums up the relations between them :
- | Project | Student | Mentor | Modules |
- | ------- | ------- | ------ | ------------------
- | i18n Multi-Language | Bernat Foj Capell | Ingo Schommer | Cms, Framework, i18n, Translation |
- | Image Manipulation | Mateusz Ujma | Sam Minnee | Cms, Framework, ImageManipulation |
- | Google Maps | Ofir Picazo Navarro | Hayden Smith | Cms, Framework, Maps |
- | Mashups | Lakshan Perera | Matt Peel | Cms, Framework, MashUps |
- | Multiple Databases | Philipp Krenn | Brian Calhoun | Cms, Framework, MultipleDatabases |
- | Reporting | Quin Hoxie | Sam Minnee | Cms, Framework, Reporting |
- | Security & OpenID | Markus Lanthaler | Hayden Smith | Cms, Framework, auth_openid |
- | SEO | Will Scott | Brian Calhoun | Cms, Framework, googleadwords, googleanalytics |
- | Usability | Elijah Lofgren | Sean Harvey | Cms, Framework, UsabilityElijah |
- | Safari 3 Support | Meg Risen | Sean Harvey | Cms, Framework, UsabilityMeg |
+ | Project | Student | Mentor |
+ | ------- | ------- | ------- |
+ | Developer Toolbar | Jakob,Ofir | Mark,Sean |
+ | Behaviour Testing | Michal,Wojtek | Ingo, Sean |
+ | Content Personalization | Yuki | Philipp |
+ | Module Management | Andrew | Marcus,Sam |
-## GSOC Projects
+### Has-One and Has-Many Relationships: Project and Student
-Before starting the relations management, we need to create a *ProjectsHolder* class where we will save the GSOC Project
-pages.
+A student can have only one project, it'll keep them busy enough.
+But each project can be done by one or more students.
+This is called a **one-to-many** relationship.
+Let's create the `Student` and `Project` objects.
-*tutorial/code/ProjectsHolder.php*
+**mysite/code/Student.php**
:::php
<?php
-
- class ProjectsHolder extends Page {
-
- static $allowed_children = array( 'Project' );
-
- }
-
- class ProjectsHolder_Controller extends Page_Controller {
-
- }
-
-## Project - Student relation
-
-**A project can only be done by one student.**
-
-**A student has only one project.**
-
-This relation is called a **1-to-1** relation.
-
-The first step is to create the student and project objects.
-
-*tutorial/code/Student.php*
-
- :::php
- <?php
-
class Student extends DataObject {
-
static $db = array(
- 'FirstName' => 'Text',
- 'Lastname' => 'Text',
- 'Nationality' => 'Text'
+ 'Name' => 'Varchar',
+ 'University' => 'Varchar',
);
-
- public function getCMSFields_forPopup() {
- $fields = new FieldList();
-
- $fields->push( new TextField( 'FirstName', 'First Name' ) );
- $fields->push( new TextField( 'Lastname' ) );
- $fields->push( new TextField( 'Nationality' ) );
-
- return $fields;
- }
-
+ static $has_one = array(
+ 'Project' => 'Project'
+ );
}
-
-*tutorial/code/Project.php*
+**mysite/code/Project.php**
:::php
<?php
-
class Project extends Page {
-
- static $has_one = array(
- 'MyStudent' => 'Student'
+ static $has_many = array(
+ 'Students' => 'Student'
);
-
}
- class Project_Controller extends Page_Controller {}
-
-This code will create a relationship between the *Project* table and the *Student* table by storing the id of the
-respective *Student* in the *Project* table.
-
-The second step is to add the table in the method *getCMSFields* which will allow you to manage the *has_one* relation.
-
- :::php
- class Project extends Page {
-
- ...
-
- public function getCMSFields() {
- $fields = parent::getCMSFields();
-
- $tablefield = new HasOneComplexTableField(
- $this,
- 'MyStudent',
- 'Student',
- array(
- 'FirstName' => 'First Name',
- 'Lastname' => 'Family Name',
- 'Nationality' => 'Nationality'
- ),
- 'getCMSFields_forPopup'
- );
- $tablefield->setParentClass('Project');
-
- $fields->addFieldToTab( 'Root.Student', $tablefield );
-
- return $fields;
- }
-
+ class Project_Controller extends Page_Controller {
}
-Let’s walk through the parameters of the *HasOneComplexTableField* constructor.
-
-1. **$this** : The first object concerned by the relation
-2. **'MyStudent'** : The name of the second object of the relation
-3. **'Student'** : The type of the second object of the relation
-4. **array(...)** : The fields of the second object which will be in the table
-5. **'getCMSFields_forPopup'** : The method which will be called to add, edit or only show a second object
-
-You can also directly replace the last parameter by this code :
+The relationships are defined through the `$has_one`
+and `$has_many` properties on the objects.
+The array keys declares the name of the relationship,
+the array values contain the class name (see the ["database structure"](/reference/database-structure)
+and ["datamodel"](/topics/datamodel) topics for more information).
+
+As you can see, only the `Project` model extends `Page`,
+while `Student` is a plain `DataObject` subclass.
+This allows us to view projects through the standard
+theme setup, just like any other page.
+It would be possible to render students separately as well,
+but for now we'll assume they're just listed as part of their `Project` page.
+Since `Project` inherits all properties (e.g. a title) from its parent class,
+we don't need to define any additional ones for our purposes.
+
+Now that we have our models defined in PHP code,
+we need to tell the database to create the related tables.
+Trigger a rebuild through *dev/build?flush=all* before you
+proceed to the next part of this tutorial.
+
+### Organizing pages: ProjectHolder
+
+A `Project` is just a page, so we could create it anywhere in the CMS.
+In order to list and organize them, it makes sense to collect them under a common parent page.
+We'll create a new page type called `ProjectsHolder` for this purpose,
+which is a common pattern in SilverStripe's page types. Holders
+are useful for listing their children, and usually restrict these children to a specific class,
+in our case pages of type `Project`.
+The restriction is enforced through the `$allowed_children` directive.
+
+**mysite/code/ProjectsHolder.php**
:::php
- new FieldList(
- new TextField( 'FirstName', 'First Name' ),
- new TextField( 'Lastname' ),
- new TextField( 'Nationality' )
- );
-
-
-<div class="tip" markdown='1'>
- Don't forget to rebuild the database using *dev/build?flush=1* before you
- proceed to the next part of this tutorial.
-</div>
-
-Now that we have created our *Project* page type and *Student* data object, let’s add some content.
-
-Go into the CMS and create one *Project* page for each project listed [above](#what-are-we-working-towards) under a
-*ProjectsHolder* page named **GSOC Projects** for instance.
-
-![tutorial:gsoc-project-creation.png](_images/gsoc-project-creation.jpg)
-
-As you can see in the tab panel *Student*, the adding functionality is titled *Add Student*. However, if you want to
-modify this title, you have to add this code in the *getCMSFields* method of the *Project* class :
-
- :::php
- $tablefield->setAddTitle( 'A Student' );
-
-
-Select now one of the *Project* page that you have created, go in the tab panel *Student* and add all the students
-listed [above](#what-are-we-working-towards) by clicking on the link **Add A Student** of your
-*HasOneComplexTableField* table.
-
-![tutorial:gsoc-student-creation.png](_images/gsoc-student-creation.jpg)
+ <?php
+ class ProjectsHolder extends Page {
+ static $allowed_children = array(
+ 'Project'
+ );
+ }
+ class ProjectsHolder_Controller extends Page_Controller {
+ }
-After having added all the students, you will see that, in the tab panel *Student* of all the *Project* pages, the
-*HasOneComplexTableField* tables have the same content.
+You might have noticed that we don't specify the relationship
+to a project. That's because its already inherited from the parent implementation,
+as part of the normal page hierarchy in the CMS.
-For each *Project* page, you can now affect **one and only one** student to it ( see the
-[list](#What_are_we_working_towards?) ).
+Now that we have created our `ProjectsHolder` and `Project` page types, we'll add some content.
+Go into the CMS and create a `ProjectsHolder` page named **Projects**.
+Then create one `Project` page for each project listed [above](#what-are-we-working-towards).
-![tutorial:gsoc-project-student-selection.png](_images/gsoc-project-student-selection.jpg)
+### Data Management Interface: GridField
-You will also notice, that you have the possibility to **unselect** a student which will make your *Project* page
-without any student affected to it.
+So we have our models, and can create pages of type
+`Project` through the standard CMS interface,
+and collect those within a `ProjectsHolder`.
+But what about creating `Student` records?
-**At the moment, the *HasOneComplexTableField* table doesn't manage totally the *1-to-1* relation because you can easily
-select the same student for two ( or more ) differents *Project* pages which corresponds to a *1-to-many* relation.**
+Since students are related to a single project, we will
+allow editing them right the on the CMS interface in the `Project` page type.
+We do this through a powerful field called `[GridField](/topics/grid-field)`.
+All customization to fields for a page type are managed through a method called
+`getCMSFields()`, so let's add it there:
-To use your *HasOneComplexTableField* table for a **1-to-1** relation, make this modification in the class *Project* :
+**mysite/code/Project.php**
:::php
+ <?php
class Project extends Page {
-
- ...
-
+ // ...
public function getCMSFields() {
-
- ...
-
- $tablefield->setParentClass('Project');
-
- $tablefield->setOneToOne();
-
- $fields->addFieldToTab( 'Root.Student', $tablefield );
-
+ // Get the fields from the parent implementation
+ $fields = parent::getCMSFields();
+
+ // Create a default configuration for the new GridField, allowing record editing
+ $config = GridFieldConfig_RelationEditor::create();
+
+ // Set the names and data for our gridfield columns
+ $config->getComponentByType('GridFieldDataColumns')->setDisplayFields(array(
+ 'Name' => 'Name',
+ 'Project.Title'=> 'Project' // Retrieve from a has-one relationship
+ ));
+
+ // Create a gridfield to hold the student relationship
+ $studentsField = new GridField(
+ 'Students', // Field name
+ 'Student', // Field title
+ $this->Students(), // List of all related students
+ $config
+ );
+
+ // Create a tab named "Students" and add our field to it
+ $fields->addFieldToTab('Root.Students', $studentsField);
+
return $fields;
}
-
}
-Now, you will notice that by checking a student in a *Project* page, you will be unable to select him again in any other
-*Project* page which is the definition of a **1-to-1** relation.
-
-
+This creates a tabular field, which lists related student records, one row at a time.
+Its empty by default, but you can add new students as required,
+or relate them to the project by typing in the box above the table.
+
+In our case, want to manage those records, edit their details, and add new ones.
+To accomplish this, we have added a specific `[api:GridFieldConfig]`.
+While we could've built the config from scratch, there's several
+preconfigured instances. The `GridFieldConfig_RecordEditor` default configures
+the field to edit records, rather than just viewing them.
+The GridField API is composed of "components", which makes it very flexible.
+One example of this is the configuration of column names on our table:
+We call `setDisplayFields()` directly on the component responsible for their rendering.
+
+<div class="note" markdown="1">
+ Adding a `GridField` to a page type is a popular way to manage data,
+ but not the only one. If your data requires a dedicated interface
+ with more sophisticated search and management logic, consider
+ using the `[ModelAdmin](reference/modeladmin)` interface instead.
+</div>
+![tutorial:tutorial5_project_creation.jpg](_images/tutorial5_project_creation.jpg)
+Select each `Project` page you have created before,
+go in the tab panel called "Students", and add all students as required,
+by clicking on the link **Add Student** of your *GridField* table.
+![tutorial:tutorial5_addNew.jpg](_images/tutorial5_addNew.jpg)
-## Student - Mentor relation
+Once you have added all the students, and selected their projects, it should look a little like this:
-**A student has one mentor.**
+![tutorial:tutorial5_students.jpg](_images/tutorial5_students.jpg)
-**A mentor has several students.**
+### Many-many relationships: Mentor
-This relation is called a **1-to-many** relation.
+Now we have a fairly good picture of how students relate to their projects.
+But students generally have somebody looking them over the shoulder.
+In our case, that's the "mentor". Each project can have many of them,
+and each mentor can be have one or more projects. They're busy guys!
+This is called a *many-many* relationship.
-The first step is to create the mentor object and set the relation with the *Student* data object.
+The first step is to create the `Mentor` object and set the relation with the `Project` page type.
-*tutorial/code/Mentor.php*
+**mysite/code/Mentor.php**
:::php
<?php
-
- class Mentor extends Page {
-
+ class Mentor extends DataObject {
static $db = array(
- 'FirstName' => 'Text',
- 'Lastname' => 'Text',
- 'Nationality' => 'Text'
+ 'Name' => 'Varchar',
);
-
- static $has_many = array(
- 'Students' => 'Student'
+ static $belongs_many_many = array(
+ 'Projects' => 'Project'
);
-
- public function getCMSFields() {
- $fields = parent::getCMSFields();
-
- $fields->addFieldToTab( 'Root.Content', new TextField( 'FirstName' ) );
- $fields->addFieldToTab( 'Root.Content', new TextField( 'Lastname' ) );
- $fields->addFieldToTab( 'Root.Content', new TextField( 'Nationality' ) );
-
- return $fields;
- }
-
}
- class Mentor_Controller extends Page_Controller {}
-*tutorial/code/Student.php*
+**mysite/code/Project.php**
:::php
- class Student extends DataObject {
-
- ...
-
- static $has_one = array(
- 'MyMentor' => 'Mentor'
+ class Project extends Page {
+ // ...
+ static $many_many = array(
+ 'Mentors' => 'Mentor'
);
-
- }
-
-
-This code will create a relationship between the *Student* table and the *Mentor* table by storing the id of the
-respective *Mentor* in the *Student* table.
-
-The second step is to add the table in the method *getCMSFields* which will allow you to manage the *has_many* relation.
-
-*tutorial/code/Mentor.php*
-
- :::php
- class Mentor extends Page {
-
- ...
-
- public function getCMSFields() {
- $fields = parent::getCMSFields();
-
- ...
-
- $tablefield = new HasManyComplexTableField(
- $this,
- 'Students',
- 'Student',
- array(
- 'FirstName' => 'FirstName',
- 'Lastname' => 'Family Name',
- 'Nationality' => 'Nationality'
- ),
- 'getCMSFields_forPopup'
- );
- $tablefield->setAddTitle( 'A Student' );
-
- $fields->addFieldToTab( 'Root.Students', $tablefield );
-
- return $fields;
- }
-
}
- class Mentor_Controller extends Page_Controller {}
-
-To know more about the parameters of the *HasManyComplexTableField* constructor, [check](#project_-_student_relation)
-those of the *HasOneComplexTableField* constructor.
-
-<div class="tip" markdown='1'>
- Don't forget to rebuild the database using *dev/build?flush=1* before you
- proceed to the next part of this tutorial.
-</div>
-
-Now that we have created our *Mentor* page type, go into the CMS and create one *Mentor* page for each mentor listed
-[above](#what-are-we-working-towards) under a simple *Page* named
-**Mentors** for instance.
-
-![tutorial:gsoc-mentor-creation.png](_images/gsoc-mentor-creation.jpg)
-
-For each *Mentor* page, you can now affect **many** students created previously ( see the
-[list](#What_are_we_working_towards?) ) by going in the tab panel *Students*.
-
-![tutorial:gsoc-mentor-student-selection.png](_images/gsoc-mentor-student-selection.jpg)
-
-You will also notice, that by checking a student in a *Mentor* page, you will be unable to select him again in any other
-*Mentor* page which is the definition of a **1-to-many** relation.
-
-As the *HasOneComplexTableField* table, you also have the possibility not to select any student which will make your
-*Mentor* page without any student affected to it.
-
-
-
-
-
-
-
-
-
-
-## Project - Module relation
-
-**A project uses several modules.**
-
-**A module is used by several projects.**
-This relation is called a **many-to-many** relation.
+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"
+(after you've performed a `dev/build` command, of course).
-The first step is to create the module object and set the relation with the *Project* page type.
+The second step is to add the table in the method `getCMSFields()`,
+which will allow you to manage the *many_many* relation.
+Again, GridField will come in handy here, we just have
+to configure it a bit differently.
-*tutorial/code/Module.php*
-
- :::php
- <?php
-
- class Module extends DataObject {
-
- static $db = array(
- 'Name' => 'Text'
- );
-
- static $belongs_many_many = array(
- 'Projects' => 'Project'
- );
-
- public function getCMSFields_forPopup() {
- $fields = new FieldList();
- $fields->push( new TextField( 'Name' ) );
- return $fields;
- }
-
- }
-
-*tutorial/code/Project.php*
+**mysite/code/Project.php**
:::php
class Project extends Page {
-
- ...
-
- static $many_many = array(
- 'Modules' => 'Module'
- );
-
- }
-
-
-This code will create a relationship between the *Project* table and the *Module* table by storing the ids of the
-respective *Project* and *Module* in a another table named **Project_Modules**.
+ // ...
+ public function getCMSFields() {
+ // ...
-The second step is to add the table in the method *getCMSFields* which will allow you to manage the *many_many*
-relation.
+ // Same setup, but for mentors
+ $mentorsField = new GridField(
+ 'Mentors',
+ 'Mentors',
+ $this->Mentors(),
+ GridFieldConfig_RelationEditor::create()
+ );
+ $fields->addFieldToTab('Root.Mentors', $mentorsField);
- :::php
- class Project extends Page {
-
- ...
-
- public function getCMSFields() {
- $fields = parent::getCMSFields();
-
- ...
-
- $modulesTablefield = new ManyManyComplexTableField(
- $this,
- 'Modules',
- 'Module',
- array(
- 'Name' => 'Name'
- ),
- 'getCMSFields_forPopup'
- );
- $modulesTablefield->setAddTitle( 'A Module' );
-
- $fields->addFieldToTab( 'Root.Modules', $modulesTablefield );
-
- return $fields;
- }
-
+ return $fields;
+ }
}
-To know more about the parameters of the *ManyManyComplexTableField* constructor,
-[check](#project_-_student_relation) those of the *HasOneComplexTableField*
-constructor.
-
-<div class="tip" markdown='1'>
- Don't forget to rebuild the database using *dev/build?flush=1* before you
- proceed to the next part of this tutorial.
-</div>
-
-Select now one of the *Project* page, go in the tab panel *Modules* and add all the modules listed
-[above](#what-are-we-working-towards) by clicking on the link **Add A
-Module** of your *ManyManyComplexTableField* table.
-
-![tutorial:gsoc-module-creation.png](_images/gsoc-module-creation.jpg)
-
-For each *Project* page, you can now affect **many** modules created previously ( see the
-[list](#What_are_we_working_towards?) ) by going in the tab panel
-*Modules*.
-
-![tutorial:gsoc-project-module-selection.png](_images/gsoc-project-module-selection.jpg)
+The important difference to our student management UI is the usage
+of `$this->Mentor()` (rather than `Mentor::get()`). It will limit
+the list of records to those related through the many-many relationship.
-You will also notice, that you are able to select several times a *Module* on different *Project* pages which is the
-definition of a **many-to-many** relation.
+In the CMS, open one of your `Project` pages and select the "Mentors" tab.
+Add all the mentors listed [above](#what-are-we-working-towards)
+by clicking on the **Add Mentor** button.
-As the *HasOneComplexTableField* and *HasManyComplexTableField* table, you also have the possibility not to select any
-module which will make your *Project* page without any module affected to it.
+![tutorial:tutorial5_module_creation.jpg](_images/tutorial5_module_creation.jpg)
+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.
+You will notice that you are able to select the same `Project` for multiple mentors.
+This is the definition of a **many-to-many** relation.
+![tutorial:tutorial5_module_selection.jpg](_images/tutorial5_module_selection.jpg)
+## Website Display
-## Displaying the data on your website
-
-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 but also how to create a template for a *DataObject*.
+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,
+but also how to create a template for a *DataObject*.
For every kind of *Page* or *DataObject*, you can access to their relations thanks to the **control** loop.
-**1. GSOC Projects**
+### Projects Overview Template
-Let's start with the *ProjectsHolder* page created before. For this template, we are will display the same table than
-[above](#what-are-we-working-towards).
+We'll start by creating a `ProjectsHolder` template,
+which lists all projects, and condenses their
+student and mentor relationships into a single line.
+You'll notice that there's no difference between
+accessing a "has-many" and "many-many" relationship
+in the template loops: To the template, its just
+a named list of object.
-![tutorial:gsoc-projects-table.png](_images/gsoc-projects-table.jpg)
+![tutorial:tutorial5_projects_table.jpg](_images/tutorial5_projects_table.jpg)
-*tutorial/templates/Layout/ProjectsHolder.ss*
+**themes/simple/templates/Layout/ProjectsHolder.ss**
:::ss
- <% include Menu2 %>
-
- <div id="Content" class="typography">
- <% if Level(2) %>
- <% include BreadCrumbs %>
- <% end_if %>
-
- $Content
-
- <table>
- <thead>
- <tr>
- <th>Project</th>
- <th>Student</th>
- <th>Mentor</th>
- <th>Modules</th>
- </tr>
- </thead>
- <tbody>
- <% loop Children %>
- <tr>
- <td>$Title</td>
- <td>
- <% if MyStudent %>
- <% loop MyStudent %>
- $FirstName $Lastname
- <% end_loop %>
- <% else %>
- No Student
- <% end_if %>
- </td>
- <td>
- <% if MyStudent %>
- <% loop MyStudent %>
- <% if MyMentor %>
- <% loop MyMentor %>
- $FirstName $Lastname
- <% end_loop %>
- <% else %>
- No Mentor
- <% end_if %>
- <% end_loop %>
- <% else %>
- No Mentor
- <% end_if %>
- </td>
- <td>
- <% if Modules %>
- <% loop Modules %>
- $Name &nbsp;
- <% end_loop %>
- <% else %>
- No Modules
- <% end_if %>
- </td>
- </tr>
- <% end_loop %>
- </tbody>
- </table>
-
- $Form
-
+ <div class="content-container typography">
+ <article>
+ <h1>$Title</h1>
+ <div class="content">
+ $Content
+
+ <table>
+ <thead>
+ <tr>
+ <th>Project</th>
+ <th>Students</th>
+ <th>Mentors</th>
+ </tr>
+ </thead>
+ <tbody>
+ <% loop Children %>
+ <tr>
+ <td>
+ <a href="$Link">$Title</a>
+ </td>
+ <td>
+ <% loop Students %>
+ $Name ($University)<% if Last !=1 %>,<% end_if %>
+ <% end_loop %>
+ </td>
+ <td>
+ <% loop Mentor %>
+ $Name<% if Last !=1 %>,<% end_if %>
+ <% end_loop %>
+ </td>
+ </tr>
+ <% end_loop %>
+ </tbody>
+ </table>
+ </div>
+ </article>
</div>
+ <% include SideBar %>
+Navigate to the holder page through your website navigation,
+or the "Preview" feature in the CMS. You should see a list of all projects now.
+Add `?flush=all` to the page URL to force a refresh of the template cache.
-<div class="notice" markdown='1'>
- If you are using the blackcandy template: You might want to move the `<% include Sidebar %>`
- (tutorial/templates/Includes/SideBar.ss) include in the *tutorial/templates/Layout/Page.ss* template above
- the typography div to get rid of the bullets
-</div>
-
-
-**2. Project**
+To get a list of all projects, we've looped through the "Children" list,
+which is a relationship we didn't define explictly.
+It is provided to us by the parent implementation,
+since projects are nothing other than children pages in the standard page hierarchy.
-We know now how to easily access and show [relations](../topics/datamodel#relations) between *DataObject* in a template.
+### Project Detail Template
-We can now do the same for every *Project* page by creating its own template.
+Creating the detail view for each `Project` page works in a very similar way.
+Given that we're in the context of a single project,
+we can access the "Students" and "Mentors" relationships directly in the template.
-![tutorial:gsoc-project.png](_images/gsoc-project.jpg)
+![tutorial:tutorial5_project.jpg](_images/tutorial5_project.jpg)
-*tutorial/templates/Layout/Project.ss*
+**themes/simple/templates/Layout/Project.ss**
:::ss
- <% include Menu2 %>
-
- <div id="Content" class="typography">
- <% if Level(2) %>
- <% include BreadCrumbs %>
- <% end_if %>
-
- $Content
-
- <% if MyStudent %>
- <% loop MyStudent %>
- <p>First Name: <strong>$FirstName</strong></p>
- <p>Lastname: <strong>$Lastname</strong></p>
- <p>Nationality: <strong>$Nationality</strong></p>
-
- <h3>Mentor</h3>
-
- <% if MyMentor %>
- <% loop MyMentor %>
- <p>First Name: <strong>$FirstName</strong></p>
- <p>Lastname: <strong>$Lastname</strong></p>
- <p>Nationality: <strong>$Nationality</strong></p>
- <% end_loop %>
- <% else %>
- <p>This student doesn't have any mentor.</p>
- <% end_if %>
- <% end_loop %>
- <% else %>
- <p>There is no any student working on this project.</p>
- <% end_if %>
-
- <h3>Modules</h3>
-
- <% if Modules %>
- <ul>
- <% loop Modules %>
- <li>$Name</li>
- <% end_loop %>
- </ul>
- <% else %>
- <p>This project has not used any modules.</p>
- <% end_if %>
-
- $Form
-
+ <div class="content-container typography">
+ <article>
+ <h1>$Title</h1>
+ <div class="content">
+ $Content
+
+ <h2>Students</h2>
+ <% if Students %>
+ <ul>
+ <% loop Students %>
+ <li>$Name ($University)</li>
+ <% end_loop %>
+ </ul>
+ <% else %>
+ <p>No students found</p>
+ <% end_if %>
+
+ <h2>Mentors</h2>
+ <% if Mentors %>
+ <ul>
+ <% loop Mentors %>
+ <li>$Name</li>
+ <% end_loop %>
+ </ul>
+ <% else %>
+ <p>No mentors found</p>
+ <% end_if %>
+
+ </div>
+ </article>
</div>
+ <% include SideBar %>
+Follow the link to a project detail from from your holder page,
+or navigate to it through the submenu provided by the theme.
-What we would like now is to create a special template for the *DataObject* *Student* and the *Page* *Mentor* which will
-be used when we will call directly the variable in the *Project* template. In our case, we will use the same template
-because these two classes have the same fields ( FirstName, Surname and Nationality ).
-
-*tutorial/templates/Includes/GSOCPerson.ss*
-
- :::ss
- <p>First Name: <strong>$FirstName</strong></p>
- <p>Lastname: <strong>$Lastname</strong></p>
- <p>Nationality: <strong>$Nationality</strong></p>
+### Student Detail Template
+You might have noticed that we duplicate the same template code
+between both views when it comes to displaying the details
+on students and mentors. We'll fix this for students,
+by introducing a new template for them.
-Now the template is created, we need to establish the link between the *Student* and *Mentor* classes with their common
-template.
-
-To do so, add this code in the two classes. This will create a control on each of those objects which can be called
-from templates either within a control block or dot notation.
-
-*tutorial/code/Student.php, tutorial/code/Mentor.php*
-
- :::php
- public function PersonalInfo() {
- $template = 'GSOCPerson';
- return $this->renderWith( $template );
- }
-
-
-We can now modify the *Project.ss* template.
+**themes/simple/templates/Includes/StudentInfo.ss**
:::ss
-
- ...
-
- <% if MyStudent %>
- $MyStudent.PersonalInfo
-
- <h3>Mentor</h3>
-
- <% loop MyStudent %>
- <% if MyMentor %>
- $MyMentor.PersonalInfo
- <% else %>
- <p>This student doesn't have any mentor.</p>
- <% end_if %>
- <% end_loop %>
- <% else %>
- <p>There is no any student working on this project.</p>
- <% end_if %>
-
- ...
-
-<div class="notice" markdown='1'>
- Remember to add `?flush=1` to the url when refreshing the project page or otherwise you will get a template error
-</div>
+ $Name ($University)
-In the *Project* template, it has been really easy to display the **1-to-1** relation with a *Student* object just by
-calling the variable **$MyStudent**. This has been made possible thanks to the code below present in the *Project*
-class.
-
- :::php
- static $has_one = array(
- 'MyStudent' => 'Student'
- );
-
-
-However, in the *Student* class, there is no any code relating to the **1-to-1** relation with a *Project* *Page*. So
-how to access it from a *Student* *DataObject* ?
-
-**3. Mentor**
-
-In this template, we are gonna try to access the *Project* details from a *Student* *DataObject*.
-
-What we want to do is to access to the *Project* page in the same way than we have done for the other relations
-**without modifying the relations between *Page* and *DataObject* and the database structure**.
-
-![tutorial:gsoc-mentor.png](_images/gsoc-mentor.jpg)
-
-To do so, we have to create a function in the *Student* class which will return the *Project* linked with it. Let's call
-it *MyProject* for instance.
+To use this template, we need to add a new method to our student class:
:::php
class Student extends DataObject {
-
- ...
-
- public function MyProject() {
- return Project::get()->filter("MyStudentID", $this->ID);
+ function getInfo() {
+ return $this->renderWith('StudentInfo');
}
-
}
-
-We can now use this value in the same way that we have used the other relations.
-That's how we can use this function in the *Mentor* template.
-
-*tutorial/templates/Layout/Mentor.ss*
-
- :::ss
- <% include Menu2 %>
-
- <div id="Content" class="typography">
- <% include BreadCrumbs %>
- $Content
-
- <h3>Personal Details</h3>
-
- <p>First Name: <strong>$FirstName</strong></p>
- <p>Lastname: <strong>$Lastname</strong></p>
- <p>Nationality: <strong>$Nationality</strong></p>
-
- <h3>Students</h3>
-
- <% if Students %>
- <table>
- <thead>
- <tr>
- <th>Student</th>
- <th>Project</th>
- </tr>
- </thead>
- <tbody>
- <% loop Students %>
- <tr>
- <td>$FirstName $Lastname</td>
- <td>
- <% if MyProject %>
- <% loop MyProject %>
- $Title
- <% end_loop %>
- <% else %>
- No Project
- <% end_if %>
- </td>
- </tr>
- <% end_loop %>
- </tbody>
- </table>
- <% else %>
- <p>There is no any student working with this mentor.</p>
- <% end_if %>
-
- $Form
- </div>
-
+Replace the student template code in both `Project.ss`
+and `ProjectHolder.ss` templates with the new placeholder, `$Info`.
+That's the code enclosed in `<% loop Students %>` and `<% end_loop %>`.
+With this pattern, you can increase code reuse across templates.
## Summary
-This tutorial has demonstrated how easy it is to manage all the type of relations between *DataObject* objects in the
-CMS and how to display them on the website.
-
-
-## Download the code
-
-Download all the [code](http://doc.silverstripe.org/framework/docs/en/tutorials/_images/tutorial5-completecode.zip) for this tutorial.
-
-You can also download the [code](http://doc.silverstripe.org/framework/docs/en/tutorials/_images/tutorial5-completecode-blackcandy.zip) for use in the blackcandy template.
+This tutorial has demonstrated how you can manage data with
+different types of relations between in the CMS,
+and how you can display this data on your website.
+We illustrated how the powerful `Page` class can be useful to structure
+your own content, and how we can correlate it to more
+lightweight `DataObject` classes. The transition between
+the two classes is intentionally fluent in the CMS, you can
+manage them depending on your needs.
+`DataObject` gives you a no-frills solution to data storage,
+but `Page` allows for built-in WYSIWIG editing, versioning,
+publication and hierarchical organization.
+
+## Exercises
+
+This is a simplified example, so there's naturally room for improvement.
+In order to challenge your knowledge gained in the tutorials so far,
+we suggest some excercises to make the solution more flexible:
+
+ * Refactor the `Student` and `Mentor` classes to inherit from a common parent class `Person`,
+ and avoid any duplication between the two subclasses.
+ * Render mentor details in their own template
+ * Change the `GridField` to list only five records per page (the default is 20).
+ This configuration is stored in the `[api:GridFieldPaginator]` component
View
BIN docs/en/tutorials/_images/gsoc-mentor-creation.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-mentor-student-selection.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-mentor.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-module-creation.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-project-creation.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-project-module-selection.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-project-student-selection.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-project.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-projects-table.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/gsoc-student-creation.jpg
Deleted file not rendered
View
BIN docs/en/tutorials/_images/tutorial5_addNew.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_mentor.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_mentor_creation.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_mentor_students.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_module_creation.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_module_selection.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_project.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_project_creation.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_projects_table.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_student_tab.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN docs/en/tutorials/_images/tutorial5_students.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 0c0bcc9

Please sign in to comment.