Статьи

OctoberCMS CRUD — Построение команды / Плагин управления проектом

Пока что мы рассмотрели различные аспекты OctoberCMS. В этой статье мы узнаем, как использовать OctoberCMS для приложений CRUD, и подробно рассмотрим, как работать с моделями, связями и контроллерами. Давайте начнем.

Логотип OctoberCMS

Требования

Я предполагаю, что вы уже знаете, как настроить рабочую установку OctoberCMS. Если нет, вы можете прочитать вводную статью или прочитать раздел установки в документации .

Что мы строим

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

Настройка плагина

Начнем с использования команды create:plugin scaffolding для создания начальной структуры плагина и определения метода Plugin::pluginDetails с нашими деталями плагина.

 php artisan create : plugin rafie . sitepointDemo 
 // Plugin.php public function pluginDetails ( ) { return [ 'name' = > 'Project management' , 'description' = > 'Manage your teams and projects.' , 'author' = > 'RAFIE Younes' , 'icon' = > 'icon-leaf' ] ; } 

Создание таблиц базы данных

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

 php artisan create : model rafie . sitepointdemo Team 
 // models/team.php class Team extends Model {  // ... public $table = 'rafie_sitepointDemo_teams' ; public $hasMany = [ 'projects' = > '\Rafie\SitepointDemo\Projects' , 'users' = > '\Backend\Models\User' ] ;  // ... } 
 // updates/create_teams_table.php class CreateTeamsTable extends Migration { public function up ( ) { Schema : : create ( 'rafie_sitepointDemo_teams' , function ( $table ) { $table - > engine = 'InnoDB' ; $table - > increments ( 'id' ) ; $table - > string ( 'name' , 100 ) ; $table - > timestamps ( ) ; } ) ; } public function down ( ) { Schema : : dropIfExists ( 'rafie_sitepointDemo_teams' ) ; } } 

Каждый проект принадлежит команде и имеет имя, описание и дату окончания.

 php artisan create : model rafie . sitepointdemo Project 
 // models/project.php class Project extends Model {  // ... public $table = 'rafie_sitepointDemo_projects' ; public $belongsTo = [ 'team' = > '\Rafie\SitepointDemo\Models\Team' ] ;  // ... } 
 // updates/create_projects_table.php class CreateProjectsTable extends Migration { public function up ( ) { Schema : : create ( 'rafie_sitepointDemo_projects' , function ( $table ) { $table - > engine = 'InnoDB' ; $table - > increments ( 'id' ) ; $table - > string ( 'name' , 100 ) ; $table - > text ( 'description' ) ; $table - > datetime ( 'ends_at' ) ; $table - > integer ( 'team_id' ) - > unsigned ( ) ; $table - > timestamps ( ) ; } ) ; }  // ... } 

Поскольку у OctoberCMS уже есть таблица пользователей для бэкэнда, мы должны добавить столбец team_id . Мы создадим миграцию для добавления индекса нашей команды.

 // updates/add_team_to_users.php class AddTeamToUsers extends Migration { public function up ( ) { if ( ! Schema : : hasColumn ( 'backend_users' , 'team_id' ) ) { Schema : : table ( 'backend_users' , function ( $table ) { $table - > integer ( 'team_id' ) - > unsigned ( ) - > index ( ) - > nullable ( ) ; } ) ; } } public function down ( ) { if ( Schema : : hasColumn ( 'backend_users' , 'team_id' ) ) { Schema : : table ( 'backend_users' , function ( $table ) { $table - > dropColumn ( 'team_id' ) ; } ) ; } } } 

Затем нам нужно связать его с новым определением отношений.

 // Plugin.php class Plugin extends PluginBase {  // ... public function boot ( ) { User : : extend ( function ( $model ) { $model - > belongsTo [ 'team' ] = [ 'Rafie\SitepointDemo\Models\Team' ] ; } ) ; } } 

Наш файл версии плагина выглядит так:

 // updates/version.yaml 1.0.1 : - First version of Sitepoint demo - add_team_to_users.php - create_teams_table.php - create_projects_table.php 

Управление командами

Внутри вашей папки моделей вы можете видеть, что у каждого класса моделей есть папка конфигурации, которая содержит два файла:

  • columns.yaml : содержит столбцы таблицы, которые вы хотите использовать при перечислении записей таблицы.
  • fields.yaml : то же самое, что и столбцы, но используется для настройки форм для создания и обновления записей.

Чтобы управлять командами, нам нужно создать контроллер для действий над определенными событиями. Мы используем следующую команду scaffolding.

 php artisan create : controller rafie . sitepointDemo Teams 

