Страница 1 из 2

findByPk

Добавлено: 2014.05.06, 19:21
vladqa
Здравствуйте!

В Yii1 я любил делать так:

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

<?php
public function getPost($post_id) {
    $post = Post::model()
        ->scope1()
        ->scope2()
        ->...etc
        ->findByPk($post_id);
}
И все было замечательно. Все поля модели и детали выборки инкапсулировались в scopes и выборка по PK происходила также через спец. метод.
В Yii2 я такого метода не нашел =(

Насколько я понял, теперь придется для каждой модели в наследнике ActiveQuery писать свой скоуп для выборки по PK?
Это.. немного муторно )) Сделать через какой-то базовый скоуп это сложно, т.к. у разных поделей колонка с PrimaryKey не обязательно называется id, бывают ведь и натуральные первичные ключи.
В Yii1 насколько я понимаю, PK выбирался на основе анализа схемы таблицы.

Как же все-таки максимально грамотно выбирать модели по PK в Yii2?
Вариант с ->where(["<my_pk_column>" => $id]) не нравится.

Re: findByPk

Добавлено: 2014.05.06, 22:48
lancedevnull

Re: findByPk

Добавлено: 2014.05.06, 22:50
lancedevnull
по pk дефолтное поведение ::find($pk) findOne($pk)[,findAll($pk)]

Re: findByPk

Добавлено: 2014.05.06, 23:29
vladqa
Спасибо, с документацией я более-менее знаком. Иначе не задавал бы вопрос.
Почему вы решили, что дефолтное поведение ::find($pk) ? Ведь ::find - статический метод модели, который возвращает new ActiveQuery и не принимает параметров (если вы об этом методе).

Грусть и печаль, что о такой важной вещи в yii2 не подумали ( Это же типичный сценарий использования модели - выборка по pk. большая часть контроллеров из этого состоит.
Или предлагается в контроллерах юзать model::find()->where(['id' => $id])->all(); ?? Так это же говнокод, когда по всему коду строковые имена колонок размазаны.

Re: findByPk

Добавлено: 2014.05.06, 23:33
vladqa
lancedevnull писал(а):по pk дефолтное поведение ::find($pk) findOne($pk)[,findAll($pk)]
Ну да. Есть findOne(), но толку мало от него, т.к. он внутри себя создает ActiveQuery, добавляет выборку по pk и сразу выполняет запрос. А прицепить scopes при этом нельзя.

Re: findByPk

Добавлено: 2014.05.06, 23:41
lancedevnull

Re: findByPk

Добавлено: 2014.05.06, 23:52
vladqa
lancedevnull, вы себе посты набиваете или действительно хотите что-то сказать?
Я уже сказал, что этот метод не подходит, т.к. к нему нельзя нормально прицепить скоупы:

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

public static function findOne($condition)
    {
        $query = static::find();
        if (ArrayHelper::isAssociative($condition)) {
            // hash condition
            return $query->andWhere($condition)->one();
        } else {
            // query by primary key
            $primaryKey = static::primaryKey();
            if (isset($primaryKey[0])) {
                return $query->andWhere([$primaryKey[0] => $condition])->one();
            } else {
                throw new InvalidConfigException(get_called_class() . ' must have a primary key.');
            }
        }
    }

Re: findByPk

Добавлено: 2014.05.07, 02:48
samdark
Кидайте на github, ещё не поздно.

Re: findByPk

Добавлено: 2014.05.07, 07:25
maleks
А что вы в scopes() хотите для запроса по первичному ключу предустановить?
Например
->select(поля)
->asArray()
?
Явно ж там не ограничение множества строк, т.к. идет то по PK все равно.

Re: findByPk

Добавлено: 2014.05.07, 10:24
vladqa
maleks писал(а):А что вы в scopes() хотите для запроса по первичному ключу предустановить?
Например
->select(поля)
->asArray()
?
Явно ж там не ограничение множества строк, т.к. идет то по PK все равно.
Конечно там ограничение количества строк.
Например мне нужно выбрать модель по id, то только такую, которая имеет статус "Active" и удовлетворяет другим доп. условиям:

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

<?php
$post = Post::find()
    ->onlyActive()
    ->onlySomeAnotherCondition()
    ->findByPk($id);
Вообще я считаю, что любые фильтры и все варианты доп. условий для выборок надо инкапсулировать scopes (они ж для этого и придуманы), ибо иначе мы получаем неподдерживаемый код, который с трудом поддается рефакторингу.

Re: findByPk

Добавлено: 2014.05.07, 10:24
vladqa
Sam Dark писал(а):Кидайте на github, ещё не поздно.
Да, спасибо. Так и поступлю.

Re: findByPk

Добавлено: 2014.05.07, 11:06
lancedevnull
Model::find($pk)->active()->all()

Re: findByPk

Добавлено: 2014.05.07, 11:59
vladqa
lancedevnull писал(а):Model::find($pk)->active()->all()
Вы меня радуете.
Вы сами пробовали смотреть код и хотя бы сигнатуру метода ::find()? Ну, или хотя бы запустить ваш код?

Скажите, куда вы тут передаете $id ?

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

/**
   * @inheritdoc
   */
  public static function find()
  {
      return new ActiveQuery(get_called_class());
  }

Re: findByPk

Добавлено: 2014.05.07, 12:02
samdark
Да не работает так :)

