I got a many to many relationship between Products and Categories using Silverstripes' ORM. All is working except when I try to add a Category for a product the choices for Category is shown by user id instead of Category name. I tried to map ID to name but did not work. Below is what I tried, what am I missing?
class Product extends DataObject {
private static $db = array(
'ProductName' => 'Varchar(32)',
);
private static $many_many = array (
'Category' => 'Category'
);
}
class Category extends DataObject {
private static $db = array(
'Category' => 'Varchar(32)',
);
public function searchableFields() {
return array (
'Category' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Category',
'field' => 'TextField'->setSource(
$this::get()->map('ID','Category')
)
)
);
}
private static $belongs_many_many = array (
'Product' => 'Product'
);
}
When Silverstripe needs to display a shorthand for a DataObject, it will call the getTitle() method on that DataObject.
getTitle() will first check if the DataObject has a Title field and return that value if it does.
If your DataObject doesn't have Title field, it will try searching for a Name field.
If it can't find a Name field either, it will default to returning the ID of your DataObject, which is probably what is happening for you.
How to fix your specific example
There's 2 ways you can fix your specific example:
Rename your DataObjects DB fields to Name or Title
Override the getTitle() method on your 2 DataObjects
Solution #1
class Product extends DataObject {
private static $db = array(
'Name' => 'Varchar(32)',
);
private static $many_many = array (
'Category' => 'Category'
);
}
class Category extends DataObject {
private static $db = array(
'Name' => 'Varchar(32)',
);
public function searchableFields() {
return array (
'Category' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Category',
'field' => 'TextField'->setSource(
$this::get()->map('ID','Category')
)
)
);
}
private static $belongs_many_many = array (
'Product' => 'Product'
);
}
Solution #2
class Product extends DataObject {
private static $db = array(
'ProductName' => 'Varchar(32)',
);
private static $many_many = array (
'Category' => 'Category' );
public function getTitle() {
return $this->ProductName;
}
}
class Category extends DataObject {
private static $db = array(
'Category' => 'Varchar(32)',
);
public function searchableFields() {
return array (
'Category' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Category',
'field' => 'TextField'->setSource(
$this::get()->map('ID','Category')
)
)
); }
private static $belongs_many_many = array (
'Product' => 'Product' );
public function getTitle() {
return $this->Category;
}
}
Which solution is best?
In your specific case, I would go with solution #1, because your DB fields are functionally name/title fields.
I would use solution #2 if my DataObject's title needs to use many fields. Let's say you a have Person DataObject with a first name and a last name:
public function getTitle() {
return $this->FirstName . ' ' . $this->LastName;
}
Related
I followed the tutorial on https://www.silverstripe.org/learn/lessons/working-with-data-relationships-has-many?ref=hub to create some featured items for my homepage. But somehow I missed one piece, because I get this error
[Error] Uncaught Exception: No has_one found on class 'HomePageFeatured', the has_many relation from 'HomePage' to 'HomePageFeatured' requires a has_one on 'HomePageFeatured'
HomePage.php
<?php
/**
* Defines the HomePage page type
*/
class HomePage extends Page {
// private static $db = array(
// );
// private static $has_one = array(
// );
private static $has_many = array (
'Featured' => 'HomePageFeatured'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Featured', GridField::create(
'Featured',
'Hervorgehobene Produkte',
$this->Featured(),
GridFieldConfig_RecordEditor::create()
));
return $fields;
}
private static $icon = "themes/hstheme/images/treeicons/home";
}
class HomePage_Controller extends Page_Controller {
}
HomePageFeatured.php
<?php
/**
* Holds the featured items from the Homepage
*/
class HomePageFeatured extends DataObject {
private static $db = array(
'Title' => 'Varchar',
'Description' => 'Text'
);
private static $has_one = array(
'Photo' => 'Image',
'HomePageFeatured' => 'HomePageFeatured'
);
public function getCMSFields() {
$fields = FieldList::create(
TextField::create('Title'),
TextareaField::create('Description'),
$uploader = UploadField::create('Photo')
);
$uploader->setFolderName('featured-photos');
$uploader->getValidator()->setAllowedExtensions(array('png','gif','jpeg','jpg'));
return $fields;
}
}
As I understand, the problem is the $has_one from HomePageFeatured.php. But it has a reference from HomePageFeatured.
HomePageFeatured needs a has_one of 'HomePage'
class HomePageFeatured extends DataObject {
private static $has_one = array(
'Photo' => 'Image',
'Parent' => 'HomePage',
);
}
I have a product catalog. There are:
Dzial.php - category page
class Dzial extends Page {
...
static $has_many = array(
'Marka' => 'Marka'
);
function getCMSFields() {
...
$gridField = new GridField("Marka", "marki:", $this->Marka(), $gridFieldConfig);
$fields->addFieldToTab("Root.Marki", $gridField);
PodDzial.php - subcategory page
class PodDzial extends Page {
.....
static $has_many = array(
'Kategoria' => 'Kategoria',
'Produkt' => 'Produkt'
);
function getCMSFields() {
....
$gridField = new GridField("Produkt", "Produkty:", $this->Produkt(), $gridFieldConfig);
$fields->addFieldToTab("Root.Produkty", $gridField);
$gridField2 = new GridField("Kategoria", "Kategoria:", $this->Kategoria(), $gridFieldConfig);
$fields->addFieldToTab("Root.Kategorie", $gridField2);
Produkt.php - product - DataObjects which belong to PodDzial.php
class Produkt extends DataObject {
.....
static $has_one = array (
'PodDzial' => 'PodDzial',
'Marka' => 'Marka',
'Kategoria' => 'Kategoria'
);
Kategoria.php - type of product- which belong to PodDzial (has_one) and to Produkt (has_many)
class Kategoria extends DataObject {
....
private static $has_one = array( 'PodDzial' => 'PodDzial' );
private static $has_many = array ('Produkt' => 'Produkt' );
Marka.php - brand of product- which belong to Dzial (has_one) and to Produkt (has_many)
class Marka extends DataObject {
...
private static $has_one = array( 'Dzial' => 'Dzial' );
private static $has_many = array ('Produkt' => 'Produkt');
I work on custom search for Produkt's on PodDzial page.
The code which doesn't work properly:
class Produkt extends DataObject {
...
public function getDefaultSearchContext() {
$fields = new FieldList(
DropdownField::create(
'MarkaID',
'Marka',
Marka::get()->map('ID','Title')
)->setEmptyString('--Wybierz marke--') ,
DropdownField::create(
'KategoriaID',
'Kategoria',
Kategoria::get()->filter(array('PodDzialID' => $this->PodDzialID))->map('ID','Title')
)->setEmptyString('--Wybierz kategorie--')
);
$filters = array(
'MarkaID' => new ExactMatchFilter('MarkaID'),
'KategoriaID' => new ExactMatchFilter('KategoriaID')
);
return new SearchContext(
$this->class,
$fields,
$filters
);
}
Both dropdown fields are working bad. They should list Kategoria's only from its PodDzial holder and Marka's from parent Dzial. Marka's field shows all Marka even from other categories now. Kategoria show nothing.
I use those same dropdowns on fieldlist. Kategoria's work ok. It shows only data from given PodDzial. Markas are on parent holder (Dzial) and I cant access it there too :
class Produkt extends DataObject {
....
$xxx = 'LEFT JOIN `SiteTree` ON `SiteTree`.ParentID=`Marka`.DzialID';
function getCMSFields(){
$fields = FieldList::create(
TabSet::create("Root",
Tab::create("Main",
....
DropdownField::create(
'MarkaID',
'Marka',
Marka::get()->filter($xxx, array('DzialID' => 'ParentID'))->map('ID','Title')
)->setEmptyString('-- None --'),
DropdownField::create(
'KategoriaID',
'Kategoria',
Kategoria::get()->filter(array('PodDzialID' => $this->PodDzialID))->map('ID','Title')
)->setEmptyString('-- None --')
Can anyone help?
I have two classes with a $many_many and $belongs_many_many relation between them. I tried to define $summary_fields in the class that contains the $many_many to display the relation between the classes but that column ('Column2.Column2') displays blank results. How do I set $summary_fields to display this data correctly?
Here is my code
class Table1 extends DataObject {
private static $db = array(
'Column1' => 'Varchar(32)'
);
private static $many_many = array (
'Column2' => 'Table2'
);
private static $summary_fields = array (
'Column1' => 'Column 1',
'Column2.Column2' => 'Column 2'
);
}
class Table2 extends DataObject {
private static $db = array(
'Column2' => 'Varchar(32)'
);
private static $belongs_many_many = array (
'Column1' => 'Table1'
);
}
The issue is a $many_many relationship or a $has_many relationship can be linked to multiple objects. We cannot put a $many_many or $has_many into $summary_fields as a single row as the GridField does not know how to display more than one item.
For example, say we have Columns.Title where Columns is a $many_many relationship on the current object. If we have three Columns objects linked to the current object, the system doesn't know to display the Title of the three columns.
What we can do is create a function to return a string displaying the data that we want to display.
The following code is written for Silverstripe 3.
class Table1 extends DataObject
{
private static $db = [
'Title' => 'Varchar(32)',
];
private static $many_many = [
'Columns' => 'Table2',
];
private static $field_labels = [
'ColumnsString' => 'Columns',
];
private static $summary_fields = [
'Title',
'ColumnsString',
];
public function ColumnsString()
{
return implode(', ', $this->Columns()->column('Title'));
}
}
class Table2 extends DataObject
{
private static $db = [
'Title' => 'Varchar(32)',
];
private static $belongs_many_many = [
'Columns' => 'Table1',
];
}
I've created a DataObject called Service and I've got a many_many relationship to RelatedServices as follows:
class Service extends DataObject {
private static $db = array (
'Name' => 'Varchar',
'Description' => 'Varchar',
);
private static $many_many = array (
'RelatedServices' => 'RelatedService'
);
public function getCMSFields() {
$fields = FieldList::create(TabSet::create('Root'));
$services = $this->get()->where("\"Service\".\"Name\" != '$this->Name'")->map('ID', 'Name')->toArray();
$fields->addFieldsToTab('Root.Main', array(
TextField::create('Name'),
TextField::create('Description'),
ListBoxField::create('RelatedServices', 'Related services')->setMultiple(true)->setSource($services)
));
return $fields;
}
}
and:
class RelatedService extends DataObject {
private static $db = array (
'Name' => 'Varchar',
);
private static $belongs_many_many = array (
'RelatedServices' => 'RelatedService'
);
}
This is being used in a ModelAdmin and the service works right including the related services text area, however it doesn't save. I did it previously that it was in a seperate tab in the CMS and had RelatedService have and admin section which looking through the DB looked like it worked however I thought it was an unnecessary section so tried to make it all in one and now no longer saves to the DB.
You don't need the 'RelatedService' class, as you are referencing to the class you are working in. So relating to the class 'Service' itself would make more sense.
The reason why your code won't work is because you have got your relations mixed up.
class Service extends DataObject{
private static $db = array (
'Name' => 'Varchar',
'Description' => 'Varchar',
);
private static $many_many = array (
'RelatedServices' => 'Service'
);
private static $belongs_many_many = array (
'ParentServices' => 'Service'
);
public function getCMSFields() {
$fields = FieldList::create(TabSet::create('Root'));
$services = $this->get()->where("\"Service\".\"Name\" != '$this->Name'")->map('ID', 'Name')->toArray();
$fields->addFieldsToTab('Root.Main', array(
TextField::create('Name'),
TextField::create('Description'),
ListBoxField::create('RelatedServices', 'Related services')->setMultiple(true)->setSource($services)
));
return $fields;
}
}
I have two user groups Administrator and Inhaltsautoren
My LandingPage has a Tab Teaser with a gridField. The normal user can not see the entries and i dont know why?
I cant find something for setting the permissions for Inhaltsautoren. Has someone an idea why there are no entries in the gridField?
Teaser.php
<?php
class Teaser extends DataObject {
private static $db = array (
'Title' => 'Varchar',
'Description' => 'HTMLText'
);
private static $has_one = array (
'Photo' => 'Image',
'Link' => 'Link'
);
private static $many_many = array(
'Tags' => 'Tag'
);
private static $summary_fields = array (
'GridThumbnail' => '',
'Title' => 'Titel',
'Description' => 'Beschreibung'
);
public function getGridThumbnail() {
if($this->Photo()->exists()) {
return $this->Photo()->SetWidth(100);
}
return "(no image)";
}
public function getCMSFields() {
$fields = FieldList::create(
TextField::create('Title'),
$tags = TagField::create('Tags','Tags',Tag::get(),$this->Tags()),
HTMLEditorField::create('Description', 'Beschreibung'),
LinkField::create('LinkID', 'Weiterleitung'),
$uploader = UploadField::create('Photo')
);
$tags->setShouldLazyLoad(true); // tags should be lazy loaded
$tags->setCanCreate(true); // new tag DataObjects can be created
$uploader->setFolderName('teaser');
$uploader->getValidator()->setAllowedExtensions(array('png','jpeg','jpg'));
return $fields;
}
}
and my LadingPage.php
$fields->addFieldToTab('Root.Teaser', $gridField = GridField::create(
'Teasers',
'Landing Page Teaser',
$this->Teasers(),
GridFieldConfig_RecordEditor::create()
));
$gridField->getConfig()->getComponentByType("GridFieldDataColumns")->setFieldCasting(array("Description"=>"HTMLText->BigSummary"));
Use canView() on your dataobject and check inside this function if your user is allowed to see this object or not.
public function canView($member = null) {
return Permission::check('ADMIN', 'any');
}