Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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...
commit 0c0bcc9aec108b9f2ecd2007d309ab0c921561f9 1 parent 0308cc2
@chillu chillu authored
Showing with 317 additions and 659 deletions.
  1. +317 −659 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
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.
Please sign in to comment.
Something went wrong with that request. Please try again.