Куда вынести метод?

Обсуждаем, как правильно строить приложения
Ответить
Bio man
Сообщения: 609
Зарегистрирован: 2013.07.22, 10:40

Куда вынести метод?

Сообщение Bio man »

Задался вопросом, куда девать подобные методы. Например

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

public function getFullName()
{
   return $this->last_name . ' ' . $this->first_name . ' ' . $this->middle_name;
}
Тут говорят, что в view слой, а как не говорят.
Создать класс со статическими методами? Например

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

class UserViewHelper
{
public static function getFullName($model)
{
   return $model->last_name . ' ' . $model->first_name . ' ' . $model->middle_name;
}
}
как бы вы решили данную проблему?
Аватара пользователя
vitalik1183
Сообщения: 1675
Зарегистрирован: 2014.07.01, 08:42

Re: Куда вынести метод?

Сообщение vitalik1183 »

fields
Yii2!
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Куда вынести метод?

Сообщение lynicidn »

в моделе ар и оставлять, это простая конкатенация значений колонок, это слой бд
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Куда вынести метод?

Сообщение lynicidn »

http://www.mysql.ru/docs/maryan/#concat можете и запросом выбирать и сделать вирт аттрибут, но так у вас тоже вирт аттрибут, только с геттером, без сеттера, ибо все равно все строки дергаете для модели
Bio man
Сообщения: 609
Зарегистрирован: 2013.07.22, 10:40

Re: Куда вынести метод?

Сообщение Bio man »

Спасибо!
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Куда вынести метод?

Сообщение zelenin »

lynicidn писал(а):в моделе ар и оставлять, это простая конкатенация значений колонок, это слой бд
это визуальное представление атрибутов
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Куда вынести метод?

Сообщение zelenin »

Bio man писал(а): Тут говорят, что в view слой, а как не говорят.
Создать класс со статическими методами?
перенести все во вью слой. Например виджет.
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Куда вынести метод?

Сообщение lynicidn »

для меня виджет это более сложная логика с хтмл или же со яваскриптами
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Куда вынести метод?

Сообщение zelenin »

lynicidn писал(а):для меня виджет это более сложная логика с хтмл или же со яваскриптами
для меня тоже, тем не менее никто не ограничивает использовать и для простых вещей. Хотя лично я бы обошелся хелпером.
maxsupermaxyii
Сообщения: 21
Зарегистрирован: 2012.07.04, 08:44

Re: Куда вынести метод?

Сообщение maxsupermaxyii »

Возможно тема более не актуальна, но я все же рискну поделиться своим опытом.

Если это не сложная логика формирования представления, такая как конкатенация или формирование ссылки, я для каждой модели создаю trait

Например есть модель Vehicle

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

class Vehicle extends ActiveRecord
{
    use VehicleTrait;
    
    const TYPE_CAR = 'car';
    const TYPE_MOTORCYCLE = 'motorcycle';

    const STATUS_ACTIVE = '1';
    const STATUS_INACTIVE = '0';
    
    // В модели храниться код, который непосредственно связан с AR, более никакой дополнительной логики
    // Условия к запросам я люблю выносить в Query класс
}
 
Методы которые мне нужны для формирования, например URL на картинку или формирования полного имени автомобиля я размещаю в VehicleTrait'e. Главное, что бы эти методы были достаточно простыми, без какой-то сложной логики

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

trait VehicleTrait
{
    use PictureTrait;

    /**
     * @return string
     */
    public function getVehicleName()
    {
        return implode(' ', [$this->carBrand->name, $this->carModel->name, $this->name]);
    }

    /**
     * @param null $size
     * @return string
     */
    public function getVehicleImage($size = null)
    {
        $picSize = is_null($size) ? self::$car_168x99 : $size;
        if (is_null($this->photos)) {
            return 'default.png';
        }
        return Html::encode($this->getPictureUrl($this->photos['0'], $picSize));
    }

    /**
     * @return bool
     */
    public function hasPictures()
    {
        return !is_null($this->photos);
    }
}
 
