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));
}
  1. function edit($id) {
  2. //Has any form data been POSTed?
  3. if(!empty($this->data)) {
  4. //If the form data can be validated and saved...
  5. if($this->Recipe->save($this->data)) {
  6. //Set a session flash message and redirect.
  7. $this->Session->setFlash("Recipe Saved!");
  8. $this->redirect('/recipes');
  9. }
  10. }
  11. //If no form data, find the recipe to be edited
  12. //and hand it to the view.
  13. $this->set('recipe', $this->Recipe->findById($id));
  14. }

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:

save(array $data = null, boolean $validate = true, array $fieldList = array())

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;
  1. $this->Ingredient->save($newData);
  2. $newIngredientId = $this->Ingredient->id;
create(array $data = array())

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).

saveField(string $fieldName, string $fieldValue, $validate = false)

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');
  1. $this->Post->saveField('title', 'A New Title for a New Day');
updateAll(array $fields, array $conditions)

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")
);
  1. $this_year = date('Y-m-d h:i:s', strtotime('-1 year'));
  2. $this->Baker->updateAll(
  3. array('Baker.approved' => true),
  4. array('Baker.created <=' => "$this_year")
  5. );

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)
);
  1. $this->Ticket->updateAll(
  2. array('Ticket.status' => "'closed'"),
  3. array('Ticket.customer_id' => 453)
  4. );

3.7.4.1 Guardando datos de Modelos Relacionados (hasOne, hasMany, belongsTo)

Cuando estamos trabajando con modelos asociados, es importante tener en cuenta que al guardar los datos de un modelo hay que hacerlo con el correspondiente modelo de CakePHP. Si estás guardando un nuevo Post y sus comentarios asociados, entonces deberías usar ambos modelos, Post y Comment, durante la operación de guardado.

Si ninguno de los registros de los modelos asociados existe aún ( por ejemplo, quieres guardar registros de un nuevo Usuario [User] y su Perfil [Profile] relacionado a la vez ), primero necesitas guardar el modelo primario o padre.

Para tener una idea de cómo funciona ésto, imaginemos que tenemos una acción en nuestro controlador de usuarios [UsersController] que maneja el guardado de un nuevo usuario y su perfil [Profile] correspondiente. En la acción de ejemplo mostrada abajo se da por hecho que se han POSTeado sufientes datos (usando el FormHelper) para crear un único Usuario [User] y un único Perfil [Perfil].

<?php
function add() {
    if (!empty($this->data)) {
        //Podemos guardar los datos del usuario[User]
        //deberían estar en: $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.

        //El ID del nuevo usuario creado ahora está
        //$this->User->id.
        $this->data['Profile']['user_id'] = $this->User->id;

        //Como nuestro usuario [User] tiene un [hasOne] perfil [Profile]
        //podemos acceder al modelo del perfil [Profile]
        // a través del modelo del usuario [User]


        $this->User->Profile->save($this->data);
    }
}

?>

  1. <?php
  2. function add() {
  3. if (!empty($this->data)) {
  4. //Podemos guardar los datos del usuario[User]
  5. //deberían estar en: $this->data['User']
  6. $this->User->save($this->data);
  7. //Now we add this information to the save data
  8. //and save the Profile.
  9. //The ID of the newly created user has been set
  10. //as $this->User->id.
  11. //El ID del nuevo usuario creado ahora está
  12. //$this->User->id.
  13. $this->data['Profile']['user_id'] = $this->User->id;
  14. //Como nuestro usuario [User] tiene un [hasOne] perfil [Profile]
  15. //podemos acceder al modelo del perfil [Profile]
  16. // a través del modelo del usuario [User]
  17. $this->User->Profile->save($this->data);
  18. }
  19. }
  20. ?>

Como norma general, cuando trabajamos con asociaciones "tiene un", "tiene muchos", y "pertenece a" ( hasOne, hasMany, belongsTo, respectivamente ), todo es cuestión de las claves. La idea básica es coger la clave de un modelo y ponerla cómo clave foránea en el otro. A veces esto puede implica usar el atributo $id de la clase del modelo después de guardar [save()], pero otras veces puede implicar obtener el ID desde un campo oculto de un formulario POSTeado a la acción del controlador

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 findCount), 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 "counter".

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);
    )
}
  1. class ImageAlbum extends AppModel {
  2. var $hasMany = array(
  3. 'Image'
  4. )
  5. }
  6. class Image extends AppModel {
  7. var $belongsTo = array(
  8. 'ImageAlbum' => array('counterCache' => true);
  9. )
  10. }

Now every time you add a new "Image" to "ImageAlbum" the number within "image_count" will increase (or decrease if you do a delete).

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'); ?>
  1. <?php echo $form->create('Tag');?>
  2. <?php echo $form->input(
  3. 'Recipe.id',
  4. array('type'=>'hidden', 'value' => $recipe_id)); ?>
  5. <?php echo $form->input('Tag.name'); ?>
  6. <?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            
    }
}
  1. function add() {
  2. //Save the association
  3. if ($this->Tag->save($this->data)) {
  4. //do something on success
  5. }
  6. }

And just like that, our new Tag is created and associated with a Recipe, whose ID was set in $this->data['Recipe']['id'].