User generated fields in the CMS within a Page

Silverstripe Version: 4

Question:

Hey guys, I’m new to Silverstripe and I’d really appreciate some pointers. I’ve have been building a portfolio using it and have designed a custom page type ‘Project page’. My project pages follow this layout:

-----Header-----
Title | Information
Title | Information
Title | Information
-------Footer--------

The title and information boxes are in different sized Bootstrap columns within in a row, (each row has 2 columns). “Title” will be a Varchar and “information” a HTMLText.

I could add a bunch of pre-existing custom fields on each project page to pull onto the page (if the data exists) but as each project may have different amounts of rows this seems sloppy.

What I’d really like to do is have the ability to add or delete different rows at will (each row contains both variables, Title and Information) in the CMS. For example something like this on each Project page:

Row 1
Title: ____________
Information: ____________

Add New Row | Delete Row

The fields from each row would be collected in a loop and each one will generate this markup to the page:

<section>
          <div class="row">
               <div class="col-md-5 col-sm-12">
                    <h3>Title</h3>
               </div>
               <div class="col-md-7 col-sm-12">
                    <div class="proj-info">
                    <p>Information</p>
               </div>
          </div>
</section>

Any ideas? Thanks heaps!

Probably the best solution for this would be to move the rows of data out of the page, and into their own DataObject. You would then make a connection from the individual data elements to the project page they relate to.

In terms of the CMS, the data would be managed in a Gridfield. This is a CMS interface designed specifically for the purpose you describe. You can add and delete as many sets of data as you need to any page. (You can even have things like drag & drop sorting of them with the right add-ons)

So, you’d set up something along the lines of the following:

class MyData extends DataObject
{
  
  //Specify the table name in the database
  private static $table_name = 'MyData';

  //Define the data for these objects
  private static $db = [
    'Title' => 'Varchar',
    'Information' => 'HTMLText'
  ];

  //Set up the relationship to the page type
  //If you want the ability to use the data on more than one page, you would use
  //a many_many relationship here instead
  private static $has_one = [
    'Project' => ProjectPage::class  //Assuming this is the name of your page class
  ];

}

Then in your project page, you’d have something like this:

class ProjectPage extends \Page 
{
  //Table name in the database
  private static $table_name = 'ProjectPage';

  //Tell the system that we have multiple data elements relating to 
  //this page
  private static $has_many = [
    'MyDataThings' => MyData::class
  ];

  //This function is where you add your user-defined fields
  //to the CMS.  Most of the hard work is done automatically
  public function getCMSFields()
  {
    $fields = parent::getCMSFields();
    $fields->addFieldToTab('Root.MyElements', 
        GridField::create('MyDataThings', 'My Data Things', $this->MyDataThings(), GridFieldConfig_RelationEditor::create());
    return $fields;
  }
}

The above is all completely untested and off the top of my head, so use it accordingly. You can call the dataobjects, etc. anything you want.

The lesson here covers the concepts and has examples of using Gridfield and how the data relationships work: https://www.silverstripe.org/learn/lessons/v4/working-with-data-relationships-has-many-1

To display the data, in your page template, you’d then just loop the data:

<% loop $MyDataThings %>

<!-- 
Inside the loop, you have direct access to the properties of the related data,
so you can access it directly with the variables as shown below
-->

<section>
          <div class="row">
               <div class="col-md-5 col-sm-12">
                    <h3>$Title</h3>
               </div>
               <div class="col-md-7 col-sm-12">
                    <div class="proj-info">
                    <p>$Information</p>
               </div>
          </div>
</section>

<% end_loop %>

Hope that gives you a few pointers. By all means ask if you can’t get it working or something isn’t clear.

Hey thank you so much for this. Works like a charm