Можно конечно вынести этот код в хелпер со статическими методами, но мне не нравится этот подход, возможно в некоторых задачах он себя оправдывает, но я решил использовать trait'ы. Так же это удобно, если у меня есть наследники от Vehicle - они автоматически получат доступ ко всем trait'ам основного класса, например

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

class Car extends Vehicle implements FeedProviderInterface, ChannelProviderInterface
{
    const TYPE = self::TYPE_CAR;

    public function init()
    {
        parent::init();
        $this->setAttribute('vehicle_type', self::TYPE_CAR);
    }

    public static function find()
    {
        return new VehicleQuery(get_called_class(), ['type' => self::TYPE]);
    }

    public function rules()
    {
        $rules = parent::rules();

        return $rules;
    }

    /**
     * @return string
     */
    public function getType()
    {
        return $this->vehicle_type;
    }

    /**
     * @return int
     */
    public function getTypeId()
    {
        return $this->id;
    }

    /**
     * @return int
     */
    public function getOwnerId()
    {
        return $this->user_id;
    }

    /**
     * @return int
     */
    public function getLanguageId()
    {
        return $this->language_id;
    }

    /**
     * @return string
     */
    public function getChannelModel()
    {
        return Channel::MODEL_CAR;
    }

    /**
     * @return int
     */
    public function getChannelKey()
    {
        return $this->getTypeId();
    }
}

$car = new Car();
echo $car->getFullName(); // вызов метода trait'а который подключен в родительском классе
 
Главное - не увлекайтесь trait'ами.

Так же несколько слов о методе fields() - он очень удобен, если вы разрабатываете RESTful API. При использовании этого метода я так же люблю применять trait'ы

