3.7.3 Récupérer vos données

3.7.3.1 find

find($type, $params)

$type est soit 'all', 'first', 'count', 'list', 'neighbors' ou 'threaded'. 'first' est le type de find par défaut.

$params est un tableau avec toutes les options disponibles suivantes commes clés :

array(
	'conditions' => array('Model.champ' => $cetteValeur), // tableau de conditions
	'recursive' => 1, // entier
	'fields' => array('Model.champ', 'Model.champ2'), // tableau de nom de champs
	'order' => 'Model.id', // chaîne ou tableau définissant le ORDER BY
	'group' => array('Model.champ'), // champs pour le GROUP BY
	'limit' => n, // entier
	'page' => n // entier
)
  1. array(
  2. 'conditions' => array('Model.champ' => $cetteValeur), // tableau de conditions
  3. 'recursive' => 1, // entier
  4. 'fields' => array('Model.champ', 'Model.champ2'), // tableau de nom de champs
  5. 'order' => 'Model.id', // chaîne ou tableau définissant le ORDER BY
  6. 'group' => array('Model.champ'), // champs pour le GROUP BY
  7. 'limit' => n, // entier
  8. 'page' => n // entier
  9. )

Si vous utilisez find('list'), la clé 'fields' dans $params définit la clé, la valeur et le groupe

// la liste générée sera indexée par Post.id, avec la valeur de Post.titre
$this->Post->find('list', array('fields'=>'Post.titre'));
 
// la liste générée sera indexée par Post.slug, avec la valeur de Post.titre
$this->Post->find('list', array('fields'=>array('Post.slug', 'Post.titre')));
 
// la liste générée sera groupée par Post.auteur_id et chaque groupe indexé (alias optgroup) par Post.id, avec la valeur de Post.titre
$this->Post->find('list', array('fields'=>array('Post.id', 'Post.titre', 'Post.author_id')));
  1. // la liste générée sera indexée par Post.id, avec la valeur de Post.titre
  2. $this->Post->find('list', array('fields'=>'Post.titre'));
  3. // la liste générée sera indexée par Post.slug, avec la valeur de Post.titre
  4. $this->Post->find('list', array('fields'=>array('Post.slug', 'Post.titre')));
  5. // la liste générée sera groupée par Post.auteur_id et chaque groupe indexé (alias optgroup) par Post.id, avec la valeur de Post.titre
  6. $this->Post->find('list', array('fields'=>array('Post.id', 'Post.titre', 'Post.author_id')));

Si vous voulez utilisez un label d'optgroup à partir d'une table parente, assurez-vous de définir 'recursive'=>1 dans les paramètres.

Si vous utilisez find('neighbors'), une clé 'field' dans $params définit le champ à analyser et une clé 'value', la valeur à rechercher pour déterminer le suivant (next) et le précédent (prev). Notez que les clés 'field' et 'value' ne sont pas utilisées par find('all'), ceci est un cas spécial de find('neighbors').

// considérons que nous avons des id de 1 à 10, nous verrons "prev" défini à 1 et "next" à 3
$this->Post->id = 2;
$one = $this->Post->find('neighbors');
// pour obtenir les données voisines d'un autre champ
$two = $this->Post->find('neighbors', array('field'=>'Post.titre', 'value'=>$data['Post']['titre']));
  1. // considérons que nous avons des id de 1 à 10, nous verrons "prev" défini à 1 et "next" à 3
  2. $this->Post->id = 2;
  3. $one = $this->Post->find('neighbors');
  4. // pour obtenir les données voisines d'un autre champ
  5. $two = $this->Post->find('neighbors', array('field'=>'Post.titre', 'value'=>$data['Post']['titre']));

Pour rétro-compatibilité, find accepte aussi l'ancienne syntaxe :

find(string $conditions, array $fields, string $order, int $recursive)

3.7.3.2 findAll

findAll(string $conditions, array $fields, string $order, int $limit, int $page, int $recursive)

findAll a été dépréciée, utilisez find('all') à la place.

