In Silverstripe 4, a standard "Add" button of a GridField does nothing - php

I'm trying to use a GridField component for the first time.
I've added an 'Add' button using GridFieldConfig_RecordEditor as followings:
class AdvertisersPage extends Page
{
//...
public function getCMSFields()
{
$fields = parent::getCMSFields();
$advertiserAccounts = AdvertiserAccount::get();
$fields->addFieldToTab('Root.Advertisers',
$gridField = new GridField(
'Advertisers',
'All advertisers',
$this->Advertisers(),
GridFieldConfig_RecordEditor::create()
)
);
return $fields;
}
}
But the button doesn't do anything besides changing the URL in the browser from
admin/pages/edit/show/7
to
admin/pages/edit/EditForm/7/field/Advertisers/item/new
Here is the related DataObject class:
class AdvertiserAccount extends DataObject
{
//...
private static $has_one = [
'AdvertisersPage' => AdvertisersPage::class,
];
public function getCMSFields()
{
$fields = FieldList::create(
TextField::create('contactNumber'),
TextField::create('nickname')
);
return $fields;
}
}
I need your advice on what should I look at to fix the issue.

It is an extremely strange bug.
I've made some experiments and renamed the related classes. I found that the problem only appears when first GridField constructor parameter name is one of the followings: 'Adv', 'Advert', 'Advertise', 'Advertiser' and 'Advertisers'. In the project, I haven't got any classes with such names. Full-text search through all the project's files hasn't gave any results.
So, I've solved the problem just by renaming the parameter, but what was that?

Related

SilverStripe ModelAdmin

I am new to silverstripe framework and trying to fetch a list of menu in the admin panel.
I found lots of example to show menu on front-end by Menu(1) and Menu(2) etc. but did not get any sample code to fetch same menu array in admin model.
The code I tried is:
public function getCMSfields() {
$fields = FieldList::create(TabSet::create('Root'));
$fields->addFieldsToTab('Root.Main', array(
TextField::create('Name'),
DropdownField::create('URL')
->setSource(SiteTree::get()),
));
return $fields;
}
ModelAdmin is mainly there to manage DataObjects and not Pages. Have a look at the Docs and this Lesson to learn more about ModelAdmin.
But if you want to manage pages in a ModelAdmin, you could do it like that
class MyPageAdmin extends ModelAdmin {
...
...
private static $managed_models = array(
'Page'
);
public function getList() {
$list = parent::getList();
if($this->modelClass == 'Page'){
$list = $list->filter('ParentID', '1');
}
return $list;
}
}
To manage only children from a specific page, use the getList() function and filter your list after your needs.
There is also (albiet for an older version) this tutorial http://www.ssbits.com/tutorials/2010/dataobjects-as-pages-part-2-using-model-admin-and-url-segments-to-create-a-product-catalogue/ from ssbits

Custom backend FormAction handler not found

My goal is it to add a custom FormAction to a DataObjects EditForm inside a ModelAdmin, next to the "Save" and "Delete" action.
The setup:
With updateCMSActions I can add that button.
class MyActionEventExtension extends DataExtension {
public function updateCMSActions(FieldList $actions) {
if($this->owner->canEdit(Member::currentUser())) {
$actions->push(FormAction::create('doMyAction', 'Action'));
}
return $actions;
}
}
This works perfectly fine.
Following this answer on stackoverflow I created a LeftAndMainExtension for the actions handler.
class MyActionLeftAndMainExtension extends LeftAndMainExtension {
private static $allowed_actions = array(
'doMyAction'
);
public function doMyAction($data, $form) {
// Do stuff.
// ... I never get here.
// Return stuff
$this->owner->response->addHeader(
'X-Status',
rawurlencode('Success message!')
);
return $this->owner->getResponseNegotiator()
->respond($this->owner->request);
}
}
The corresponding config.yml file looks like this:
LeftAndMain:
extensions:
- MyActionLeftAndMainExtension
TheDataObject:
extensions:
- MyActionEventExtension
The Problem:
When I click the button, the response gives me "404 Not Found".
The requested URL always is the same:
http://localhost/admin/model-admin-url-slug/TheDataObject/EditForm/field/TheDataObject/item/878/ItemEditForm
Some other solutions I found, suggested to extend the ModelAdmins GridField. This sadly is not an option, since the DataObject I need that action for has alot of relations, which means, it's EditForm also appears in other DataObjects EditForms (nested).
I'm really running out of ideas. Did I miss something within my ModelAdmin? The one I created only implements the basic static vars, so I didn't posted it here.
Any help would be great!
Update:
I ended up, providing a getEditForm method on my ModelAdmin.
public function getEditForm($id = null, $fields = null) {
$form = parent::getEditForm($id, $fields);
$listField = $form->Fields()->fieldByName($this->modelClass);
if ($gridField = $listField->getConfig()->getComponentByType('GridFieldDetailForm')) {
$gridField->setItemRequestClass('MyAdminForm_ItemRequest');
}
return $form;
}
and extending the GridFieldDetailForm_ItemRequest:
class MyAdminForm_ItemRequest extends GridFieldDetailForm_ItemRequest {
private static $allowed_actions = array (
'edit',
'view',
'ItemEditForm'
);
public function ItemEditForm() {
$form = parent::ItemEditForm();
$formActions = $form->Actions();
// Adds all FormActions provided by the model's `getCMSActions` callback
if ($actions = $this->record->getCMSActions()) {
foreach ($actions as $action) {
$formActions->push($action);
}
}
return $form;
}
public function doAction($data, $form) {
// do stuff here
}
}
Sadly this doesn't add an action on has_many or many_many relation gridfields.. and because of that, I'll leave the question opened and unanswered. Maybe sometime there will be a better solution.. :)
One very simple answer to this question (if it's an option for you) is to use the Better Buttons module: https://github.com/unclecheese/silverstripe-gridfield-betterbuttons#creating-a-custom-action
It lets you define the actions on the model, which is a bit questionable from an architecture standpoint, but also works pretty well in the context of Silverstripe and ModelAdmin.

Silverstripe - return an array of pages selected with TreeMultiselectField

I am trying to use TreeMultiselectField to associate many pages to one page in Silverstripe. I was able to do that, the CMS saves them. But now I need to be able to display the pages I selected on the template. How do I do that?
Here's my code so far
class FundsAndPerformancePage extends Page {
public static $many_many = array(
"Funds" => "SiteTree",
"Information" => "SiteTree"
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Main', new TreeMultiselectField('Funds','Select pages for section "Our Funds"', 'SiteTree'));
$fields->addFieldToTab('Root.Main', new TreeMultiselectField('Information','Select pages for section "More Information"', 'SiteTree'));
return $fields;
}
public function Funds(){
//Need to return an array of pages selected with TreeMultiselectField
}
}
class FundsAndPerformancePage_Controller extends Page_Controller {
}
if you want to loop the related Pages in a template, you don't have to define a getter method yourself.
<% loop $Funds %> should work by default, by using the default (magic) getter method ->Funds()
If you need an array anyway, you could do something like this:
public function FundsAsArray(){
return $this->Funds()->toArray();
}
as the getter method returns an instance of ManyManyList, which would have an toArray method inherited from DataList
see http://api.silverstripe.org/3.0/source-class-DataList.html#_toArray

How to add extra summary_field in Silverstripe when extending an object?

I'm wanting to extend an existing Silverstripe module (Swipestripe) where Attribute has_many Options.
The following code successfully extends Option so a Cost is added to each Option.
class OptionCost extends DataExtension {
private static $db = array(
'Cost' => 'Decimal(19,4)'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Main', new PriceField('Cost'));
return $fields;
}
}
However when viewing the options via the parent Attribute the Cost is not display. This is controlled via the $summary_fields static but I can't work how to add Cost as a new summary field.
I've tried adding the following code to OptionCost, and to an extension of Attribute - but neither method worked.
private static $summary_fields = array(
'Cost' => 'Cost'
);
What is the correct approach to add Cost to the summary_fields table?
Thank you in advance for any advice.
In Silverstripe 3.1 adding fields to $summary_fields in the extension is the correct way to do this.
The following code worked for me:
class OptionCost extends DataExtension {
private static $db = array(
'Cost' => 'Decimal(19,4)'
);
private static $summary_fields = array(
'Cost'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Main', new PriceField('Cost'));
return $fields;
}
}
Declare the extension in your config or in a config yaml file.
config.yml
...
Attribute:
extensions:
- OptionCost
...
Run a dev/build?flush=all.
Also make sure you call ?flush=all on your admin page.

Self-referential Relationships in PHP Active Record

I am trying to create a tree of pages using PHP Active Record and I seem to be having trouble getting it setup to work correctly.
Here is the code I am using for the Page class:
class Page extends ActiveRecord\Model {
static $belongs_to = array(array('parent_page', 'class_name' => 'Page'));
static $has_many = array(
array('pages')
);
public static function get_top_level_pages() {
return Page::all(array('conditions' => 'parent_page_id = 0'));
}
}
My database table called Pages has the following columns inside:
id
parent_page_id
type
title
meta_title
content
Does anyone know what I am doing wrong here?
I am not sure if this is the "most correct" way to link a class to itself, but it seems to work for me.
First I changed the parent_page_id in the table to just be page_id and I made the class come together by using the following class:
class Page extends ActiveRecord\Model {
static $has_many = array(
array('pages')
);
static $belongs_to = array(array('page', 'class_name' => 'Page'));
public static function get_top_level_pages() {
return Page::all(array('conditions' => 'page_id = 0'));
}
public function get_parent() {
return $this->page;
}
public function get_children() {
return $this->pages;
}
}
I created the get_parent() and get_children() functions because ->page and ->pages did not make sense to me and the functions help clear that up.
I am open to a better or "more correct" solution.
JP
I was also struggling with this (annoying issue).
You where missing the foreign_key (this will simply point to your column name).
I've modified your code and pasted it here below:
class Page extends ActiveRecord\Model {
//make sure that you define the 'foreign_key'
static $belongs_to = array(
array('parent_page', 'class_name' => 'Page', 'foreign_key' => 'parent_page_id')
);
static $has_many = array(
array('pages')
);
public static function get_top_level_pages() {
return Page::all(array('conditions' => 'parent_page_id = 0'));
}
}
$parent_page = Page::find(20)->parent_page; // this works fine

Categories