Models
Introdução
Models são usados no CakePHP para acessar os dados. Models representam, normalmente, tabelas de banco de dados, mas também podem ser usadas para acessar arquivos, registros LDAP, eventos iCal ou uma linha num arquivo CSV.
Models também podem ser associados, a fim de tornar mais fácil o acesso aos dados. Se quando você procura dados do model Recipe e quer saber os dados de Author e Ingredient associadas, as associações de models podem lhe ajudar.
Novidades no CakePHP são as DataSources e Behaviors. DataSources foram uma base unificada para os models CakePHP. Isso permite uma maior interação entre os models, incluindo as associações. Model behaviors oferecem uma maneira de misturar as funcionalidades. Especificando behaviors para models é dado acesso a lógicas de manipulação de dados comuns: itens como remoção de itens na árvore de dados, manipulação de upload e muito mais. Abaixo um model completamente funcional:
<?php
class Ingredient extends AppModel {
var $name = 'Ingredient';
}
?>
<?phpclass Ingredient extends AppModel {var $name = 'Ingredient';}?>
Repare que os models do CakePHP derivam da classe especial AppModel: o pai para todos os models da sua aplicação. Assim como AppController (e AppHelper), esta é uma classe especial que é ideal para você compartilhar a lógica comum para todos os models. A classe AppModel deve ser criada em /app/app_model.php e deve estender (extends) a classe Model.
Com apenas algumas linhas de código, você pode ter completas funcionalidades de CRUD (create, retrieve, update, delete). A maioria dessas funcionalidades provém da classe avô Model. Uma vez que você tenha criado seu model, você poderá usá-lo em seus controllers. Segue um exemplo básico de um model a ser utilizado ($this->Ingredient) para buscar a lista de registros e passá-la para a view.
<?php
class IngredientsController extends AppController {
function index() {
// Passa uma lista de todos os ingredientes para a view:
$this->set(
'ingredients',
$this->Ingredient->findAll()
);
}
}
?>
<?phpclass IngredientsController extends AppController {function index() {// Passa uma lista de todos os ingredientes para a view:$this->set('ingredients',$this->Ingredient->findAll());}}?>
Usando models para buscar e salvar os dados pode poupar sérias dores de cabeça. Models no CakePHP prove uma abordagem padronizada e centralizada para o armazenamento de dados, ao mesmo tempo oferecendo um nível de segurança para sua aplicação.
Classes de model podem ser expandidas para incluir mais atributos e métodos para melhorar suas funcionalidades. Estes atributos e métodos serão explicados depois.
Campos mágicos
CakePHP pode fazer algumas coisas pra você se você nomear alguns campos das tabelas do banco de dados de acordo com as regras do CakePHP.
created & modified (ou updated)
Estes dois campos são automaticamente preenchidos pelo CakePHP quando você chama a função save(). O campo created será preenchido apenas quando estiver inserindo um novo registro no banco de dados, já modified será preenchida em cada alteração que você faça. O campo chamado updated terá o mesmo comportamento que o modified.
Ambos os campos devem ser do tipo datetime e o valor padrão (default) setado para NULL.
Chaves primárias usadas como UUIDs
Se a chave primária (‘id’ ou sobrescrito na variável $primaryKey) para o model estiver definido como um char(36), o CakePHP criará automaticamente um UUID para usar neste campo quando criar um registro.
Este recurso é extremamente útil para aplicações que utilizam vários bancos de dados.
Atributos
Para uma lista completa de atributos do model e suas descrições visite a API do CakePHP. Dê uma olhada http://api.cakephp.org/1.2/classModel.html.
O nome da configuração de banco de dados que este model irá utilizar. As configurações são especificadas em /app/config/database.php numa classe com variáveis. O padrão é a 'default'.
O nome da tabela no banco de dados que este model irá utilizar. A tabela padrão usada é o nome da classe com todas letras em minúsculo e no plural. Apenas atribua valor se você usar uma tabela alternativa ou atribua false se você quer que seu banco de dados não acesse nenhuma tabela.
Se o model está vinculado a uma tabela no banco de dados, este atributo carrega o nome da coluna que representa a chave primária (primary key). O padrão é 'id'.
Este atributo é usado para definir qual campo será utilizado para mostrar os elementos scaffolding. Quando há uma associação de dados (por exemplo, que Ingredient deseja associar a uma Recipe), scaffolding utilizará o campo ID, 'name' ou 'title' caso $displayField não seja especificado.
Um inteiro especificando o número de nível que você deseja que o CakePHP associe outros models nas operações find() e findAll().
Imagine que na sua aplicação tenha Grupos que possuem vários Users que estes tenham vários Articles. Você pode definir $recursive para receber um valor diferente baseado na quantidade de dados que você quer que $this->Group->find() retorne:
- 0 - Cake irá retornar apenas os dados de Group;
- 1 - Cake irá retornar os dados de Group e as associações com Users;
- 2 - Cake irá retornar os dados de Group e suas associações com Users, e as associações de Users com Articles.
Atribua o menor valor necessário a fim de evitar que o CakePHP faça buscas desnecessários e deixe sua aplicação lenta.
Define a ordenação padrão dos dados em qualquer operação de busca. Possíveis valores:
$order = "field"
$order = "Model.field";
$order = "Model.field asc";
$order = "Model.field ASC";
$order = "Model.field DESC";
$order = array("Model.field" => "asc", "Model.field2" => "DESC");
$order = "field"$order = "Model.field";$order = "Model.field asc";$order = "Model.field ASC";$order = "Model.field DESC";$order = array("Model.field" => "asc", "Model.field2" => "DESC");
O valor da chave primária que este registro está apontando. Se você está salvando dados no model através de um loop, set $id para null ($this->NomeDoModel->id = null) para que o model esteja pronto para receber o novo conjunto de dados.
O conteúdo buscado da model. Normalmente os dados retornados são feitos através do método find(), porém as vezes você precisa acessar os dados armazenados em $data, como nas funções de callback.
Contém a descrição dos campos das tabelas do banco de dados. Cada campo é descrito como:
- name
- type (integer, string, datetime, etc.)
- null
- default value
- length
Este atributo guarda as regras permitem ao model tomar decisões de validação antes de salvar. As chaves com nome de campos possuem o correspondente valor regex para que o model tente fazer a validação.
Para mais informações sobre validação, veja o capítulo Validação de dados deste manual.
O nome deste model. O nome do CakePHP são sempre no singular e CamelCased. Usuários PHP4 do CakePHP devem sempre declarar $name a fim de evitar que permitam invocações do nome da classe com PHP4.
Se definido para true, os valores retornados nas pesquisas simples serão armazenados. Este cache ficará apenas na memória e apenas a última requisição será armazenada. Qualquer requisição duplicada com os mesmos parâmetros será utilizada do cache.
Estes atributos serão descritos depois neste capítulo. Eles guardam as associações com outros models.
Este atributo também será abordado depois. O atributo $actsAs guarda o nome dos Behaviors que este modelo usa.
$useDbConfig
The name of the database configuration this model uses. Configurations are specified in /app/config/database.php as class variables. Defaults to 'default'.
$useTable
The name of the database table this model uses. The default table used is the lower-cased, plural version of the model’s classname. Set this attribute to the name of an alternate table, or set it to false if you wish the model to have no database table at all.
$primaryKey
If the model is tied to a database table, this attribute holds the name of the column of the tables primary key. Defaults to 'id'.
$displayField
This attribute is used to define the field the model will use for display in scaffolding elements. When associated data is presented (i.e., which Ingredient you’d like to associate to a Recipe) scaffolding will use the row’s ID, 'name', or 'title' fields unless $displayField has been specified.
For example, if you want the 'username' field of the model to show rather than the default, include the following line of code in your model:
var $displayField = 'username';
var $displayField = 'username';
$recursive
An integer specifying the number of levels you wish CakePHP to fetch associated model data in find() and findAll() operations.
Imagine your application features Groups which belong to a domain and have many Users which in turn have many Articles. You can set $recursive to different values based on the amount of data you want back from a $this->Group->find() call:
- -1 - Cake fetches Group data only, no joins.
- 0 - Cake fetches Group data and its domain
- 1 - Cake fetches a Group, its domain and its associated Users
- 2 - Cake fetches a Group, its domain, its associated Users, and the Users' associated Articles
Set it no higher than you need–having CakePHP fetch data you aren’t going to use unnecessarily slows your app.
$order
The default ordering of data for any find operation. Possible values include:
$order = "field"
$order = "Model.field";
$order = "Model.field asc";
$order = "Model.field ASC";
$order = "Model.field DESC";
$order = array("Model.field" => "asc", "Model.field2" => "DESC");
$order = "field"$order = "Model.field";$order = "Model.field asc";$order = "Model.field ASC";$order = "Model.field DESC";$order = array("Model.field" => "asc", "Model.field2" => "DESC");
Value of the primary key ID of the record that this model is currently pointing to. If you are saving model data inside of a loop, set $id to null ($this->ModelName->id = null) in order to ready the model class for the next set of data.
$data
The container for the model’s fetched data. While data returned from a model class is normally used as returned from a find() call, you may need to access information stored in $data inside of model callbacks.
$_schema
Contains metadata describing the model’s database table fields. Each field is described by:
- name
- type (integer, string, datetime, etc.)
- null
- default value
- length
$validate
This attribute holds rules that allow the model to make data validation decisions before saving. Keys named after fields hold regex values allowing the model to try to make matches.
For more information on validation, see the Data Validation chapter later on in this manual.
$name
The name of this model. CakePHP model names are CamelCased and singular. CakePHP users on PHP4 should always declare $name in order to avoid the quirks involved with PHP4 class-naming.
$cacheQueries
If set to true, data fetched by the model during a single request is cached. This caching is in-memory only, and only lasts for the duration of the request. Any duplicate requests for the same data is handled by the cache.
$belongsTo, $hasOne, $hasMany, $hasAndBelongsToMany
These model attributes will be covered later in this chapter. They hold arrays that define how this model is related to others.
$actsAs
This attribute is also covered later on. The $actsAs attribute stores the names of Behaviors this model uses.
Métodos
Para uma lista completa de atributos do model e suas descrições visite a API do CakePHP. Dê uma olhada http://api.cakephp.org/1.2/classModel.html.
Recuperando seus dados
Retorna no máximo $limite registros com as condições definidas em $conditions (se houver), começando da página $page (o padrão é 1). Se não houver registros, retornará um array vazio.
O $conditions deve ser formada apenas como eles são usados em SQL, por exemplo:
$conditions = "Pastry.type LIKE '%cake%' AND Pastry.created_on > ‘2007-01-01’"
$conditions = "Pastry.type LIKE '%cake%' AND Pastry.created_on > ‘2007-01-01’"
Prefixando os campos com o nome dos models (‘Pastry.type’ ao invés de ‘type’) é uma boa prática, principalmente quando associa-se dados é usado nas queries.
Configurando o parâmetro $recursive para um inteiro, força o findAll() a buscar os dados de acordo com os behaviors descritos em Atributos anteriormente.
Os dados de findAll() são retornados em um array, seguindo o modelo básico abaixo:
Array
(
[0] => Array
(
[ModelName] => Array
(
[id] => 83
[field1] => value1
[field2] => value2
[field3] => value3
)
[AssociatedModelName] => Array
(
[id] => 1
[field1] => value1
[field2] => value2
[field3] => value3
)
)
[1] => Array
(
[ModelName] => Array
(
[id] => 85
[field1] => value1
[field2] => value2
[field3] => value3
)
[AssociatedModelName] => Array
(
[id] => 2
[field1] => value1
[field2] => value2
[field3] => value3
)
)
)
Array([0] => Array([ModelName] => Array([id] => 83[field1] => value1[field2] => value2[field3] => value3)[AssociatedModelName] => Array([id] => 1[field1] => value1[field2] => value2[field3] => value3))[1] => Array([ModelName] => Array([id] => 85[field1] => value1[field2] => value2[field3] => value3)[AssociatedModelName] => Array([id] => 2[field1] => value1[field2] => value2[field3] => value3)))
Assim como em findAll(), exceto que esta busca retorna o primeiro registro, seguindo as regras de $conditions.
Estas funções mágicas podem ser usadas como atalhos para procurar dados nos campos especificados nas suas tabelas. Basta adicionar o nome do campo, no formato CamelCased, no fim das funções e informar os critérios de busca como primeiro parâmetro.
| Exemplos de findAllBy<x> em PHP5 | Fragmento SQL correspondente |
|---|---|
| $this->Product->findAllByOrderStatus(‘3’); | Product.order_status = 3 |
| $this->Recipe->findAllByType(‘Cookie’); | Recipe.type = ‘Cookie’ |
| $this->User->findAllByLastName(‘Anderson’); | User.last_name = ‘Anderson’ |
| $this->Cake->findById(7); | Cake.id = 7 |
| $this->User->findByUserName(‘psychic’); | User.user_name = ‘psychic’ |
Usuários de PHP4 devem usar essa função com uma pequena diferença:
| Exemplos de findAllBy<x> em PHP4 | Fragmento SQL correspondente |
|---|---|
| $this->Product->findAllByOrder_status(‘3’); | Product.order_status = 3 |
| $this->Recipe->findAllByType(‘Cookie’); | Recipe.type = ‘Cookie’ |
| $this->User->findAllByLast_name(‘Anderson’); | User.last_name = ‘Anderson’ |
| $this->Cake->findById(7); | Cake.id = 7 |
| $this->User->findByUser_name(‘psychic’); | User.user_name = ‘psychic’ |
O resultado retornado é um array formatado como descrito em find() e findAll().
Retorna um array contendo os models vizinhos (com apenas os campos especificados), especificados por $field e $value, filtrando pelas condições de SQL em $conditions.
Isto é um atalho para criar links de ‘Anterior’ e ‘Próximo’ que os usuários percorrem em alguma seqüência através das entradas do seu model. Apenas funciona para campos baseados em números ou datas.
class ImagesController extends AppController {
function view($id) {
// Diz o que é necessário para mostrar a imagem...
$this->set('image', $this->Image->findById($id);
// Mas também fala os links para a imagem anterior e próxima...
$this->set(
'vizinhos',
$this->Image->findNeighbours(null, 'id', $id);
)
}
}
class ImagesController extends AppController {function view($id) {// Diz o que é necessário para mostrar a imagem...$this->set('image', $this->Image->findById($id);// Mas também fala os links para a imagem anterior e próxima...$this->set('vizinhos',$this->Image->findNeighbours(null, 'id', $id);)}}
Isso nos retorna um array completo com $image[‘Image’], mas também com $vizinhos[‘prev’][‘Image’][‘id’] e $vizinhos[‘next’][‘Image’][‘id’] para ser usada na view.
Retorna o valor de um simples campo, especificado em $name, para o primeiro registro filtrado por $conditions e ordenado por $order.
Retorna o número de registros que seguem a $conditions. Use o parâmetro $recursive para que o CakePHP busque mais (ou menos) níveis dos models associados.
Esta função é um atalho para pegar a lista de chaves e seus valores – principalmente utilizada para criar select de HTML com a lista dos seus models. Use os parâmetros $conditions, $order e $limit, assim como você usa nas requisições com findAll().
Se $primaryKey e $displayField estiverem configuradas no seu model, você não precisa configurar os dois últimos parâmetros, pois eles agem como $keyPath e $valuePath, respectivamente. Além disso, se você não definir $keyPath e $valuePath, CakePHP tentará carregar as informações de ‘title’ ou ‘name’.
Os parâmetros $keyPath e $valuePath especifica qual será o campo para as chaves e qual o campo dos valores para gerar a lista. Por exemplo, se você deseja gerar a lista de roles baseada no model Role, chaveado com suas id’s inteiras, uma chamada completa pode ser:
$this->Role->generateList(
null,
'role_name ASC',
null,
'{n}.Role.id',
'{n}.Role.role_name'
);
// Isto vai retornar algo como:
array(
'1' => 'Head Honcho',
'2' => 'Marketing',
'3' => 'Department Head',
'4' => 'Grunt'
);
$this->Role->generateList(null,'role_name ASC',null,'{n}.Role.id','{n}.Role.role_name');// Isto vai retornar algo como:array('1' => 'Head Honcho','2' => 'Marketing','3' => 'Department Head','4' => 'Grunt');
Muitos estão perplexos pela sintaxe ‘{n}’ usada em generateList(). Fique frio, isto serve de suporte para alterar os models do DataSource, explicado mais adiante neste capítulo.
Chamadas personalizadas de SQLs podem ser feitas usando os métodos query() ou execute(). A diferença entre os dois é que query() é usada para fazer queries SQL personalizadas (o resultado será retornado) e execute() é usado para fazer comandos SQL personalizados (não retornará valor).
Se você está usando queries SQL na sua aplicação, lembre-se de usar a biblioteca de Limpeza de dados (descrita mais além deste manual), que ajuda na limpeza dos dados fornecidos pelo usuário, como injection code e ataques de scripts cross-site.
findAll
findAll(string $conditions, array $fields, string $order, int $limit, int $page, int $recursive);
findAll(string $conditions, array $fields, string $order, int $limit, int $page, int $recursive);
Returns the specified fields up to $limit records matching $conditions (if any), start listing from page $page (default is page 1). If there are no matching fields, an empty array is returned.
The $conditions should be formed just as they would in an SQL statement: $conditions = "Pastry.type LIKE '%cake%' AND Pastry.created_on > ‘2007-01-01’", for example. Prefixing conditions with the model’s name (‘Pastry.type’ rather than just ‘type’) is always a good practice, especially when associated data is being fetched in a query.
Setting the $recursive parameter to an integer forces findAll() to fetch data according to the behavior described in the Model Attributes $recursive section outlined earlier.
Data from findAll() is returned in an array, following this basic format:
Array
(
[0] => Array
(
[ModelName] => Array
(
[id] => 83
[field1] => value1
[field2] => value2
[field3] => value3
)
[AssociatedModelName] => Array
(
[id] => 1
[field1] => value1
[field2] => value2
[field3] => value3
)
)
[1] => Array
(
[ModelName] => Array
(
[id] => 85
[field1] => value1
[field2] => value2
[field3] => value3
)
[AssociatedModelName] => Array
(
[id] => 2
[field1] => value1
[field2] => value2
[field3] => value3
)
)
)
Array([0] => Array([ModelName] => Array([id] => 83[field1] => value1[field2] => value2[field3] => value3)[AssociatedModelName] => Array([id] => 1[field1] => value1[field2] => value2[field3] => value3))[1] => Array([ModelName] => Array([id] => 85[field1] => value1[field2] => value2[field3] => value3)[AssociatedModelName] => Array([id] => 2[field1] => value1[field2] => value2[field3] => value3)))
find
find(string $conditions, array $fields, string $order, int $recursive);
find(string $conditions, array $fields, string $order, int $recursive);
Same as in findAll(), except that find only returns the first record matching the supplied $conditions.
The find methods were recently consolidated and find() can now be used to fulfil all of your find-ing needs. There is a slightly different syntax to the calls:
find($type, $params);
find($type, $params);
$type is either 'all', 'list' or 'first'. First is the default find type.
$params is an array with any of the following available options as keys:
array(
'conditions' => array('Model.field' => $thisValue), //array of conditions
'recursive' => 1, //int
'fields' => array('Model.field1', 'Model.field2'), //array of field names
'order' => 'Model.created', //string or array defining order
'limit' => n, //int
'page' => n //int
) array('conditions' => array('Model.field' => $thisValue), //array of conditions'recursive' => 1, //int'fields' => array('Model.field1', 'Model.field2'), //array of field names'order' => 'Model.created', //string or array defining order'limit' => n, //int'page' => n //int)
If you are using find('list'), the 'fields' key in $params defines the key, value and group
// generated list will be indexed by Post.id, with value of Post.title
$this->Post->find('list', array('fields'=>'Post.title'));
// generated list will be indexed by Post.slug, with value of Post.title
$this->Post->find('list', array('fields'=>array('Post.slug', 'Post.title')));
// generated list will be grouped by Post.author_id, and each group indexed by Post.id, with value of Post.title
$this->Post->find('list', array('fields'=>array('Post.id', 'Post.title', 'Post.author_id')));
// generated list will be indexed by Post.id, with value of Post.title$this->Post->find('list', array('fields'=>'Post.title'));// generated list will be indexed by Post.slug, with value of Post.title$this->Post->find('list', array('fields'=>array('Post.slug', 'Post.title')));// generated list will be grouped by Post.author_id, and each group indexed by Post.id, with value of Post.title$this->Post->find('list', array('fields'=>array('Post.id', 'Post.title', 'Post.author_id')));
findAllBy
findAllBy<fieldName>(string $value)
findAllBy<fieldName>(string $value)
These magic functions can be used as a shortcut to search your tables by a certain field. Just add the name of the field (in CamelCase format) to the end of these functions, and supply the criteria for that field as the first parameter.
findBy
findBy<fieldName>(string $value);
findBy<fieldName>(string $value);
These magic functions can be used as a shortcut to search your tables by a certain field. Just add the name of the field (in CamelCase format) to the end of these functions, and supply the criteria for that field as the first parameter.
| PHP5 findAllBy<x> Example | Corresponding SQL Fragment |
|---|---|
| $this->Product->findAllByOrderStatus(‘3’); | Product.order_status = 3 |
| $this->Recipe->findAllByType(‘Cookie’); | Recipe.type = ‘Cookie’ |
| $this->User->findAllByLastName(‘Anderson’); | User.last_name = ‘Anderson’ |
| $this->Cake->findById(7); | Cake.id = 7 |
| $this->User->findByUserName(‘psychic’); | User.user_name = ‘psychic’ |
PHP4 users have to use this function a little differently due to some case-insensitivity in PHP4:
| PHP4 findAllBy<x> Example | Corresponding SQL Fragment |
|---|---|
| $this->Product->findAllByOrder_status(‘3’); | Product.order_status = 3 |
| $this->Recipe->findAllByType(‘Cookie’); | Recipe.type = ‘Cookie’ |
| $this->User->findAllByLast_name(‘Anderson’); | User.last_name = ‘Anderson’ |
| $this->Cake->findById(7); | Cake.id = 7 |
| $this->User->findByUser_name(‘psychic’); | User.user_name = ‘psychic’ |
The returned result is an array formatted just as it would be from find() or findAll().
findNeighbours
This shortcut method creates an array containing values helpful in generating 'Previous' and 'Next' links in a view.
The method determines which data rows to return based on the values submitted in the $field and $value parameters. Further refinement can be done with the $conditions parameter.
For example, if you call the function like this:
$conditions = array('Article.status' => 'published');
$field = array('date', 'id');
$value = '2008-03-24';
$this->Article->findNeighbours( $conditions, $field, $value ) );
$conditions = array('Article.status' => 'published');$field = array('date', 'id');$value = '2008-03-24';$this->Article->findNeighbours( $conditions, $field, $value ) );
The resulting array will contain values for the 'date' and 'id' fields from the articles who have a status of "published", and whose dates are just before and after the date '2008-03-24'.
// note that the comparison was made on date field, and that
// the id values were not used to determine neighboring data
Array
(
[prev] => Array ([Article] =>
Array ([date] => 2008-03-20, [id] => 99 )
),
[next] => Array ( [Article] =>
array( [date] => 2008-03-27, [id] => 15 )
)
);
// note that the comparison was made on date field, and that// the id values were not used to determine neighboring dataArray([prev] => Array ([Article] =>Array ([date] => 2008-03-20, [id] => 99 )),[next] => Array ( [Article] =>array( [date] => 2008-03-27, [id] => 15 )));
This method can also be called with the $field value being a single string. When an array is used, the first field listed will be the field used in the comparison query.
class ImagesController extends AppController {
function view($id) {
// Say we want to be able to show the image...
$this->set('image', $this->Image->findById($id);
// But we also want links to the previous and next images...
$this->set(
'neighbors',
$this->Image->findNeighbours(null, 'id', $id);
)
}
}
class ImagesController extends AppController {function view($id) {// Say we want to be able to show the image...$this->set('image', $this->Image->findById($id);// But we also want links to the previous and next images...$this->set('neighbors',$this->Image->findNeighbours(null, 'id', $id);)}}
This gives us the full $image['Image'] array, along with $neighbors['prev']['Image']['id'] and $neighbors['next']['Image']['id'] for use in the view.
field
field(string $name, string $conditions, string $order)
field(string $name, string $conditions, string $order)
Returns the value of a single field, specified as $name, from the first record matched by $conditions as ordered by $order.
findCount
findCount(string $conditions, int $recursive)
findCount(string $conditions, int $recursive)
Returns the number of records that match the given conditions. Use the $recursive parameter to have CakePHP fetch more (or fewer) levels of associated models.
generateList
generateList(string $conditions, string $order, int $limit, string $keyPath, string $valuePath)
generateList(string $conditions, string $order, int $limit, string $keyPath, string $valuePath)
This is deprecated and will be replaced by usage of find('list') on it's own or find('all') combined with a call to Set::combine().
This function is a shortcut to getting a list of key/value pairs - especially handy for creating an HTML select tag from a list of your models. Use the $conditions, $order, and $limit parameters just as you would for a findAll() request.
If $primaryKey and $displayField have been set in the model, you don’t need to supply the last two parameters, as they act as $keyPath and $keyValue, respectively. Additionally, if neither $keyPath nor $displayField have been supplied, CakePHP will try to load the information using ‘title’ or ‘name’.
The $keyPath and $valuePath specify 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->Role->generateList(
null,
'role_name ASC',
null,
'{n}.Role.id',
'{n}.Role.role_name'
);
//This would return something like:
array(
'1' => 'Head Honcho',
'2' => 'Marketing',
'3' => 'Department Head',
'4' => 'Grunt'
);
$this->Role->generateList(null,'role_name ASC',null,'{n}.Role.id','{n}.Role.role_name');//This would return something like:array('1' => 'Head Honcho','2' => 'Marketing','3' => 'Department Head','4' => 'Grunt');
Many people are a little bewildered by the ‘{n}’ syntax used by generateList(). Fret not, for it serves as a place holder for switching between model DataSources, covered later on in this chapter.
query and execute
query(string $query), execute(string $query)
query(string $query), execute(string $query)
Custom SQL calls can be made using the model's query() and execute() methods. The difference between the two is that query() is used to make custom SQL queries (the results of which are returned), and execute() is used to make custom SQL commands (which supply no return value).
If you’re ever using custom SQL queries in your application, be sure to check out CakePHP’s Sanitize library (covered later in this manual), which aids in cleaning up user-provided data from injection and cross-site scripting attacks.
Complex Find Conditions
Most of the model's find calls involve passing sets of conditions in one way or another. The simplest approach to this is to use a WHERE clause snippet of SQL. If you find yourself needing more control, you can use arrays.
Using arrays is clearer and easier to read, and also makes it very easy to build queries. This syntax also breaks out the elements of your query (fields, values, operators, etc.) into discreet, manipulatable parts. This allows CakePHP to generate the most efficient query possible, ensure proper SQL syntax, and properly escape each individual part of the query.
At it's most basic, an array-based query looks like this:
$conditions = array("Post.title" => "This is a post");
//Example usage with a model:
$this->Post->find($conditions);
$conditions = array("Post.title" => "This is a post");//Example usage with a model:$this->Post->find($conditions);
The structure here is fairly self-explanatory: it will find any post where the title equals "This is a post". Note that we could have used just "title" as the field name, but when building queries, it is good practice to always specify the model name, as it improves the clarity of the code, and helps prevent collisions in the future, should you choose to change your schema.
What about other types of matches? These are equally simple. Let's say we wanted to find all the posts where the title is not "This is a post":
array("Post.title" => "<> This is a post")
array("Post.title" => "<> This is a post")
Notice the '<>' that prefixes the expression. CakePHP can parse out any valid SQL comparison operator, including match expressions using LIKE, BETWEEN, or REGEX, as long as you leave a space between the operator and the value. The one exception here is IN (...)-style matches. Let's say you wanted to find posts where the title was in a given set of values:
array(
"Post.title" => array("First post", "Second post", "Third post")
)
array("Post.title" => array("First post", "Second post", "Third post"))
Adding additional filters to the conditions is as simple as adding additional key/value pairs to the array:
array
(
"Post.title" => array("First post", "Second post", "Third post"),
"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
)
array("Post.title" => array("First post", "Second post", "Third post"),"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks")))
By default, CakePHP joins multiple conditions with boolean AND; which means, the snippet above would only match posts that have been created in the past two weeks, and have a title that matches one in the given set. However, we could just as easily find posts that match either condition:
array
("or" =>
array
(
"Post.title" =>
array("First post", "Second post", "Third post"),
"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
)
)
array("or" =>array("Post.title" =>array("First post", "Second post", "Third post"),"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))))
Cake accepts all valid SQL boolean operations, including AND, OR, NOT, XOR, etc., and they can be upper or lower case, whichever you prefer. These conditions are also infinitely nest-able. Let's say you had a hasMany/belongsTo relationship between Posts and Authors, which would result in a LEFT JOIN
login to add a comment