computer and cloud
Articles » Working with "many many" and "belongs many many" relationships in SilverStripe

Working with "many many" and "belongs many many" relationships in SilverStripe

26 June, 2021

In this short guide we will go through the steps to link two dataobjects through a $many_many and a $belongs_many_many relationship.

A common example where this relation can be useful is usually the following scenario.

We have two dataobjects, one is ‘Product’ and the other is ‘ProductCategory’. What we want is to be able to assign each ‘Product’ one or more ‘Categories’, and we want a ‘Category’ to have one or more ‘Products’.

Areas we will cover:

  1. Setting the $many_many relationship
  2. Setting the $belongs_many_many relationship
  3. Configuring the user interface for the $many_many relationship
  4. Getting the data onto the template

1. Setting the $many_many relationship

Let’s say we have a ProductsPage where we will list all our products. This ProductsPage is already linked to the ‘Product’ and the ‘ProductCategory’ dataobjects through a $has_many and $has_one relationship, click here to read more about $has_many relationships.

With the $has_many relationships already taken care of, we can define the $many_many relationship between the ‘Product’ and the ‘ProductCategory’ dataobject as shown below. This tells SilverStripe that each ‘Product’ can have one or more ‘ProductCategory’.

 

<?php

namespace MySite\Web\DataObjects;

use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DataObject;
use MySite\Web\Pages\ProductsPage;
use MySite\Web\DataObjects\ProductCategory;

class Product extends DataObject {
  private static $table_name = "Product";

  private static $db = [
    'Title' => 'Varchar',
    'Description' => 'Varchar'
  ];

  private static $has_one = [
    'ProductPage' => ProductsPage::class
  ];

  private static $many_many = [
    'Categories' => ProductCategory::class
  ];
}

 

Make sure to rebuild your database by appending ‘dev/build?flush=all’ to the end of your base URL to enforce the relationship above.

2. Setting the $belongs_many_many relationship

Now in our ‘ProductCategory’ dataobject we want to define the $belongs_many_many relationship between the ‘Product’ and ‘ProductCategory’. This tells SilverStripe that each ‘ProductCategory’ can belong to one or more ‘Product’.

Let’s do this with the following code.

<?php

namespace MySite\Web\DataObjects;

use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DataObject;
use MySite\Web\DataObjects\Product;
use MySite\Web\Pages\ProductsPage; 

class ProductCategory extends DataObject {
  private static $table_name = "ProductCategory";

  private static $db = [
    'Category' => 'Varchar',
    'Description' => 'Varchar'
  ];

  private static $has_one = [
    'ProductPage' => ProductsPage::class
  ];

  private static $belongs_many_many = [
    'Products' => Product::class
  ];

  public function getCMSFields() {
    $fields = Parent::getCMSFields();

    $fields->addFieldsToTab('Root.Main', [
      TextField::create('Category'),
      TextareaField::create('Description')
    ]);

    return $fields;
  }
}

 

Rebuild your database by appending ‘dev/build?flush=all’ to the end of your base URL to enforce the relationship above.

3. Configuring the user interface for the $many_many relation

Next we need to configure the CMS to show all the available ‘ProductCategories’ we can assign to each ‘Product’. Usually, the dataobject that has the $many_many relationship (Product) will be where you will show some type of checkboxes that returns a list of options (ProductCategories) to choose from.

Let’s bring in the ‘ProductCategories’ into the ‘Product’ interface in the CMS by using the CheckboxSetField. If you’re going to have a lot of ‘ProductCategories’ then use ListBoxField instead.

<?php

namespace MySite\Web\DataObjects;

use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DataObject;
use MySite\Web\Pages\ProductsPage;
use MySite\Web\DataObjects\ProductCategory;
use SilverStripe\Forms\CheckboxSetField;

class Product extends DataObject {

  // …

  private static $many_many = [
    'Categories' => ProductCategory::class
  ];

  public function getCMSFields() {
    $fields = Parent::getCMSFields();

    $fields->removeByName('Categories');

    $fields->addFieldsToTab('Root.Main', [

      // …

      CheckboxSetField::create(
        'Categories',
        'Selected categories',
        $this->ProductPage()->ProductCategories()->map('ID', 'Category')
      )
    ]);

    return $fields;
  }
}

After implementing the code above, if you click on each ‘Product’ in the CMS you should see a set of checkboxes containing the ‘ProductCategories’ like the image below. If the configuration has been done correctly, you should be able to select a category or two and save and publish the changes.

4. Getting the data onto the template

Getting the data onto the template is relatively straight forward. All we need is to loop the ‘Products’ in the ProductsPage.ss template and inside the ‘Products’ loop we now have access to loop the ‘ProductCategories’ since it’s within the scope of the ‘Products’.

<% if $Products %>
  <% loop $Products %>
    <div class="product">
      <h2>$Title</h2>
      <p>$Description</p>
      <ul>
        <% if $Categories %>
          <% loop $Categories %>
            <li>
              <h5>$Category</h5>
              <p>$Description</p>
            </li>
          <% end_loop %>
        <% end_if %>
      </ul>
    </div>
  <% end_loop %>
<% end_if %>

 

Check the ProductsPage.ss template and you should now be able to see that ‘Products’ are also showing their selected categories.

Post your comment

Comments

No one has commented on this page yet.

RSS feed for comments on this page | RSS feed for all comments