Before begin this practical, you must read previous lessons of the training
Specifications
You have to do the same things we did for departments on the last two parts.
Our jobs grid will have the following columns :
– Checkboxes for mass actions
– ID
– Title
– Type
– Location
– Date : We will set the format date to display
– Status : We will display “Enabled” if value is 1, “Disabled” if 0
– Department : We will display department’s name instead of the ID
– Action
Filters we will display are :
– ID
– Date
– Title
– Type
– Location
– Status
– Department
Your grid will look like this :
The add/edit form for jobs will have the following fields :
– Title : Type text
– Type : Type text
– Location : Type text
– Date : Type date (On add action, we will put the date of the day on the field with this format : YYYY-MM-DD)
– Status : Dropdown Enabled/Disabled (On add action, selected value will be Enabled)
– Description : Type textarea
– Department ID : Dropdown with departments names inside
The final display will be like this :
Dont’t worry, I will give you some help for the elements we didn’t learn before 😉
Retrieve customs options in a grid and in a dropdown
We will have an array like this for the status :
[0 => 'Disabled', 1 => 'Enabled']
First, we will make the column’s display
Create the file :
app/code/Maxime/Jobs/Model/Source/Job/Status.php
With this content :
<?php namespace Maxime\Jobs\Model\Source\Job; class Status implements \Magento\Framework\Data\OptionSourceInterface { /** * @var \Maxime\Jobs\Model\Job */ protected $_job; /** * Constructor * * @param \Maxime\Jobs\Model\Job $job */ public function __construct(\Maxime\Jobs\Model\Job $job) { $this->_job = $job; } /** * Get options * * @return array */ public function toOptionArray() { $options[] = ['label' => '', 'value' => '']; $availableOptions = $this->_job->getAvailableStatuses(); foreach ($availableOptions as $key => $value) { $options[] = [ 'label' => $value, 'value' => $key, ]; } return $options; } }
Modify the Job model in order to add the method getAvailableStatuses
app/code/Maxime/Jobs/Model/Job.php
Here is the full code of the class :
<?php namespace Maxime\Jobs\Model; use \Magento\Framework\Model\AbstractModel; class Job extends AbstractModel { const JOB_ID = 'entity_id'; // We define the id fieldname /** * Prefix of model events names * * @var string */ protected $_eventPrefix = 'jobs'; /** * Name of the event object * * @var string */ protected $_eventObject = 'job'; /** * Name of object id field * * @var string */ protected $_idFieldName = self::JOB_ID; /** * Initialize resource model * * @return void */ protected function _construct() { $this->_init('Maxime\Jobs\Model\ResourceModel\Job'); } public function getEnableStatus() { return 1; } public function getDisableStatus() { return 0; } public function getAvailableStatuses() { return [$this->getDisableStatus() => __('Disabled'), $this->getEnableStatus() => __('Enabled')]; } }
In order to add the column on the uiComponent, we will add this part of XML (I don’t give you the full XML 😉 )
<argument name="data" xsi:type="array"> <item name="options" xsi:type="object">Maxime\Jobs\Model\Source\Job\Status</item> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">select</item> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item> <item name="editor" xsi:type="string">select</item> <item name="dataType" xsi:type="string">select</item> <item name="label" xsi:type="string" translate="true">Status</item> </item> </argument>
And to make a select filter, it will look like this :
<argument name="optionsProvider" xsi:type="configurableObject"> <argument name="class" xsi:type="string">Maxime\Jobs\Model\Source\Job\Status</argument> </argument> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">status</item> <item name="label" xsi:type="string" translate="true">Status</item> <item name="caption" xsi:type="string" translate="true">Select...</item> </item> </argument>
Retrieve customs options on database for the grid and filters
To display departments name, we will do the same thing we did for the status.
The toOptionArray method will retrieve a table on DB 🙂
app/code/Maxime/Jobs/Model/Source/Department.php
Put this content inside :
<?php namespace Maxime\Jobs\Model\Source; class Department implements \Magento\Framework\Data\OptionSourceInterface { /** * @var \Maxime\Jobs\Model\Department */ protected $_department; /** * Constructor * * @param \Maxime\Jobs\Model\Department $department */ public function __construct(\Maxime\Jobs\Model\Department $department) { $this->_department = $department; } /** * Get options * * @return array */ public function toOptionArray() { $options[] = ['label' => '', 'value' => '']; $departmentCollection = $this->_department->getCollection() ->addFieldToSelect('entity_id') ->addFieldToSelect('name'); foreach ($departmentCollection as $department) { $options[] = [ 'label' => $department->getName(), 'value' => $department->getId(), ]; } return $options; } }
You will have to update the column display and filter on the uiComponent.
You can do it yourself 🙂
To change the date format, I give you this line of XML which can be useful for you :
<item name="dateFormat" xsi:type="string" translate="true">MMM dd, YYYY</item>
And now, play on
With the two previous units of the traning, and the elements above, you will be able to develop the specifications of this practical.
…
…
…
…
…
…
…
…
…
…
…
…
Have you finished ?
Correction
We will find all the files content to compare with what you did !
app/code/Maxime/Jobs/Model/Source/Job/Status.php
<?php namespace Maxime\Jobs\Model\Source\Job; class Status implements \Magento\Framework\Data\OptionSourceInterface { /** * @var \Maxime\Jobs\Model\Job */ protected $_job; /** * Constructor * * @param \Maxime\Jobs\Model\Job $job */ public function __construct(\Maxime\Jobs\Model\Job $job) { $this->_job = $job; } /** * Get options * * @return array */ public function toOptionArray() { $options[] = ['label' => '', 'value' => '']; $availableOptions = $this->_job->getAvailableStatuses(); foreach ($availableOptions as $key => $value) { $options[] = [ 'label' => $value, 'value' => $key, ]; } return $options; } }
app/code/Maxime/Jobs/Model/Source/Department.php
<?php namespace Maxime\Jobs\Model\Source; class Department implements \Magento\Framework\Data\OptionSourceInterface { /** * @var \Maxime\Jobs\Model\Department */ protected $_department; /** * Constructor * * @param \Maxime\Jobs\Model\Department $department */ public function __construct(\Maxime\Jobs\Model\Department $department) { $this->_department = $department; } /** * Get options * * @return array */ public function toOptionArray() { $options[] = ['label' => '', 'value' => '']; $departmentCollection = $this->_department->getCollection() ->addFieldToSelect('entity_id') ->addFieldToSelect('name'); foreach ($departmentCollection as $department) { $options[] = [ 'label' => $department->getName(), 'value' => $department->getId(), ]; } return $options; } }
app/code/Maxime/Jobs/view/adminhtml/layout/jobs_job_index.xml
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <uiComponent name="jobs_job_listing"/> </referenceContainer> </body> </page>
app/code/Maxime/Jobs/etc/di.xml (Edition du fichier déjà existant)
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <!-- Create our type DepartmentGridDataProvider --> <virtualType name="DepartmentGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider"> <arguments> <argument name="collection" xsi:type="object" shared="false">Maxime\Jobs\Model\Resource\Department\Collection</argument> <argument name="filterPool" xsi:type="object" shared="false">DepartmentGridFilterPool</argument> <!-- Define new object for filters --> </arguments> </virtualType> <!-- Create our type DepartmentGridFilterPool --> <virtualType name="DepartmentGridFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> <arguments> <argument name="appliers" xsi:type="array"> <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> </argument> </arguments> </virtualType> <!-- Custom collection factory here --> <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> <arguments> <argument name="collections" xsi:type="array"> <!-- Type for jobs_department_listing_data_source --> <item name="jobs_department_listing_data_source" xsi:type="string">Maxime\Jobs\Model\ResourceModel\Grid\Department\Collection</item> <!-- Type for jobs_job_listing_data_source --> <item name="jobs_job_listing_data_source" xsi:type="string">Maxime\Jobs\Model\ResourceModel\Grid\Job\Collection</item> </argument> </arguments> </type> <!-- Simulate our class Maxime\Jobs\Model\ResourceModel\Grid\Department\Collection --> <virtualType name="Maxime\Jobs\Model\ResourceModel\Grid\Department\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> <argument name="mainTable" xsi:type="string">maxime_department</argument> <argument name="resourceModel" xsi:type="string">Maxime\Jobs\Model\ResourceModel\Department</argument> </arguments> </virtualType> <!-- Create our type JobGridDataProvider --> <virtualType name="JobGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider"> <arguments> <argument name="collection" xsi:type="object" shared="false">Maxime\Jobs\Model\Resource\Job\Collection</argument> <argument name="filterPool" xsi:type="object" shared="false">JobGridFilterPool</argument> <!-- Define new object for filters --> </arguments> </virtualType> <!-- Create our type JobGridFilterPool --> <virtualType name="JobGridFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool"> <arguments> <argument name="appliers" xsi:type="array"> <item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item> <item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item> </argument> </arguments> </virtualType> <!-- Simulate our class Maxime\Jobs\Model\ResourceModel\Grid\Job\Collection --> <virtualType name="Maxime\Jobs\Model\ResourceModel\Grid\Job\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> <argument name="mainTable" xsi:type="string">maxime_job</argument> <argument name="resourceModel" xsi:type="string">Maxime\Jobs\Model\ResourceModel\Job</argument> </arguments> </virtualType> </config>
app/code/Maxime/Jobs/Ui/Component/Listing/Column/JobActions.php
<?php /** * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace Maxime\Jobs\Ui\Component\Listing\Column; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\Column; use Magento\Framework\UrlInterface; /** * Class JobActions */ class JobActions extends Column { /** * @var UrlInterface */ protected $urlBuilder; /** * @param ContextInterface $context * @param UiComponentFactory $uiComponentFactory * @param UrlInterface $urlBuilder * @param array $components * @param array $data */ public function __construct( ContextInterface $context, UiComponentFactory $uiComponentFactory, UrlInterface $urlBuilder, array $components = [], array $data = [] ) { $this->urlBuilder = $urlBuilder; parent::__construct($context, $uiComponentFactory, $components, $data); } /** * Prepare Data Source * * @param array $dataSource * @return array */ public function prepareDataSource(array $dataSource) { if (isset($dataSource['data']['items'])) { foreach ($dataSource['data']['items'] as &$item) { $item[$this->getData('name')]['edit'] = [ 'href' => $this->urlBuilder->getUrl( 'jobs/job/edit', ['id' => $item['entity_id']] ), 'label' => __('Edit'), 'hidden' => false, ]; $item[$this->getData('name')]['delete'] = [ 'href' => $this->urlBuilder->getUrl( 'jobs/job/delete', ['id' => $item['entity_id']] ), 'label' => __('Delete'), 'hidden' => false, ]; } } return $dataSource; } }
app/code/Maxime/Jobs/view/adminhtml/ui_component/jobs_job_listing.xml
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> <!-- Integration --> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <!-- we define a provider --> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing_data_source</item> <!-- same string as above --> <item name="deps" xsi:type="string">jobs_job_listing.jobs_job_listing_data_source</item> </item> <!-- define column type --> <item name="spinner" xsi:type="string">jobs_job_columns</item> <!-- Button to add new item --> <item name="buttons" xsi:type="array"> <item name="add" xsi:type="array"> <item name="name" xsi:type="string">add</item> <item name="label" xsi:type="string" translate="true">Add New job</item> <item name="class" xsi:type="string">primary</item> <item name="url" xsi:type="string">*/*/new</item> <!-- magento will translate it by jobs/job/new --> </item> </item> </argument> <!-- Data source --> <dataSource name="jobs_job_listing_data_source"> <argument name="dataProvider" xsi:type="configurableObject"> <argument name="class" xsi:type="string">JobGridDataProvider</argument> <!-- Data provider class --> <argument name="name" xsi:type="string">jobs_job_listing_data_source</argument> <!-- provider defined above --> <argument name="primaryFieldName" xsi:type="string">entity_id</argument> <!-- Primary key --> <argument name="requestFieldName" xsi:type="string">id</argument> <!-- URL name parameter --> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item> <item name="update_url" xsi:type="url" path="mui/index/render"/> <item name="storageConfig" xsi:type="array"> <!-- Primary key column name --> <item name="indexField" xsi:type="string">entity_id</item> </item> </item> </argument> </argument> </dataSource> <!-- Container Listing Top --> <container name="listing_top"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="template" xsi:type="string">ui/grid/toolbar</item> </item> </argument> <!-- Button to manage views --> <bookmark name="bookmarks"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/controls/bookmarks/bookmarks</item> <item name="displayArea" xsi:type="string">dataGridActions</item> <item name="storageConfig" xsi:type="array"> <item name="saveUrl" xsi:type="url" path="mui/bookmark/save"/> <item name="deleteUrl" xsi:type="url" path="mui/bookmark/delete"/> <item name="namespace" xsi:type="string">jobs_job_listing</item> </item> </item> </argument> </bookmark> <!-- Button to manage columns --> <container name="columns_controls"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="columnsData" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.jobs_job_columns</item> </item> <item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item> <item name="displayArea" xsi:type="string">dataGridActions</item> </item> </argument> </container> <!-- Filters --> <filters name="listing_filters"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="storageConfig" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.bookmarks</item> <item name="namespace" xsi:type="string">current.filters</item> </item> <item name="childDefaults" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.listing_filters</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.bookmarks:current.columns.${ $.index }.visible</item> </item> </item> </item> </argument> <!-- Job ID Filter --> <filterRange name="job_id"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">entity_id</item> <!-- Column name in DB --> <item name="label" xsi:type="string" translate="true">ID</item> <!-- Label on grid --> <item name="childDefaults" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.listing_filters</item> </item> </item> </argument> <filterInput name="from"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">from</item> <item name="label" xsi:type="string" translate="true">from</item> <item name="placeholder" xsi:type="string" translate="true">From</item> </item> </argument> </filterInput> <filterInput name="to"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">to</item> <item name="label" xsi:type="string" translate="true">to</item> <item name="placeholder" xsi:type="string" translate="true">To</item> </item> </argument> </filterInput> </filterRange> <!-- Job title Filter --> <filterInput name="job_title"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">title</item> <!-- Column name in DB --> <item name="label" xsi:type="string" translate="true">Title</item> <!-- Label on grid --> </item> </argument> </filterInput> <!-- Job type Filter --> <filterInput name="job_type"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">type</item> <!-- Column name in DB --> <item name="label" xsi:type="string" translate="true">Type</item> <!-- Label on grid --> </item> </argument> </filterInput> <!-- Job location Filter --> <filterInput name="job_location"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">location</item> <!-- Column name in DB --> <item name="label" xsi:type="string" translate="true">Location</item> <!-- Label on grid --> </item> </argument> </filterInput> <!-- Job date Filter / Custom Select--> <filterRange name="job_date" class="Magento\Ui\Component\Filters\Type\DateRange"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">date</item> <item name="label" xsi:type="string" translate="true">Date</item> <item name="childDefaults" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.listing_filters</item> </item> </item> </argument> <filterDate name="from"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">from</item> <item name="label" xsi:type="string" translate="true">from</item> <item name="placeholder" xsi:type="string" translate="true">From</item> </item> </argument> </filterDate> <filterDate name="to"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">to</item> <item name="label" xsi:type="string" translate="true">to</item> <item name="placeholder" xsi:type="string" translate="true">To</item> </item> </argument> </filterDate> </filterRange> <!-- Job status Filter / Custom Select--> <filterSelect name="job_status"> <argument name="optionsProvider" xsi:type="configurableObject"> <argument name="class" xsi:type="string">Maxime\Jobs\Model\Source\Job\Status</argument> </argument> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">status</item> <item name="label" xsi:type="string" translate="true">Status</item> <item name="caption" xsi:type="string" translate="true">Select...</item> </item> </argument> </filterSelect> <!-- Job Department Filter / Custom Select on DB --> <filterSelect name="job_department"> <argument name="optionsProvider" xsi:type="configurableObject"> <argument name="class" xsi:type="string">Maxime\Jobs\Model\Source\Department</argument> </argument> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="dataScope" xsi:type="string">department_id</item> <item name="label" xsi:type="string" translate="true">Department</item> <item name="caption" xsi:type="string" translate="true">Select...</item> </item> </argument> </filterSelect> </filters> <!-- Filter Search --> <filterSearch name="fulltext"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing_data_source</item> <item name="chipsProvider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.listing_filters_chips</item> <item name="storageConfig" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.bookmarks</item> <item name="namespace" xsi:type="string">current.search</item> </item> </item> </argument> </filterSearch> <!-- Mass action --> <massaction name="listing_massaction"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="selectProvider" xsi:type="string">jobs_job_listing.jobs_job_listing.jobs_job_columns.ids</item> <item name="indexField" xsi:type="string">entity_id</item> </item> </argument> <action name="delete"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="type" xsi:type="string">delete</item> <item name="label" xsi:type="string" translate="true">Delete</item> <item name="url" xsi:type="url" path="jobs/job/massDelete"/> <item name="confirm" xsi:type="array"> <item name="title" xsi:type="string" translate="true">Delete items</item> <item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected items?</item> </item> </item> </argument> </action> </massaction> <!-- Paging --> <paging name="listing_paging"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="storageConfig" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.bookmarks</item> <item name="namespace" xsi:type="string">current.paging</item> </item> <item name="selectProvider" xsi:type="string">jobs_job_listing.jobs_job_listing.jobs_job_columns.ids</item> <item name="displayArea" xsi:type="string">bottom</item> </item> </argument> </paging> </container> <!-- Columns --> <columns name="jobs_job_columns"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <!-- Bookmarks behaviour --> <item name="storageConfig" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.bookmarks</item> <item name="namespace" xsi:type="string">current</item> </item> <item name="childDefaults" xsi:type="array"> <item name="controlVisibility" xsi:type="boolean">true</item> <!-- Bookmarks behaviour --> <item name="storageConfig" xsi:type="array"> <item name="provider" xsi:type="string">jobs_job_listing.jobs_job_listing.listing_top.bookmarks</item> <item name="root" xsi:type="string">columns.${ $.index }</item> <item name="namespace" xsi:type="string">current.${ $.storageConfig.root}</item> </item> </item> </item> </argument> <!-- Add columns with checkboxes --> <selectionsColumn name="ids"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="resizeEnabled" xsi:type="boolean">false</item> <item name="resizeDefaultWidth" xsi:type="string">55</item> <item name="indexField" xsi:type="string">entity_id</item> </item> </argument> </selectionsColumn> <!-- ID Column --> <column name="entity_id"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">textRange</item> <item name="sorting" xsi:type="string">asc</item> <item name="label" xsi:type="string" translate="true">ID</item> </item> </argument> </column> <!-- Title Column --> <column name="title"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">text</item> <item name="editor" xsi:type="array"> <item name="editorType" xsi:type="string">text</item> <item name="validation" xsi:type="array"> <item name="required-entry" xsi:type="boolean">true</item> </item> </item> <item name="label" xsi:type="string" translate="true">Title</item> </item> </argument> </column> <!-- Type Column --> <column name="type"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">text</item> <item name="editor" xsi:type="array"> <item name="editorType" xsi:type="string">text</item> <item name="validation" xsi:type="array"> <item name="required-entry" xsi:type="boolean">true</item> </item> </item> <item name="label" xsi:type="string" translate="true">Type</item> </item> </argument> </column> <!-- Location Column --> <column name="location"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">text</item> <item name="editor" xsi:type="array"> <item name="editorType" xsi:type="string">text</item> <item name="validation" xsi:type="array"> <item name="required-entry" xsi:type="boolean">true</item> </item> </item> <item name="label" xsi:type="string" translate="true">Location</item> </item> </argument> </column> <!-- Date Column / Date type --> <column name="date" class="Magento\Ui\Component\Listing\Columns\Date"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">dateRange</item> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item> <item name="dataType" xsi:type="string">date</item> <item name="label" xsi:type="string" translate="true">Date</item> <item name="dateFormat" xsi:type="string" translate="true">MMM dd, YYYY</item> </item> </argument> </column> <!-- Status Column / Custom Source --> <column name="status"> <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">Maxime\Jobs\Model\Source\Job\Status</item> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">select</item> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item> <item name="editor" xsi:type="string">select</item> <item name="dataType" xsi:type="string">select</item> <item name="label" xsi:type="string" translate="true">Status</item> </item> </argument> </column> <!-- Department Column / Custom Source on DB --> <column name="department_id"> <argument name="data" xsi:type="array"> <item name="options" xsi:type="object">Maxime\Jobs\Model\Source\Department</item> <item name="config" xsi:type="array"> <item name="filter" xsi:type="string">select</item> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item> <item name="editor" xsi:type="string">select</item> <item name="dataType" xsi:type="string">select</item> <item name="label" xsi:type="string" translate="true">Department</item> </item> </argument> </column> <!-- Action columns --> <actionsColumn name="actions" class="Maxime\Jobs\Ui\Component\Listing\Column\JobActions"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="resizeEnabled" xsi:type="boolean">false</item> <item name="resizeDefaultWidth" xsi:type="string">107</item> <item name="indexField" xsi:type="string">entity_id</item> </item> </argument> </actionsColumn> </columns> </listing>
app/code/Maxime/Jobs/Controller/Adminhtml/Job/NewAction.php
<?php namespace Maxime\Jobs\Controller\Adminhtml\Job; use Magento\Backend\App\Action; class NewAction extends Action { /** * @var \Magento\Backend\Model\View\Result\Forward */ protected $_resultForwardFactory; /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory ) { $this->_resultForwardFactory = $resultForwardFactory; parent::__construct($context); } /** * {@inheritdoc} */ protected function _isAllowed() { return $this->_authorization->isAllowed('Maxime_Jobs::job_save'); } /** * Forward to edit * * @return \Magento\Backend\Model\View\Result\Forward */ public function execute() { /** @var \Magento\Backend\Model\View\Result\Forward $resultForward */ $resultForward = $this->_resultForwardFactory->create(); return $resultForward->forward('edit'); } }
app/code/Maxime/Jobs/Controller/Adminhtml/Job/Edit.php
<?php namespace Maxime\Jobs\Controller\Adminhtml\Job; use Magento\Backend\App\Action; class Edit extends Action { /** * Core registry * * @var \Magento\Framework\Registry */ protected $_coreRegistry = null; /** * @var \Magento\Framework\View\Result\PageFactory */ protected $_resultPageFactory; /** * @var \Maxime\Jobs\Model\Job */ protected $_model; /** * @param Action\Context $context * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory * @param \Magento\Framework\Registry $registry * @param \Maxime\Jobs\Model\Job $model */ public function __construct( Action\Context $context, \Magento\Framework\View\Result\PageFactory $resultPageFactory, \Magento\Framework\Registry $registry, \Maxime\Jobs\Model\Job $model ) { $this->_resultPageFactory = $resultPageFactory; $this->_coreRegistry = $registry; $this->_model = $model; parent::__construct($context); } /** * {@inheritdoc} */ protected function _isAllowed() { return $this->_authorization->isAllowed('Maxime_Jobs::job_save'); } /** * Init actions * * @return \Magento\Backend\Model\View\Result\Page */ protected function _initAction() { // load layout, set active menu and breadcrumbs /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $this->_resultPageFactory->create(); $resultPage->setActiveMenu('Maxime_Jobs::job') ->addBreadcrumb(__('Job'), __('Job')) ->addBreadcrumb(__('Manage Jobs'), __('Manage Jobs')); return $resultPage; } /** * Edit Job * * @return \Magento\Backend\Model\View\Result\Page|\Magento\Backend\Model\View\Result\Redirect * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { $id = $this->getRequest()->getParam('id'); $model = $this->_model; // If you have got an id, it's edition if ($id) { $model->load($id); if (!$model->getId()) { $this->messageManager->addError(__('This job not exists.')); /** \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('*/*/'); } } $data = $this->_getSession()->getFormData(true); if (!empty($data)) { $model->setData($data); } $this->_coreRegistry->register('jobs_job', $model); /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $this->_initAction(); $resultPage->addBreadcrumb( $id ? __('Edit Job') : __('New Job'), $id ? __('Edit Job') : __('New Job') ); $resultPage->getConfig()->getTitle()->prepend(__('Jobs')); $resultPage->getConfig()->getTitle() ->prepend($model->getId() ? $model->getTitle() : __('New Job')); return $resultPage; } }
app/code/Maxime/Jobs/Controller/Adminhtml/Job/Save.php
<?php namespace Maxime\Jobs\Controller\Adminhtml\Job; use Magento\Backend\App\Action; class Save extends Action { /** * @var \Maxime\Jobs\Model\Job */ protected $_model; /** * @param Action\Context $context * @param \Maxime\Jobs\Model\Job $model */ public function __construct( Action\Context $context, \Maxime\Jobs\Model\Job $model ) { parent::__construct($context); $this->_model = $model; } /** * {@inheritdoc} */ protected function _isAllowed() { return $this->_authorization->isAllowed('Maxime_Jobs::job_save'); } /** * Save action * * @return \Magento\Framework\Controller\ResultInterface */ public function execute() { $data = $this->getRequest()->getPostValue(); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); if ($data) { /** @var \Maxime\Jobs\Model\Job $model */ $model = $this->_model; $id = $this->getRequest()->getParam('id'); if ($id) { $model->load($id); } $model->setData($data); $this->_eventManager->dispatch( 'jobs_job_prepare_save', ['deparment' => $model, 'request' => $this->getRequest()] ); try { $model->save(); $this->messageManager->addSuccess(__('Job saved')); $this->_getSession()->setFormData(false); if ($this->getRequest()->getParam('back')) { return $resultRedirect->setPath('*/*/edit', ['id' => $model->getId(), '_current' => true]); } return $resultRedirect->setPath('*/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addError($e->getMessage()); } catch (\RuntimeException $e) { $this->messageManager->addError($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addException($e, __('Something went wrong while saving the job')); } $this->_getSession()->setFormData($data); return $resultRedirect->setPath('*/*/edit', ['entity_id' => $this->getRequest()->getParam('id')]); } return $resultRedirect->setPath('*/*/'); } }
app/code/Maxime/Jobs/view/adminhtml/layout/jobs_job_edit.xml
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <update handle="editor"/> <body> <referenceContainer name="content"> <block class="Maxime\Jobs\Block\Adminhtml\Job\Edit" name="jobs_job_edit"/> </referenceContainer> </body> </page>
app/code/Maxime/Jobs/Block/Adminhtml/Job/Edit.php
<?php namespace Maxime\Jobs\Block\Adminhtml\Job; use Magento\Backend\Block\Widget\Form\Container; class Edit extends Container { /** * Core registry * * @var \Magento\Framework\Registry */ protected $_coreRegistry = null; /** * @param \Magento\Backend\Block\Widget\Context $context * @param \Magento\Framework\Registry $registry * @param array $data */ public function __construct( \Magento\Backend\Block\Widget\Context $context, \Magento\Framework\Registry $registry, array $data = [] ) { $this->_coreRegistry = $registry; parent::__construct($context, $data); } /** * Job edit block * * @return void */ protected function _construct() { $this->_objectId = 'entity_id'; $this->_blockGroup = 'Maxime_Jobs'; $this->_controller = 'adminhtml_job'; parent::_construct(); if ($this->_isAllowedAction('Maxime_Jobs::job_save')) { $this->buttonList->update('save', 'label', __('Save Job')); $this->buttonList->add( 'saveandcontinue', [ 'label' => __('Save and Continue Edit'), 'class' => 'save', 'data_attribute' => [ 'mage-init' => [ 'button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form'], ], ] ], -100 ); } else { $this->buttonList->remove('save'); } } /** * Get header with Job name * * @return \Magento\Framework\Phrase */ public function getHeaderText() { if ($this->_coreRegistry->registry('jobs_job')->getId()) { return __("Edit Job '%1'", $this->escapeHtml($this->_coreRegistry->registry('jobs_job')->getTitle())); } else { return __('New Job'); } } /** * Check permission for passed action * * @param string $resourceId * @return bool */ protected function _isAllowedAction($resourceId) { return $this->_authorization->isAllowed($resourceId); } /** * Getter of url for "Save and Continue" button * tab_id will be replaced by desired by JS later * * @return string */ protected function _getSaveAndContinueUrl() { return $this->getUrl('jobs/*/save', ['_current' => true, 'back' => 'edit', 'active_tab' => '']); } }
app/code/Maxime/Jobs/Block/Adminhtml/Job/Edit/Form.php
<?php namespace Maxime\Jobs\Block\Adminhtml\Job\Edit; use \Magento\Backend\Block\Widget\Form\Generic; class Form extends Generic { /** * @var \Magento\Store\Model\System\Store */ protected $_systemStore; /** * @var \Magento\Store\Model\System\Store */ protected $_status; /** * @var \Magento\Store\Model\System\Store */ protected $_department; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Data\FormFactory $formFactory * @param \Magento\Store\Model\System\Store $systemStore * @param \Maxime\Jobs\Model\Source\Job\Status $status * @param \Maxime\Jobs\Model\Source\Department $department * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\Registry $registry, \Magento\Framework\Data\FormFactory $formFactory, \Magento\Store\Model\System\Store $systemStore, \Maxime\Jobs\Model\Source\Job\Status $status, \Maxime\Jobs\Model\Source\Department $department, array $data = [] ) { $this->_systemStore = $systemStore; $this->_status = $status; $this->_department = $department; parent::__construct($context, $registry, $formFactory, $data); } /** * Init form * * @return void */ protected function _construct() { parent::_construct(); $this->setId('job_form'); $this->setTitle(__('Job Informations')); } /** * Prepare form * * @return $this */ protected function _prepareForm() { /** @var \Maxime\Jobs\Model\Job $model */ $model = $this->_coreRegistry->registry('jobs_job'); /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create( ['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']] ); $form->setHtmlIdPrefix('job_'); $fieldset = $form->addFieldset( 'base_fieldset', ['legend' => __('General Information'), 'class' => 'fieldset-wide'] ); if ($model->getId()) { $fieldset->addField('entity_id', 'hidden', ['name' => 'entity_id']); } // Title - Type Text $fieldset->addField( 'title', 'text', ['name' => 'title', 'label' => __('Title'), 'title' => __('Title'), 'required' => true] ); // Type - Type Text $fieldset->addField( 'type', 'text', ['name' => 'type', 'label' => __('Type'), 'title' => __('Type'), 'required' => true] ); // Location - Type text $fieldset->addField( 'location', 'text', ['name' => 'location', 'label' => __('Location'), 'title' => __('Location'), 'required' => true] ); // Date - Type date if (!$model->getId()) { $model->setDate(date('Y-m-d')); // Day date when adding a job } $fieldset->addField( 'date', 'date', ['name' => 'date', 'label' => __('Date'), 'title' => __('Date'), 'required' => false, 'date_format' => 'Y-MM-dd'] ); // Status - Dropdown if (!$model->getId()) { $model->setStatus('1'); // Enable status when adding a Job } $statuses = $this->_status->toOptionArray(); $fieldset->addField( 'status', 'select', ['name' => 'status', 'label' => __('Status'), 'title' => __('Status'), 'required' => true, 'values' => $statuses] ); // Description - Type textarea $fieldset->addField( 'description', 'textarea', ['name' => 'description', 'label' => __('Description'), 'title' => __('Description'), 'required' => true] ); // Department - Dropdown $departments = $this->_department->toOptionArray(); $fieldset->addField( 'department_id', 'select', ['name' => 'department_id', 'label' => __('Department'), 'title' => __('Department'), 'required' => true, 'values' => $departments] ); $form->setValues($model->getData()); $form->setUseContainer(true); $this->setForm($form); return parent::_prepareForm(); } }
app/code/Maxime/Jobs/Controller/Adminhtml/Job/Delete.php
<?php namespace Maxime\Jobs\Controller\Adminhtml\Job; use Magento\Backend\App\Action; class Delete extends Action { protected $_model; /** * @param Action\Context $context * @param \Maxime\Jobs\Model\Job $model */ public function __construct( Action\Context $context, \Maxime\Jobs\Model\Job $model ) { parent::__construct($context); $this->_model = $model; } /** * {@inheritdoc} */ protected function _isAllowed() { return $this->_authorization->isAllowed('Maxime_Jobs::job_delete'); } /** * Delete action * * @return \Magento\Framework\Controller\ResultInterface */ public function execute() { $id = $this->getRequest()->getParam('id'); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); if ($id) { try { $model = $this->_model; $model->load($id); $model->delete(); $this->messageManager->addSuccess(__('Job deleted')); return $resultRedirect->setPath('*/*/'); } catch (\Exception $e) { $this->messageManager->addError($e->getMessage()); return $resultRedirect->setPath('*/*/edit', ['id' => $id]); } } $this->messageManager->addError(__('Job does not exist')); return $resultRedirect->setPath('*/*/'); } }
app/code/Maxime/Jobs/Controller/Adminhtml/Job/MassDelete.php
<?php namespace Maxime\Jobs\Controller\Adminhtml\Job; use Magento\Framework\Controller\ResultFactory; use Magento\Backend\App\Action\Context; use Magento\Ui\Component\MassAction\Filter; use Maxime\Jobs\Model\ResourceModel\Job\CollectionFactory; class MassDelete extends \Magento\Backend\App\Action { /** * @var Filter */ protected $filter; /** * @var CollectionFactory */ protected $collectionFactory; /** * @param Context $context * @param Filter $filter * @param CollectionFactory $collectionFactory */ public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory) { $this->filter = $filter; $this->collectionFactory = $collectionFactory; parent::__construct($context); } /** * Execute action * * @return \Magento\Backend\Model\View\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException|\Exception */ public function execute() { $collection = $this->filter->getCollection($this->collectionFactory->create()); $collectionSize = $collection->getSize(); foreach ($collection as $item) { $item->delete(); } $this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize)); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath('*/*/'); } }
app/code/Maxime/Jobs/Model/ResourceModel/Job/CollectionFactory.php
<?php namespace Maxime\Jobs\Model\ResourceModel\Job; class CollectionFactory { /** * Object Manager instance * * @var \Magento\Framework\ObjectManagerInterface */ protected $_objectManager = null; /** * Instance name to create * * @var string */ protected $_instanceName = null; /** * Factory constructor * * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param string $instanceName */ public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager, $instanceName = '\\Maxime\\Jobs\\Model\\ResourceModel\\Job\\Collection') { $this->_objectManager = $objectManager; $this->_instanceName = $instanceName; } /** * Create class instance with specified parameters * * @param array $data * @return \Maxime\Jobs\Model\ResourceModel\Job\Collection */ public function create(array $data = array()) { return $this->_objectManager->create($this->_instanceName, $data); } }
I think I put all the files 🙂
If I forget one or if you saw an error, you can write a comment to warn me.
You can also write a comment if you have some questions 😉
Now we have got all the elements of our module on the BO, we will make the front part of it !
Hi Maxim,
Compliments for the great tutorial.
However i am facing a little issue.
When i am using the delete action then i always get redirected to the admin.
You can take a look at my repo: https://bitbucket.org/magento20/uicomponents/overview
and maybe you can see what i did wrong.
The adminurl for the test shop is: magento2.assaka.nl/admin
Login: upwork
Password: upwork2016
Thanks again for the great tutorial.
Greetings,
Hamid
hi guy, model in post, pls help me show all title of gird view go outside frontend !! pls
Two years later these posts are still saving lives. Thank you! A beer for you!