Back to Start Page
The hasOne relation
With the hasOne relation a record from a parent table can be linked to exactly one record of a child table without adding a column to the parent table. This may be useful e.g. to extend an existing (parent) table that should not be altered for some reasons.
Todo Cheat Sheet
- Required: a parent table and a child table
- Add a field named: [parentModelName]_id to the child table
- Create a model for the child table with list and form
- Extend the parent controller with relation behaviour
- Extend the parent model with the desired relation definition (hasOne)
- Define the details of how to handle the relation in the config_relation.yaml in the parent controller
- Use a Partial widget to include the child fields/forms into the parent form
In the library example used in this wiki, the parent will be the book table, the child table is a description table where a single description (a record) can be linked to exactly one book.
What it will look like
This is what a record with an related record will look like in the example by adding a description to a book. In this example the related form was added into an extra tab on the book form with the options to create a new description, to edit or delete an existing description.
Preparation
Prerequisites are
- an installed builder plugin
- any source code editor or e.g. the Developer Tools plugin with built-in Code editor like to be seen on the screenshoots
Required skills
- how to work with builder
- how to create a plugin with builder
- how to create tables and forms with builder
Preparations
- a parent table to embed a relation to a child table
- a child table for the additional field(s) to embed into the parent table
Step 1: Add a reference field to the child table
- In the library example the parent table is books (plural).
- According to this, the parent model is called book (singular).
- The related child table shall be named descriptions and the respective model description.
For a hasOne relation the child table requires a field named by the parent model trailed by '_id'. So in this example the required field has to be called 'book_id'.
In this field the id of the record of the parent table will be stored. So the field has to be of the same type as the parent id field (integer in the example)
Step 2: Create a model with list and form for the child table
Next for the child table (descriptions in the example) a model (called Description)has to be created…
… as well as a list that will be shown to select a description …
… and a form to create and edit them.
Here is the complete code to copy, the highlighted rows are the lines probably to add, if the controller was already created. Please remember Pds\Library is the namespace of this exampe that has to be adapted to your needs!
<?php namespace Pds\Library\Controllers; use Backend\Classes\Controller; use BackendMenu; class Books extends Controller { public $implement = ['Backend\Behaviors\ListController', 'Backend\Behaviors\FormController', 'Backend\Behaviour\RelationController']; // this line adds the relation behaviour public $listConfig = 'config_list.yaml'; public $formConfig = 'config_form.yaml'; //add configuration of relation for Books // this file has to be created then manually inside the // [namespace]\controllers\[controllername] folder public $relationConfig = 'config_relation.yaml'; public function __construct() { parent::__construct(); BackendMenu::setContext('Pds.Library', 'library-main-menu-item'); } }
Step 3: Extend the parent controller
As for all relations the controller of the parent table has to be extendend by a few lines to add the relation behaviour. This simlpy is to tell OctoberCMS and the parent table how deal with relations.
In case this step has been done already for another relation, skip it because this is required only once!
Here is the complete code to copy, the highlighted rows are the lines probably to add, if the controller was already created. Please remember Pds\Library is the namespace of this exampe that has to be adapted to your needs!
<?php namespace Pds\Library\Controllers; use Backend\Classes\Controller; use BackendMenu; class Books extends Controller { public $implement = ['Backend\Behaviors\ListController', 'Backend\Behaviors\FormController', 'Backend\Behaviour\RelationController']; // this line adds the relation behaviour public $listConfig = 'config_list.yaml'; public $formConfig = 'config_form.yaml'; //add configuration of relation for Books // this file has to be created then manually inside the // [namespace]\controllers\[controllername] folder public $relationConfig = 'config_relation.yaml'; public function __construct() { parent::__construct(); BackendMenu::setContext('Pds.Library', 'library-main-menu-item'); } }
Step 4: Announce the relation to the parent model
To make the parent model able to work with the relation, the type of the relation, the name of the relation and the child model have to be announced in the parent model.
The required file is placed in plugins /models/ directory. In case of our example the full path is 'pds/library/models/book.php'.
It's only a few lines to add:
<?php ... public $hasOne = [ 'description' => ['Pds\Library\Models\Description'] ];
In line 4 the public variable '$hasOne' is the type of relation, to let OctoberCMS know which relations to take care for in this model.
In line 5 'description' is the name of the relation, which can be used similar to a table field name. The path assigned to the name of the relation is the path to the model of the child table.
Remember each definition for a relation is like a table field. A definition can hold several definitions for different relations. Like so:
<?php ... // multiple definitions for one type of relation public $hasOne = [ 'description' => ['Pds\Library\Models\Description'], 'otherrelation' => ['Pds\Library\Models\Otherrelation'], 'foo' => ['Pds\Library\Models\Foo'] ];
And here is the code embedded into the model:
If the name of the key field within the child table (the description table in our case) cannot be 'book_id' for what reason ever, we have to define the correct name of the id field. E.g. like this:
<?php ... public $hasOne = [ 'description' => ['Pds\Library\Models\Description', 'key' => 'alternative_id'] ];
Step 5: Create the config_relation.yaml controller file
In the controller file (see step 3) we have announced a relation configuration file. This announcement was:
<?php ... public $relationConfig = 'config_relation.yaml'; ...
Now we have to create and edit this controller-configuration-for-books-file as:
pds/library/controllers/books/config_relation.yaml
This can be done via Code editor like this:
This file will be our central configuration for all relations of the books table. So handle with care!
The code required for the hasOne relation to the description table has to be:
... description: label: Description view: form: $/pds/library/models/description/fields.yaml toolbarButtons: create|update|delete manage: form: $/pds/library/models/description/fields.yaml list: $/pds/library/models/description/columns.yaml ...
- The root entry description is the name we will refer to from the book model (see code: public $hasOne = ['description' ⇒ ['Pds\Library\Models\…' ) .
- The item label: holds the text for a label for the field 'description'.
- The view segment indicates how the relation will be shown: As a form in our example because there is only one related child record (because of hasOne) per parent record.
- The item toolbarButtons with 'create|update|delete' buttons will add the ability to create a new descrpton, to edit or delete an existing one.
- The manage sections defines how things are displayed when we are searching or editing an entry
Please note: For the item toolbarButtons there are two more options available, not used in this example:
- link will give the option to add a already existing but not yet related (linked) entry of the child table to a parent record
- unlink will cut the relation from the parent table to the child table and orphan the child record
Both options are useful in many cases, but not exactly for this example.
This is the screenshot for the example above:
Step 6: Create a rendering file
There is another file to create in the controller subdirectory of the parent model to enable OctoberCMS to render the child model.
This file has to be named by convention _field_[reference variable].htm where the [reference variable] has to be the name as set in the config_relation.yaml file root for this reference.
The content of this file has (at least) to be:
<?= $this->relationRender('description') ?>
Step 7: Add the rendering file to the parent form
Once these preparations are done, we are ready to integrate the relation into our parent model. In our example the parent model is the book form view.
So we return to Builder > Models > Book > Forms > field.yaml an add a widget Partial displaying and managing the created relation:
Now the form is ready to create, show, edit, delete, link and unlink a record from the child table within/to a record of the parent table.