Главная | Документаци для разраотчиков | Рекомендации по работе с Yii 2

Рекомендации по работе с Yii 2

В данном разделе опубликована информация, которая призвана решить спорные вопросы и определить порядок действий при их возникновении. Группой разработчиков мы собрали наиболее полный список рекомендаций, который поможет вам в создании новых модулей и приложений на Yii

Бизнес-логика

  1. Модели ActiveRecord должны описывать структуру данных таблиц в базе данных;
  2. Бизнес-логике не место в ActiveRecord, контроллере и тем более в представлении. Размещать бизнес-логику необходимо в отдельных классах с говорящими названиями “СущностьДействие”. В результате получаются “худые” контроллер и ActiveRecord. Данный подход повышает читабельность проекта в разы, а также позволяет производить тестирование бизнес-логики.

    1. class UserCreator extends BaseObject
    2. {
    3. public $emailAccept = false;
    4. public $beforeCreateEvent = UserEvent::EVENT_BEFORE_CREATE;
    5. public $afterCreateEvent = UserEvent::EVENT_AFTER_CREATE;
    6. public $validate = true;
    7. public $role = DbManager::ROLE_USER;
    8. protected $model;
    9. /**
    10. * UserCreator constructor.
    11. * @param User $model
    12. * @param array $config
    13. */
    14. public function __construct(User $model, $config = [])
    15. {
    16. $this->model = $model;
    17. parent::__construct($config);
    18. }
    19. /**
    20. * @return false|User
    21. */
    22. public function execute()
    23. {
    24. $event = new UserEvent();
    25. Yii::$app->user->trigger($this->beforeCreateEvent, $event);
    26. if(!$event->release) return false;
    27. $transaction = Yii::$app->db->beginTransaction();
    28. try {
    29. if($this->emailAccept) {
    30. $this->model->status = User::STATUS_INACTIVE;
    31. $this->model->generateEmailAcceptToken();
    32. }
    33. $this->model->confirm = true;
    34. $this->model->generateAuthKey();
    35. if ($this->model->save($this->validate)) {
    36. if($this->emailAccept && !self::sendMail($this->model)) {
    37. return false;
    38. }
    39. $role = Yii::$app->authManager->getRole($this->role);
    40. Yii::$app->authManager->assign($role, $this->model->id);
    41. Yii::$app->user->trigger($this->afterCreateEvent, new UserEvent(['userId' => $this->model->id]));
    42. $transaction->commit();
    43. return $this->model;
    44. }
    45. } catch (\Exception $e) {
    46. $transaction->rollBack();
    47. }
    48. return false;
    49. }
    50. /**
    51. * @param User $model
    52. * @return bool
    53. */
    54. public static function sendMail(User $model)
    55. {
    56. return Yii::$app->mailer->compose([
    57. 'html' => '@common/modules/users/mail/emailAcceptToken-html',
    58. 'text' => '@common/modules/users/mail/emailAcceptToken-text'
    59. ], [
    60. 'model' => $model
    61. ])
    62. ->setFrom(Yii::$app->params['noReplyEmail'])
    63. ->setTo($model->email)
    64. ->setSubject(Module::t('register_form_email_subject'))
    65. ->send();
    66. }
    67. }
  3. Не рекомендуется использовать beforeSave, afterSave, beforeFind, afterFind в ActiveRecord для тяжелой бизнес-логики (например, запросы к сторонним ресурсам, парсинг Excel, тяжелых XML файлов и т.д).
  4. Не рекомендуется размещать любую бизнес-логику в классах ActiveRecord.

