CMS Field for many_many relation in SilverStripe 4 - silverstripe-4

I have relation between BlogArticle and BlogCategory many_many & belongs_many_many. I would like to add CheckboxSetField or ListBoxField to cmsFields on BlogArticle, which contains BlogCategories.
Following code shows correct checkboxes in cms, but from some reason it doesn't store the values:
class BlogCategory extends DataObject
{
private static $db = [
'Title' => 'Varchar(255)'
];
private static $belongs_many_many = [
'BlogArticles' => BlogArticle::class
];
}
class BlogArticle extends Page
{
private static $many_many = [
"BlogCategories" => BlogCategory::class,
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
$field = CheckboxSetField::create(
'BlogCategories',
'Categories',
BlogCategory::get()
);
$fields->add($field);
return $fields;
}
}
Any ideas what's wrong? Thanks a lot!

On your BlogArticle.php you aren’t referencing the relationship so it can’t save.
So BlogCategory::get() should be $this->BlogCategories() - you will probably have to map() the values aswell.
There’s an example of using the checkbox field with a $many_many here: https://www.silverstripe.org/learn/lessons/v4/working-with-data-relationships-many-many-1

Related

I Can Not Remove The Default Drop Down of a $has_one in Silverstripe

I have tried:
removeFieldFromTab
removeByName
replaceField
But the field persists.
use SilverStripe\ORM\DataObject;
use //.....
class Product extends DataObject {
private static $db = [
'ProductName'=>'Varchar',
'TagLine'=>'Text',
'GeneralDescription'=>'HTMLText'
];
private static $has_one = [
'SplashImage'=>Image::Class,
'ProductCategory'=>ProductCategory::Class
];
private static $has_many = [
'ProductImage'=>Image::Class,
'Features'=>'Feature'
];
private static $owns = [
'SplashImage',
'ProductImage'
];
private static $summary_fields = array(
'ProductName'=>'Product Name'
);
private static $searchable_fields = [
];
public function getCMSFields(){
$fields = parent::getCMSFields();
$categoryField = DropdownField::create('ProductCategory', 'Choose Product Category', ProductCategory::get()->map('ID', 'ProductCategoryTitle'));
$fields->replaceField('ProductCategory', $categoryField);
return $fields;
}
}
I am not getting any errors but the default drop down field that has the id #'s is at the top.
With has_one relations the field name should be <RelationName>ID, so in your case ProductCategoryID.
You have to reference the append ID to the relation name in order for SilverStripe to remove the field from the CMS tab by name.
$fields->removeByName('ProductCategoryID');
Also, if you create a custom field for a has_one relation, make sure you use <RelationName>ID as the name for the field. Eg. to create a Dropdown, use:
DropdownField::create(
'ProductCategoryID', // Use RelationName + ID
'Choose Product Category',
ProductCategory::get()->map('ID', 'ProductCategoryTitle')
);

SilverStripe - DataObject to add tab and save data in Page

