filterModel, Mysql, createCommand, addSelect

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Аватара пользователя
Maxim Glushko
Сообщения: 98
Зарегистрирован: 2017.04.24, 19:16
Откуда: Україна, Одеса

filterModel, Mysql, createCommand, addSelect

Сообщение Maxim Glushko »

Нужна сортировка по расстоянию от ресторана до заказчика.

В методе search:

Код: Выделить всё

$this->query = Restaurant::find()->select([...]);

$this->setSort();
В методе setSort:

Код: Выделить всё

$distances = Yii::$app->db->createCommand('
    SELECT ' . $formula . ' AS `distance`
    FROM `address`
    WHERE `address`.`restaurant_id`=`rest`.`id`
');
Запрос, выдающий несколько строк с вычисленным полем distance. Проверен, работает правильно.
$formula - это кусок mysql c вычислениями расстояний

В конце мне нужно из $distances выбрать минимальную (для этого ресторана) и соединить сюда:

Код: Выделить всё

$this->query->addSelect(['distance' => ...]);
Как мне сделать переход?
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: filterModel, Mysql, createCommand, addSelect

Сообщение samdark »

Что такое переход?
Аватара пользователя
Maxim Glushko
Сообщения: 98
Зарегистрирован: 2017.04.24, 19:16
Откуда: Україна, Одеса

Re: filterModel, Mysql, createCommand, addSelect

Сообщение Maxim Glushko »

$distances - это результат из нескольких строк
Мне нужно из них выбрать одну с наименьшим значением дистации
И это значение добавить в общий запрос:

Код: Выделить всё

$this->query->addSelect(['distance' => сюда]);
Аватара пользователя
Maxim Glushko
Сообщения: 98
Зарегистрирован: 2017.04.24, 19:16
Откуда: Україна, Одеса

Re: filterModel, Mysql, createCommand, addSelect

Сообщение Maxim Glushko »

Код: Выделить всё

$formula = 'IF (`coords` REGEXP "^(-?[0-9]{1,2}\.[0-9]{6});(-?[0-9]{1,2}\.[0-9]{6})$", 
    6371 * acos(
        cos(radians(\'' . $userCoords[0] . '\'))
        * cos(radians(SUBSTRING_INDEX(`coords`, \';\', 1)))
        * cos(radians(SUBSTRING_INDEX(`coords`, \';\', -1)) - radians(\'' . $userCoords[1] . '\'))
        + sin(radians(\'' . $userCoords[0] . '\')) * sin(radians(SUBSTRING_INDEX(`coords`, \';\', 1)))
    ),
    9999
)';

$distances = Yii::$app->db->createCommand('
    SELECT ' . $formula . ' AS `distance`
    FROM ' . Adds::tableName() . '
    WHERE ' . Adds::tableName() . '.`restaurant_id`=' . Restaurant::tableName() . '.`id`
');

//$distance = (new Query())->select('distance')->from(['distances' => $distances])
//    ->orderBy(['distance' => SORT_ASC])->limit(1);
// не работает, выдаёт strpos() expects parameter 1 to be string, object given
// в vendor\yiisoft\yii2\db\QueryBuilder.php на 839 строке
    
//$distance = (new Query())->from(['distances' => $distances])->min('distance');
// та же ошибка
    
$this->query->addSelect(['distance' => $distance]);
Мне нужно придумать что-то вместо закомментированного кода.
phpshko
Сообщения: 260
Зарегистрирован: 2015.03.21, 02:49

Re: filterModel, Mysql, createCommand, addSelect

Сообщение phpshko »

попробуйте так

Код: Выделить всё

$this->query->addSelect(new Expression('distance IN (' . $distances . ')'));
upd чтобы только минимальный, попробуйте так

Код: Выделить всё

$distances = Yii::$app->db->createCommand('
    SELECT MIN(' . $formula . ') AS `distance`
    FROM ' . Adds::tableName() . '
    WHERE ' . Adds::tableName() . '.`restaurant_id`=' . Restaurant::tableName() . '.`id`
');

$this->query->addSelect(new Expression('distance=(' . $distances . ')'));
Аватара пользователя
Maxim Glushko
Сообщения: 98
Зарегистрирован: 2017.04.24, 19:16
Откуда: Україна, Одеса

Re: filterModel, Mysql, createCommand, addSelect

Сообщение Maxim Glushko »

С форматами разобрался.
Как оказалось, у меня совсем другое. MySQL

С inner join получается такая выборка:

rest_id address_id distance
1 1 0.3
1 2 1.2
2 3 0.4
2 4 0.6
2 5 1.5
3 6 0.7

Т.е. у одного ресторана пара филиалов на разном удалении от клиента. Как допилить запрос, чтобы оставить по одной строке ресторана с ближайшим филиалом?

select distance, ...
from address
inner join restaurant on restaurant.id=address.restaurant_id
Аватара пользователя
Maxim Glushko
Сообщения: 98
Зарегистрирован: 2017.04.24, 19:16
Откуда: Україна, Одеса

Re: filterModel, Mysql, createCommand, addSelect

Сообщение Maxim Glushko »

Так сейчас выглядит и работает:

Код: Выделить всё

$formula = 'IF (`coords` REGEXP "^(-?[0-9]{1,2}\.[0-9]{6});(-?[0-9]{1,2}\.[0-9]{6})$", 
    6371 * acos(
        cos(radians(\'' . $userCoords[0] . '\'))
        * cos(radians(SUBSTRING_INDEX(`coords`, \';\', 1)))
        * cos(radians(SUBSTRING_INDEX(`coords`, \';\', -1)) - radians(\'' . $userCoords[1] . '\'))
        + sin(radians(\'' . $userCoords[0] . '\')) * sin(radians(SUBSTRING_INDEX(`coords`, \';\', 1)))
    ),
    9999
)';
$distances = 'SELECT ' . $formula . ' AS `distance`
    FROM ' . Adds::tableName() . '
    WHERE ' . Adds::tableName() . '.`restaurant_id`=' . Restaurant::tableName() . '.`id`';
$this->query->addSelect(['distance' => '(' . $distances . ')']);
$this->query->innerJoinWith('adds');
Вот только при наличии у ресторана двух филиалов выводится две строки ресторана.
Это пока не победил.
Аватара пользователя
Maxim Glushko
Сообщения: 98
Зарегистрирован: 2017.04.24, 19:16
Откуда: Україна, Одеса

Re: filterModel, Mysql, createCommand, addSelect

Сообщение Maxim Glushko »

Код: Выделить всё

$distances = 'SELECT ' . $formula . ' AS `distance`
    FROM ' . Adds::tableName() . '
    WHERE ' . Adds::tableName() . '.`restaurant_id`=' . Restaurant::tableName() . '.`id`
    ORDER BY `distance` ASC
    LIMIT 1'; // ближайший филиал
Странно, что поначалу это не работало.
Наверное, другие ошибки были.
Ответить