@Monitorkin

Что лучше: yii\db\Query или Active Record?

Есть запрос (рабочий), созданный по учебнику при помощи построителя запросов yii\db\Query:
$data = (new \yii\db\Query())
    ->select(['page.name', 'page_category.name', 'page_value.value', 'page_value.position', 'page_params.type','page_params.variants'])
    ->from('page')
    ->where(['page.page_id' => 1])
    ->leftJoin('page_category', 'page.cat_id = page_category.cat_id')
    ->leftJoin('page_value', 'page.page_id = page_value.page_id')
    ->leftJoin('page_params', 'page_value.param_id = page_params.param_id')
    ->all();

Как думаете, лучше использовать его в таком виде, или переделать его в Active Record и использовать существующие модели? Если да, то как правильно составить запрос в Active Record?
  • Вопрос задан
  • 1267 просмотров
Пригласить эксперта
Ответы на вопрос 3
maxkh
@maxkh
Web developer
В батле Query VS ActiveRecord - нет победителя - это инструменты для решения разных задач.

Но я хочу отвлечься от вашего вопроса и поинтересоваться настроены ли в вашей БД внешние ключи ? Если нет, то советую начать изучение именно с этого момента, понимание внешних ключей в MySQL.

После чего, нужно очень вдумчиво и хорошо прочесть официальную документацию на тему ActiveRecord (www.yiiframework.com/doc-2.0/guide-db-active-recor...

После этого, у вас должна сложиться более целостная картинка о том, как это использовать вместе и что за что отвечает.

Теперь вернемся к использованию Query и AR, ну тут все просто, приведу просто пример кода

/**
 * Class VehicleQuery
 * @package frontend\modules\v1\models\vehicle
 */
class VehicleQuery extends ActiveQuery
{
    public $type;

    /**
     * @param \yii\db\QueryBuilder $builder
     * @return \yii\db\Query
     */
    public function prepare($builder)
    {
        if ($this->type !== null) {
            $this->andWhere([Vehicle::tableName() . '.vehicle_type' => $this->type]);
        }
        return parent::prepare($builder);
    }

    /**
     * Returns popular and active vehicles based on vehicle type
     * @return $this
     */
    public function popular()
    {
        $this->andWhere([
            Vehicle::tableName() . '.promoted' => 1,
            Vehicle::tableName() . '.deleted' => 0,
            Vehicle::tableName() . '.status' => '1',
            Vehicle::tableName() . '.in_use' => 1
        ]);
        return $this;
    }

    /**
     * @return $this
     */
    public function active()
    {
        $this->andWhere([
            Vehicle::tableName() . '.status' => '1',
            Vehicle::tableName() . '.deleted' => 0
        ]);
        return $this;
    }

    /**
     * @return $this
     */
    public function inUse()
    {
        $this->andWhere([Vehicle::tableName() . '.in_use' => 1]);
        return $this;
    }


    /**
     * Returns most rated vehicles
     * @return $this
     */
    public function rated()
    {
        $this->addOrderBy([
            Vehicle::tableName() . '.rating' => SORT_DESC,
            Vehicle::tableName() . '.created_at' => SORT_DESC
        ]);
        return $this;
    }

    /**
     * Returns most recently vehicles
     * @return $this
     */
    public function recently()
    {
        $this->addOrderBy([
            Vehicle::tableName() . '.created_at' => SORT_DESC,
            Vehicle::tableName() . '.rating' => SORT_DESC
        ]);
        return $this;
    }
}


Мы имеем модель Vehicle и для работы с этой моделью нам необходимо строить запросы, именно этим и занимается класс VehicleQuery - он берет на себя эту ответственность. Очень важно что бы Query класс был ответственный только за построение запросов, методы не должны возвращать find или save, они должны возвращать только экземпляр Query.

Приведу код модели Vehicle
class Vehicle extends ActiveRecord
{
    public static function find()
    {
        return new VehicleQuery(get_called_class());
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getCarBrand()
    {
        return $this->hasOne(CarBrand::className(), ['id' => 'car_brand_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getCarGeneration()
    {
        return $this->hasOne(CarGeneration::className(), ['id' => 'car_generation_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getLanguage()
    {
        return $this->hasOne(Language::className(), ['id' => 'language_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getUser()
    {
        return $this->hasOne(User::className(), ['id' => 'user_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getPosts()
    {
        return $this->hasMany(Post::className(), ['car_id' => 'id']);
    }
}


Модель описывает entity, rules, relations, attributes - собственно это главная ответственность модели, никакую дополнительную логику в моделе не рекомендуется имплементировать.

Из выше описанного ваш запрос может выглядеть следующим образом

Vehicle::find()->with(['user', 'posts'])->active()->recently();


Этот запрос выберет активные транспортные средства за последнее время со связями getUser(владелец) и getPosts(блоги).
Ответ написан
kimono
@kimono
Web developer
Если настроить связи с page_category, page_value и page_params то можно получить такой запрос:
$data = Page::find()
    ->where(['page.page_id' => 1])
    ->with('page_category')
    ->with('page_value')
    ->with('page_params')
    ->all();
Ответ написан
mitaichik
@mitaichik
В первую очередь нужно настроить связи таблиц в БД (прописать ключи) - это делается с помощью MySQL менеджера (или какая бд у вас там).

Далее сгенерировать классы объектов (в терминологии Yii - модели) с помощью Gii (чтоб не писать код вручную). Если вы правильно сделали пункт 1, то он вам сам сгенерирует все необходимые связи (после чего имеет смысл удалить ненужные и переименовать, например связь pageValue в value - но это по желанию).

После этого просто пользуйтесь ActiveRecord'ом, например:

$page = Page::findOne(1);
echo $page->value->text;
echo $page->category->title;

foreach($page->params as $param){
   echo $param->name .' : '. $param->value;
}


Yii сам выполнит необходимые запросы и подгрузит необходимые данные.

Приведенный коллегами метод with позволяет сократить кол-во запросов (подгружает связи сразу). joinWith позволяет джойнить тадлицу в запросе чтоб делать по ней условия, и много чего другого есть в ActiveRecord - все это описано в документации.

В целом - ActiveRecord - потрясающе удобный механизм, его однозначно стоит изучить.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы