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
Related
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?
after looking at the documentation for the built-in CSV importing, it's still not clear to me how to add a custom CsvBulkUploader to ModelAdmin. I see how you can easily add the default uploader and how you can create a custom controller for importing but it's not clear to me how you would add this to a ModelAdmin. I've spent the morning looking through Stack Overflow and the SilverStripe community forums, but haven't been able to find anything yet. Any direction would be greatly appreciated!
I figured it out.
You can add the CSV Bulk Loader to your ModelAdmin by declaring it in $model_importers:
<?php
class PlayerAdmin extends ModelAdmin {
private static $managed_models = array(
'Player'
);
private static $model_importers = array(
'Player' => 'CsvBulkLoader',
);
private static $url_segment = 'players';
}
?>
And as indicated in the CSV Import documentation, you can extend the CsvBulkLoader class. For example:
<?php
class PlayerCsvBulkLoader extends CsvBulkLoader {
public $columnMap = array(
'Number' => 'PlayerNumber',
...
);
public $duplicateChecks = array(
'Number' => 'PlayerNumber'
);
public $relationCallbacks = array(
'Team.Title' => array(
'relationname' => 'Team',
'callback' => 'getTeamByTitle'
)
);
public static function getTeamByTitle(&$obj, $val, $record) {
return FootballTeam::get()->filter('Title', $val)->First();
}
}
?>
In the documentation what wasn't made explicit was that you pull in the new extended Bulk Loader by simply adding it to $model_importers in your ModelAdmin. So now instead of using CsvBulkLoader, you'll be using PlayerCsvBulkLoader. The snippet up top would be revised thusly:
<?php
class PlayerAdmin extends ModelAdmin {
private static $managed_models = array(
'Player'
);
private static $model_importers = array(
'Player' => 'PlayerCsvBulkLoader',
);
private static $url_segment = 'players';
}
?>
Fairly simple. I had tried this approach early on, but had misspelled the name of the subclass!
UPDATE: Just added this to SilverStripe's documentation
Like superficial descriped in SilverStripe Docs, I'm trying to set a custom controller for my homepage.
I changed the default homepage link to 'custom-home' and added those two routes.
The second one, with the path in it works and directs me to my controller. The first (empty) one just sends me to an 404-error page.
Couldn't figure out how to fix that. Any suggestions?
routes.yml
Director:
rules:
'': 'MyHome_Controller'
'custom-home': 'MyHome_Controller
_config.php
RootURLController::set_default_homepage_link('custom-home');
MyHome_Controller.php
<?php
class MyHome_Controller extends Page_Controller {
private static $allowed_actions = [];
private static $url_handlers = [];
public function init() {
parent::init();
}
public function Link($action = null) {
return Director::baseURL() . 'custom-home';
}
public function index() {
$data = [
'Title' => 'Hello World',
'ClassName' => __CLASS__,
];
return $this
->customise($data)
->renderWith([__CLASS__, 'Page']);
}
}
I believe the way the empty route (RootURLController) works is that you're telling it the URLSegment of a page in the CMS that should resolve to the root URL. So I think what you need to do is go into the CMS and change the URLSegment of your CustomHomePage to 'custom-home'.
I've been trying to make an extension to add some function to the CMS. As it's a setting for the CMS I've added it to the settings tab. While I can take values and save them I needed an action on the page to synchronise a system and I can't get my action to be called, here is my code.
private static $db = array(
'Path' => 'Varchar(50)',
);
private static $allowed_actions = array (
'update',
);
public function updateCMSFields(FieldList $fields)
{
$fields->addFieldsToTab('Root.Importer', array(
ImporterPathField::create('Path', 'Path')->setDescription('Path to area'),
FormAction::create('update', 'Synchronise')
));
}
public function update() {
SS_Log::add_writer(new SS_LogEmailWriter('test#example.com'), SS_Log::ERR);
}
It doesn't get called. If I need to add the function to the left nav rather than part of the settings I'm ok with that too but I also tried that with even less success. Is it possible to get the action called on button press?
You need to place the $allowed_actions and the update method in an extension for CMSSettingsController. Also you should probably put the FormAction into the CMSActions list.
Here's how I would do this:
SiteConfigExtension.php
class SiteConfigExtension extends DataExtension
{
private static $db = array(
'Path' => 'Varchar(50)',
);
public function updateCMSFields(FieldList $fields)
{
$fields->addFieldsToTab('Root.Importer', array(
ImporterPathField::create('Path', 'Path')->setDescription('Path to area')
));
}
public function updateCMSActions(FieldList $actions)
{
$actions->push(
FormAction::create('update', 'Synchronise')
);
}
}
CMSSettingsControllerExtension.php
class CMSSettingsControllerExtension extends DataExtension
{
private static $allowed_actions = array (
'update',
);
public function update() {
SS_Log::add_writer(new SS_LogEmailWriter('test#example.com'), SS_Log::ERR);
}
}
I have a model:
class Service extends CActiveRecord
{
public static function model($className = __CLASS__)
{
return parent::model($className);
}
public static function getMainPageItems()
{
return self::model()->findAll(array(
'condition' => 'on_main = 1',
'order' => 'pos ASC'
));
public static function getNonMainPageItems()
{
return self::model()->findAll(array(
'condition' => 'on_main = 0',
'order' => 'pos ASC'
));
}
I want to set the model's default order to pos ASC.
How can I set the model's default order?
Use CActiveRecord::defaultScope() method as follows:
class Service extends CActiveRecord
{
...
public function defaultScope(){
return array(
'order'=>'pos ASC'
);
}
...
}
This will be added for all find methods on the model. Read on scopes and defaultScopes for more information
One way to go about this is to add a private member to the parent class called $order.
You would need to create getters and setters for it (so that you can change the defalut when you want)
Then simply call $this->$order in the 'order' element in each function that requires it.