Миграции в базе данных

  1. Категорически запрещено вносить изменения в структуру базы данных, а также добавлять/изменять/удалять данные в таблицах auth_item и auth_item_child в обход миграций. Все обновления в БД осуществляются только посредством оформления миграции.
    1. Все изменения данных, которые необходимо произвести единожды у всех участников проекта, необходимо реализовывать через миграции;
    2. Если изменения данных в БД необходимо производить с некой периодичностью, следует вынести такие изменения в консольное приложение.
      На практике встречаются случаи, когда разработчик размещает функционал, который более подходит для консольного приложения либо для миграций, в контроллер веб приложения, что недопустимо.
  2. Категорически запрещено изменять старые (уже попавшие в Git) миграции даже при условии, что вы работаете в отдельной ветке. Для внесения правок необходимо готовить новую миграцию.
    Данным пунктом можно пренебречь только в одном случае, если старая (уже попавшая в Git) миграция заведомо содержит ошибки/баги, и вносимые изменения направлены на устранение этих самых ошибок/багов.
  3. Категорически запрещено использовать в миграциях: модели, конфигурацию или любые другие элементы из основного проекта. Миграция должна быть самодостаточной сущностью и может использовать только методы, унаследованные от yii\db\Migration.
  4. Если необходимо сделать SELECT в миграции, используем класс yii\db\Query.
    1. $users = (new Query())->select('id')->from('{{%user}}')->all();
  5. Для MySQL используем движок InnoDB, так как он предоставляет полезные функции, например, такие как транзакции и внешние связи.
  6. Если в проекте используется СУБД MySQL для использования внешних ключей (Foreign keys) и транзакций, в качестве системы хранения данных (Storage engine) должна быть выбрана InnoDB. При создании таблицы в БД необходимо использовать следующую конструкцию:

    1. $tableOptions = null;
    2. if ($this->db->driverName === 'mysql') {
    3. $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
    4. }
    5. $this->createTable('{{%auth2fa}}', [
    6. 'id' => $this->primaryKey(),
    7. 'user_id' => $this->integer(11)->notNull()->comment('Пользователь'),
    8. 'secret_key' => $this->string(16)->notNull()->comment('Секретный ключ'),
    9. 'created_at' => $this->integer(11)->comment('Дата создания записи'),
    10. 'updated_at' => $this->integer(11)->comment('Дата изменения записи'),
    11. ], $tableOptions);
  7. Метод down() в миграциях обязателен к реализации, если миграция является обратимой. Нежелательно, чтобы метод down возвращал false.
  8. Перед публикации миграций в Git необходимо обязательно убедиться в том, что миграции исправно накатываются и откатываются php yii migrate/redo n.
  9. Для добавления нескольких записей в базу данных стоит использовать batchInsert() вместо конструкции, когда вызывается insert() в цикле (то же касается и консольных алгоритмов).
  10. При получении большого количества данных следует использовать методы batch(), each() вместо all() (то же касается и консольных алгоритмов).
  11. В платформе TaskOn 2 обязательно используется шаблон при именовании таблиц в миграциях: {{%table_name}}. Это даст возможность использовать префиксы таблиц в БД. При использовании данного шаблона не забудьте применить следующий пункт в кодогенераторе Gii при генерации модели:

Структура базы данных

  1. Наименование таблиц и столбцов в БД.
    1. Необходимо убедиться в том, что имя уникально и его нет в списке зарезервированных ключевых слов ANSI SQL (92, 99 and 2003), MySQL версий с 3, PostgreSQL 8.1, MS SQL Server 2000, MS ODBC и Oracle 10.2.
    2. Названия таблиц должны быть в единственном числе. Исключением являются слова в английском языке, у которых нет формы единственного числа (например: “news”).
    3. В именах необходимо использовать только буквы, цифры и символ подчеркивания.
    4. Необходимо избегать нескольких подряд идущих символов подчеркивания.
  2. Индексы.
    1. Не стоит забывать об индексах уникальности (UNIQUE INDEX) для того, чтобы избежать переполнение таблицы дубликатами.
    2. Если по определенному полю очень часто происходит выборка, ему необходимо добавить индекс (INDEX).
    3. Если запрос содержит много запросов JOIN, вам необходимо убедиться, что столбцы, к которым вы присоединяетесь, индексируются в обеих таблицах. Это влияет на то, как MySQL внутренне оптимизирует операцию соединения.

Кеширование и переиспользование данных

  1. Кэшировать нужно данные, которые медленно генерируются и часто запрашиваются.
  2. Помимо внешнего кэширования данных (в файловом хранилище, Memcached, и т.д), во многих случаях необходимо кэшировать промежуточные результаты выполнения некоторых операций в рамках жизненного цикла приложения. В качестве:
    1. переменных;
    2. синглтон-классов;
    3. статических свойств класса;
    4. свойств компонентов Yii.

Представления

  1. Название представления стоит начинать с символа нижнего подчеркивания “_” в случае, если представление рендерится без layout. Например:
    1. _header;
    2. _footer.
  2. Категорически запрещено размещать js/css внутри представления, если оно не будет использоваться в качестве шаблона писем или для дальнейшей генерации pdf, image и т.д.
  3. Категорически запрещено выполнять запросы в базу данных из представления.
  4. Не стоит в представлении производить всю верстку с помощью “оберток” типа Html::tag(), Html::beginTag(), Html::endTag(). За счет этого html верстка становится нечитабельной и нет никакой подсветки html тегов.
    1. $textAreaPlaceholder = "ДА\nДа\nда\ny\nYes\nYES\n+";
    2. $valuePlaceholder = "1";
    3. echo Html::beginTag('div', ['class' => 'hidden']);
    4. echo $form->field($model, 'convert')->textarea(['rows' => 6]);
    5. echo Html::endTag('div');
    6. $model->convert = json_decode($model->convert, true);
    7. echo Html::beginTag('table', ['class' => 'table list_table_for_validation_rules']);
    8. echo Html::beginTag('thead');
    9. echo Html::beginTag('tr');
    10. echo Html::tag('th', 'Список возможных значений', ['width' => '48%']);
    11. echo Html::tag('th', 'Заменить на', ['width' => '48%']);
    12. echo Html::tag('th', '', ['width' => '4%']);
    13. echo Html::endTag('tr');
    14. echo Html::endTag('thead');
    15. echo Html::beginTag('tbody');
    16. $help = Html::tag('div', '', ['class' => 'help-block']);
    17. if (is_array($model->convert))
    18. foreach ($model->convert as $listJson) {
    19. echo Html::beginTag('tr');
    20. echo Html::tag('td', Html::textarea('list_replaced_list[]', implode("\n", $listJson['list']), ['rows' => 4, 'class' => 'form-control list_replaced_list', 'placeholder' => $textAreaPlaceholder]) . $help, ['class' => 'list_replaced_list_wrapper']);
    21. echo Html::tag('td', Html::textarea('list_replaced_val[]', $listJson['value'], ['rows' => 4, 'class' => 'form-control list_replaced_val', 'placeholder' => $valuePlaceholder]) . $help, ['class' => 'list_replaced_val_wrapper']);
    22. $button = Html::a(Html::tag('i', '', ['class' => 'fa fa-times']), 'javascript:;', ['class' => 'btn btn-danger remove_list_replaced_val']);
    23. echo Html::tag('td', $button);
    24. echo Html::endTag('tr');
    25. }
    26. echo Html::endTag('tbody');
    27. echo Html::beginTag('tfoot');
    28. echo Html::beginTag('tr');
    29. echo Html::tag('td', Html::textarea('list_replaced_list[]', '', ['rows' => 4, 'class' => 'form-control list_replaced_list', 'placeholder' => $textAreaPlaceholder]) . $help, ['class' => 'list_replaced_list_wrapper']);
    30. echo Html::tag('td', Html::textarea('list_replaced_val[]', '', ['rows' => 4, 'class' => 'form-control list_replaced_val', 'placeholder' => $valuePlaceholder]) . $help, ['class' => 'list_replaced_val_wrapper']);
    31. $button = Html::a(Html::tag('i', '', ['class' => 'fa fa-save']), 'javascript:;', ['class' => 'btn btn-success add_list_replaced_val']);
    32. echo Html::tag('td', $button);
    33. echo Html::endTag('tr');
    34. echo Html::endTag('tfoot');
    35. echo Html::endTag('table');

Рекомендации по оформление кода в приложениях Yii 2

