Bonjour à tous,

Voila je cherche à modifier le resultat d'une requête, notamment pour intégrer dans l'object un nouveau champ.
Voici un exemple:

Je fais une requête pour récupérer l'ensemble de mes articles :

$query = $this->Posts->find('all');

Ce qui va me donner un object avec plusieurs items, qui eux-mêmes contiennent plusieurs champs (relatif à ma base de données). Mon but est de pouvoir ajouter un champ à la volée après la requête (ou durant). Je ne vois pas comment faire cela.

Par avance merci pour votre aide.

13 réponses


Blinkers
Réponse acceptée

Hello,

Le champ que tu veux ajouter provient d'une autre table ?
Tu peux soit utiliser les champs virtuelles (à définir dans ton modèle)
http://book.cakephp.org/2.0/fr/models/virtual-fields.html

Soit utiliser Containable si tu a bien fait tes liaisons entre tes tables :
http://book.cakephp.org/2.0/fr/core-libraries/behaviors/containable.html

Soit utiliser une boucle foreach sur ton premier find et injecter d'autre "champs".

PS : CakePHP 2 ou 3 ?

Majid Cameleon
Réponse acceptée

'Posts' non lu pour chaque 'Websites' et 'Categories'
Ajout un countercache à tes tables websites et categories.
CounterCache

Pour ma part, j'utilise de plus en plus des vues SQL.
Je m'explique au départ, je m'embrouillait avec tout mes models et leurs relations et des containble à en plus finir.
L'avantage avec une vue c'est que tu peu combiner n'importe qu'elles de tes tables. Elle sera ensuite disponible sous CakePHP comme une seule table.

Un exemple d'une vue

SELECT Post.id, referenceID, content, created, modified, like_count, pin_count, comment_count, authorType, authorName, GROUP_CONCAT(tags.label) AS tags
FROM posts AS Post
JOIN 
(SELECT posts_vendors.post_id, posts_vendors.store_id as referenceID, 'store' as authorType, stores.storename as authorName
FROM posts_vendors
JOIN stores ON posts_vendors.store_id = stores.id
UNION
SELECT posts_customers.post_id, posts_customers.user_id, 'customer', users.username
FROM posts_customers
JOIN users ON posts_customers.user_id = users.id) AS listName ON Post.id = listName.post_id
LEFT OUTER JOIN posts_tags ON Post.id = posts_tags.post_id
LEFT OUTER JOIN tags ON posts_tags.tag_id = tags.id
GROUP BY Post.id;

Cette simple requête SQL aura combiner 4 tables en une seul et unique table.
Dans le détails
Le premier SELECT correspond aux champs de ma vu:
=> Je peu ajouter autant de champs que je veux
=> Je peu les nommés indépendamment des champs des tables sources, ce qui compte c'est l'ordre d'apparution des colonnes dans chaque SELECT.
=> Respecter le nombres de colonnes à afficher dans chaque SELECT.
Je récupère tout les posts, ceux des vendeurs(posts_vendors) et ceux des clients(posts_customers).
Si c'est un vendeur, mon champs virtuel (authorType) prend 'store' ou 'customer' pour un client
Je récupère les tags de chacun de ces posts, que je concatène et j'obtient 'montag1, montag2' ou 'null' si le post n'a pas de tag.

Il ne me reste plus qu'à créer le model dans CakePHP pour cette vue, qui sera utiliser comme une table.
Par contre pour les sauvegarde ça se fait directement dans les tables.
Les vues sont en somme utilisé pour fait une synthése de tes tables.

Merci pour vos réponses.

J'utilise cakePHP3.

Mon idée était effectivement d'insérer de nouvelles données dans mon premier find, au sein du controller.

Voici mon cas concret :

J'ai 3 tables liées entre elles (les liaisons fonctionne bien), une table 'Posts', une 'Websites', et une 'Categories'. Je parviens à afficher toutes mes 'Categories', avec à l'intérieure tous les 'Websites' (un 'Websites' appartient à une 'Categories'). Je souhaite ensuite afficher le nombre de 'Posts' (un 'Posts' appartient à un 'Websites') non lu pour chaque 'Categories' et 'Websites'. Pour cela j'ai une variable 'reading' sur chaque 'Posts' pour indiquer s'il a été lu ou pas.

Mon but était donc d'insérer le nombre de 'Posts' non lu pour chaque 'Websites' et 'Categories', au sein de ma requête. Il y a peut être une méthode plus adéquate ?

Effectivement le CounterCache semble plus approprié. Néanmoins je n'arrive pas à le mettre en place.

Voici mes models liés avec le CounterCache :

class PostsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Websites',[
                'className' => 'websites',
                'foreignKey' => 'websites_id']
        );

        $this->addBehavior('CounterCache', [
            'Websites' => [
                'nb_unread' => [
                    'conditions' => ['Posts.reading' => 0]
                ]
            ]
        ]);
    }
}
class WebsitesTable extends Table
{

    public function initialize(array $config)
    {
        $this->hasMany('Posts',[
                'className' => 'posts',
                'foreignKey' => 'websites_id']
        );
    }
}

Lorsque je suis dans mon foreach pour 'Websites', la variable $website->nb_unread est null.
De plus, après avoir mis en place le CounterCache, cette action de mon controller me retourne une erreur :

    public function read(){
        $this->loadModel('Posts');

            $id = $this->request->data['id'];

            $postsTable = TableRegistry::get('Posts');
            $post = $postsTable->get($id);

            $post->reading = 1;

            $postsTable->save($post);
    }

Erreur :

Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'nb_unread' in 'field list'

nb_unread est lu sur ta table website, pas sur ta table posts.

Oui, effectivement.
J'ai intégré directement le CountercCache dans ma relation. En le sortant de ma relation j'avais cette erreur :

Error: Call to a member function foreignKey() on a non-object 

Warning (4096): Argument 3 passed to Cake\ORM\Behavior\CounterCacheBehavior::_processAssociation() must be an instance of Cake\ORM\Association, null given, called in /Applications/MAMP/htdocs/Readat/vendor/cakephp/cakephp/src/ORM/Behavior/CounterCacheBehavior.php on line 122 and defined [CORE/src/ORM/Behavior/CounterCacheBehavior.php, line 135]

Néanmoins je n'arrive toujours pas à accéder au sein de ma vue à ma variable nb_unread.

class PostsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Websites',[
                'className' => 'websites',
                'foreignKey' => 'websites_id',
                'counterCache' => array(
                    'nb_unread' => array('Posts.reading' => 0),
                )]
        );
    }
}

Merci pour ton aide.

C'est en récuperant(find) website, que tu aura le nombre de posts non lu pour chacun des websites.
Et c'est pas une variable que tu récupére, mais un champs (résultant de ta requête).

Oui, lorsque je fait cette requête :

$websites = $this->Websites->find('all');

Dans mon foreach, en débeugant l'objet envoyé par le controller je n'ai aucune réfférence au champs 'nb_unread', qui devrait être le résultat du countercache. Voila ce que j'obtient :

object(Cake\ORM\Entity) {

    'id' => (int) 2,
    'title' => 'siteweb.com',
    'url' => 'http://www.siteweb.com/',
    'favicon' => 'http://www.siteweb.com/favicon.ico',
    'picture' => 'https://siteweb/img.jpg',
    'categories_id' => (int) 2,
    '[new]' => false,
    '[accessible]' => [
        '*' => true
    ],
    '[dirty]' => [],
    '[original]' => [],
    '[virtual]' => [],
    '[errors]' => [],
    '[repository]' => 'Websites'

}

Plus de message d'erreur ?
La mise à jour auto de 'nb_unread' dans ta table website fonctionne, countercache est fonctionnel donc ?
Si oui, alors est-il possible que l'option 'fields' de ta requête ne récupére pas 'nb_unread' ?

Non, je n'ai plus de message d'erreur.
Comment savoir si le CounterCache fonctionne ? Je ne vois aucune réfférence au CounterCache ni à nb_unread dans mes requêtes via le debug.

Ben tu regarde dans ta base de donnée si le champs nb_unread est mise à jour.

Après avoir supprimé le cache, cela fonctionne bien.
Merci beaucoup pour ton aide.