1. Model Functions

From PHP view, models are classes extending AppModel class. The AppModel class is originally defined in the cake/ directory, but should you want to create your own, place it in app/app_model.php. It should contain methods that are shared between two or more models. It itself extends the Model class which is a standard Cake library defined in libs/model.php.

Important

While this section will treat most of the oft-used functions in Cake's Model, its important to remember to use http://api.cakephp.org for a full reference.

1.1. User-Defined Functions

An example of a table-specific method in the model is a pair of methods for hiding/unhiding posts in a blog.

Example 6.1. Example Model Functions

<?php
class Post extends AppModel
{
   function hide ($id=null)
   {
      if ($id) $this->setId($id);
      $this->set('hidden', '1');
      $this->save();
   }
 
   function unhide ($id=null)
   {
      if ($id) $this->setId($id);
      $this->set('hidden', '0');
      $this->save();
   }
}
?>

1.2. Retrieving Your Data

Below are a few of the standard ways of getting to your data using a model:

  • findAll ($conditions, $fields, $order, $limit, $page, $recursive)

    • returns the specified fields up to $limit (default is 50) records matching $conditions (if any), start listing from page $page (default is page 1). $conditions should look like they would in an SQL statement: $conditions = "race = 'wookie' AND thermal_detonators > 3", for example.

    • When the $recursive option is set to a integer value between 1 and 3, the findAll() operation will make an effort to return the models associated to the ones found by the findAll(). The recursive find can go up to three levels deep. If your property has many owners who in turn have many contracts, a recursive findAll() on your Property model will return up to three levels deep of associated models.

  • find ($conditions, $fields, $order, $recursive)

    • returns the specified (or all if not specified) fields from the first record that matches $conditions.

    • When the $recursive option is set to a integer value between 1 and 3, the findAll() operation will make an effort to return the models associated to the ones found by the findAll(). The recursive find can go up to three levels deep. If your property has many owners who in turn have many contracts, a recursive findAll() on your Property model will return up to three levels deep of associated models.

  • findAllBy<FieldName>($value) and findBy<fieldName>($value)

    • These magic functions can be used as a shortcut to search your tables for a row given a certain field, and a certain value. Just tack on the name of the field you wish to search, and CamelCase it. Examples (as used in a Controller) might be:

      $this->Post->findByTitle('My First Blog Post');
      $this->Author->findByLastName('Rogers');
      $this->Property->findAllByState('AZ');
      $this->Specimen->findAllByKingdom('Animalia');

      The returned result is an array formatted just as would be from find() or findAll().

  • field($name, $conditions, $order)

    • returns as a string a single field from the first record matched by $conditions as ordered by $order.

  • findCount($conditions)

    • returns the number of records that match the given conditions

  • generateList ($conditions=null, $order=null, $limit=null, $keyPath=null, $valuePath=null)

    • This function is a shortcut to getting a list of key value pairs from list of your models - especially handy for creating a <select> list from a list of your models. Use the $conditions, $order, and $limit parameters just as you would for a findAll() request. The $keyPath and $valuePath are where you tell the model where to find the keys and values for your generated list. For example, if you wanted to generate a list of roles based on your Role model, keyed by their integer ids, the full call might look something like:

      $this->set('Roles' $this->Role->generateList(null, 'role_name ASC', null, 'id', 'role_name'));
      
      //This would return something like:
      array(
          '1' => 'Account Manager',
          '2' => 'Account Viewer',
          '3' => 'System Manager',
          '4' => 'Site Visitor'
      );
  • read($fields=null, $id=null)

    • Use this function to get the fields and their values from the currently loaded record, or the record specified by $id.

Custom SQL calls can be made using the model's findBySql() method. For example, inside of a blog application, let's say I wanted to store the poster's first name in a table that is not a part of my Cake schema (simplified example). I could then get that data by placing a custom function in the model. Results are returned as a multidimensional array.

Example 6.2. Custom Sql Calls with findBySQL()

<?php
class Post extends AppModel
{
        function posterFirstName()
        {
          $ret = $this->findBySql("SELECT first_name FROM posters_table WHERE poster_id = 1");
          $firstName = $ret[0]['first_name'];
          return $firstName;
        }

}
?>

There's also query() which returns true on success and false on failure, for queries that don't return a result set, like "DELETE FROM problems WHERE solved = true".

1.3. Saving Your Data

To save data to your model, you need to supply it with the data you wish to save. The data handed to the save() method should be in the following form:

Array
(
    [modelname] => Array
        (
            [fieldname1] => value
            [fieldname2] => value
        )
)

Data posted from forms is automatically formatted like this and placed in $this->params['data'] inside of your controller, so saving your data from web forms is a snap. An edit function for a propery controller might look something like the following:

function edit($id) 
{

   //Note: The property model is automatically loaded for us at $this->property.
      
   if (isset($this->params['form']['data']['property'])) // Check to see if we have form data...
   {  
      if ($this->property->save($this->params['data'])) // <-- Here's where we try to save our data...
      {
         //Show the user that her data has been saved...

         $this->flash('Your information has been saved.','/properties/edit/' . $this->params['data']['property']['id'], 2);
         exit();
      }
      else
      {
         //If the data couldn't be validated, show the validation errors
         //and repopulate form fields with submitted data...

         $this->set('form', $this->params['data']);
         $this->validateErrors($this->property);
         $this->render();
      }      
   }
   
   //If no form data was submitted, just render the edit view...
   $this->render();
}

Notice how the save operation is placed inside a conditional: when you try to save data to your model, Cake automatically attempts to validate your data using the rules you've provided. To learn more about data validation, see Chapter 10. If you do not want save() to try to validate your data, use save($data, false).

Other useful save functions:

  • saveField($name, $value)

    • used to save a single field value.

  • getLastInsertID()

    • Returns the ID of the most recently created record.

1.4. Model Callbacks

As we approach 0.10.x final, we've added some model callbacks that allow you to sneak in logic before or after certain model operations. To gain this functionality in your applications, use the parameters provided and override these functions in your Cake models.

  • beforeFind($conditions)

    • The beforeFind() callback is executed just before a find operation begins. Place any pre-find logic in this method. When you override this in your model, return true when you want the find to execute, and false when you want it to abort.

  • afterFind($results)

    • Use this callback to modify results that have been returned from a find operation, or perform any other post-find logic. The parameter for this function is the returned results from the model's find operation, and the return value is the modified results.

  • beforeSave()

    • Place any pre-save logic in this function. This function should also return true if you want the save operation to continue, and false if you want to abort.

  • afterSave()

    • Place any logic that you want to be executed after every save in this callback method.

  • beforeDelete()

    • Place any pre-deletion logic in this function. This function should return true if you want the deletion to continue, and false if you want to abort.

  • afterDelete()

    • Place any logic that you want to be executed after every deletion in this callback method.