I am looking to make a DataObject class which injects fields into a Page object but I'm having a bit of trouble doing so. The only solutions I've found so far require me to do something within the Page (other than adding in a relationship between the two classes), I'm not all that certain as to what I'm doing wrong...
What I need would be for the DataObject to add a tab and some fields within Page and have the data for that Page to save to the DataObjects table.
In a sense I would like them to be somewhat independent of each other, so that I can link it to Article now and then some other page types at some later date.
This is what I have so far :
Article.php
class Article extends Page {
private static $description = 'An article page for writing and posting content';
private static $has_many = array(
'MyExtraFields' => 'MyExtraFields'
);
}
The DataObject
class MyExtraFields extends DataObject {
private static $db = array(
'ExtraText' => 'Varchar(255)',
'ExtraWYSIWYG' => 'HTMLText'
);
private static $has_many = array(
'Article' => 'Article'
);
private static $summary_fields = array(
'ExtraText' => 'ExtraText',
'ExtraWYSIWYG' => 'ExtraWYSIWYG'
);
public function updateCMSFields(){
$fields = parent::getCMSFields();
$fields->addFieldsToTab('Root.Content.Translation', array(
TextField::create('ExtraText'),
HTMLEditorField::create('ExtraWYSIWYG')
)
);
return $fields;
}
}
If I understand it correctly, you want to be able to add the DataObject MyExtraField to the Pagetype Article. If the MyExtraField is dependent on the Article, then you need to change the Relation of the ExtraField to has_one like jberculo says:
class MyExtraField extends DataObject {
...
private static $has_one = array(
'Article' => 'Article'
);
}
Then you have to add a GridField to the Article's CMS view manage the DataObject:
class Article extends Page {
...
private static $has_many = array(
'MyExtraFields' => 'MyExtraField'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$gridFieldConfig = GridFieldConfig_RecordEditor::create();
$gridfield = new GridField( "MyExtraFields", "MyExtraField", $this->MyExtraField(), $gridFieldConfig );
$fields->addFieldToTab( 'Root.ExtraFields', $gridfield );
return $fields;
}
If you want to reuse the DataObject in different Articles, you need to use a many to many relation and a different GridField-Configuration:
$gridFieldConfig = GridFieldConfig_RelationEditor::create();
You will find the documentation of GridField here:
https://docs.silverstripe.org/en/3.4/developer_guides/forms/field_types/gridfield
If you want to manage has_one relation to be able to add just one optional instance of MyExtraField, you could use:
https://github.com/burnbright/silverstripe-hasonefield

How to save multiple values entered into ListBoxField and loop through values?

I want to be able to associate team members to a project using the ListBoxField . I have a ProjectHolder which has Project pages as it's children. I also have a TeamHolder which has TeamPage's as its children.
I want to be able to save multiple team members in the ListBoxField and then loop through them on a Project page. I also want to be able to link to the team members pages. e.g
<% loop $TeamMemberNames %>
$Name
<% end_loop %>
My Current code:
TeamPage.php
class TeamPage extends Page
{
private static $db = array(
'Name' => 'Varchar(255)',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.TeamMembers', TextField::create('Name'));
return $fields;
}
}
Project.php
class Project extends Page
{
private static $db = array(
'Name' => 'Varchar(255)',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.TeamMembers', TextField::create('Name'));
$fields->addFieldToTab('Root.TeamMembers', ListBoxField::create(
'TeamPage',
'Select Team Members for project',
TeamPage::get()->map("ID", "Name")->toArray()
)->setMultiple(true));
return $fields;
}
}
Screenshot:
I can pull through the names from the TeamPage Object into the ListBoxField however after selecting the names I need a way to save the multiple values and also get the Link so I can link to the appropriate team members pages that are listed.
To save data to the database we must first define a relationship to the data in our class. In this case we want to create a $many_many relationship between Project and TeamPage.
In the Project class we add a $many_many relationship to TeamPage. Here, the relationship name is TeamPages.
When creating the ListBoxField we pass the TeamPages relationship name so the field knows where to save this data:
Project.php
class Project extends Page
{
private static $db = array(
'Name' => 'Varchar(255)'
);
private static $many_many = array(
'TeamPages' => 'TeamPage'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.TeamMembers', TextField::create('Name'));
$fields->addFieldToTab('Root.TeamMembers', ListBoxField::create(
'TeamPages',
'Select Team Members for project',
TeamPage::get()->map('ID', 'Name')->toArray()
)->setMultiple(true));
return $fields;
}
}
Now in our template we can loop through a Project's $TeamPages by calling the following:
Template
<% loop $TeamPages %>
$Name
<% end_loop %>
If we would like a TeamPage to be able to access it's related Projects we can add a $belongs_many_many to the TeamPage class to point back to the Project class. You can also add a ListBoxField to control Projects from the TeamPage.
TeamPage.php
class TeamPage extends Page
{
private static $db = array(
'Name' => 'Varchar(255)'
);
private static $belongs_many_many = array(
'Projects' => 'Project'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.TeamMembers', TextField::create('Name'));
$fields->addFieldToTab('Root.TeamMembers', ListBoxField::create(
'Projects',
'Select project for this team page',
Project::get()->map('ID', 'Name')->toArray()
)->setMultiple(true));
return $fields;
}
}
Something to note is TeamPage and Project both extend Page. This means both classes inherit a Title field. I would suggest using Title instead of Name, unless you have a specific reason to do so.
This would make the code:
Project.php
class Project extends Page
{
private static $many_many = array(
'TeamPages' => 'TeamPage'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.TeamMembers', ListBoxField::create(
'TeamPages',
'Select Team Members for project',
TeamPage::get()->map('ID', 'Title')->toArray()
)->setMultiple(true));
return $fields;
}
}
TeamPage.php
class TeamPage extends Page
{
private static $belongs_many_many = array(
'Projects' => 'Project'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Projects', ListBoxField::create(
'Projects',
'Select project for this team page',
Project::get()->map('ID', 'Title')->toArray()
)->setMultiple(true));
return $fields;
}
}
Template
<% loop $TeamPages %>
$Title
<% end_loop %>

Silverstripe - Different tabs and fields per page

In SilverStripe 3.1 is it possible to add different tabs and fields on the about page for example.
And then different tabs and fields on a services page for example.
About Page - Images Tab / Attachments Tab
Services Page - Images Tab / Attachments Tab / Staff Person Tab
The following code is an example. I have added the if statements around a snippet that does work. But it only seems to work for all pages by showing the same tabs on all pages.
I've been doing the video lessons on the SilverStripe website and I can see that you can create page types but I really need to know if you can achieve this without having to create extra page types.
// I want this on the about page
// if page=about {
class Page extends SiteTree {
private static $has_one = array (
'Photo' => 'image',
'Brochure' => 'file',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Images', $photo = UploadField::create('Photo'));
$fields->addFieldToTab('Root.Attachments', $brochure = UploadField::create('Brochure'));
return $fields;
}
}
// I want this on the services page
// } elseif page=services {
class Page extends SiteTree {
private static $has_one = array (
'Photo' => 'image',
'Brochure' => 'file',
'Staff Person' => 'image',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Images', $photo = UploadField::create('Photo'));
$fields->addFieldToTab('Root.Attachments', $brochure = UploadField::create('Brochure'));
$fields->addFieldToTab('Root.Staff', $staff = UploadField::create('Staff'));
return $fields;
}
}
// }
class Page_Controller extends ContentController {
private static $allowed_actions = array();
public function init() {
parent::init();
}
}
I would recommend to use individual page types for what you want to do.
However, if you only want to use one page type you can use an if statement in your getCMSFields function to display different fields.
In this example code I check the URLSegment, although you could check something else like Title.
class Page extends SiteTree {
private static $has_one = array (
'Photo' => 'image',
'Brochure' => 'file',
'Staff Person' => 'image',
);
public function getCMSFields() {
$fields = parent::getCMSFields();
if ($this->URLSegment == 'about' || $this->URLSegment == 'services') {
$fields->addFieldToTab('Root.Images', $photo = UploadField::create('Photo'));
$fields->addFieldToTab('Root.Attachments', $brochure = UploadField::create('Brochure'));
}
if ($this->URLSegment == 'services') {
$fields->addFieldToTab('Root.Staff', $staff = UploadField::create('Staff'));
}
return $fields;
}
}

Silverstripe remove irrelevant has_one relationship fields in CMS tab

I have a DataObject called ContentSection that has 2 has_one relationships: to a page type LandingPage and another DataObject Person.
class ContentSection extends DataObject {
protected static $has_one = array(
'Person' => 'Person',
'LandingPage' => 'LandingPage'
);
}
Both LandingPage and Person define a has_many relationship to ContentSection.
class LandingPage extends Page {
protected static $has_many = array(
'ContentSections' => 'ContentSection'
);
}
class Person extends DataObject {
protected static $has_many = array(
'ContentSections' => 'ContentSection'
);
}
ContentSections are editable through the LandingPage and Person with the GridFieldConfig_RelationEditor e.g.:
function getCMSFields() {
$fields = parent::getCMSFields();
$config = GridFieldConfig_RelationEditor::create(10);
$fields->addFieldToTab('Root.Content', new GridField('ContentSections', 'Content Sections', $this->ContentSections(), $config));
return $fields;
}
My question is how can you hide/remove irrelevant has_one fields in the CMS editor tab? The Person and LandingPage relationship dropdown fields both display when you are editing a ContentSection, whether it is for a Person or LandingPage. I only want to show the relevant has_one relationship field. I've tried using dot notation on the has_many relationships:
class Person extends DataObject {
protected static $has_many = array(
'ContentSections' => 'ContentSection.Person'
);
}
I've also tried using the removeFieldFromTab method in the getCMSFields method of the ContentSection class, where I define the other CMS fields for the ContentSection:
$fields->removeFieldFromTab('Root.Main', 'Person');
Instead of removeFieldFromTab use the removeByName function. removeFieldFromTab will not work if there is no 'Root.Main' tab.
Also we remove PersonID, not Person. has_one variables have ID appended to the end of their variable name.
function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('PersonID');
$fields->removeByName('LandingPageID');
return $fields;
}
If you would like to selectively hide or display these fields you can put in some if statements in your getCMSFields function.
function getCMSFields() {
$fields = parent::getCMSFields();
if (!$this->PersonID) {
$fields->removeByName('PersonID');
}
if (!$this->LandingPageID) {
$fields->removeByName('LandingPageID');
}
return $fields;
}

Resources