php merge class variables instead of replacing them - php

i have multiple php classes
// a Base class
abstract class Base_Page {
protected static $config = array(
'status' => 'int',
);
}
// an inheriting class
class Page extends Base_Page{
protected static $config = array(
'title' => 'varchar',
'description' => 'text',
);
// and one more level of inheritance
class Page_Redirect extends Base_Page {
protected static $config = array(
'href' => 'http://domain.com',
);
}
now id'd like to do this:
$page_redirect = new Page_Redirect();
$page_redirect->getConfig(); // which i assume to be implemented (this is my problem)
// should return:
// array(
// 'status' => 'int',
// 'title' => 'varchar',
// 'description' => 'text',
// 'href' => 'http://domain.com',
// )
Due to the fact that the variable gets overwrote by the extending class a dont't get how to accomplish this. Thanks for your look at it.

You cannot do this with a bare property. It would be much better to use methods instead:
abstract class Base_Page {
protected function getConfig() {
return array('status' => 'int');
}
}
// an inheriting class
class Page extends Base_Page{
protected function getConfig() {
return array(
'title' => 'varchar',
'description' => 'text',
) + parent::getConfig();
}
}
// and one more level of inheritance
class Page_Redirect extends Base_Page {
protected function getConfig() {
return array(
'href' => 'http://domain.com',
) + parent::getConfig();
}
}
Of course now you have lost the ability to get the configuration statically, but it's highly likely that this does not matter. If it does (i.e. you need to know the configuration without having an instance at hand, and it is meaningless to create one on a whim) then the code needs further refactoring.

<?php
// a Base class
abstract class Base_Page {
protected static $config = array(
'status' => 'int',
);
}
// an inheriting class
class Page extends Base_Page {
protected static $config = array_merge(
parent::$config,
array(
'title' => 'varchar',
'description' => 'text',
)
);
}
Try something like this.

Related

SilverStripe 3.4: How to add default records to db from model

Unable to locate in the SilverStripe Documentation how to have a DataObject Model inject a collection of default records on /dev/build
Anybody able to point me in the right direction
This is what I currently have, and obviously I would like to inject pre-configured options into this aptly named Configuration model for my Module.
class Configuration extends DataObject
{
private static $db = array(
'Option' => 'Varchar',
'Value' => 'Varchar'
);
private static $summary_fields = array(
'Option' => 'Option',
'Value' => 'Value',
);
}
Thanks in advance for any direction/pointers.
UPDATE
I was turned onto SiteConfig by #Barry below
However in following his practice, requireDefaultRecords() is not injecting defaults
Note: I have since revisited /dev/build?flush
class RMSConfiguration extends DataExtension
{
private static $db = array(
'username' => 'Varchar',
'password' => 'Varchar',
'agent_id' => 'Varchar(15)',
'client_id' => 'Varchar(15)',
'testMode' => 'Int(1)',
'timezone' => 'Varchar',
'apiUrl' => 'Varchar(255)'
);
public function updateCMSFields(FieldList $fields)
{
$fields->addFieldsToTab(
"Root.RMSConfig",
array(
TextField::create('username', 'RMS Username'),
TextField::create('password', 'RMS Password'),
TextField::create('agent_id', 'RMS Agent ID'),
TextField::create('client_id', 'RMS Client ID'),
TextField::create('apiUrl', 'API Url'),
CheckboxField::create("testMode", 'Toggle Test Mode'),
DropdownField::create("timezone", 'Timezone', static::$timezones)
)
);
}
public function requireDefaultRecords()
{
parent::requireDefaultRecords();
$arrOptions = array(
'timezone' => 'Australia/Sydney',
'apiUrl' => 'https://api.example.com.au/',
'testMode' => 0
);
foreach ($arrOptions as $strOption => $strValue) {
if (!$configuration = self::get()->filter('Option', $strOption)->first()) {
$configuration = self::create(array( 'Option' => $strOption ));
}
$configuration->Value = $strValue;
$configuration->write();
}
}
/**
* List of timezones supported by PHP >=5.3.x
*
* #var array
*/
public static $timezones = array(
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
"Africa/Algiers",
...
...
"Zulu"
);
}
Using the function requireDefaultRecords in the DataObject - this is called during every dev/build.
Note: First check if the option exists to prevent duplicates as this will be called every time you dev build.
class Configuration extends DataObject {
private static $db = array(
'Option' => 'Varchar',
'Value' => 'Varchar'
);
private static $summary_fields = array(
'Option' => 'Option',
'Value' => 'Value',
);
function requireDefaultRecords() {
parent::requireDefaultRecords();
$arrOptions = array(
'Option1' => 'Value1',
'Option2' => 'Value2',
'Option3' => 'Value3',
);
foreach ($arrOptions as $strOption => $strValue) {
if (!$configuration = Configuration::get()->filter('Option',$strOption)->first())
$configuration = Configuration::create(array('Option' => $strOption));
$configuration->Value = $strValue;
$configuration->write();
}
}
}
One final comment is that there is a module for SiteConfig which is used by SilverStripe, most modules and where I would recommend you put configuration values like this instead.
If you do choose SiteConfig then please see the function populateDefaults and documentation for it's use, this is an example...
/**
* Sets the Date field to the current date.
*/
public function populateDefaults() {
$this->Date = date('Y-m-d');
parent::populateDefaults();
}
(if the above is used in an extensions it might need $this->owner->Date instead of $this->Date)
The above function isn't needed if all the values are static, instead it will read them just from this array (again within DataObject)
public static $defaults = array(
'Option1' => 'Value1',
'Option2' => 'Value2'
);
This works on any DataObject as well, but as SiteConfig manages one record and this populates that record once upon creation this is much more convenient for to use instead of requireDefaultRecords.

Saving to a many_many relationship with a ListBoxField

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;
}
}

