Back to [[:start|Start Page]]\\ ====== The hasMany relation ====== **With the //hasMany// relation a record from a parent table can be linked to one or more record of a child table. No changes in the parent table are required. The key(s) to link a parent record to the child record(s) are stored in the child table. **\\ \\ **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 (//hasMany//) * 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 **review** table where a book may have several reviews. Each review is stored in a different record and is linked via the record id of the book (stored in the review records) to a book. \\ ===== What it will look like ===== This is the parent form //** book **// embedding the related child list //**reviews**// showing all reviews for the displayed book:\\ {{:examples:builder:hasmany-child-in-parent-integration.jpg?direct&960 |}} \\ Additional reviews can be crated with the button //Create Reviews//. By clicking on a row in the reviews table, an existing review can be updated directly. {{:examples:builder:hasmany-child-in-parent-integration2.jpg?direct&960 |}} \\ ===== Preparation ===== Prerequisites are\\ * an installed [[https://octobercms.com/plugin/rainlab-builder|builder plugin]] * any source code editor or e.g. the [[https://octobercms.com/plugin/indikator-devtools|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 **controller** for the parent is called //**books**// (plural) like the table. * The related **child table** shall be named //**reviews**// and * the respective **model** is named **//review//**. * The **controller** for the child is named //**reviews**//.\\ For a //hasMany// relation the **child table** requires a field named by the parent model trailed by '_id'. So in this library example where reviews will be added to books, the required field in the reviews table has to be called 'book_id'. In this field the id of the record of the parent table will be stored. This field has to be of the same type as the parent id field (unsually integer for IDs).\\ The table definitions for the //hasMany// relation looks pretty much the same like the //hasOne// definition. The difference is, that the //has Many// child table can hold more than one record with the same key to the parent record.\\ Here is what the libraray example reviews table looks like. Notice the last line where the field //**book_id**// is already defined:\\ {{:examples:builder:hasmany-review-table.jpg?direct&960|}} ===== Step 2: Create a model with list and form for the child table ===== Next a child table (//reviews// in the example) and a model (called //Review//) has to be created...\\ \\ This is the form for the reviews:\\ {{:examples:builder:hasmany-review-form.jpg?direct&960|}} \\ \\ This is the list view for the reviews:\\ {{:examples:builder:hasmany-review-columns.jpg?direct&960|}} \\ ===== 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!\\ {{:examples:builder:relations-controller-extend.jpg?direct&960 |}} \\ 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! \\ \\ ===== Step 4: Define the relation within 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 defined in the parent model. \\ \\ {{:examples:builder:hasmany-relation-definition.jpg?direct&960|}} \\ 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:\\ ['Pds\Library\Models\Review'] ]; ... \\ In line 4 the public variable '$hasMany' is the type of relation, to let OctoberCMS know which relations to take care for in this model.\\ In line 5 'reviews' 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.\\ By default the key field to look for the records with related IDs will be created by the name of the model trailed by '_id'. In our example this would be 'book_id'.\\ \\ Remember each definition for a relation is like a table field. A definition can hold several definitions for different relations. Like so:\\ ['Pds\Library\Models\Review'], 'otherrelation' => ['Pds\Library\Models\Otherrelation'], 'foo' => ['Pds\Library\Models\Foo'] ... ]; \\ And here is the code embedded into the model:\\ /* {{:examples:builder:hasone-model-extend.jpg?direct&960 |}} */ \\ If the name of the key field within the child table (the reviews 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:\\ ['Pds\Library\Models\Review', 'key' => 'alternative_id'] ]; ... \\ ===== Step 5: Create the config_relation.yaml controller file ===== In the controller file of the parent table (pds/library/controllers/Books.php, see step 3) we have announced a relation configuration file. This announcement was:\\ Now we have to create (if it is not created yet) 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:\\ {{:examples:builder:pds_library_controllers_config_relation_create.jpg?direct&960 |}} \\ then:\\ {{:examples:builder:pds_library_controllers_config_relation_create2.jpg?direct&960 |}} \\ */ This file is the central configuration for all relations of the books table. Handle with care! The (additional) code required for the hasMany relation to the reviews table has to be:\\ ... reviews: label: Reviews view: list: $/pds/library/models/review/columns.yaml toolbarButtons: create|delete manage: form: $/pds/library/models/review/fields.yaml ... * The root entry **reviews** is the name we will refer to from the book model (see code: public $hasMany = ['**review**' => ['Pds\Library\Models\...' ) . * The item **label:** holds the text for a label for the field 'review'. /* 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|delete' buttons will add the ability to create a new review directly from the book view or to delete existing ones. The 'update' button is not required, because an update/edit form will be opened by ckicking on a row in the review list. * The **manage** sections defines how things are displayed when we are searching or editing an entry \\ Please note: For the item //toolbarButtons// there are some options more 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 - notice that a definition is already set (see the hasOne example):\\ {{:examples:builder:hasmany-config-relation.jpg?direct&960|}} \\ ===== Step 6: Create a rendering file ===== The next file to create in the controller subdirectory of the parent model is a partial htm file 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.\\ in our library example the file (includong the path) will be //**pds/library/controllers/books/_field_review.htm**// \\ The content of this file has (at least) to be in our example:\\ relationRender('review') ?> See this screenshot too:\\ {{:examples:builder:hasmany-relation-partial.jpg?direct&960|}} \\ ===== Step 7: Add the rendering file to the parent form ===== Finally we are now ready to integrate the relation into our parent model. In our example the parent model is the book form view defined in the //**pds/library/models/book/field.yaml**// file. \\ So we return to Builder > Models > Book > Forms > field.yaml an add a widget //Partial// displaying and managing the created relation.\\ The field name we have to refer to is //**review**// as defined before. The partial to display is the one defined in step 6: //**$/pds/library/controllers/books/_field_reviews.htm**// \\ Notice the '$' in front of the path. This tells october to find subpath and file in the plugin folder.\\ \\ {{:examples:builder:hasmany-relation-partialfield-in-parent-form.jpg?direct&960|}} \\ 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. ----