CakePHP - Set Default Field Values in the Model - cakephp

How do you set a default value for a field in a model?
EDIT:
I have tried the method using _schema as suggested, but the default value is not being used.
public $_schema = array(
'newsletter' => array(
'default' => 1
),
);

you should always try to set default values from the controller:
http://www.dereuromark.de/tag/default-values/

It would be better to set the default value in the database? I don't really see why you would want to do it CakePHP side...

As the above suggestions do not work for me, so I have found my own. The answer is very similar to the written above, but with one little correction. (works with CakePHP 2.6.1)
The default value can be set in the controller in add function ("request" is needed).
$this->request->data['Country']['hasFlag'] = 1;
Full code example:
public function add() {
if ($this->request->is('post')) {
$this->Country->create();
if ($this->Country->save($this->request->data)) {
...
} else {
...
}
}
$this->request->data['Country']['hasFlag'] = 1; // default value passing to the view
}
Some philosophy:
1) Why this is needed - If we have a boolean attribute in the database, the newly created object in Cakephp does not take into the account default values from database. And if we leave checkbox unchecked in the Add form of the new object and submit it to the database - it means this attribute value is false (not value not set)
2) Is this an ideal place to set default value? - No, this is not an ideal place, as all information about the Object and its data must be in the Model, but I haven't managed to assign default value in the model. Even using _schema variable or create function.

You can set the value in database and get it managed in schema, e.g.:
public $items = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 10, 'unsigned' => false, 'key' => 'primary'),
'quantity' => array('type' => 'decimal', 'null' => false, 'default' => '1.00', 'length' => '12,2', 'unsigned' => false),
// ^^^^^^^^^^^^^^^^^^^
'indexes' => array(
'PRIMARY' => array('column' => 'id', 'unique' => 1),
),
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_spanish_ci', 'engine' => 'InnoDB')
);
This default value can be read later in model or controller though the model's schema property:
// Controller example
$itemSchema = $this->Item->schema();
$defaultQuantity = $itemSchema['quantity']['default'];
// ... or:
$quantityInfo = $this->Item->schema('quantity');
$defaultQuantity = $quantityInfo['default'];
In recent PHP versions it can be a one-liner:
$defaultQuantity = $this->Item->schema('quantity')['default'];
This works in Cake/2.5 with MySQL adapter (no idea of other scenarios).

databases get big, so you cannot remember all those defaults you've set. Let's keep it simple:
You set default values in the controller (it's easy to read the code, if default values for certain actions are set at the beginning as class properties).
For example:
class UsersController extends AppController {
private $registerDefaults = array(
'group_id' => '1'
);
public function register() {
if ($this->request->is('post')) {
/*
* This is where you set default value
* Here's what I do for default group that user should be assigned to
*/
$this->request->data['User']['group_id'] = $this->registerDefaults['group_id'];
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('You have been successfully registered.'));
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(__('We're unable register this user.'));
}
}
}
You can't always remember default values set in the database if you've got about 60-80 tables with complicated relations.
AND
My advice is that you don't set defaults that depend on your current settings, be more flexible: create configuration table or set defaults in AppController in order to find it wth a blink of an eye.

Related

Fetch default value from schema [duplicate]

How do you set a default value for a field in a model?
EDIT:
I have tried the method using _schema as suggested, but the default value is not being used.
public $_schema = array(
'newsletter' => array(
'default' => 1
),
);
you should always try to set default values from the controller:
http://www.dereuromark.de/tag/default-values/
It would be better to set the default value in the database? I don't really see why you would want to do it CakePHP side...
As the above suggestions do not work for me, so I have found my own. The answer is very similar to the written above, but with one little correction. (works with CakePHP 2.6.1)
The default value can be set in the controller in add function ("request" is needed).
$this->request->data['Country']['hasFlag'] = 1;
Full code example:
public function add() {
if ($this->request->is('post')) {
$this->Country->create();
if ($this->Country->save($this->request->data)) {
...
} else {
...
}
}
$this->request->data['Country']['hasFlag'] = 1; // default value passing to the view
}
Some philosophy:
1) Why this is needed - If we have a boolean attribute in the database, the newly created object in Cakephp does not take into the account default values from database. And if we leave checkbox unchecked in the Add form of the new object and submit it to the database - it means this attribute value is false (not value not set)
2) Is this an ideal place to set default value? - No, this is not an ideal place, as all information about the Object and its data must be in the Model, but I haven't managed to assign default value in the model. Even using _schema variable or create function.
You can set the value in database and get it managed in schema, e.g.:
public $items = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 10, 'unsigned' => false, 'key' => 'primary'),
'quantity' => array('type' => 'decimal', 'null' => false, 'default' => '1.00', 'length' => '12,2', 'unsigned' => false),
// ^^^^^^^^^^^^^^^^^^^
'indexes' => array(
'PRIMARY' => array('column' => 'id', 'unique' => 1),
),
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_spanish_ci', 'engine' => 'InnoDB')
);
This default value can be read later in model or controller though the model's schema property:
// Controller example
$itemSchema = $this->Item->schema();
$defaultQuantity = $itemSchema['quantity']['default'];
// ... or:
$quantityInfo = $this->Item->schema('quantity');
$defaultQuantity = $quantityInfo['default'];
In recent PHP versions it can be a one-liner:
$defaultQuantity = $this->Item->schema('quantity')['default'];
This works in Cake/2.5 with MySQL adapter (no idea of other scenarios).
databases get big, so you cannot remember all those defaults you've set. Let's keep it simple:
You set default values in the controller (it's easy to read the code, if default values for certain actions are set at the beginning as class properties).
For example:
class UsersController extends AppController {
private $registerDefaults = array(
'group_id' => '1'
);
public function register() {
if ($this->request->is('post')) {
/*
* This is where you set default value
* Here's what I do for default group that user should be assigned to
*/
$this->request->data['User']['group_id'] = $this->registerDefaults['group_id'];
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('You have been successfully registered.'));
return $this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(__('We're unable register this user.'));
}
}
}
You can't always remember default values set in the database if you've got about 60-80 tables with complicated relations.
AND
My advice is that you don't set defaults that depend on your current settings, be more flexible: create configuration table or set defaults in AppController in order to find it wth a blink of an eye.

Unable to specify Model->alias

I'm using a custom Class in my program which can connect to multiple tables as and when I need to.
I'm trying to create a new instance of the class, and I can pass in a different table name and alias via the constructor. This works for the table name, but it doesn't set the Model alias as it should in the documentation
Creating an new Object. The Class being instantiated is Lists
$lists = new Lists(null, 'list_musicians', null, null, 'Musician');
Class Constructor
public function __construct($id = false, $table = null, $ds = null, $name = null, $alias) {
parent::__construct($id, $table, $ds, $name, $alias);
$this->virtualFields['full_name'] = sprintf(
'CONCAT(%s.first_name, " ", %s.last_name)', $this->alias, $this->alias
);
//debug($this->alias);die;
}
This will connect to the table named 'list_musicians' or whatever table name I pass in, but the $alias field does not get assigned to $this->alias
If I reassign the alias manually, the virtual fields are not included in the same array as the result, as specified in the documentation. The alias is always Lists
How can I set the model alias via the constructor?
I'm using a custom Class in my program which can connect to multiple
tables as and when I need to.
Honestly that's a pretty bad idea in the most cases. If you already read the MVC chapter from of the book read it again, if not read it now.
And you're wrong, the constructor of the model class doesn't know a 4th argument for the alias, check your own link and read it again.
public function __construct($id = false, $table = null, $ds = null)
However, if you want to insist on bad practice you can always instantiate Model objects by using ClassRegistry::init().
Examples Simple Use: Get a Post model instance
ClassRegistry::init('Post');
Expanded: array('class' => 'ClassName', 'alias' =>
'AliasNameStoredInTheRegistry');
Model Classes can accept optional array('id' => $id, 'table' =>
$table, 'ds' => $ds, 'alias' => $alias);
Just use Model as class name and provide whatever table you want.
Solution to this is just to create model associations directly to the db tables. Use the table name as the classname and voila
'Religion' => array(
'className' => 'list_religions',
'foreignKey' => 'religion_id',
'conditions' => '',
'fields' => '',
'order' => ''
)

How should I declare my tree behavior in my model to preserve scope in CakePHP 2.5.x?

I'll be straightforward: I want to manage multiple (Link) trees according to their respective menu_id. As long as there is only one tree: no problem. Things get messed up when I start another tree in my link model with a different menu id.
I whish to be able to add, edit, remove, moveUp or moveDown while preserving the scope (menu_id).
This part of the documentation is unclear to me :
http://api.cakephp.org/2.5/source-class-TreeBehavior.html#41-49
Here my Link model.
<?php
App::uses('AppModel', 'Model');
class Link extends AppModel {
public $name = 'Link';
public $displayField = 'title';
public $actsAs = array('Tree' => array(
'parent' => 'parent_id',
'left' => 'lft',
'right' => 'rght',
'scope' => "WHAT-SHOULD-I-PLACE-HERE??",
));
public $belongsTo = array(
'Menu' => array(
'className' => 'Menu',
'foreignKey' => 'menu_id',
)
);
}
And my Menu model.
<?php
App::uses('AppModel', 'Model');
class Menu extends AppModel {
public $displayField = 'title';
public $hasMany = array(
'Link' => array(
'className' => 'Link',
'foreignKey' => 'menu_id',
'dependent' => false,
)
);
}
Thanks to BadHorsie I finally came up to understand that is it pointless to declare the scope in the model's behavior settings.
Instead you (currently I) need to attach the behavior on the fly, with the required scope before any action (add, edit, move up, move down etc) for this to work.
Now, what I need to make sure is that a link always has a menu_id.
And when someone edits a link, the parent_id dropdown must be repopulated according to the menu_id dropdown (javascript needed here and more validation rules in the model to ensure integrity).
The solution was to create a new function in the Link model. it's goal is to preserve the scope when any modification is made to the tree (move up, move down, remove from tree, delete, etc).
public function preserveScope($menuId) {
$this->Behaviors->attach('Tree', array(
'scope' => array(
'Link.menu_id' => $menuId
),
));
return true;
}
The scope is basically a SQL condition (in Cake format).
So you probably need to set the scope to the menu ID that you want.
'scope' => array(
'Link.menu_id' => 5
);
However, you probably don't know which ID yet when trying to set up the array on the class definition, so you might have to do it on the fly.
$this->Link->Behaviors->attach('Tree', array(
'scope' => array(
'Link.menu_id' => $id // You need to decide how to get this ID
),
));
I don't know when you would need to do this though. It's up to you to decide when to attach the behavior.
Edit: If the moveUp/moveDown methods are not working correctly, perhaps the scope field you are using is not correct?

Cakephp SaveAssociated and Save - Using same Model Validation code

Question: How can I use the same code in the model validation (in particular for child models) for both SaveAssociated and Save function calls in CakePHP,... given that SaveAssociated implementations expect the form data array to contain a numeric index [0] for data fields belonging to a child model?
Scenario:
Assuming I have a parent model with a hasMany relationship to several child models.
Typically if you use SaveAssociated to save data to all models at once, you would need to specify an index number (typically 0) on the view form input. Example:
echo $this->Form->input('MerchantControl.0.startdate', array('type' => 'text', 'class' => 'datepicker_start'));
As a result, any custom child model validation code will need to be written with [0] as well. See function urlParamNotUsedByOtherMerchants in the code sample below.
public $validate = array(
'urlparam' => array(
'In Use by other Merchants' => array(
'rule' => 'urlParamNotUsedByOtherMerchants',
'message' => 'URLPARAM belongs to another Merchant'
)
)
);
public function urlParamNotUsedByOtherMerchants($data) {
$searchfilter = array(
//Because of SaveAssociated, need to refer to index [0]
'MerchantControl.id !=' => $this->data['MerchantControl'][0]['merchant_id'],
'MerchantControl.urlparam ' => $data,
);
$merchantcontrol = $this->find('all', array('conditions' => $searchfilter));
if (sizeof($merchantcontrol) > 0) {
return false;
} else {
return true;
}
}
The problem is there are many other instances where I will also be using a "Save" and not a "SaveAssociated" in maintainence views where i directly update or create the child model only. In this case, this model validation code is going to fail with an error saying index "[0]" not defined or something similar.
How can I use the same code in the model validation (in particular for child models) for both SaveAssociated and Save function calls in CakePHP?
If I understand you correctly you want to check whether the urlparam is already used by another merchant or in other words whether it is unique.
Why don't you use the built-in validation rule isUnique?
Example:
public $validate = array(
'urlparam' => array(
'In Use by other Merchants' => array(
'rule' => 'isUnique',
'message' => 'URLPARAM belongs to another Merchant'
)
)
);

cake populate a dropdown from model

I am trying to populate a dropdpwn with some options. I am trying to have a dropdown with name title and country list for my input form.
For example:
Titles I need 'Mr', 'Mrs', 'Miss'
I have tried to ways:
Model
// Built a list of search options (unless you have this list somewhere else)
public function __construct($id = false, $table = null, $ds = null) {
$this->titles = array(
0 => __('Mr', true),
1 => __('Mrs', true),
2 => __('Miss', true));
parent::__construct($id, $table, $ds);
}
Controller
public function add() {
if ($this->request->is('post')) {
$this->Member->create();
if ($this->Member->save($this->request->data)) {
$this->Session->setFlash(__('The member has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The member could not be saved. Please, try again.'));
}
}
}
View
<?php echo $this->Form->input('title',array('class' => 'span10', 'options' => $titles )); ?>
I get the error Undefined variable: titles
I have also tried in the model
public $validate = array(
'member_no' => array(
'notempty' => array(
'rule' => array('notempty'),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'title' => array(
'titlesValid' => array(
'rule' => array('multiple', array(
'in' => array('Mr', 'Mrs', 'Miss'),
'min' => 1
)),
What I am missing and what would be the best solution for a longer list such as countries, it is only on form so i didnt think I would nees a title and countries table and link ids?
I find really weird that you have to create a variable in a construct of a model for a dropdown. Specially if you're only going to use it once.
But first things first. You're getting that error because you haven't set the variable to the view. In your controller, there needs to be something like this:
public function add() {
$this->set('titles', $your_titles_array);
if ($this->request->is('post')) {
//etc
Now, please please get the dropdown array away from there. There are two possible nicer places to put that. I'm not going to get picky and say you need to have this values as a table in you database. If you say it's just for one place and you want to have it hardcode, so be it.
One option is to put it in one model, like
class YourModel extends AppModel {
//all your other definitions
public function getTitles() {
return array(
0 => __('Mr', true),
1 => __('Mrs', true),
2 => __('Miss', true));
}
}
And in the controller do
public function add() {
$this->set('titles', $this->YourModel->getTitles());
if ($this->request->is('post')) {
//etc
But, I don't think you have an appropriate model to add that function. Where did you plan to add it? User model maybe? It can't be like Post model, for example, that wouldn't make any sense... So, give it a thought, if there's a logical place in a model you can put that function, then go ahead.
Otherwise, and if it's just for one form in one place just that one time, why not hardcode it to the view or the controller?
For larger lists, my recommendation is to do it in a model like I showed (but I insist, put it in the model where is logical to have it). Well, I rather have a table and reference the values, but it's not mandatory (though give it a thought, is really not that much work, is it? for larger lists, I mean).

Resources