How to load select option from database in zf2

I Have been following zf2 guide for blog I have created everything Controller, Factory, Form, Mapper, Model, Service, view etc
In my form I have a select element
$this->add(array(
'type' => 'select',
'name' => 'roleId',
'attributes' => array(
'id' => 'roleId',
'options' => array(
'1' => 'Admin',
'2' => 'Manager',
),
),
'options' => array(
'label' => 'Role',
),
));
Now in this form I want to load the option for the role from the database.
I tried loading the option by creating a simple function, which can be accessed in the element as below, but Am not able to fetch the result. I have already created Controller, Factory, Form, Mapper, Model, Service and view, Where I can do CRUD operation on Role.
$this->add(array(
'type' => 'select',
'name' => 'roleId',
'attributes' => array(
'id' => 'roleId',
'options' => $this->getAllRoles(),
),
'options' => array(
'label' => 'Role',
),
));
public function getAllRoles()
{
$roles = $this->getServiceLocator()->get('Admin\Service\RoleService');
$allRoles = $this->getAllTheRoles();
return $allroles;
}
Can anybody guide me how can I load all the Roles in option as listed in the IndexAction following Blog Post with ID and Name of the Role.
You could create a reusable form element that is pre-populated with the roles. To do so you must register the service with the form element manager in module.config.php.
return [
'form_elements' => [
'factories' => [
'RoleSelect' => 'MyModule\Form\Element\RoleSelectFactory',
],
],
];
There is not need to extend the standard select class as the changes are configuration only. This is best done in a factory class.
namespace MyModule\Form\Element;
use Zend\Form\Element\Select;
class RoleSelectFactory
{
public function __invoke($formElementManager, $name, $rname)
{
$select = new Select('role_id');
$select->setOptions(['label' => 'Role']);
$select->setAttributes(['id' => 'role_id']);
$serviceManager = $formElementManager->getServiceLocator();
$roleService = $serviceManager->get('Admin\Service\RoleService');
$options = [];
foreach($roleService->getAllTheRoles() as $role){
$options[$role->getId()] => $role->getName();
}
$select->setValueOptions($options);
return $select;
}
}
Adding the element within a form can then be updated to use the name of the service we registered.
$this->add([
'name' => 'role_id'
'type' => 'RoleSelect',
]);
One important point to remember is that the form using this element must be created using the $formElementManager->get('FormWithRoleSelect').
Finally found out the simple way to do this, Am really not sure if this is the correct way.
Added the Role service in the User Controller
Code in my userController.php
use Admin\Service\RoleServiceInterface;
class UserController extends AbstractActionController
{
/**
* #var \Admin\Service\UserServiceInterface
*/
protected $userService;
protected $roleService;
protected $userForm;
public function __construct(
UserServiceInterface $userService,
RoleServiceInterface $roleService,
FormInterface $userForm
)
{
$this->userService = $userService;
$this->roleService = $roleService;
$this->userForm = $userForm;
}
public function addAction()
{
$request = $this->getRequest();
$roles = $this->roleService->findAllRoles();
foreach ($roles as $role) {
if($role->getStatus() == 1) {
$allRoles[$role->getId()] = $role->getName();
}
}
$this->userForm->__construct(null, array('roleOptions'=>$allRoles));
}
}
My UserControllerFactory.php
<?php
namespace Admin\Factory;
use Admin\Controller\UserController;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class UserControllerFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
*
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$realServiceLocator = $serviceLocator->getServiceLocator();
$userService = $realServiceLocator->get('Admin\Service\UserServiceInterface');
$roleService = $realServiceLocator->get('Admin\Service\RoleServiceInterface');
$userInsertForm = $realServiceLocator->get('FormElementManager')->get('Admin\Form\UserForm');
return new UserController(
$userService,
$roleService,
$userInsertForm
);
}
}
Finally the UserForm.php
<?php
namespace Admin\Form;
use Zend\Form\Form;
use Admin\Model\User;
use Zend\Stdlib\Hydrator\ClassMethods;
use Zend\Form\Element\Select;
class UserForm extends Form
{
public function __construct($name = null, $options = array())
{
$roleOptions = array();
if($options) {
$roleOptions = $options['roleOptions'];
}
parent::__construct($name, $options);
$this->setHydrator(new ClassMethods(false));
$this->setObject(new User());
$this->add(array(
'type' => 'hidden',
'name' => 'id'
));
$this->add(array(
'type' => 'select',
'name' => 'roleId',
'attributes' => array(
'id' => 'roleId',
'options' => $roleOptions
),
'options' => array(
'label' => 'Role',
),
));
}
}
This way using service manager I was successfully able to load the data in my for Select option.
Call getAllRoles() inside the controller then You can pass your custom array as parameter for form when you create form object. In form __construct function you can retrieve that array and set like this
'options' => $roles,