Re: findByPk

Добавлено: 2014.05.07, 12:04
lancedevnull
ну да угнал, но можно же заюзать where('id'=>1), а так и правда получается нет по пк ) т.е. findOne бесполезен, и перекрывать его нет смысла

Re: findByPk

Добавлено: 2014.05.07, 12:16
lancedevnull
смысла как такового заводить findByPk в ядро не вижу, т.к. этот метод должен вернуть one и сюда уже не встроить скоупы, но с другой стороны, для дефолтных скоупов лучше переопределять find(){return (new ActiveQuery())->active();}

Re: findByPk

Добавлено: 2014.05.07, 12:18
lancedevnull
лучше и правда скоуп делать byPk($pk)

Re: findByPk

Добавлено: 2014.05.07, 12:21
vladqa
Да не обязательно он должен вернуть One. Можно передать массив PK и получить масисв объектов (аналог findAllByPk).
Предлагаю просто встроить этот скоуп в ActiveQuery, чтоб он был из коробки, ибо очень частый юзкейс.

Re: findByPk

Добавлено: 2014.05.07, 12:50
maleks
vladqa писал(а): Конечно там ограничение количества строк.
Например мне нужно выбрать модель по id, то только такую, которая имеет статус "Active" и удовлетворяет другим доп. условиям:

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

<?php
$post = Post::find()
    ->onlyActive()
    ->onlySomeAnotherCondition()
    ->findByPk($id);
Вообще я считаю, что любые фильтры и все варианты доп. условий для выборок надо инкапсулировать scopes (они ж для этого и придуманы), ибо иначе мы получаем неподдерживаемый код, который с трудом поддается рефакторингу.
Так Primary key уникален же для всех строк таблицы, и только по нему точно одна строка и найдется и никакая другая.

Re: findByPk

Добавлено: 2014.05.07, 13:09
vladqa
maleks писал(а):
vladqa писал(а): Конечно там ограничение количества строк.
Например мне нужно выбрать модель по id, то только такую, которая имеет статус "Active" и удовлетворяет другим доп. условиям:

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

<?php
$post = Post::find()
    ->onlyActive()
    ->onlySomeAnotherCondition()
    ->findByPk($id);
Вообще я считаю, что любые фильтры и все варианты доп. условий для выборок надо инкапсулировать scopes (они ж для этого и придуманы), ибо иначе мы получаем неподдерживаемый код, который с трудом поддается рефакторингу.
Так Primary key уникален же для всех строк таблицы, и только по нему точно одна строка и найдется и никакая другая.
Либо несколько строк, если передали массив PK:
Должно быть как-то так:

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

<?php
$posts = Post::find()
    ->onlyActive()
    ->onlySomeAnotherCondition()
    ->byPK([10, 20, 30])
    ->all();