Retourne les champs $fields spécifiés, jusqu'à $limit enregistrement(s) vérifiant les $conditions (s'il y en a), en commençant à la page $page (par défaut c'est la page 1). S'il n'y a aucun champ trouvé, un tableau vide est retourné.

Les $conditions devraient juste être formulées telles qu'elles le seraient dans une déclaration SQL : $conditions = "Patisserie.type LIKE '%cake%' AND Patisserie.date_creation > '2007-01-01'", par exemple. En préfixant les conditions avec le nom du modèle ('Patisserie.type' plutôt que seulement 'type') est toujours une bonne pratique, spécialement lorsque les données associées sont destinées à être récupérées dans une requête.

Définir le paramètre $recursive par un entier force findAll() à récupérer les données en respectant le comportement décrit dans la section de l'attribut de Modèle $recursive exposée avant. N'oubliez pas d'ajouter manuellement les colonnes clé étrangère requises au tableau $fields tel que décrit ici.

Les données issues de findAll() sont retournées dans un tableau suivant ce format basique :

Array
(
    [0] => Array
        (
            [NomModele] => Array
                (
                    [id] => 83
                    [champ1] => valeur1
                    [champ2] => valeur2
                    [champ3] => valeur3
                )

            [NomModeleAssocie] => Array
                (
                    [id] => 1
                    [champ1] => valeur1
                    [champ2] => valeur2
                    [champ3] => valeur3
                )
        )
    [1] => Array
        (
            [NomModele] => Array
                (
                    [id] => 85
                    [champ1] => valeur1
                    [champ2] => valeur2
                    [champ3] => valeur3
                )

            [NomModeleAssocie] => Array
                (
                    [id] => 2
                    [champ1] => valeur1
                    [champ2] => valeur2
                    [champ3] => valeur3
                )
        )
)

3.7.3.3 findAllBy

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.

3.7.3.4 findBy

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

3.7.3.5 findNeighbours

findNeighbours(string $conditions, mixed $field, string $value)

findNeighbours has been deprecated, use find('neighbors') instead.

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 ) );
  1. $conditions = array('Article.status' => 'published');
  2. $field = array('date', 'id');
  3. $value = '2008-03-24';
  4. $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'.

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

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);
        )
    }
}
  1. class ImagesController extends AppController {
  2. function view($id) {
  3. // Say we want to be able to show the image...
  4. $this->set('image', $this->Image->findById($id);
  5. // But we also want links to the previous and next images...
  6. $this->set(
  7. 'neighbors',
  8. $this->Image->findNeighbours(null, 'id', $id);
  9. )
  10. }
  11. }

This gives us the full $image['Image'] array, along with $neighbors['prev']['Image']['id'] and $neighbors['next']['Image']['id'] for use in the view.

3.7.3.6 query

query(string $query)

Custom SQL calls can be made using the model's query() method.

If you’re ever using custom SQL queries in your application, be sure to check out CakePHP’s Sanitize library, which aids in cleaning up user-provided data from injection and cross-site scripting attacks.

query() uses the table name in the query as the array key for the returned data, rather than the model name. For example,

$this->Picture->query("SELECT * FROM pictures LIMIT 2;");
  1. $this->Picture->query("SELECT * FROM pictures LIMIT 2;");

might return

Array
(
    [0] => Array
        (
            [pictures] => Array
                (
                    [id] => 1304
                    [user_id] => 759
                )
        )

    [1] => Array
        (
            [pictures] => Array
                (
                    [id] => 1305
                    [user_id] => 759
                )
        )
)
  1. Array
  2. (
  3. [0] => Array
  4. (
  5. [pictures] => Array
  6. (
  7. [id] => 1304
  8. [user_id] => 759
  9. )
  10. )
  11. [1] => Array
  12. (
  13. [pictures] => Array
  14. (
  15. [id] => 1305
  16. [user_id] => 759
  17. )
  18. )
  19. )

To use the model name as the array key, and get a result consistent with that returned by the Find methods, the query can be rewritten:

$this->Picture->query("SELECT * FROM pictures AS Picture LIMIT 2;");
  1. $this->Picture->query("SELECT * FROM pictures AS Picture LIMIT 2;");

which returns

Array
(
    [0] => Array
        (
            [Picture] => Array
                (
                    [id] => 1304
                    [user_id] => 759
                )
        )

    [1] => Array
        (
            [Picture] => Array
                (
                    [id] => 1305
                    [user_id] => 759
                )
        )
)
  1. Array
  2. (
  3. [0] => Array
  4. (
  5. [Picture] => Array
  6. (
  7. [id] => 1304
  8. [user_id] => 759
  9. )
  10. )
  11. [1] => Array
  12. (
  13. [Picture] => Array
  14. (
  15. [id] => 1305
  16. [user_id] => 759
  17. )
  18. )
  19. )

3.7.3.7 generateList

generateList(string $conditions, string $order, int $limit, string $keyPath, string $valuePath)

generateList is deprecated and replaced by usage of find('list'), 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'
);
  1. $this->Role->generateList(
  2. null,
  3. 'role_name ASC',
  4. null,
  5. '{n}.Role.id',
  6. '{n}.Role.role_name'
  7. );
  8. //This would return something like:
  9. array(
  10. '1' => 'Head Honcho',
  11. '2' => 'Marketing',
  12. '3' => 'Department Head',
  13. '4' => 'Grunt'
  14. );

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.

3.7.3.8 findCount

findCount(string $conditions, int $recursive)

This method has been deprecated, use find('count').

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.

3.7.3.9 field

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.

3.7.3.10 Conditions de recherche complexes

La plupart des appels de recherche de modèles impliquent le passage d’un jeu de conditions d’une manière ou d’une autre. L’approche la plus simple pour cela est d’utiliser un bout de clause WHERE SQL. Si vous vous retrouvez à avoir besoin de plus de contrôle, vous pouvez utiliser des tableaux.

L’utilisation de tableaux est plus claire et simple à lire, et rend également la construction de requêtes très simple. Cette syntaxe sépare également les éléments de votre requête (champs, valeurs, opérateurs etc.) en parties manipulables et discrètes. Cela permet à CakePHP de générer les requêtes les plus efficaces possibles, d’assurer une syntaxe SQL correcte, et d’échapper convenablement chaque partie de la requête.

Dans sa forme la plus simple, une requête basée sur un tableau ressemble à ceci :

$conditions = array("Billet.titre" => "Ceci est un billet");

//Exemple d’utilisation avec un modèle:
$this->Billet->find($conditions);
  1. $conditions = array("Billet.titre" => "Ceci est un billet");
  2. //Exemple d’utilisation avec un modèle:
  3. $this->Billet->find($conditions);

La structure ici est assez significative : cela va trouver tous les billets dont le titre à pour valeur « Ceci est un billet ». Notons que nous aurions pu utiliser juste « titre » comme nom de champ, mais lorsque l’on construit des requêtes, c’est une bonne pratique que de toujours spécifier le nom du modèle. Cela améliore la clarté du code, et aide à prévenir des collisions futures, si vous deviez changer votre schéma.

Qu’en est-il des autres types de correspondances ? Ils sont aussi simples. Disons que nous voulons trouver tous les billets dont le titre n’est pas "Ceci est un billet" :

array("Billet.titre" => "<> Ceci est un billet")
  1. array("Billet.titre" => "<> Ceci est un billet")

Notez le '<>' qui précède l’expression. CakePHP peut parser tout opérateur de comparaison valide de SQL, même les expressions de correspondance utilisant LIKE, BETWEEN, ou REGEX, tant que vous laissez un espace entre l'opérateur et la valeur. La seule exception à ceci sont les correspondance du genre IN(...). Admettons que vous vouliez trouver les billets dont le titre appartient à un ensemble de valeur données :

array(
    "Billet.titre" => array("Premier billet", "Second billet", "Troisième billet")
)
  1. array(
  2. "Billet.titre" => array("Premier billet", "Second billet", "Troisième billet")
  3. )

Ajouter des filtres additionnels aux conditions est aussi simple que d’ajouter des paires clé/valeur au tableau :

array
(
    "Billet.titre" => array("Premier billet", "Second billet", "Troisième billet"),
    "Billet.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
)
  1. array
  2. (
  3. "Billet.titre" => array("Premier billet", "Second billet", "Troisième billet"),
  4. "Billet.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
  5. )

Par défaut, CakePHP joint les conditions multiples avec l’opérateur booléen AND, ce qui signifie que le bout de code ci-dessus correspondra uniquement aux billets qui ont été créés durant les deux dernières semaines, et qui ont un titre correspondant à ceux donnés. Cependant, nous pouvons de manière aussi simple trouver les billets qui correspondent à l’une ou l’autre des conditions :

array
("or" =>
    array
    (
	"Billet.titre" => array("Premier billet", "Second billet", "Troisième billet"),
    	"Billet.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
    )
)
  1. array
  2. ("or" =>
  3. array
  4. (
  5. "Billet.titre" => array("Premier billet", "Second billet", "Troisième billet"),
  6. "Billet.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
  7. )
  8. )

Cake accepte toute opération booléenne SQL valide, telles que AND, OR, NOT, XOR, etc., et elles peuvent être en majuscule comme en minuscule, comme vous préférez. Ces conditions sont également infiniment "NEST-ABLE". Admettons que vous ayez une relation hasMany/belongsTo entre Billets et Auteurs, ce qui reviendrait à un LEFT JOIN. Admettons aussi que vous vouliez trouver tous les billets qui contiennent un certain mot-clé "magique" ou qui a été créé au cours des deux dernières semaines, mais que vous voulez restreindre votre recherche aux billets écrits par Bob :

array 
("Auteur.nom" => "Bob", "or" => array
    (
        "Billet.titre" => "LIKE %magique%",
        "Billet.created" => "> " . date('Y-m-d', strtotime("-2 weeks")
    )
)
  1. array
  2. ("Auteur.nom" => "Bob", "or" => array
  3. (
  4. "Billet.titre" => "LIKE %magique%",
  5. "Billet.created" => "> " . date('Y-m-d', strtotime("-2 weeks")
  6. )
  7. )