2010-02-22 4 views
11

Ho un'applicazione CakePHP 1.3 e mi piace davvero il Containable behavior per il recupero dei dati.CakePHP Model: COUNT (*) in Containable

Supponiamo di avere post nella relazione uno-a-molti con commenti. Uso Contenibile per interrogare (per impaginazione) un elenco di tutti i Post e dei Commenti di appartenenza. Ma sono interessato solo a quanti commenti ha ogni post. Non ho trovato alcun modo per raggiungere questa query contenibile senza recuperare tutte le righe di commenti. Ho provato:

$this->paginate=array(
      'fields' => 'Post.title, Post.created', 
      'contain' => array('Comment'=>'COUNT(*) AS count'), 
     ); 

risultati in "Modello" Commento "non è associato al messaggio di errore" Conteggio "del modello.

$this->paginate=array(
      'fields' => array('Post.title, Post.created'), 
      'contain' => array('Comment'=>array('fields'=>'COUNT(*) AS count'), 
     ); 

non funziona, set di risultati contiene per ogni post un array commento vuoto tranne l'ultimo, dove contiene il campo di conteggio, ma avere il numero di tutte le osservazioni non solo quelli appartenenti.

mia altra ipotesi era

$this->paginate=array(
      'fields' => 'Post.title, Post.created, COUNT(Comment.id)', 
      'contain' => array('Comment'=>array('fields'=>''), 
     ); 

ma questo si traduce in un errore, perché le relazioni hasMany vengono interrogati in modo indipendente, in modo che la tabella di risposta non è nella query per le voci sul post. Come posso contare il numero di commenti di un post?

risposta

11

Beh, il modo migliore per farlo è quello di creare un campo denominato comment_count nella tabella posts e aggiungere questa chiave al modello Commento $ belongsTo Messaggio matrice:

'counterCache' => true

Ogni volta che nulla sarà sta accadendo con i commenti, il campo comment_count nel post correlato verrà aggiornato (in realtà, ogni volta lo racconta, invece di aggiungere o eliminare semplicemente).

È meglio, perché quando recuperi i dati per l'utente, la sua strada è più veloce e non tocca nemmeno la tabella dei commenti. Dal momento che stai utilizzando il comportamento Contenente, immagino che la velocità e la leggerezza siano ciò che stai cercando.

+3

Questa è una buona idea, ma non risolve il problema per query più complesse in cui il mantenimento di un conteggio associato a una riga non funzionerebbe (ad esempio un sistema di segnalazione in cui determinate condizioni vengono scelte dall'utente). Non c'è altro modo per ottenere un conteggio utilizzando contenibile? – Zxaos

6

Ho avuto la stessa domanda. La risposta che PawelMysior ha dato è stata grande e ha portato alla soluzione.

Ho trovato ulteriori dettagli nel libro CakePHP: 3.7.4.1.1 counterCache - Cache your count(). Questo è stato utile ma ancora un po 'vago. Per il bene degli altri, voglio fornire ulteriori dettagli.

Ho due tabelle: Posta e Immagine. Quando ho aggiunto un'immagine a un post, voglio tenere traccia del numero di immagini che ogni post aveva per la visualizzazione nell'elenco degli indici Post, senza la necessità di eseguire una query di conteggio extra nella tabella Immagine.

CREATE TABLE posts 
(
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, 
user_id INTEGER UNSIGNED NOT NULL, 
title VARCHAR(255), 
body TEXT, 
created DATETIME, 
modified DATETIME, 
image_count INTEGER UNSIGNED, 
PRIMARY KEY (id) 
) ENGINE=InnoDB; 

CREATE TABLE images 
(
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, 
post_id INTEGER UNSIGNED NOT NULL, 
filename VARCHAR(255), 
created DATETIME, 
modified DATETIME, 
PRIMARY KEY (id) 
) ENGINE=InnoDB; 

Si noti che il image_count va nella tabella posts. Nulla di speciale è necessario nella tabella images.

class Post extends AppModel 
{ 
    var $name = 'Memory'; 
    var $hasMany = array(
     'Image' => array(
      'className' => 'Image', 
      'foreignKey' => 'post_id', 
      'dependent' => false 
     ) 
    ); 
} 

class Image extends AppModel 
{ 
    var $name = 'Image'; 
    var $belongsTo = array(
     'Post' => array(
      'className' => 'Post', 
      'foreignKey' => 'post_id', 
      'counterCache' => true 
     ) 
    ); 
} 

noti che counterCache si aggiunge al modello Image. Nulla di speciale è necessario nel modello Post.

$this->Post->Behaviors->attach('Containable'); 
$post = $this->Post->find('all', array(
    'conditions' => array('user_id' => 7), 
    'order' => array('Post.created DESC'), 
    'contain' => array(
     'Post' => array(
      'User' => array('first_name', 'last_name') 
     ) 
    ) 
)); 

Ora, quando facciamo un find non c'è bisogno di fare nulla di speciale per trovare il conteggio perché è automaticamente un campo nelle Post risultati. Si noti lo image_count qui sotto.

Array 
(
[Post] => Array 
     (
      [0] => Array 
       (
        [id] => 14 
        [user_id] => 7 
        [title] => Another great post 
        [body] => Lorem ipsum... 
        [created] => 2011-10-26 11:45:05 
        [modified] => 2011-10-26 11:45:05 
        [image_count] => 21 
        [User] => Array 
        (
         [first_name] => John 
         [last_name] => Doe 
        ) 
       ) 
     ) 
) 

Una cosa da notare, se si aggiunge counterCache ad una struttura di database esistente, i conti in Post sarà pari a zero fino a quando viene aggiunto un altro Image. Al momento, CakePHP eseguirà un conteggio effettivo e aggiornerà lo image_count alla quantità corretta.

Spero che questa informazione extra sia utile per qualcuno.