Если вы будете следовать соглашениям об именах, контроллер автоматически сопоставится с моделью. Если вы backend/rafie/sitepointDemo/teams/create в свой браузер по адресу backend/rafie/sitepointDemo/teams/create , вы увидите форму новой записи.

Внутри config_form.yaml вы увидите, что свойства form и modelClass сопоставлены с моделью нашей команды. Новая форма команды показывает только отключенный ввод для идентификатора. Мы можем добавить другие входные данные, используя файл fields.yaml внутри нашей модели.

 // models/team/fields.yaml fields : name : label : Name type : text required : true users : label : Users type : checkboxlist 

У каждой команды есть имя и список пользователей. Имя представляет собой простое текстовое значение, а пользователи отображаются с помощью компонента checkboxlist . Вы можете проверить список типов полей в документации .

У вас также есть возможность использовать виджеты формы в типах полей. Виджеты — это богатые компоненты, такие как WYSWYG-редактор, Color picker, Media Finder и т. Д. Здесь осталось только создать метод Team::getUsersOptions для заполнения списка пользователей.

 // models/team.php class Team extends Model {  // ... public function getUsersOptions ( ) { return \ Backend \ Models \ User : : lists ( 'login' , 'id' ) ; } } 

Создать команду

На приведенном выше снимке экрана видно, что поля, отмеченные как required имеют звездочку после имени ввода. Однако это не означает, что проверка выполняется для вас. Вам все еще нужно добавить проверку внутри ваших моделей.

 // models/Team.php class Team extends Model { use \ October \ Rain \ Database \ Traits \ Validation ; public $rules = [ 'name' = > 'required' ] ;  // ... } 

Поля в файле конфигурации автоматически сопоставляются с моделью, если она найдена. Если нет, нам нужно использовать методы create_onSave и update_onSave для изменения стратегии сохранения.

 // controllers/teams.php class Teams extends Controller {  // ... public function create_onSave ( ) { $inputs = post ( 'Team' ) ;  // save team $teamModel = new \ Rafie \ SitepointDemo \ Models \ Team ; $teamModel - > name = $inputs [ 'name' ] ; $teamModel - > save ( ) ;  // update users team_id \ Backend \ Models \ User : : whereIn ( 'id' , $inputs [ 'users' ] ) - > update ( [ 'team_id' = > $teamModel - > id ] ) ; \ Flash : : success ( "Team saved successfully" ) ; return $this - > makeRedirect ( 'update' , $teamModel ) ; } } 

Метод post является вспомогательной функцией, позволяющей избежать разрешения объекта запроса из контейнера. После сохранения модели команды и обновления пользовательского отношения мы показываем сообщение об успехе и создаем ответ перенаправления с помощью FormController::makeRedirect .

 // controllers/teams.php class Teams extends Controller {  // ... public function update_onSave ( $recordId ) { $inputs = post ( 'Team' ) ;  // update team $teamModel = \ Rafie \ SitepointDemo \ Models \ Team : : findOrFail ( $recordId ) ; $teamModel - > name = $inputs [ 'name' ] ; $teamModel - > save ( ) ; \ Backend \ Models \ User : : where ( 'team_id' , $teamModel - > id ) - > update ( [ 'team_id' = > 0 ] ) ;  // update users team_id \ Backend \ Models \ User : : whereIn ( 'id' , $inputs [ 'users' ] ) - > update ( [ 'team_id' = > $teamModel - > id ] ) ; \ Flash : : success ( "Team updated successfully" ) ; } } 

Метод update_onSave имеет один параметр, содержащий обновленный идентификатор записи. Мы обновляем команду и прикрепленных пользователей соответственно. Другой способ сделать это — использовать метод FormController::update_onSave . Он заботится о сопоставлении полей формы с моделью и ее сохранении.

 // controllers/teams.php class Teams extends Controller {  // ... public function update_onSave ( $recordId ) { $inputs = post ( 'Team' ) ; \ Backend \ Models \ User : : where ( 'team_id' , $recordId ) - > update ( [ 'team_id' = > 0 ] ) ;  // update users team_id \ Backend \ Models \ User : : whereIn ( 'id' , $inputs [ 'users' ] ) - > update ( [ 'team_id' = > $recordId ] ) ; $this - > asExtension ( 'FormController' ) - > update_onSave ( $recordId , $context ) ; } } 