Общие положения

  1. Имена методов, свойств и переменных должны быть объявлены с использованием т.н. «camelCase» (первое слово пишется в нижнем регистре, далее каждое слово начинается с большой буквы, а между словами нет разделителей).
    Данный пункт не имеет никакого отношения к виртуальным атрибутам ActiveRecord.
  2. Открывающая фигурная скобка в определении класса или метода должна располагаться на новой строке, а закрывающая фигурная скобка должна располагаться на следующей строке после тела класса/метода.
  3. Одиночный знак подчеркивания в начале имени свойства или метода не следует использовать как признак защищенной (protected) или приватной (private) области видимости.
  4. Открывающая фигурная скобка в управляющих конструкциях должна располагаться в той же строке, что и сама конструкция, а закрывающая фигурная скобка должна располагаться на следующей строке после тела конструкции.
    p
    1. if ($password) {
    2. $this->password_hash = Yii::$app->security->generatePasswordHash($password);
    3. }
  5. Пустые строки могут быть добавлены в код для повышения удобочитаемости и разделения блоков кода.
  6. В одной строке не должно быть более одного выражения.

Классы

  1. Каждый класс должен располагаться в отдельном файле.
  2. Имена классов должны быть объявлены с использованием так называемого «StudlyCaps» (каждое слово начинается с большой буквы, между словами нет разделителей).

Константы

  1. Константы классов должны быть объявлены исключительно в верхнем регистре с использованием символа подчеркивания для разделения слов.
  2. Константы PHP true, false и null должны быть написаны в нижнем регистре.

Свойства

  1. Область видимости должна быть явно указана для каждого свойства.
  2. В одном выражении не должно быть определено более одного свойства.

Методы

  1. Метод должен служить одной четко определенной цели, эта цель должна быть полностью отражена в его имени, например: получить текущего пользователя — getСurrentUser().
  2. Метод не должен иметь слишком большое количество параметров.
  3. Параметры следует упорядочивать по степени их важности либо по порядку их использования внутри метода.
  4. Список аргументов может быть разделен на несколько строк, каждая из которых дополнена слева одним отступом (четырьмя пробелами). В таком случае первый элемент списка аргументов должен начинаться с новой строки, и в каждой строке должен быть указан только один аргумент.
    1. $foo->bar(
    2. $longArgument,
    3. $longerArgument,
    4. $muchLongerArgument
    5. );
  5. Область видимости должна быть явно указана для каждого метода.

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

Комментирование

Комментирование - важный аспект разработки приложений на Yii 2. Игнорирование создания понятных комментариев отнимает значительно большего времени при поддержке и командной разработке проектов. Ниже мы привели основные нюансы простановки комментариев в коде, чтобы на выходе можно было сгенерировать удобную и понятную документацию по проекту.

Область комментирования

  1. В ходе написания кода комментарии описывают константы, методы, классы, свойства интерфейсов.
  2. В коде разрешено комментировать тремя способами:
    1. Для добавления комментария 1 строкой - //;
    2. Для комментирования нескольких строк- /_/ ;
    3. В формате PHPDoc (для описания классов, свойств, методов, констант, интерфейсов).

Обратите внимание, что комментарий типа # не используется. Это связано с тем, что на основании оставленных комментариев генерируется документация к разрабатываемому программному продукту.

Порядок составления комментария

  1. Комментарий формируется в следующей последовательности:
    1. класс;
    2. константа;
    3. свойство;
    4. метод.
      Пример формирования комментария “Класс”:
      1. /**
      2. * Форма редактирования пользователя
      3. *
      4. * @property string $email
      5. * @property string $name
      6. * @property string $surname
      7. * @property string $position
      8. * @property string $password
      9. * @property string $repeatPassword
      10. * @property boolean $sendEmailPassword
      11. * @property boolean $sendEmailRegister
      12. * @property integer $status
      13. * @property UploadedFile $photo
      14. * @property boolean $deletePhoto
      15. * @property string $timezone
      16. * @property string $role
      17. */
  2. Необходимо описывать виртуальные свойства класса. Свойства начинаются с символа @ с добавлением слова «property». Это сильно облегчает написание кода на PHP в IDE PHPStorm.
  3. Используйте @mixin для подключенных поведений к классу для того, чтобы PHPStorm знал о методах, реализованных в этих поведениях:
    1. /**
    2. * @mixin UploadImageBehavior
    3. * @mixin AccessBehavior
    4. * @mixin MultilingualBehavior
    5. */
    Далее описываются константы, если есть, и записываются в верхнем регистре:
    1. /**
    2. * Тип пользователя: юр. лицо
    3. * @var integer
    4. */
    5. const TYPE_CORP = 3;
    При описании свойства необходимо задать тип данных через @var и дать описание:
    1. /**
    2. * @var string Название базы данных
    3. */
    4. public $database;
    Описание статических свойств:
    1. /**
    2. * @var array Индикаторы цен за 6 месяцев
    3. */
    4. public static $priceSixMonths = [4, 8];
    Методы могут содержать комментарий родительского метода класса. Это характерезуется @inheritdoc параметром. Пример ниже говорит о том, что у метода уже есть комментарий. Читай комментарий родительского метода.