Без trait этот метод может выглядеть так

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

    public function fields()
    {
        return [
            'id',
            'user_id',
            'name',
            'text',
            'photos' => function () {
        // Какой-то код
        // В несколько строчек
        // С использованием оператора сравнения
            },
            'photo' => function () {
        // Какой-то код
        // В несколько строчек
        // С использованием оператора сравнения
            },
            'fullName' => function () {
                return implode(' ', [$this->carBrand->name, $this->carModel->name, $this->name]);
            },
      }
 
использование trait'ов помогут немного привести в порядок данный код и инкапсулировать его

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

    public function fields()
    {
        return [
            'id',
            'user_id',
            'name',
            'text',
            'photos' => function (Vehicle $model) {
            return $model->photos();
            },
            'photo' => function (Vehicle $model) {
            return $model->mainPhoto();
            },
            'fullName' => function (Vehicle $model) {
            return $model->fullName();
            },
      }
 
Для меня этот подход кажется более интересным чем статические помошники.

Есть разные мнения о трейтах, многие считают, что трейты нарушают принципы ООП, связь между объектами и это очень плохо и категорически против их использования, а многие находят в этом нововведении большую пользу.

Я отношусь ко второму лагерю, но считаю, что излишнее использование trait'ов может навредить проекту и рассматриваю трейты как инструмент, который помогает избавиться от дублирующего кода, главное в меру использовать трейты, а если вы решили писать код, который изменяет поведение родительского объекта, например __get или __set методы, документируйте это, что бы другие разработчики знали об этой особенности.

Лучше конечно документировать каждый метод или свойство, IDE нам в этом помогает.

К слову об IDE, последняя версия PHPStorm'а отлично поддерживает трейты. Ранее была проблема с контекстом $this, теперь эту проблему устранили.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Куда вынести метод?

Сообщение zelenin »

только учтите, что добавляя в модель метод типа getFullName, вы используете модель в качестве хелпера для view, и в аду (а вы точно там будете) вам придется несладко.
maxsupermaxyii
Сообщения: 21
Зарегистрирован: 2012.07.04, 08:44

Re: Куда вынести метод?

Сообщение maxsupermaxyii »

zelenin писал(а):только учтите, что добавляя в модель метод типа getFullName, вы используете модель в качестве хелпера для view, и в аду (а вы точно там будете) вам придется несладко.
Ну зачем же так жестко ? Лучше бы описали или аргументировали, показали пример как лучше. У меня например нет представления, я использую Yii в качестве REST фреймворка, зачем мне, например widget ?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Куда вынести метод?

Сообщение zelenin »

maxsupermaxyii писал(а):
zelenin писал(а):только учтите, что добавляя в модель метод типа getFullName, вы используете модель в качестве хелпера для view, и в аду (а вы точно там будете) вам придется несладко.
Ну зачем же так жестко ? Лучше бы описали или аргументировали, показали пример как лучше. У меня например нет представления, я использую Yii в качестве REST фреймворка, зачем мне, например widget ?
модель - это бизнес-слой. Отображение имени в одном формате в api и еще в трех форматах в представлении не относится к бизнесу.
maxsupermaxyii
Сообщения: 21
Зарегистрирован: 2012.07.04, 08:44

Re: Куда вынести метод?

Сообщение maxsupermaxyii »

Давайте разбираться.

в Yii документации есть описание для метода fields() который используется для представления ресурса, вот ссылка http://www.yiiframework.com/doc-2.0/gui ... ing-fields

а так же пример кода

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

public function fields()
{
    return [
        // field name is the same as the attribute name
        'id',
        // field name is "email", the corresponding attribute name is "email_address"
        'email' => 'email_address',
        // field name is "name", its value is defined by a PHP callback
        'name' => function ($model) {
            return $model->first_name . ' ' . $model->last_name;
        },
    ];
}
То есть в модели, для ресурса REST приложения используется формирование его представления, верно ?

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

        'name' => function ($model) {
            return $model->first_name . ' ' . $model->last_name;
        },
Эту часть кода, можно вынести или в статический хелпер или перенести в trait, какие еще варианты ? и почему trait в этом примере - зло, по вашему мнению ?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Куда вынести метод?

Сообщение zelenin »

я про методы вида getFullName(). если мы про fields, то ок, пользуемся встроенными методами yii, закрывая глаза на то, что это все сделано, чтобы "сделать рест максимально быстро" и "так сойдет". Модель не должна заботиться о том, как она будет выглядеть в представлении или выхлопе апи.
maxsupermaxyii
Сообщения: 21
Зарегистрирован: 2012.07.04, 08:44

Re: Куда вынести метод?

Сообщение maxsupermaxyii »

zelenin писал(а):Модель не должна заботиться о том, как она будет выглядеть в представлении или выхлопе апи.
Я согласен с этим правилом, если мы рендерим представление на сервере, работаем непосредственно с представлением. Имеет смысл использовать виджеты, а трейты перенести в widget с модели.

По сути, trait от хелпера который вы напишите сами отличается только названием и тем, что его можно внедрить в класс и использовать в контексте этого класса

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

class HelloWidget extends Widget
{
   use HelpfulTrait

    public $data;
    public $view = 'something';

    public function run()
    {
        return $this->render($this->view, ['data'=>$this->data])
    }
}

// Родительское представление в котором вызывается widget
<?= HelloWidget::widget(['data' => $this->dataFromController]) ?>

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

trait HelpfulTrait {

    public function doSomething() {
        return $this->name . ' ' . $this->secondName;
    }

}

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

// Представление something.php которое используется в widget
<p>SomeViewHelper::doSomething($this->data)</p>
смотрится хуже, чем

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

// Представление something.php которое используется в widget
<p>$this->doSomething()</p>
Код становится чище и понятней.

Если мы говорим о разработке REST и использование Yii2 для этой задачи, как фреймворка, я не вижу каких-то органичней, для того, что бы использовать трейты непосредственно в моделях в контексте Yii2, если это не большой энтерпрайз проект.

Опять таки, я следую Yii концепции, где в модели есть специальные методы fields() для представления полей модели и extraFields() для представления связей модели. Если заглянуть в Class yii\rest\Serializer он ответсвенный за представление модели.

Эти методы покрывают наверное 70-80% потребностей, очень просты и понятны.
А модель в некотором смысле становится представлением данных.

Если мы вернемся к методу getFullName и использование этого метода в REST то можно обратиться за примером к Facebook Graph API, при вызове

/me?fields=first_name,last_name,middle_name,name

мы получим ответ вида

{
"first_name": "FirstName",
"last_name": "LastName",
"name": "FirstName LastName",
"id": "{id}"
}

Мы видим, что модель есть некоторым представлением данных.

Разработчику на frontend не нужно писать метод, которы делает конкатенацию из двух полей, он просто берет данные и отображает их без особых усилий.

Так же и у меня, я хочу с сервера получить данные и на frontend их просто отобразить, без какой-то сложной логики в шаблонах (использую handlebars)

Я не говорю, что api должен отдавать html, нет, но подготовить некоторые данные - это удобно и Yii2 дает для этого инструменты и использование trait'а в модели мне кажется не нарушает эту концепцию.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Куда вынести метод?

Сообщение zelenin »

maxsupermaxyii писал(а):
zelenin писал(а):Модель не должна заботиться о том, как она будет выглядеть в представлении или выхлопе апи.
Я согласен с этим правилом, если мы рендерим представление на сервере, работаем непосредственно с представлением. Имеет смысл использовать виджеты, а трейты перенести в widget с модели.
вообще речи нет от трейтах или виджетах. трейт это способ реюзать код, а не какая-то отдельная сущность, поэтому правильность или неправильность обьсуждаем в контексте объекта куда этот трейт внедряется (т.е. модель).
maxsupermaxyii писал(а):По сути, trait от хелпера который вы напишите сами отличается только названием и тем, что его можно внедрить в класс и использовать в контексте этого класса
если мы говорим об отвественностях, то трейт ею не обладает, но может добавить таковую объекту, в который внедряется. сам по себе трейт ничто.
поэтому хелпер может вынести лишнюю ответственность из модели, а трейт только ее добавит.
maxsupermaxyii писал(а):Код становится чище и понятней.

Если мы говорим о разработке REST и использование Yii2 для этой задачи, как фреймворка, я не вижу каких-то органичней, для того, что бы использовать трейты непосредственно в моделях в контексте Yii2, если это не большой энтерпрайз проект.
причем тут трейты? я могу еще раз повторить: о методе из трейта нельзя рассуждать вне контекста объекта, в который внедряется. если трейт добавляет в модель метод getFullName, то я говорю об этом методе как о методе модели, утверждая, что модель не должна заниматься собственным отображением в различных представлениях (html/json).
maxsupermaxyii писал(а):Если мы вернемся к методу getFullName и использование этого метода в REST то можно обратиться за примером к Facebook Graph API, при вызове

/me?fields=first_name,last_name,middle_name,name

мы получим ответ вида

{
"first_name": "FirstName",
"last_name": "LastName",
"name": "FirstName LastName",
"id": "{id}"
}

Мы видим, что модель есть некоторым представлением данных.
мы не видим тут модели - мы видим json-представление. что было между моделями и json бог весть. очевидно, что цепочка построена примерно так: UserModel -> UserApiAssembler -> UserApiDTO. И именно UserApiDTO мы и видим. В yii в качестве ассемблера выступает сериалайзер, который с помощью fields, преобразует в модель в dto. Но вводя в модель fields, мы заставляем модель еще думать и о своем отображении в апи, что неверно.

PS еще раз повторю: есть два способа: а) отдельный класс (хелпер, сервис, вьюмодел итд) б) метод в модели. Трейт - не способ. Это вариант реюзать код, который может быть использован как в а) так и в б)
m.khartanovych
Сообщения: 3
Зарегистрирован: 2016.04.08, 15:35

Re: Куда вынести метод?

Сообщение m.khartanovych »

Все это сводиться к тому, что в Yii2 присутствует связность, но для данного проекта меня устраивает подход, который предоставляет Yii.
Мне интересно как решается задача REST на FOSRestBundle для SF2, а так же на Phalcon и Laravel.

p.s. такой подход в обсуждении мне нравится больше, чем ваше первое сообщение.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Куда вынести метод?

Сообщение ElisDN »

В FOSRestBundle через адаптеры https://github.com/FriendsOfSymfony/FOS ... Serializer используется стандартный http://symfony.com/doc/current/componen ... lizer.html или JMSSerializer.
Ответить