Единственная оставшаяся часть — удаление записей. Вы можете использовать метод update_onDelete чтобы сбросить team_id в таблице пользователей, а затем удалить команду.

 // controllers/teams.php class Teams extends Controller {  // ... public function update_onDelete ( $recordId ) { $teamModel = \ Rafie \ SitepointDemo \ Models \ Team : : findOrFail ( $recordId ) ; \ Backend \ Models \ User : : where ( 'team_id' , $teamModel - > id ) - > update ( [ 'team_id' = > 0 ] ) ; $teamModel - > delete ( ) ; \ Flash : : success ( "Team deleted successfully" ) ; return $this - > makeRedirect ( 'delete' , $teamModel ) ; } } 

Или вы можете просто использовать formAfterDelete для сброса team_id .

 // controllers/teams.php class Teams extends Controller {  // ... public function formAfterDelete ( $model ) { \ Backend \ Models \ User : : where ( 'team_id' , $model - > id ) - > update ( [ 'team_id' = > 0 ] ) ; } } 

Если вы заметили, форма обновления не будет автоматически выбирать пользователей, прикрепленных к команде. Возможно, нам придется сделать это вручную, используя метод formExtendFields внутри контроллера Teams . Метод getContext возвращает информацию о том, создает ли пользователь модель или обновляет ее.

 // controllers/teams.php class Teams extends Controller {  // ... public function formExtendFields ( $form ) { if ( $form - > getContext ( ) === 'update' ) { $team = $form - > model ; $userField = $form - > getField ( 'users' ) ; $userField - > value = $team - > users - > lists ( 'id' ) ; } } } 

Управление проектами

Мы выполняем те же шаги для управления проектами: мы начинаем с определения полей формы.

 models/project/fields.yaml fields : name : label : Name type : text required : true description : label : Description type : textarea required : true ends_at : label : Ends At type : datepicker required : true team_id : label : Team type : dropdown 

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

 // models/project.php class Project extends Model {  // ... public function getTeamIdOptions ( ) { $teams = \ Rafie \ SitepointDemo \ Models \ Team : : all ( [ 'id' , 'name' ] ) ; $teamsOptions = [ ] ; $teams - > each ( function ( $team ) use ( & $teamsOptions ) { $teamsOptions [ $team - > id ] = $team - > name ; } ) ; return $teamsOptions ; } } 

Создать проект

Поскольку все поля формы сопоставлены с моделью, нам не нужно подключаться к процессу сохранения, чтобы обновить некоторые отношения. В этом случае действия по create , update и delete обрабатываются автоматически.

Листинг

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

Листинг Команды

Файл controllers/teams/config_list.yaml содержит наши параметры листинга. Каждое свойство имеет комментарий, описывающий его использование.

 // controllers/teams/config_list.yaml # Model List Column configuration list : $/rafie/sitepointdemo/models/team/columns.yaml # Model Class name modelClass : Rafie\SitepointDemo\Models\Team # List Title title : Manage Teams # Link URL for each record recordUrl : rafie/sitepointdemo/teams/update/ : id # Message to display if the list is empty noRecordsMessage : backend : : lang.list.no_records # Records to display per page recordsPerPage : 20 # Displays the list column set up button showSetup : true # Displays the sorting link on each column showSorting : true // ... 

Вы можете видеть, что свойство list указывает на файл columns.yaml , который определяет столбцы, которые должны отображаться. Опция showSetup позволяет пользователю выбирать, какие столбцы показывать в списке.

Настройка списка

 // models/team/columns.yaml columns : id : label : ID searchable : true name : label : Name users : label : Users relation : users select : login searchable : false 

Свойства id и name не требуют пояснений. По умолчанию searchable свойства searchable установлено значение true , поэтому мы не указали его в свойстве name .

OctoberCMS имеет свойство relation которое можно использовать для отображения значений из связанных моделей. В этом случае мы выбираем атрибут login в модель отношений users . Вы можете проверить документацию для полного списка доступных опций столбца.

Список команд

Листинговые проекты

Мы выполняем те же шаги для перечисления проектов: у нас есть имя, описание, дата окончания и команда.

 // models/project/columns.yaml columns : id : label : ID searchable : true name : label : Name description : label : Description type : text ends_at : label : End At type : datetime team : label : Team relation : team select : name 

Чтобы правильно отформатировать дату окончания в нашем списке, мы указываем тип столбца как datetime . Это может вызвать исключение для вас, потому что вам нужно добавить ends_at в вашей модели внутри массива $dates ends_at . Проверьте документацию для полного списка доступных типов столбцов.

 // models/project.php class Project extends Model {  // ... protected $dates = [ 'ends_at' ] ;  // ... } 

Что касается столбца команды, мы указываем тип отношения и выбираем атрибут name из модели. Конечный результат выглядит следующим образом.

Список проектов

Расширение списков

Если вы хотите изменить поведение списка, вы можете переопределить метод index внутри вашего контроллера и представление index.htm . Что мы хотим сделать сейчас — это усечь столбец описания. Вы можете проверить документацию для более подробной информации о расширении поведения списка.

 // controllers/projects.php class Projects extends Controller {  // ... public function listOverrideColumnValue ( $record , $columnName ) { if ( $columnName == "description" && strlen ( $record - > description ) > 20 ) { $description = substr ( $record - > description , 0 , 20 ) ; return " <span title='{$record-> description } ' > { $description } . . . </ span > " ; } } } 

Возможно, вы думаете о расширении списка пользователей, чтобы показать их текущую команду. Мы используем метод boot внутри нашего файла определения плагинов для расширения других плагинов.

 // Plugin.php class Plugin extends PluginBase {  // ... public function boot ( ) {  // ... \ Backend \ Controllers \ Users : : extendListColumns ( function ( $list ) { $list - > addColumns ( [ 'team' = > [ 'label' = > 'Team' , 'relation' = > 'team' , 'select' = > 'name' ] ] ) ; } ) ; } } 

Список пользователей

фильтры

Фильтрация списков в OctoberCMS проста. Сначала вы ссылаетесь на файл конфигурации фильтра в файле config_list.yaml .

 // controllers/projects/config_list.yaml // ... filter : config_filter.yaml // ... 

Внутри вашего файла config_filter.yaml вы определяете список областей, которые вы хотите использовать.

 // controllers/projects/config_filter.yaml scopes : team : label : Team modelClass : \Rafie\SitepointDemo\Models\Team nameFrom : name conditions : team_id = : filtered 

Наша область именуется team и будет перечислять наши доступные команды, используя указанные свойства modelClass и nameFrom . Условия будут фильтровать проекты, где team_id равен выбранным командам; Вы можете думать об этом как о сыром SQL-выражении where. На снимке экрана ниже показан список проектов, выполненных Backend командой.

Команда отфильтрованных проектов

OctoberCMS имеет два типа области видимости. Первый тип группы, и это тот, который мы использовали ранее. Второй тип флажка, который используется для логических ситуаций. Мы будем использовать последний, чтобы скрыть просроченные проекты из списка.

 // controllers/projects/config_filter.yaml scopes : // ... hide_past_due : label : Hide past due type : checkbox conditions : ends_at > now() 

Единственный просроченный проект в нашем списке — это проект с идентификатором 2 .

Просроченные отфильтрованные проекты

права доступа

Мы не можем говорить об операциях CRUD, не покрывая разрешения и как охранять ваши контроллеры. В сочетании с простотой Laravel, OctoberCMS позволяет вам определить список разрешений для вашего плагина и сгруппировать их на определенной вкладке, а затем использовать их для защиты ваших контроллеров.

 // Plugin.php class Plugin extends PluginBase {  // ... public function registerPermissions ( ) { return [ 'rafie.sitepointDemo.manage_teams' = > [ 'label' = > 'Manage Teams' , 'tab' = > 'SitepointDemo' ] , 'rafie.sitepointDemo.manage_projects' = > [ 'label' = > 'Manage Projects' , 'tab' = > 'SitepointDemo' ] ] ; } } 

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

Пользовательские разрешения

Теперь внутри контроллеров проектов и команд вы добавляете атрибут $requiredPermission .

 // controllers/teams.php class Teams extends Controller {  // ... public $requiredPermissions = [ 'rafie.sitepointDemo.manage_teams' ] ;  // ... } 
 // controllers/projects.php class Projects extends Controller {  // ... public $requiredPermissions = [ 'rafie.sitepointDemo.manage_projects' ] ;  // ... } 

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

 // controllers/teams.php class Teams extends Controller {  // ... public function update ( ) { if ( ! $this - > user - > hasPermissions ( [ 'rafie.sitepointDemo.update_teams' ] ) ) {  // redirect Unauthorized 401 } } } 

Вывод

Каждая CMS пытается сделать CRUD-операции более простыми и понятными для новичков, и я думаю, что OctoberCMS успешно достиг этой цели, сделав каждый ее аспект понятным и расширяемым.

Вы можете проверить окончательный результат на GitHub, и если у вас есть какие-либо вопросы или мнения, дайте мне знать в комментариях!