Требования к комментариям в коде

  1. Комментарий не должен содержать сленговых выражений типа - “юзер”, “штука”, “фигня”, “хрень”, “непонятная вещь”, “задолбался это писать” и т.д.
  2. Комментарий не должен содержать матерных и других неприличных выражений, так как на основе оставленного комментария генерируется документация.
  3. Обязательно необходимо вставлять PHPDoc комментарий передаваемого параметра из «контроллера» в «вид». Обычно такие коментарии встречаются в начале файла /** @var \common\modules\content\models\Post $model */
  4. Обязательно необходимо описывать с помощью PHPDoc комментариев переменные, передаваемые в анонимные функции (Closures) в случае, если тип переменной не описан явно:
    1. 'value' => function($model) {
    2. /** @var \common\modules\document\models\Document $model */
    3. return $model->user->getUsernameWithEmail();
    4. }
  5. В случае явного описания типа переменной в PHPDoc комментарии нет необходимости:
    1. 'value' => function(Document $model) {
    2. return $model->user->getUsernameWithEmail();
    3. }

Рекомендации к комментированию кода

  1. Не нужно комментировать очевидные вещи.
  2. Если код использует стандартные функции, конструкции или классы языка, то не тратьте свое время на его комментирование, в данном случае поможет правильно оформленный код.

Комментарии к полям (столбцам) таблиц БД

Во время проектирования таблиц БД необходимо добавлять комментарии для полей таблиц:

  1. $this->createTable('{{%document}}', [
  2. 'id' => $this->primaryKey(),
  3. 'author_id' => $this->integer(11)->notNull()->comment('Автор'),
  4. 'user_id' => $this->integer(11)->notNull()->comment('Пользователь'),
  5. 'name' => $this->string(150)->notNull()->comment('Наименование'),
  6. 'filename' => $this->string(50)->notNull()->comment('Файл'),
  7. 'size' => $this->string(50)->notNull()->comment('Размер файла'),
  8. 'extension' => $this->string(10)->notNull()->comment('Расширение файла'),
  9. 'hash' => $this->string(64)->notNull()->comment('Hash файла (SHA-256)'),
  10. 'is_active' => $this->smallInteger(1)->notNull()->defaultValue(0)->comment('Видимость'),
  11. 'order' => $this->smallInteger()->unsigned()->notNull()->defaultValue(0)->comment('Сортировка'),
  12. 'created_at' => $this->integer(11)->notNull()->comment('Дата создания записи'),
  13. 'updated_at' => $this->integer(11)->notNull()->comment('Дата обновления записи')
  14. ], $tableOptions);

Компонент gii во фреймворке Yii2 позволяет генерировать AR модели для таблиц БД. Установив галочку “Generate Labels from DB Comments”,

мы получим указанные комментарии в методе attributeLabels , который задает метки атрибутов.

  1. public function attributeLabels()
  2. {
  3. return [
  4. 'id' => 'ID',
  5. 'author_id' => 'Автор',
  6. 'user_id' => 'Пользователь',
  7. 'name' => 'Наименование',
  8. 'filename' => 'Файл',
  9. 'size' => 'Размер файла',
  10. 'extension' => 'Расширение файла',
  11. 'hash' => 'Hash файла (SHA-256)',
  12. 'is_active' => 'Видимость',
  13. 'order' => 'Сортировка',
  14. 'created_at' => 'Дата создания записи',
  15. 'updated_at' => 'Дата обновления записи',
  16. ];
  17. }

А также название полей появятся в веб-приложении для администрирования СУБД.