1

I have 3 tables: sp_pages, sp_messages and sp_messages_pages (for many-to-many relations). The sp_messages_pages table have 5 columns:

  • id
  • page_id
  • message_id
  • enabled
  • sorting_order

I want to get all messages for specific page sorting by sp_messages_pages.sorting_order using Yii2 framework.

I try this code in pages class:

public function getMessages() {
    return $this->hasMany(Messages::className(), ['id' => 'id_messages'])
    ->viaTable('sp_messages_pages', ['id_pages' => 'id'], function ($query) {
        $query->andWhere(['enabled' => 'Yes'])
                ->orderBy(['sp_messages_pages.sorting' => SORT_ASC]);
    });
}

I use this code in my controller:

$this->findModel(['slug' => $slug])->getMessages();

This give me all messages sorted by sp_message.id. The generated SQL is

SELECT * FROM sp_messages WHERE id IN ('2', '3', '1', '4', '5')

IN condition is sorted as I want, but how to sort sp_messages?

DimaS
  • 571
  • 2
  • 5
  • 19
  • check this http://stackoverflow.com/questions/38355984/yii2-order-items-of-many-to-many-relation/38367858 the same topic – oakymax Jul 20 '16 at 14:17

1 Answers1

0

First way -- joining relation table to already selected messages

$this->hasMany(Messages::className(), ['id' => 'id_messages']) 
    ->viaTable('sp_messages_pages', ['id_pages' => 'id'], 
        function ($query) { 
            $query->andWhere(['enabled' => 'Yes']); 
        }
    )
    ->leftJoin('sp_messages_pages MP', 'MP.id_messages = sp_messages.id')
    ->orderBy(['MP.sorting' => SORT_ASC]);

almost the the same question here: Yii2 order items of many-to-many relation

Other way -- using findBySql

$sql = "
    SELECT m.* FROM sp_messages AS m
    JOIN WITH sp_messages_pages AS mp ON mp.id_messages = m.id
    JOIN WITH sp_pages AS p ON p.id = mp.id_pages
    WHERE mp.enabled = 'Yes' AND p.id = :page_id
    ORDER BY mp.sorting ASC
";

return Messages::findBySql($sql, [':page_id' => $this->id]);

but this is will be not a relation in Yii therms, just an method which returns ActiveQuery for further work. You can use such method as $page->getMessages()->all() for example.

Community
  • 1
  • 1
oakymax
  • 1,454
  • 1
  • 14
  • 21
  • I am sorry, relation table name is `sp_messages_pages`. Unfortunately you code is not working for me, bot I began to study via + join.. – DimaS Jul 21 '16 at 11:19
  • @Psyhos also you can try do it using `findBySql` method: http://www.yiiframework.com/doc-2.0/yii-db-activerecord.html#findBySql()-detail – oakymax Jul 21 '16 at 11:23
  • @Psyhos the answer updated. Added second option with `findBySql` usage and fixed error in first example (i forgot to include `enabled` field in where). Check it out – oakymax Jul 21 '16 at 11:52
  • Generic SQL variant is working perfect! (only add `->all()` after `findBySql` command). The first variant did not give any result, but idea is clear. Many thanks, Maxim! – DimaS Jul 21 '16 at 13:15
  • @Psyhos you are welcome. Glad to help you. But I would like also to fix the mistake in the first example, so that the response remains correct. Maybe this will help someone else. Do you see what is wrong here? – oakymax Jul 21 '16 at 13:20
  • Great! Let's fix the mistake! The code in you answer return error: `Getting unknown property: app\modules\staticpages\models\Pages::sp_pages.id`. Then I change `sp_pages.id` to `id` in `viaTable` and get error: `Undefined index: sp_messages_pages.id_messages`. I think that in `$this->hasMany(Messages::className(), ..` we don't need to use table names. So I try to use `$this->hasMany(Messages::className(), ['id' => 'id_messages'])->viaTable('sp_messages_pages', ['id_pages' => 'id'], ..`. – DimaS Jul 22 '16 at 08:22
  • Generated SQL is: SELECT `sp_messages`.* FROM `sp_messages` LEFT JOIN `sp_messages_pages` `MP` ON (`MP`.`id_pages`='sp_pages.id') AND (`MP`.`id_messages`='sp_messages.id') WHERE `sp_messages`.`id` IN ('1', '2', '5') ORDER BY `MP`.`sorting` – DimaS Jul 22 '16 at 08:23
  • This give right result without sorting. I think that LEFT JOIN is wrong - 'sp_pages.id' and 'sp_messages.id' in single quotes like string values.. – DimaS Jul 22 '16 at 08:26
  • @Psyhos got it. Using column name with table name as value in where condition is not working, because yii will escape it. Fixed. Try now – oakymax Jul 22 '16 at 08:43
  • 1
    Yes, you are right. Working variant is: return $this->hasMany(Messages::className(), ['id' => 'id_messages']) ->viaTable('sp_messages_pages', ['id_pages' => 'id'], function ($query) { $query->andWhere(['enabled' => 'Yes']); }) ->leftJoin('sp_messages_pages MP', ' MP.id_messages = sp_messages.id') ->orderBy(['MP.sorting' => SORT_ASC]); Thank you! Greetings from Estonia :) – DimaS Jul 22 '16 at 08:52