I try to make a simple letters storage:
Letter.php:
class Letter extends DataObject
{
private static $db = array (
'DateUpload' => 'Date',
'LetterNumber' => 'Text',
'Theme' => 'Text',
'Sender' => 'Text',
'SendTo' => 'Text'
);
private static $has_many = array (
'LetterFiles' => 'LetterFiles'
);
public function getCMSFields(){
$fields = FieldList::create (
TextField::create('Theme','Theme'),
DropdownField::create('Sender','Sender'),
...
$uploader = UploadField::create('FileName','Attached Files')
)
...
}
}
LetterFiles.php:
class LetterFiles extends File
{
private static $has_one = array (
'LetterOfFile' => 'Letter'
);
}
LetterAdmin.php:
class LetterAdmin extends ModelAdmin
{
private static $managed_models = array (
'Letter'
);
private static $menu_title = 'Letters';
private static $url_segment = 'letters';
}
But when creating a new letter in the admin interface I can't attach a file: I can upload it, but after pressing the button "Save" I can not see it in the "Attached Files" field.
Your UploadField should use the relation you have on your DataObject. In your case, that would be 'LetterFiles':
$uploader = UploadField::create('LetterFiles', 'Attached Files')
Another minor thing: I strongly suggest you don't use File subclasses for custom file-relations. It'll only work when you upload files directly, if you upload a file somewhere else in the CMS (eg. the assets-admin) and want to link them using the "Choose existing" dialog, then it'll fail.
I suggest you just delete the LetterFiles class and use a many_many relation on your DataObject. Example:
// Change from this…
private static $has_many = array (
'LetterFiles' => 'LetterFiles'
);
// … to this:
private static $many_many = array (
'LetterFiles' => 'File'
);
Related
In DataObjects, the getCMSFields method creates all the appropriate CMS Fields automatically (it is called scaffolding). However, in classes that extend SiteTree (i.e. Pages) this does not happen.
How can I use this form field scaffolding in Pages?
Besides calling DataObject::getCMSFields() as you already suggested in your own answer, it is also possible to instantiate a scafolder directly:
public function getCMSFields() {
// with tabs
$scaffolder = new FormScaffolder($this);
$scaffolder->restrictFields = ['Title', 'Content'];
$scaffolder->tabbed = true;
$fields = $scaffolder->getFieldList();
$fields->addFieldToTab('Root.Main', [
new MySpecialFieldWithCustomOptions('Links', 'My Links', $foobar),
]);
return $fields;
}
public function getCMSFields() {
// without tabs
$scaffolder = new FormScaffolder($this);
$scaffolder->restrictFields = ['Title', 'Content'];
$fields = $scaffolder->getFieldList();
$fields->push(
new MySpecialFieldWithCustomOptions('Links', 'My Links', $foobar)
);
return $fields;
}
This will work with any DataObject ($this has to be an instace of DataObject). Pages a subclass of DataObjects.
restrictFields is optional, if not provided, it will do all fields it can find.
We go back to DataObject and get the scaffolded fields:
use SilverStripe\ORM\DataObject;
use Page;
class MyPage extends Page
{
private static $db = [
'MyField' => 'Varchar',
];
private static $has_one = [
'MyRelation' => 'MyClass',
];
public function getCMSFields()
{
// fields from Page class
$fields = parent::getCMSFields();
// fields from DataObject class.
$fieldRepository = DataObject::getCMSFields();
$fields->addFieldsToTab(
'Root.MyExtraFields',
[
$fieldRepository->dataFieldByName('MyField'),
$fieldRepository->dataFieldByName('MyRelationID'),
]
);
return $fields;
}
}
I am working on a SilverStripe project. Basically, I updated my project to SilverStripe version 4.4.4. After the upgrade, I found out that the search/ filter forms of the ModelAdmin were changed as in the screenshot below.
What I am trying to do now is that I am trying to customize the fields of the search/ filter form of the ModelAdmin following this lesson. https://www.silverstripe.org/learn/lessons/v4/introduction-to-modeladmin-1.
I have a data object class that is linked to a model admin class. Following is the dummy code of the model admin class.
class EnquirySubmission extends DataObject
{
private static $db = [
//some hidden fields are here
];
private static $has_one = [
'Member' => Member::class
];
private static $summary_fields = [
'Member.Name' => 'Member',
//some hidden fields are here
];
//some hidden code goes here
public function searchableFields()
{
return [
'Member.Name' => [
'filter' => 'PartialMatchFilter',
'title' => 'Member',
'field' => \SilverStripe\Forms\DropdownField::create('Member.Name')
->setSource(
Member::get()->map('ID','Email')
)->setEmptyString('-- Member --')
],
];
}
}
As you can see in the code, I am customizing the filter/ search form by overriding the searchableFields method. But it does not work in the upgraded version of SilverStripe. What am I missing and how can I fix it?
Silverstripe and ModelAdmin are ace, but it is confusing why this date range search issue has required a tweak in every version so far. This is a complete example that I've just got working on 4.7.2 (latest stable at time of post)...
app/src/Test/MyDataObject.php
namespace MyVendor\MyNamespace;
use SilverStripe\Forms\DateField;
use SilverStripe\ORM\DataObject;
class MyDataObject extends DataObject {
private static $db = [
'Title' => 'Varchar',
'MyDateTimeField' => 'DBDatetime'
];
private static $summary_fields = ['Title','MyDateTimeField'];
public function updateAdminSearchFields($fields) {
$fields->removeByName('MyDateTimeField');//needed as added in summary field
$fields->push(DateField::create('MyDateTimeField:GreaterThanOrEqual', 'MyDateTimeField (Start)'));
$fields->push(DateField::create('MyDateTimeField:LessThanOrEqual', 'MyDateTimeField (End)'));
}
}
app/src/Test/MyAdmin.php
namespace MyVendor\MyNamespace;
use SilverStripe\Admin\ModelAdmin;
class MyAdmin extends ModelAdmin {
private static $menu_title = 'MyAdmin';
private static $url_segment = 'myadmin';
private static $managed_models = [MyDataObject::class];
public function getList() {
$list = parent::getList();
if ($params = $this->getRequest()->requestVar('filter'))
if ($filters = $params[$this->sanitiseClassName($this->modelClass)])
return $list->filter($filters);
return $list;
}
}
app/src/Test/MyAdminExtension.php
namespace MyVendor\MyNamespace;
use SilverStripe\ORM\DataExtension;
class MyAdminExtension extends DataExtension {
public function updateSearchContext($context) {
$class = $context->getQuery([])->dataClass();
if (method_exists($class, 'updateAdminSearchFields'))
(new $class)->updateAdminSearchFields($context->getFields());
return $context;
}
}
app/_config/mysite.yml
MyVendor\MyNamespace\MyAdmin:
extensions:
- MyVendor\MyNamespace\MyAdminExtension
I'm trying to use https://github.com/silverstripe-australia/silverstripe-gridfieldextensions/ to create a gridfield where I can add different types of dataobjects.
Sadly I can't figure out how to write the correct code for that on my class where I want the gridfield.
Could someone please point me in the right direction?
UPDATE:
Based on your answers, I now have the following structure
class ModularPage extends Page {
private static $has_many = array(
'Sections' => 'MP_Section',
'Galleries' => 'MP_Gallery',
'Paragraphs' => 'MP_Paragraph'
);
public function getCMSFields() {
...
$fields->addFieldToTab('Root.Main', $mutli_grid = GridField::create('Sections', 'Sektionen', $this->Sections(), MultiClassGrid::create(15)));
...
}
}
class MP_Section extends DataObject {
private static $has_one = array(
'Section' => 'MP_Section',
'ModularPage' => 'ModularPage'
);
}
class MP_Gallery extends MP_Section {
private static $has_one = array(
'Section' => 'MP_Section',
'ModularPage' => 'ModularPage'
);
}
So far, so good? Is this right until now?
Cause If I want to add for example a gallery, I receive the following error
[User Error] Couldn't run query: SELECT DISTINCT "MP_Section"."ID", "MP_Section"."SortID" FROM "MP_Section" LEFT JOIN "MP_Gallery" ON "MP_Gallery"."ID" = "MP_Section"."ID" LEFT JOIN "MP_Paragraph" ON "MP_Paragraph"."ID" = "MP_Section"."ID" WHERE ("ModularPageID" = '13') ORDER BY "MP_Section"."SortID" ASC LIMIT 9223372036854775807 Column 'ModularPageID' in where clause is ambiguous
Here is how I usually setup my GridField:
$c = GridFieldConfig_RelationEditor::create();
$c->removeComponentsByType('GridFieldAddNewButton')
->addComponent(new GridFieldAddNewMultiClass())
;
$c->getComponentByType('GridFieldAddNewMultiClass')
->setClasses(array(
'SectionThemesBlock' => SectionThemesBlock::get_section_type(),
'SectionFeaturedCourse' => SectionFeaturedCourse::get_section_type(),
'SectionCallForAction' => SectionCallForAction::get_section_type(),
'SectionContactSheet' => SectionContactSheet::get_section_type()
//....
));
$f = GridField::create('Sections', "Sections", $this->Sections(), $c);
$fields->addFieldToTab("Root.Sections", $f);
Based on the GridFieldConfig_RelationEditor, just remove GridFieldAddNewButton then add GridFieldAddNewMultiClass. Then configure the component to know which classes to have available in the dropdown to create. All those SectionThemesBlock, SectionFeaturedCourse etc extend a common Section dataObject as base. The get_section_type() function is a custom static function on the Section dataobject to get a nice looking name in the dropdown and not have to type it manually all the time....
The basics of the Section dataobject looks like so:
class Section extends DataObject {
public static function get_section_type()
{
return trim(preg_replace('/([A-Z])/', ' $1', str_ireplace('Section', '', get_called_class())));
}
//...
}
And the page where that gridField goes and that has the relation defined on:
class Page extends SiteTree {
//...
private static $has_many = array(
'Slides' => 'Slide'
);
//...
}
Something like this should work
$config = new GridFieldConfig_RecordEditor();
$config->addComponent(new GridFieldAddNewMultiClass());
...
$grid = GridField::create('Grid', 'Grid', $this->GalleryItems(), $config);
You need three DataObjects:
GalleryItem extends DataObject{}
FooGalleryItem extends GalleryItem{}
BarGalleryItem extends GalleryItem{}
In SilverStripe 3 I have two related DataObjects, Order and OrderItem. Order has many OrderItems. OrderItem has one Order.
I am managing Order with ModelAdmin.
I can create a new OrderItem but when it tries to load I get the following error:
SELECT DISTINCT "OrderItem"."ClassName", "OrderItem"."Created", "OrderItem"."LastEdited", "OrderItem"."ItemQuantity", "OrderItem"."ItemDiscount", "OrderItem"."OrderID", "OrderItem"."ProductID", "OrderItem"."ID", CASE WHEN "OrderItem"."ClassName" IS NOT NULL THEN "OrderItem"."ClassName" ELSE 'OrderItem' END AS "RecordClassName", "Product"."Title"
FROM "OrderItem"
WHERE ("OrderID" = '9') AND ("OrderItem"."ID" = 11)
ORDER BY Product.Title ASC
LIMIT 1
Unknown column 'Product.Title' in 'field list'
Here is my code:
class Order extends DataObject {
public static $db = array(
'OrderDate'=>'Date',
'FulfilledDate'=>'Date',
'OrderStatus'=>'Enum("New, InvoiceRequested, InvoiceSent, Paid, Cancelled")',
'ShippingStatus'=>'Enum("Unshipped, Shipped")'
);
public static $has_one = array(
'Customer' => 'Customer'
);
public static $has_many = array(
'OrderItems' => 'OrderItem'
);
// ...
}
class OrderItem extends DataObject {
public static $db = array(
'ItemQuantity'=>'Int',
'ItemDiscount'=>'Decimal'
);
public static $summary_fields = array(
'Product.Title',
'ItemQuantity',
'ItemDiscount'
);
public static $has_one = array(
'Order' => 'Order',
'Product' => 'Product'
);
// ...
}
Any thoughts on how can I add a join in ModelAdmin to the Product object/table?
Edit
I have found the problem I had.
public static $default_sort = array('Product.Title');
Removing that fixed the issue.
Just so there is one less unanswered question, #MilesParker edited into the question:
I have found the problem I had.
public static $default_sort = array('Product.Title');
Removing that fixed the issue.
This line would have been on the OrderItem class as that is what the SQL error shows. The issue could have been caused by the lack of a dev/build however it may have also been due to a bug in that specific version of Silverstripe 3.0.
I'm having trouble with how to do something similar to this http://www.silverstripe.org/archive/show/2431. Basically I want the user to be able to create content and have their ID put into the database with the new content. I'm sorry if this is fairly obvious. I am a little stumped though. I am having trouble wrapping my head around how it will actually work. I know I can retrieve the current user using the following code but I'm not sure where to go from there.
$currentUser = Member::currentUser();
In SilverStripe usually the 3rd argument to a FormField is the value, so for example a TextField has the following arguments:
new TextField($name = 'myField', $title = 'Please write something in my Field', $value = "yay");
but this would not work in the CMS (at least in SilverStripe 2 if you are using a Page, not sure on DataObject) because SilverStripe overwrites all values when it tries to populate the Form with the values of the current object
so you have several alternatives, the 2 easiest alternatives are:
class MyContentObject extends DataObject {
public static $db = array(
'Text' => 'HTMLText',
);
public static $has_one = array(
'Member' => 'Member',
)
public function getCMSFields() {
$fields = new FieldSet();
$fields->push(new Textarea('Text', 'Text'));
if (!$this->MemberID)
$this->MemberID = Member::currentUserID();
$fields->push(new HiddenField('MemberID'));
return $fields;
}
}
And 2nd option, which is way better in this case, you don't even need a hidden field, you can just set the MemberID right before the record gets written to database by using onBeforeWrite:
class MyContentObject extends DataObject {
public static $db = array(
'Text' => 'HTMLText',
);
public static $has_one = array(
'Member' => 'Member',
)
public function getCMSFields() {
$fields = new FieldSet();
$fields->push(new Textarea('Text', 'Text'));
return $fields;
}
public function onBeforeWrite() {
// this method will be called every time the object gets saved
parent::onBeforeWrite();
if (!$this->MemberID)
$this->MemberID = Member::currentUserID();
}
}