Наилучший способ переопределения классов в стороннем модуле
Наилучший способ переопределения классов в стороннем модуле
Давайте подискутируем на данную тему, какой способ более правильный и какие средства для этого у нас есть.
Задача
- Переопределить модель стороннего модуля для добавления или изменения существующей логики
- Переопределить контроллер стороннего модуля для добавления еще одного действия
С контроллерами вроде все понятно. У класса Module имеется $controllerMap который можно переопределить.
Для моделей соответственно приходит на ум такое же решение. Сделать свойство $modelMap, в котором можно будет указать модель и новый класс для нее.
Какие способы используете вы?
Задача
- Переопределить модель стороннего модуля для добавления или изменения существующей логики
- Переопределить контроллер стороннего модуля для добавления еще одного действия
С контроллерами вроде все понятно. У класса Module имеется $controllerMap который можно переопределить.
Для моделей соответственно приходит на ум такое же решение. Сделать свойство $modelMap, в котором можно будет указать модель и новый класс для нее.
Какие способы используете вы?
Re: Наилучший способ переопределения классов в стороннем модуле
Yii::$classMap
Yii::$container
Yii::$container
Re: Наилучший способ переопределения классов в стороннем модуле
Код: Выделить всё
Yii::$container->set(\common\test\UserModel::className(), \common\test\UserModelCustom::className());
-
- Сообщения: 101
- Зарегистрирован: 2012.06.05, 14:32
- Откуда: Петрозаводск
- Контактная информация:
Re: Наилучший способ переопределения классов в стороннем модуле
Через контейнер. При этом нужно учитывать, что если разработчик модуля не озаботился возможностью переопределения моделей (или других классов), то придется перепопределять контроллеры и менять классы вручную. Что я имею в виду:
Отдельную сложность вызывают модели ActiveRecord, ведь они во работают через использование статических методов (::findOne(), ::find()), что переопределить через контейнер нельзя. В модуле, который я разрабатываю, я разрулил это введением как раз $modelMap и компонента Finder, который дергает метод find() соответствующей модели (можете глянуть код и доки).
На истину в последней инстанции не претендую, просто как вариант, используемый мной.
Код: Выделить всё
public function actionIndex()
{
// вот такое переопределить нельзя - хардкод
$model = new Model();
// а вот такое переопределить можно с помощью контейнера
$model = Yii::createObject(Model::className());
}
На истину в последней инстанции не претендую, просто как вариант, используемый мной.
Re: Наилучший способ переопределения классов в стороннем модуле
можно получить модель черезdmeroff писал(а):Через контейнер. При этом нужно учитывать, что если разработчик модуля не озаботился возможностью переопределения моделей (или других классов), то придется перепопределять контроллеры и менять классы вручную. Что я имею в виду:
Отдельную сложность вызывают модели ActiveRecord, ведь они во работают через использование статических методов (::findOne(), ::find()), что переопределить через контейнер нельзя. В модуле, который я разрабатываю, я разрулил это введением как раз $modelMap и компонента Finder, который дергает метод find() соответствующей модели (можете глянуть код и доки).Код: Выделить всё
public function actionIndex() { // вот такое переопределить нельзя - хардкод $model = new Model(); // а вот такое переопределить можно с помощью контейнера $model = Yii::createObject(Model::className()); }
На истину в последней инстанции не претендую, просто как вариант, используемый мной.
Код: Выделить всё
$modelObject = Yii::$container->get('modelName');
Код: Выделить всё
$modelObject::findOne();
-
- Сообщения: 101
- Зарегистрирован: 2012.06.05, 14:32
- Откуда: Петрозаводск
- Контактная информация:
Re: Наилучший способ переопределения классов в стороннем модуле
Нашел способ и без создания лишнего объекта:
Не уверен, правда насчет прозрачности вот такого подхода, но право на жизнь он тоже имеет.
Код: Выделить всё
if (isset(\Yii::$container->getDefinitions()['modelName'])) {
$class = \Yii::$container->getDefinitions()['modelName']['class'];
} else {
$class = 'modelName';
}
$search = $class::findOne(1);
Re: Наилучший способ переопределения классов в стороннем модуле
буквально вчера обсуждали на хабре все это http://habrahabr.ru/post/254179/#comment_8361591 в том числе getDefinitions(). Этот способ имхо чересчур костылен.dmeroff писал(а):Нашел способ и без создания лишнего объекта:Не уверен, правда насчет прозрачности вот такого подхода, но право на жизнь он тоже имеет.Код: Выделить всё
if (isset(\Yii::$container->getDefinitions()['modelName'])) { $class = \Yii::$container->getDefinitions()['modelName']['class']; } else { $class = 'modelName'; } $search = $class::findOne(1);
-
- Сообщения: 101
- Зарегистрирован: 2012.06.05, 14:32
- Откуда: Петрозаводск
- Контактная информация:
Re: Наилучший способ переопределения классов в стороннем модуле
На мой вкус не хуже создания лишнего объекта и вызова у него статического метода. В любом случае красивого решения не получается.
На хабре говорили о проблемах с реляциями, но вот такого примера никто не привел: Допустим стороннее расширение имеет две модели User и Profile.
Теперь мы решили переопределить Profile:
Но при этом модель User совершенно ничего не знает о том, что мы переопределили Profile, поэтому при вызове $user->profile мы получим инстанс Profile, вместо MyProfile. Поэтому нам придется перепопределить еще и метод getProfile модели User - имхо лишнее действие. Поправте меня, если я ошибаюсь здесь.
У себя в модуле я обхожу это именно использованием modelMap, с помощью которой классы регистрируются в контейнере и плюс modelMap прокидывается в модели, где уже используется таким образом:
На хабре говорили о проблемах с реляциями, но вот такого примера никто не привел: Допустим стороннее расширение имеет две модели User и Profile.
Код: Выделить всё
class User extends ActiveRecord
{
public static function tableName() {return 'user';}
public function getProfile()
{
return $this->hasOne(Profile::className(), ['user_id' => 'id']);
}
}
Код: Выделить всё
class Profile extends ActiveRecord
{
public static function tableName() {return 'profile';}
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
}
Код: Выделить всё
class MyProfile extends Profile
{
// ...
}
Код: Выделить всё
Yii::$container->set(Profile::className, MyProfile::className());
У себя в модуле я обхожу это именно использованием modelMap, с помощью которой классы регистрируются в контейнере и плюс modelMap прокидывается в модели, где уже используется таким образом:
Код: Выделить всё
public function getProfile()
{
return $this->hasOne($this->module->modelMap['Profile'], ['user_id' => 'id']);
}
Re: Наилучший способ переопределения классов в стороннем модуле
$this->module
это завязка на модуль - id там статичен у вас, т.е. модуль под другим id не заюзать
это завязка на модуль - id там статичен у вас, т.е. модуль под другим id не заюзать
Re: Наилучший способ переопределения классов в стороннем модуле
В общем где то надо держать имена классов моделей.
В модуле не вариант, т.к привязка к id.
Через контейнер не получится пользоватся статическими методами.
Более менее универсального метода я так понимаю нет
В модуле не вариант, т.к привязка к id.
Через контейнер не получится пользоватся статическими методами.
Более менее универсального метода я так понимаю нет
Re: Наилучший способ переопределения классов в стороннем модуле
Вот это переопределение определенных частей стороннего модуля полнейший тупняк. При использовании кучи сторонних модулей и компонентов и глобальном использовании переопределений, событий и тп. проект превращается в полную свалку, а когда разрастается то этот весь мусор будет прилично тормозить дело. Не говоря уже о том, что если зайдет новый разработчик в проект, как долго он будет разбираться в этой тонне конфигов.
За время пока я отписывал вопросы по каждому чиху на форме, изучал каждый бит символа в коде yii2, решая достаточно редкие и интересные задачи, я понял, что практически все готовые сторонние модули, которые предлагают разработчики годятся только для создания простого проекта, самого простого.
Все доки и примеры типа "Пишем просто блог на yii2" или "Пишем просто магазин на yii2", показывают написание "Hello World", так как в 99% случаях обладают только 1% нужного функционала.
За время практики я столкнулся со следующими проблемами сторонних модулей. Для примера давайте разберем любой модуль пользователей на гите, берем самый первый модуль в поиске по запросу "Yii2 users" - https://github.com/amnah/yii2-user
Проблема номер 1
Если необходимо добавить свой функционал, нужно создавать 100500 контейнеров, переопределять модели, добавлять методы, переопределять контроллеры добавлять экшины, переопределять виды (тоесть уже обязательно нужно использовать тему) и др.
Проблема номер 2
Есть экшин авторизации https://github.com/amnah/yii2-user/blob ... er.php#L70, была такая у меня задача в сфере сетевого маркетинга, что нужно было сделать следующее: Пользователь покупает фин. пакет и для него регистрируется 10 аккаунтов (все аккаунты на его почту с его паролем). Тоесть авторизоваться можно на 10 аккаунтов под одними и теме-же данным. Вкратце делаем префиксы к email (1|a@mail.ru, 2|a@mail.ru, 3|a@mail.ru) и при авторизации выдаем лист аккаунтов, чтобы пользователь выбрал под каким зайти. Сделано для того, что бы потом пользователь мог их продавать. Так вот, мне нужно перед авторизацией пропустить username через фильтр, а после авторизации сделать определенные действия.
Тут конечно спасают события, а что если событий нет в нужном месте ? И задача редкая и автор не захочет добавить событие чисто для тебя.
Тоесть нужно будет переопределять целый экшин, из-за одной строчки?
Проблема номер 3
С темой отдельная история, если пользоваться твигом, то всплывает вот такой баг https://github.com/yiisoft/yii2/issues/7984. Я точно не могу сказать всплывет ли эта проблема, если файлы видов в модуле на php, а вы используете твиг, тема может не подхватить. Далее необходимо перекрывать переводы, если нужно добавить или изменить фразы на свои и выходит, чтобы костомизировать модуль нужно хорошо попотеть и сделать свалку конфигов, переопределений и еще 1 слой непонятно чего.
Проблема 4
Порог вхождения. Когда в проекте свалка из 10 000 DI, 100500 событий, разбросов файлов видов и переводов, попробуйте объяснить новому разработчику весь этот поток.
За время пока я отписывал вопросы по каждому чиху на форме, изучал каждый бит символа в коде yii2, решая достаточно редкие и интересные задачи, я понял, что практически все готовые сторонние модули, которые предлагают разработчики годятся только для создания простого проекта, самого простого.
Все доки и примеры типа "Пишем просто блог на yii2" или "Пишем просто магазин на yii2", показывают написание "Hello World", так как в 99% случаях обладают только 1% нужного функционала.
За время практики я столкнулся со следующими проблемами сторонних модулей. Для примера давайте разберем любой модуль пользователей на гите, берем самый первый модуль в поиске по запросу "Yii2 users" - https://github.com/amnah/yii2-user
Проблема номер 1
Если необходимо добавить свой функционал, нужно создавать 100500 контейнеров, переопределять модели, добавлять методы, переопределять контроллеры добавлять экшины, переопределять виды (тоесть уже обязательно нужно использовать тему) и др.
Проблема номер 2
Есть экшин авторизации https://github.com/amnah/yii2-user/blob ... er.php#L70, была такая у меня задача в сфере сетевого маркетинга, что нужно было сделать следующее: Пользователь покупает фин. пакет и для него регистрируется 10 аккаунтов (все аккаунты на его почту с его паролем). Тоесть авторизоваться можно на 10 аккаунтов под одними и теме-же данным. Вкратце делаем префиксы к email (1|a@mail.ru, 2|a@mail.ru, 3|a@mail.ru) и при авторизации выдаем лист аккаунтов, чтобы пользователь выбрал под каким зайти. Сделано для того, что бы потом пользователь мог их продавать. Так вот, мне нужно перед авторизацией пропустить username через фильтр, а после авторизации сделать определенные действия.
Тут конечно спасают события, а что если событий нет в нужном месте ? И задача редкая и автор не захочет добавить событие чисто для тебя.
Тоесть нужно будет переопределять целый экшин, из-за одной строчки?
Проблема номер 3
С темой отдельная история, если пользоваться твигом, то всплывает вот такой баг https://github.com/yiisoft/yii2/issues/7984. Я точно не могу сказать всплывет ли эта проблема, если файлы видов в модуле на php, а вы используете твиг, тема может не подхватить. Далее необходимо перекрывать переводы, если нужно добавить или изменить фразы на свои и выходит, чтобы костомизировать модуль нужно хорошо попотеть и сделать свалку конфигов, переопределений и еще 1 слой непонятно чего.
Проблема 4
Порог вхождения. Когда в проекте свалка из 10 000 DI, 100500 событий, разбросов файлов видов и переводов, попробуйте объяснить новому разработчику весь этот поток.
-
- Сообщения: 610
- Зарегистрирован: 2015.07.16, 10:50
Re: Наилучший способ переопределения классов в стороннем модуле
Подниму старую тему.
Чем плоха подмена через Yii::$classMap?
Чем плоха подмена через Yii::$classMap?
Re: Наилучший способ переопределения классов в стороннем модуле
Да, отсутствие единого стандарта конфигурации и, как говорил zelenin, низкая компетентность сообщества во многих вопросах приводят к полнейшему разноброду. Даже введение наследования модулей (как бандлов в Symfony) перед этим бессильно.nepster писал(а):За время практики я столкнулся со следующими проблемами сторонних модулей...
Re: Наилучший способ переопределения классов в стороннем модуле
ElisDN писал(а):Да, отсутствие единого стандарта конфигурации и, как говорил zelenin, низкая компетентность сообщества во многих вопросах приводят к полнейшему разноброду. Даже введение наследования модулей (как бандлов в Symfony) перед этим бессильно.nepster писал(а):За время практики я столкнулся со следующими проблемами сторонних модулей...
Интересно, а есть смысл подумать в сторону так сказать стандартов написания расширений для yii и навязать более строгий стиль кодирования ?
Re: Наилучший способ переопределения классов в стороннем модуле
Навязать что? SOLID, GRASP, модульность и прочий здравый смысл? Для Yii это слишком сложно, поэтому изначально лишнее и никогда этого не будет. Позиционирование Yii - максимальная простота любой ценой для удобства RAD разработки в стиле "фигак, фигак и в продакшен". В большей степени за счёт замкнутой экосистемы и отбрасывания всего чужеродного.nepster писал(а):Интересно, а есть смысл подумать в сторону так сказать стандартов написания расширений для yii и навязать более строгий стиль кодирования ?
Сам фреймворк для удобства напичкан статическими методами ::find() и ::getDb() (что делает классы моделей неподменяемыми и практически нетестируемыми стандартными моками), не предоставляет ни одного легального лёгкого способа переопределить модуль (кроме DI и костыльного classMap) и несовместим "из коробки" со внешним миром. Так что в таком "многообразии возможностей" просто подвиг сделать любой более-менее работающий вариант с минимумом костылей. А Вы про стиль кода...
-
- Сообщения: 610
- Зарегистрирован: 2015.07.16, 10:50
Re: Наилучший способ переопределения классов в стороннем модуле
Nepster видимо имел ввиду, что раз уж так получилось, что без костылей модели расширений не переопределить, то хотябы определить единый костыль переопределения, и прописать его в документации.
Re: Наилучший способ переопределения классов в стороннем модуле
andrei.obuhovski писал(а):хотябы определить единый костыль переопределения, и прописать его в документации.
Re: Наилучший способ переопределения классов в стороннем модуле
По сути самое плохое в yii2 это формы, модели и виджеты. Причем это все обладает как плюсами так и минусами и если задача стоит быстренько завести, чтобы хоть как-то работало, то тут проблем никаких нет. Однако в долгосрочной перспективе многое выливается в проблемы. С другой стороны команда Александра Макарова не жалуется и разрабатывает достаточно большой проект на yii.ElisDN писал(а):Навязать что? SOLID, GRASP, модульность и прочий здравый смысл? Для Yii это слишком сложно, поэтому изначально лишнее и никогда этого не будет. Позиционирование Yii - максимальная простота любой ценой для удобства RAD разработки в стиле "фигак, фигак и в продакшен". В большей степени за счёт замкнутой экосистемы и отбрасывания всего чужеродного.nepster писал(а):Интересно, а есть смысл подумать в сторону так сказать стандартов написания расширений для yii и навязать более строгий стиль кодирования ?
Сам фреймворк для удобства напичкан статическими методами ::find() и ::getDb() (что делает классы моделей неподменяемыми и практически нетестируемыми стандартными моками), не предоставляет ни одного легального лёгкого способа переопределить модуль (кроме DI и костыльного classMap) и несовместим "из коробки" со внешним миром. Так что в таком "многообразии возможностей" просто подвиг сделать любой более-менее работающий вариант с минимумом костылей. А Вы про стиль кода...
Что касается тестирования тут у меня вопросы, зачем тестировать модели, работа которых на 100% ложится на yii. Тоесть нет смысла тестировать find() так как можно предположить, что это часть инструмента, которая работает. Тоже самое, что нет смысла тестировать yii helper`ы. Еще что касается тестирования, даже если это нельзя протестировать просто, то есть возможность использовать например рефлекшин апи, пусть это и сложнее, но техническая возможность существует.
С другой стороны, очень много идей включая ar одолжили у рельсов, а я честно говоря не разу не видел не рельсы ни то, чтобы их гнобили за сильную связанность.
Вернемся к yii. По сути использование виджетов это дело добровольное, я например использую их в админке, на самом сайте только кастомные штуки. Тут никаких проблем нет. А вот с моделями нужно что-то делать. Так как 90% разработчиков делают 1 модель, 50 сценариев и всю логику туда-же и это в лучшем случае.
Тут нужно подумать в сторону того, чтобы сделать актив рекорд например final классом и заставлять всех уходить от статики в пользу композиции.
Если по большому счету разобраться у меня претензии в основном к моделям.
Re: Наилучший способ переопределения классов в стороннем модуле
с точки зрения архитектуры проекта формы и виджеты вообще ничто. они могут быть во фреймворке, но могут и не быть, что вернее. эти типа ты про дворника говоришь, типа дворник херовый, потому что у него прическа немодная - для человека было бы неплохо иметь нормальную прическу, а для дворника надо иметь метлу и ведро.
Re: Наилучший способ переопределения классов в стороннем модуле
Как раз об этом призадумался при подготовке своего скринкаста о тестировании. Может я перфекционист, но всё-таки... Вот о чём думал:nepster писал(а):Что касается тестирования тут у меня вопросы, зачем тестировать модели, работа которых на 100% ложится на yii.
С логикой фреймворка проблем нет. Другое дело, когда мы вмешиваем свою логику в процесс сохранения. В модели часто навешаны поведения и прочий код в beforeSave()/afterSave(). Многое работает с changedAttributes и прочим. И надо вызвать $model->save() и проверить, что все timestamps расставлены, поля отфильтрованы, файлы загружены и все счётчики и связи обновлены. В крайнем случае, чтобы элементарно найти баг, что забыли вернуть true из beforeSave() и модель не сохраняется.
Комбинаций для проверки может быть десятки. Сотни раз записывать в базу и вычищать порой долго. Можно вместо вызова save() вручную эмулировать работу фреймворка, дёргая эти методы beforeSave()/afterSave(), beforeDelete()/afterDelete() и расставлять dirtyAttributes, setIsNewRecord и прочее. Или смириться с необходимостью работы с базой. А хотелось бы для ускорения тестов просто заглушить код сохранения или удаления из БД и вызвать save() или delete(), но там в https://github.com/yiisoft/yii2/blob/50 ... d.php#L451 такой код:
Код: Выделить всё
$primaryKeys = static::getDb()->schema->insert($this->tableName(), $values))
Последний раз редактировалось ElisDN 2016.01.20, 22:24, всего редактировалось 3 раза.