2009-08-03 11 views
1

Je souhaite afficher le nombre de commentaires pour chaque article de blog (avec la catégorie, la date, l'auteur, etc.) sur une page contenant une liste d'articles. Comment écrire la requête mysql suivante dans propel?Requête Sql (jointure interne + compte + grouper par) en utilisant Propel

SELECT post.id, post.title, post.more_columns , COUNT(comments.post_id) AS numofcomments FROM post INNER JOIN comments ON post.id = comments.post_id GROUP BY post.id, post.title, post.more_columns 

où poste est une table de blog et les commentaires, une table des commentaires avec post_id comme une clé étrangère à post.id. Je ne peux pas sembler obtenir «numofcomments» comme une colonne dans le jeu de résultats. Actuellement, je suis en utilisant une approche non-ORM (qui sera mon dernier recours):

$con = Propel::getConnection(PostPeer::DATABASE_NAME); 

    $sql = "SELECT post.* , COUNT(comments.post_id) AS numcomments FROM post INNER JOIN comments ON post.id = comments.post_id GROUP BY post.id"; 
    $stmt = $con->prepare($sql); 
    $stmt->execute(); 

    $result = PostPeer::populateObjects($stmt); 
    return $result; 

Comment puis-je accéder à « numofcomments » dans le Propel résultant ResultSet?

EDIT: Ce que je voulais savoir, c'est comment je peux écrire la requête ci-dessus dans Propel? Ce que je peux faire maintenant, c'est obtenir la table des messages avec la jointure interne sur la table des commentaires, puis exécuter doCount sur la table des commentaires pour chaque post-id. Cela entraîne 1 requête pour la table Post et de nombreuses requêtes pour la table des commentaires. Je souhaite réduire les requêtes sql au minimum. Merci :)

Répondre

0

Eh bien c'est ce qui a fonctionné mais c'est une triste façon de le faire: |

//inside a static function in class PostPeer: 
$c = new Criteria(); 
self::addSelectColumns($c); //add all columns from PostPeer 
$c->addJoin(PostPeer::ID, CommentPeer::POST_ID, Criteria::LEFT_JOIN); 
$cu->addAsColumn('numofcomments', 'COUNT('.CommentPeer::POST_ID.')'); 
$cu->addGroupByColumn(PostPeer::ID); 
$cu->addGroupByColumn(PostPeer::TITLE); 
: 
: 
//more group-by columns if needed 
return PostPeer::doSelectStmt($c); 

Ensuite, dans le modèle i accéder au tableau en tant que:

<div id="title"> 
    <?php echo post_array[1] ?> 
</div> 
//...and so on for other Post fields 

Comment puis-je faire une association de tableau dans le modèle afin que je puisse écrire "post_array [ 'title']" dans le modèle au lieu de " post_array [1] "? En outre, cette solution de contournement est-elle sécurisée? Toute meilleure suggestion. J'utilise Propel 1.3

1

SELECT post.* , COUNT(comments.post_id) AS numcomments FROM post INNER JOIN comments ON post.id = comments.post_id GROUP BY post.id,post.secondcol,post.thirdcol; et ainsi de suite, la liste que toutes les colonnes individuelles dans votre table de poste, puisque vous sélectionnez après. *, Vous devez les énumérer tous, et pas seulement post.post_id.

Alternativly

SELECT post.*,sub.numcomments from post, 
(select post.post_id as post_id,COUNT(comments.post_id) AS numcomments 
    FROM post INNER JOIN comments ON post.id = comments.post_id GROUP BY post.id) as sub 
    where post.post_id = sub.post_id; 

(Il y a une façon plus facile et 3. ainsi que j'oublié maintenant ..)

+0

Merci @nos ...c'était un oubli fondamental de ma part de ne pas écrire correctement la requête groupée, mais la question est différente. Je l'ai édité ... pouvez-vous vérifier à nouveau? :) – fenderplayer

0

Voici la version Propel de votre requête SQL.

$c = new Criteria(); 
// count comments with 
$c->addAsColumn('numofcomments', 'COUNT('.CommentPeer::POST_ID.')'); 
$c->addJoin(PostPeer::ID, CommentPeer::POST_ID, Criteria::LEFT_JOIN); 
$c->addGroupByColumn(PostPeer::ID); 
$c->addGroupByColumn(PostPeer::TITLE); 
// you can add more groupby column here 
//$c->addGroupByColumn(...more); 
$this->posts = PostPeer::doSelect($c); 

Vous devez utiliser LEFT JOIN à la place INNER JOIN. Car. Avec la jointure interne, vous ne pouvez accéder qu'aux publications qui ont au moins un commentaire. Avec la jointure à gauche, vous pouvez sélectionner les publications non commentées.

J'espère que cela aide.

+0

Je ne peux pas accéder à 'numofcomments' dans $ this-> posts. Cette approche nécessite probablement des méthodes d'hydratation personnalisées. Je ne suis pas sûr de savoir comment faire cela. – fenderplayer

1

Si vous souhaitez inclure des données supplémentaires dans vos objets renvoyés par une requête personnalisée, vous pouvez remplacer la méthode par défaut doSelect() dans votre classe homologue et ajouter ces données supplémentaires à partir de votre requête dans chaque objet c'est retourné.

Dans votre classe Posts, vous pouvez ajouter une variable protégée, appelons-la "numComments".

class Post extends BasePost { 
    protected $numComments; 

    public function setNumComments($v) 
    { 
    $this->numComments = $v; 
    } 

    public function getNumComments() 
    { 
    return $this->numComments; 
    } 
    ... 
} 

puis dans votre classe PostPeer dans votre méthode statique doSelectWithCount():

public static function doSelectWithCount() { 
    $c = new Criteria(); 
    self::addSelectColumns($c); //add all columns from PostPeer 
    $c->addAsColumn('numofcomments', 'COUNT('.CommentPeer::POST_ID.')'); 
    $c->addJoin(PostPeer::ID, CommentPeer::POST_ID, Criteria::LEFT_JOIN); 
    $c->addGroupByColumn(PostPeer::ID); 
    $c->addGroupByColumn(PostPeer::TITLE); 
    // ... 
    // more group-by columns if needed 
    $rs = PostPeer::doSelectRS($c); 

    $posts = array(); 
    while ($rs->next()) { 
    $post = new Post(); 
    $post->hydrate($rs); 
    $post->setNumComments($rs->getInt(PostPeer::NUM_COLUMNS + 1)); 
    $posts[] = $post; 
    } 

    return $posts; 
}