Adding dynamic form field options to FuelPHP model properties

I'm creating a select form element within FuelPHP's model _properties variable:
protected static $_properties = array(
'category_id' => array(
'data_type' => 'int',
'label' => 'Category',
'form' => array(
'type' => 'select',
'options' => array()
)
)
);
I want to set [category_id][form][options] to the result of an SQL query, however this obviously cannot be done within the class declaration and I've tried modifying the variable from with __construct(), this code is below but yielded errors.
function _construct() {
parent::__construct();
self::$_properties['category_id']['form']['options'] = array('a');
}
My question is, how do I set the field options to something dynamic using FuelPHP?
You are almost there. Fuel provides a static constructor called init that will allow you to assign static properties.
function _init() {
parent::_init();
self::$_properties['category_id']['form']['options'] = array('a');
}

not found class $has_many through phpactiverecord

I have a many to many relationship between Profesional and Plan so I wrote the following code:
Profesional.php
class Profesional extends BaseModel {
static $table_name = 'profesionales';
static $primary_key = 'idprofesional';
static $has_many = array(
array(
'_planes',
'class_name' => 'Plan',
'through' => '_profplanes'
),
array(
'_profplanes',
'class_name' => 'ProfPlan',
'foreign_key' => 'profesional'
)
);
}
profplan.php
class ProfPlan extends BaseModel{
static $table_name = 'profplanes';
static $belongs_to = array(
array(
'_plan',
'class_name' => 'Plan',
'foreign_key' => 'plan'
),
array(
'_profesional',
'class_name' => 'Profesional',
'foreign_key' => 'profesional'
)
);
}
plan.php
class Plan extends BaseModel {
static $table_name = 'planes';
static $primary_key = 'idplan';
static $has_many = array(
array(
'_profplanes',
'class_name' => 'ProfPlan',
'foreign_key' => 'plan'
)
);
}
But I get Class Profplane does not exist. Where is the error? What am I doing wrong?
Fix the class / table spellings! it'll be easy to fix early in your code and becomes a maintenance nightmare later on =P! "Plan->Plans' 'Profesional=>Professional', "Professionales->"Professionals".
Honestly the through relationship code in phpactiverecord is annoying, fickle, and really needs to be looked at in general.
You should show how you're actually calling the code, but I'm going to assume you're doing
Professional::find(1)
$professional->_plans, //Error is thrown here
You need to set up your code so that ProfPlan->_plans(or _planes in your case), is definded, and not just ProfPlan->_plan. Phpactiverecord needs to explicitly know the relationship it's going to go through. Let me know if you need a more clear explanation.

Categories