findByPk

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
vladqa
Сообщения: 30
Зарегистрирован: 2013.02.01, 19:18

findByPk

Сообщение 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]) не нравится.
lancedevnull
Сообщения: 1268
Зарегистрирован: 2013.07.17, 17:37

Re: findByPk

Сообщение lancedevnull »

по pk дефолтное поведение ::find($pk) findOne($pk)[,findAll($pk)]
vladqa
Сообщения: 30
Зарегистрирован: 2013.02.01, 19:18

Re: findByPk

Сообщение vladqa »

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

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

Re: findByPk

Сообщение vladqa »

lancedevnull писал(а):по pk дефолтное поведение ::find($pk) findOne($pk)[,findAll($pk)]
Ну да. Есть findOne(), но толку мало от него, т.к. он внутри себя создает ActiveQuery, добавляет выборку по pk и сразу выполняет запрос. А прицепить scopes при этом нельзя.
vladqa
Сообщения: 30
Зарегистрирован: 2013.02.01, 19:18

Re: findByPk

Сообщение 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.');
            }
        }
    }
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: findByPk

Сообщение samdark »

Кидайте на github, ещё не поздно.
Аватара пользователя
maleks
Сообщения: 1985
Зарегистрирован: 2012.12.26, 12:56

Re: findByPk

Сообщение maleks »

А что вы в scopes() хотите для запроса по первичному ключу предустановить?
Например
->select(поля)
->asArray()
?
Явно ж там не ограничение множества строк, т.к. идет то по PK все равно.
Yii2 universal module sceleton - for basic and advanced templates
vladqa
Сообщения: 30
Зарегистрирован: 2013.02.01, 19:18

Re: findByPk

Сообщение vladqa »

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

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

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

Re: findByPk

Сообщение vladqa »

Sam Dark писал(а):Кидайте на github, ещё не поздно.
Да, спасибо. Так и поступлю.
lancedevnull
Сообщения: 1268
Зарегистрирован: 2013.07.17, 17:37

Re: findByPk

Сообщение lancedevnull »

Model::find($pk)->active()->all()
vladqa
Сообщения: 30
Зарегистрирован: 2013.02.01, 19:18

Re: findByPk

Сообщение vladqa »

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

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

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

/**
   * @inheritdoc
   */
  public static function find()
  {
      return new ActiveQuery(get_called_class());
  }
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: findByPk

Сообщение samdark »

Да не работает так :)
lancedevnull
Сообщения: 1268
Зарегистрирован: 2013.07.17, 17:37

Re: findByPk

Сообщение lancedevnull »

ну да угнал, но можно же заюзать where('id'=>1), а так и правда получается нет по пк ) т.е. findOne бесполезен, и перекрывать его нет смысла
lancedevnull
Сообщения: 1268
Зарегистрирован: 2013.07.17, 17:37

Re: findByPk

Сообщение lancedevnull »

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

Re: findByPk

Сообщение lancedevnull »

лучше и правда скоуп делать byPk($pk)
vladqa
Сообщения: 30
Зарегистрирован: 2013.02.01, 19:18

Re: findByPk

Сообщение vladqa »

Да не обязательно он должен вернуть One. Можно передать массив PK и получить масисв объектов (аналог findAllByPk).
Предлагаю просто встроить этот скоуп в ActiveQuery, чтоб он был из коробки, ибо очень частый юзкейс.
Аватара пользователя
maleks
Сообщения: 1985
Зарегистрирован: 2012.12.26, 12:56

Re: findByPk

Сообщение maleks »

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

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

<?php
$post = Post::find()
    ->onlyActive()
    ->onlySomeAnotherCondition()
    ->findByPk($id);
Вообще я считаю, что любые фильтры и все варианты доп. условий для выборок надо инкапсулировать scopes (они ж для этого и придуманы), ибо иначе мы получаем неподдерживаемый код, который с трудом поддается рефакторингу.
Так Primary key уникален же для всех строк таблицы, и только по нему точно одна строка и найдется и никакая другая.
Yii2 universal module sceleton - for basic and advanced templates
vladqa
Сообщения: 30
Зарегистрирован: 2013.02.01, 19:18

Re: findByPk

Сообщение 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();
Ответить