2010-02-22 14 views
11

Tengo una aplicación CakePHP 1.3 y realmente disfruto de Containable behavior para buscar datos.CakePHP Modelo: COUNT (*) en Containable

Supongamos que tengo publicaciones en relación uno a muchos con comentarios. Yo uso Containable para consultar (para paginación) una lista de todas las Publicaciones y los Comentarios que pertenecen. Pero solo estoy interesado en cuántos comentarios tiene cada publicación. No encontré ninguna forma de lograr esta consulta con contenido sin obtener todas las filas de Comentarios. Intenté:

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

resultados en 'Model "Comentario" no está asociado con el modelo "Count"' mensaje de error.

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

no funciona, conjunto de resultados contiene información para cada puesto una matriz comentario a vacío a excepción de la última, donde se contiene el campo de cuenta, pero tener el número de todos los comentarios y no sólo los que pertenecen.

Mi otra suposición era

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

pero esto resulta en un error, porque las relaciones hasMany se consultan de forma independiente, por lo que la mesa de respuesta no está en la consulta para las entradas de correos. ¿Cómo puedo contar la cantidad de comentarios que tiene una publicación?

Respuesta

11

Bueno, la mejor manera de hacer esto es la creación de un campo llamado comment_count en la tabla posts y añadir esta clave a la Observación modelo $ belongsTo Mensaje matriz:

'counterCache' => true

Cada vez que algo va Con los comentarios, el campo comment_count en la publicación relacionada se actualizará (en realidad, se repite cada vez, en lugar de solo agregar o eliminar).

Es mejor, porque cuando obtiene datos para el usuario, es mucho más rápido y ni siquiera toca la tabla de comentarios. Dado que estás usando el comportamiento Containable, supongo que lo que estás buscando es velocidad y peso ligero.

+3

Esa es una buena idea, pero no resuelve el problema para consultas más complejas donde no funcionaría el mantenimiento de un recuento asociado a una fila (por ejemplo, un sistema de informes donde el usuario elige determinadas condiciones). ¿No hay otra forma de obtener un conteo usando Containable? – Zxaos

6

Tenía la misma pregunta. La respuesta que dio PawelMysior fue excelente y condujo a la solución.

Encontré algunos más detalles en el libro de CakePHP: 3.7.4.1.1 counterCache - Cache your count(). Esto fue útil, pero aún un poco vago. Por el bien de los demás, quiero brindar más detalles.

Tengo dos tablas: Publicación e Imagen. Cuando agregué una imagen a una publicación, quiero hacer un seguimiento de la cantidad de imágenes que cada publicación tenía para mostrar en la lista de índice de publicaciones, sin necesidad de ejecutar una consulta de conteo adicional en la tabla de imágenes.

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; 

en cuenta que la image_count va en la tabla posts. No se necesita nada especial en la tabla 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 
     ) 
    ); 
} 

en cuenta que counterCache se añade al modelo Image. No se necesita nada especial en el modelo 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') 
     ) 
    ) 
)); 

Ahora, cuando hacemos un find no es necesario hacer nada especial para encontrar la cuenta, ya que es un campo de forma automática en los Post resultados. Observe el image_count a continuación.

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 a tener en cuenta, si se agrega counterCache a una estructura de base de datos existente, los recuentos en Post será cero hasta que se añada otra Image. En el punto, CakePHP hará un conteo real y actualizará el image_count a la cantidad correcta.

Espero que esta información adicional sea útil para alguien.

Cuestiones relacionadas