3.7.4 Saving Your Data
CakePHP makes saving model data a snap. Data ready to be saved should be passed to the model’s save() method using the following basic format:
Array
(
[ModelName] => Array
(
[fieldname1] => 'value'
[fieldname2] => 'value'
)
)
Most of the time you won’t even need to worry about this format: CakePHP's HtmlHelper, FormHelper, and find methods all package data in this format. If you're using either of the helpers, the data is also conveniently available in $this->data for quick usage.
Here's a quick example of a controller action that uses a CakePHP model to save data to a database table:
function edit($id) {
//Has any form data been POSTed?
if(!empty($this->data)) {
//If the form data can be validated and saved...
if($this->Recipe->save($this->data)) {
//Set a session flash message and redirect.
$this->Session->setFlash("Recipe Saved!");
$this->redirect('/recipes');
}
}
//If no form data, find the recipe to be edited
//and hand it to the view.
$this->set('recipe', $this->Recipe->findById($id));
}
function edit($id) {//Has any form data been POSTed?if(!empty($this->data)) {//If the form data can be validated and saved...if($this->Recipe->save($this->data)) {//Set a session flash message and redirect.$this->Session->setFlash("Recipe Saved!");$this->redirect('/recipes');}}//If no form data, find the recipe to be edited//and hand it to the view.$this->set('recipe', $this->Recipe->findById($id));}
One additional note: when save is called, the data passed to it in the first parameter is validated using CakePHP validation mechanism (see the Data Validation chapter for more information). If for some reason your data isn't saving, be sure to check to see if some validation rules aren't being broken.
There are a few other save-related methods in the model that you'll find useful:
Featured above, this method saves array-formatted data. The second parameter allows you to sidestep validation, and the third allows you to supply a list of model fields to be saved. For added security, you can limit the saved fields to those listed in $fieldList.
Once a save has been completed, the ID for the object can be found in the $id attribute of the model object - something especially handy when creating new objects.
$this->Ingredient->save($newData); $newIngredientId = $this->Ingredient->id;
$this->Ingredient->save($newData);$newIngredientId = $this->Ingredient->id;
This method initializes the model class for saving new information. If you're ever calling save() inside of a loop (in order to create many records at once), make sure to call create() just before save().
If you supply the $data parameter (using the array format outlined above), the newly created model will be ready to save with that data (accessible at $this->data).
Used to save a single field value. Set the ID of the model ($this->ModelName->id = $id) just before calling saveField(). When using this method, $fieldName should only contain the name of the field, not the name of the model and field.
For example, to update the title of a blog post, the call to saveField from a controller might look something like this:
$this->Post->saveField('title', 'A New Title for a New Day');
$this->Post->saveField('title', 'A New Title for a New Day');
Updates many records in a single call. Records to be updated are identified by the $conditions array, and fields to be updated, along with their values, are identified by the $fields array.
For example, if I wanted to approve all bakers who have been members for over a year, the update call might look something like:
$this_year = date('Y-m-d h:i:s', strtotime('-1 year'));
$this->Baker->updateAll(
array('Baker.approved' => true),
array('Baker.created <=' => "$this_year")
);
$this_year = date('Y-m-d h:i:s', strtotime('-1 year'));$this->Baker->updateAll(array('Baker.approved' => true),array('Baker.created <=' => "$this_year"));
The $fields array accepts SQL expressions. Literal values should be quoted manually. For example, if you want to close all tickets that belong to a certain customer:
$this->Ticket->updateAll(
array('Ticket.status' => "'closed'"),
array('Ticket.customer_id' => 453)
);
$this->Ticket->updateAll(array('Ticket.status' => "'closed'"),array('Ticket.customer_id' => 453));
3.7.4.1 Saving Related Model Data (hasOne, hasMany, belongsTo)
When working with associated models, it is important to realize that saving model data should always be done by the corresponding CakePHP model. If you are saving a new Post and its associated Comments, then you would use both Post and Comment models during the save operation.
If neither of the associated model records exists in the system yet (for example, you want to save a new User and their related Profile records at the same time), you'll need to first save the primary, or parent model.
To get an idea of how this works, let's imagine that we have an action in our UsersController that handles the saving of a new User and a related Profile. The example action shown below will assume that you've POSTed enough data (using the FormHelper) to create a single User and a single Profile.
<?php
function add() {
if (!empty($this->data)) {
// We can save the User data:
// it should be in $this->data['User']
$this->User->save($this->data);
// Now we add this information to the save data
// and save the Profile.
// The ID of the newly created user has been set
// as $this->User->id.
$this->data['Profile']['user_id'] = $this->User->id;
// Because our User hasOne Profile, we can access
// the Profile model through the User model:
$this->User->Profile->save($this->data);
}
}
?>
<?phpfunction add() {if (!empty($this->data)) {// We can save the User data:// it should be in $this->data['User']$this->User->save($this->data);// Now we add this information to the save data// and save the Profile.// The ID of the newly created user has been set// as $this->User->id.$this->data['Profile']['user_id'] = $this->User->id;// Because our User hasOne Profile, we can access// the Profile model through the User model:$this->User->Profile->save($this->data);}}?>
As a rule, when working with hasOne, hasMany, and belongsTo associations, its all about keying. The basic idea is to get the key from one model and place it in the foreign key field on the other. Sometimes this might involve using the $id attribute of the model class after a save(), but other times it might just involve gathering the ID from a hidden input on a form that’s just been POSTed to a controller action.
3.7.4.1.1 counterCache - Cache your count()
This function helps you caching the count of related data. Instead of counting via SELECT (i.e. using find('count')), the model itself tracks any addition/deleting towards the associated $hasMany model and increases/decreases a dedicated integer field within the parent model table.
The name of the field consists of the singular model name followed by a underscore and the word "count".
Let's say you have a model called "ImageAlbum" and a model called "Image", you would add an INT-field to the "image_album" table and name it "image_count". Or if your names are more complex, here is another example: With "BlogEntry" and "BlogEntryComment", the name of the field would be "blog_entry_comment_count" and needs to be added to "blog_entries".
Once you have added the counter field you are good to activate this functionality by adding the "counterCache" key to the "$belongsTo" association array and set the value to "true".
class ImageAlbum extends AppModel {
var $hasMany = array(
'Image'
)
}
class Image extends AppModel {
var $belongsTo = array(
'ImageAlbum' => array('counterCache' => true);
)
}
class ImageAlbum extends AppModel {var $hasMany = array('Image')}class Image extends AppModel {var $belongsTo = array('ImageAlbum' => array('counterCache' => true);)}
Now every time you add a new "Image" to "ImageAlbum" the number within "image_count" will increase (or decrease if you do a delete).
3.7.4.2 Saving Related Model Data (HABTM)
Saving models that are associated by hasOne, belongsTo, and hasMany is pretty simple: you just populate the foreign key field with the ID of the associated model. Once that's done, you just call the save() method on the model, and everything gets linked up correctly.
With HABTM, you need to set the ID of the associated model in your data array. We'll build a form that creates a new tag and associates it on the fly with some recipe.
The simplest form might look something like this (we'll assume that $recipe_id is already set to something):
<?php echo $form->create('Tag');?>
<?php echo $form->input(
'Recipe.id',
array('type'=>'hidden', 'value' => $recipe_id)); ?>
<?php echo $form->input('Tag.name'); ?>
<?php echo $form->end('Add Tag'); ?>
<?php echo $form->create('Tag');?><?php echo $form->input('Recipe.id',array('type'=>'hidden', 'value' => $recipe_id)); ?><?php echo $form->input('Tag.name'); ?><?php echo $form->end('Add Tag'); ?>
In this example, you can see the ‘Recipe.id’ hidden field whose value is set to the ID of the recipe we want to link the tag to. The controller action that handles saving this form, is very simple:
function add() {
//Save the association
if ($this->Tag->save($this->data)) {
//do something on success
}
}
function add() {//Save the associationif ($this->Tag->save($this->data)) {//do something on success}}
And just like that, our new Tag is created and associated with a Recipe, whose ID was set in $this->data['Recipe']['id'].
