简体   繁体   中英

Showing data from 2 models in a grid with Yii

I'm trying to select data from two tables and show that data in a paginated grid view. The problem is that Yii is joining the data with a SQL join (because I told it to do it) and because of that it's not showing the correct number of items per page.

To make in clear, I'm selecting from topics and messages_in_topics , and I'm joining then with

$criteria->together = true;

This makes MySQL to return a row for each message (and the related topics). Example:

id_topic    topic    id_messages    message

1           topic1   1              message1_in_topic1
1           topic1   2              message2_in_topic1
1           topic1   3              message3_in_topic1
2           topic2   4              message1_in_topic2
2           topic2   5              message2_in_topic2

There are only 2 topics, but Yii's paginator thinks there are 5.

The fastest way to "fix" this is grouping by the id_topic field, anyways, I can't do that because there's a where condition which searches with the like statement.

Thank you

EDIT:

Here's my action code:

$criteria = new CDbCriteria;

$get_s = Yii::app()->request->getQuery('s', '');
if( $get_s ){
    $criteria->compare("topic_title", $get_s, true);
    $criteria->compare("message_text", $get_s, true, 'OR');
}

$criteria->with = array('messages');
$criteria->addCondition(array( ......  )); <--- some rules like if the topic is validated...

$dataProvider = new CActiveDataProvider('Topics', array(
    'criteria'=>$criteria,
    'pagination=>array('pageSize'=>15)
));

Actually, what happens there is that the information being displayed is in regard to your messages. The repeated topic values are because these topics are the corresponding related data to the message.

You could try to tell your query to use GROUP BY in the results...

SELECT t.id_topic, t.topic, COUNT(m.id_messages)
  FROM topics t LEFT JOIN messages m ON t.id_topic = m.id_topic
GROUP BY t.id_topic

This way, more or less, you can display the count of messages-per-topic. I could help you more if you'd show us your SQL.

EDIT : After seeing your code, here is my guess: $criteria = new CDbCriteria;

$get_s = Yii::app()->request->getQuery('s', '');
if( $get_s ){
    $criteria->compare("topic_title", $get_s, true);
    $criteria->compare("message_text", $get_s, true, 'OR');
}

$criteria->with = array('messages');
$criteria->addCondition(array( ......  )); <--- some rules like if the topic is validated...

$dataProvider = new CActiveDataProvider('Topics', array(
    'criteria'=>$criteria,
    'pagination=>array('pageSize'=>15)
));

You can make sure the query isn't complicated by yii's formatting by pouring it using only three basic properties of CDbCriteria:

  1. $criteria->select is exactly the list of columns you want to select.
  2. $criteria->condition is exactly the WHERE condition, I honestly prefer using a string to an array, since using the string allows me to use the exact condition I put in here.
  3. $criteria->join is attached immediately after the $model->tableName() you specify in the CActiveDataProvider constructor.
  4. $criteria->group is added at the end of the query, you just need to specify the grouping column.

Also, You can also make sure to set these properties directly, so you actually descompose your query into the CDbCriteria object. For instance:

SELECT t.id_topic, t.topic, COUNT(m.id_messages)
  FROM topics t LEFT JOIN messages m ON t.id_topic = m.id_topic
GROUP BY t.id_topic

would be like this

/*1*/ $criteria->select = 't.id_topic, t.topic, COUNT(m.id_messages)';
/*2*/ $criteria->condition = 't.topic_title LIKE %'.$get_s.'% OR ...';/* add your conditions here*/
/*3*/ $criteria->join = 'LEFT JOIN messages m ON t.id_topic = m.id_topic'; //Full join statement here, including the nature of the join.
/*4*/ $criteria->group = 't.id_topic';

IMPORTANT : take into account that since you pass your Topics classname to the CActiveDataProvider constructor, the table under Topics will be known as t . Any other tables must be specified as well (Pretty much like messages m or messages AS m )in the join condition otherwise you might get a column xxxx is ambiguous warning.

Don't pass out the chance of giving an eye to CDbCriteria and CActiveDataProvider for any questions you might have.

The problem isn't the paginator, it is you query. If you run the query:

SELECT *
FROM topics as t
INNER JOIN messages_in_topics as mt
    ON mt.topics_id = t.id
INNER JOIN messages as m
    ON m.id = mt.messages_id

You will get 5 results, as show in your example above.

So more importantly, what are you trying to do?

Also, your table shows a MANY_MANY relationship (message1 has 2 topics, topic1 has 3 messages), is your database set up the right way, and are your model relations configured accordingly?

Are you trying to show ALL messages in each topic on a single line?

Are you trying to show ALL topics and list each message with that topic?

Assuming you have relations set up correctly, you can just use: $messages=$topics->messages; and get an array with all the messages listed.

Conversely, you can do $topics=$messages->topics; to get the topics.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM