@DarthPavel

Yii2 Как добавить условие в запросе в связанных данных?

Всем доброго времени суток!

Прошу помочь, потому что голову уже сломал, пытаясь разобраться почему в выборке оказываются "лишние" данные. Почему лишние ? Вот смотрите.

Есть 2 таблички (MariaDB):

CREATE TABLE `email` (
  `email_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `subject` varchar(512) NOT NULL,
  `body` longtext NOT NULL,
  `status` tinyint(1) unsigned DEFAULT '0',
  `created_by` int(10) unsigned NOT NULL,
  `created_at` int(10) unsigned NOT NULL,
  `updated_at` int(10) unsigned DEFAULT '0',
  PRIMARY KEY (`email_id`),
  KEY `email_status_IDX` (`status`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='Таблица для хранения email для отправки';


и

CREATE TABLE `email_recipient` (
  `email_id` int(10) unsigned NOT NULL,
  `email_to` varchar(512) NOT NULL,
  `status` tinyint(3) unsigned DEFAULT '0',
  KEY `email_recipient_email_id_IDX` (`email_id`,`email_to`(255)) USING BTREE,
  CONSTRAINT `email_recipient_fk` FOREIGN KEY (`email_id`) REFERENCES `email` (`email_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Таблица с получателями';


Первая хранит в себе общую информацию об отправляемом письме, вторая хранит получателей (связь по `email_id`).

Тестовые данные такие:

MariaDB [service]> select * From email where email_id = 3;
+----------+----------+----------+--------+------------+------------+------------+
| email_id | subject  | body     | status | created_by | created_at | updated_at |
+----------+----------+----------+--------+------------+------------+------------+
|        3 | тест     | тест     |      0 |          2 | 1568143024 |          0 |
+----------+----------+----------+--------+------------+------------+------------+


и соответственно

+----------+---------------+--------+
| email_id | email_to      | status |
+----------+---------------+--------+
|        3 | test2@test.ru |      0 |
|        3 | test@test.ru  |      1 |
+----------+---------------+--------+


Версия Yii2: 2.0.11.2 (знаю, что нужно обновиться, но пока не могу).

Полный вывод моделей для них приводить не буду, т.к. считаю пока это не требуется, приведу лишь только relation из модели Email:

/**
	     * @return \yii\db\ActiveQuery
	     */
	    public function getEmailRecipients()
	    {
	        return $this->hasMany(EmailRecipient::className(), ['email_id' => 'email_id']);
	    }


Далее есть консольное приложение, которое и занимается отправкой. Естественно, отправлять нужно только на те адреса, статус у которых 0 (1 - отправка уже была).

Собственно, получаю я данные следующим образом:

$emails = Email::find()
        ->joinWith('emailRecipients')
        ->where(['email.status' => Email::EMAIL_STATUS_NEW])
        ->andWhere(['email_recipient.status' => EmailRecipient::EMAIL_RECIPIENT_STATUS_NEW])
        ->asArray()
        ->all();


Я был на 100500% уверен, что получу только письма готовые к отправке (`email`.`status` = 0) и список получателей, которым ещё не отправилось письмо по тем или иным причинам (`email_recipient`.`status` = 0). Но или я где-то сильно ошибся или что-то пошло не так - возвращается мне массив с полным количеством получателей:

array(1) {
  [0]=>
  array(8) {
    ["email_id"]=>
    string(1) "3"
    ["subject"]=>
    string(8) "тест"
    ["body"]=>
    string(8) "тест"
    ["status"]=>
    string(1) "0"
    ["created_by"]=>
    string(1) "2"
    ["created_at"]=>
    string(10) "1568143024"
    ["updated_at"]=>
    string(1) "0"
    ["emailRecipients"]=>
    array(2) {
      [0]=>
      array(3) {
        ["email_id"]=>
        string(1) "3"
        ["email_to"]=>
        string(12) "test@test.ru"
        ["status"]=>
        string(1) "1"
      }
      [1]=>
      array(3) {
        ["email_id"]=>
        string(1) "3"
        ["email_to"]=>
        string(13) "test2@test.ru"
        ["status"]=>
        string(1) "0"
      }
    }
  }
}


Решил подебажить и получить запрос, которым достаются данные:

SELECT * 
FROM `email` 
    LEFT JOIN `email_recipient` ON `email`.`email_id` = `email_recipient`.`email_id` 
WHERE (`email`.`status`=0) 
    AND (`email_recipient`.`status`=0);


Всё ровно:
+----------+----------+----------+--------+------------+------------+------------+----------+---------------+--------+
| email_id | subject  | body     | status | created_by | created_at | updated_at | email_id | email_to      | status |
+----------+----------+----------+--------+------------+------------+------------+----------+---------------+--------+
|        3 | тест     | тест     |      0 |          2 | 1568143024 |          0 |        3 | test2@test.ru |      0 |
+----------+----------+----------+--------+------------+------------+------------+----------+---------------+--------+
1 row in set (0.00 sec)


Ничего специфического не писал. Модели создавались с помощью gii.

Подскажите, пожалуйста, что я упустил или чего-то недопонял в работе связей в Yii2 ?

Заранее всем спасибо!
  • Вопрос задан
  • 270 просмотров
Решения вопроса 1
myks92
@myks92 Куратор тега Yii
Нашёл решение — пометь вопрос ответом!
$emails = Email::find()
    ->joinWith(['emailRecipients' => static function (ActiveQuery $query) {
        $query->andWhere(['email_recipient.status' => EmailRecipient::EMAIL_RECIPIENT_STATUS_NEW]);
    }])
    ->where(['email.status' => Email::EMAIL_STATUS_NEW])
    ->asArray()
    ->all();
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы