Magento 2 Training : Practical 1

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 :

jobsgridfilter

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 :

jobsform

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

Show

<?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;
    }
}

[collapse]


app/code/Maxime/Jobs/Model/Source/Department.php

Show

<?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;
    }
}

[collapse]


app/code/Maxime/Jobs/view/adminhtml/layout/jobs_job_index.xml

Show

<?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>

[collapse]


app/code/Maxime/Jobs/etc/di.xml (Edition du fichier déjà existant)

Show

<?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>

[collapse]


app/code/Maxime/Jobs/Ui/Component/Listing/Column/JobActions.php

Show

<?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;
    }
}

[collapse]


app/code/Maxime/Jobs/view/adminhtml/ui_component/jobs_job_listing.xml

Show

<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>

[collapse]


app/code/Maxime/Jobs/Controller/Adminhtml/Job/NewAction.php

Show

<?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');
    }
}

[collapse]


app/code/Maxime/Jobs/Controller/Adminhtml/Job/Edit.php

Show

<?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;
    }
}

[collapse]


app/code/Maxime/Jobs/Controller/Adminhtml/Job/Save.php

Show

<?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('*/*/');
    }
}

[collapse]


app/code/Maxime/Jobs/view/adminhtml/layout/jobs_job_edit.xml

Show

<?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>

[collapse]


app/code/Maxime/Jobs/Block/Adminhtml/Job/Edit.php

Show

<?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' => '']);
    }
}

[collapse]


app/code/Maxime/Jobs/Block/Adminhtml/Job/Edit/Form.php

Show

<?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();
    }
}

[collapse]


app/code/Maxime/Jobs/Controller/Adminhtml/Job/Delete.php

Show

<?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('*/*/');
    }
}

[collapse]


app/code/Maxime/Jobs/Controller/Adminhtml/Job/MassDelete.php

Show

<?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('*/*/');
    }
}

[collapse]


app/code/Maxime/Jobs/Model/ResourceModel/Job/CollectionFactory.php

Show

<?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);
    }
}

[collapse]


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 !

Continue training
Return to previous lesson
Practical : Create grid, forms and controllers for Job object
Tagged on:

3 thoughts on “Practical : Create grid, forms and controllers for Job object

  • 06/01/2016 at 22:32
    Permalink

    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

    Reply
  • 09/14/2017 at 09:06
    Permalink

    hi guy, model in post, pls help me show all title of gird view go outside frontend !! pls

    Reply
  • 03/01/2018 at 21:20
    Permalink

    Two years later these posts are still saving lives. Thank you! A beer for you!

    Reply

Leave a Reply to nobackall Cancel reply

Your email address will not be published. Required fields are marked *

We use cookies to ensure that we give you the best experience on our website.
Ok