Formation Magento 2 : Travaux Pratiques 1

Avant de commencer ce TP, vous devez avoir suivi les cours précédents de la formation

Cahier des charges

Vous allez devoir refaire ce qu’on a fait pour les departments dans les deux dernières parties, mais pour les jobs cette fois-ci.
Notre grid des jobs devra contenir les colonnes suivantes :
– Cases à cocher pour les actions de masse
– ID
– Title
– Type
– Location
– Date : On précisera le format d’affichage de la date à Magento
– Status : On affichera « Enabled » si 1, « Disabled » si 0
– Department : On affichera le nom du department au lieu de son ID
– Action

Nos filtres seront les suivants :
– ID
– Date
– Title
– Type
– Location
– Status
– Department

L’affichage final ressemblera à ceci :

jobsgridfilter

Notre formulaire d’ajout/édition de jobs sera composé des champs suivants :
– Title : Type text
– Type : Type text
– Location : Type text
– Date : Type date (Lors de l’ajout on pré-remplira ce champ avec la date du jour au format comme en BDD : YYYY-MM-DD)
– Status : Menu déroulant Enabled/Disabled (Lors de l’ajout la valeur par défaut sera à Enabled)
– Description : Type textarea
– Department ID : Menu déroulant avec les différents départments identifiés par leurs noms

L’affichage final ressemblera à ceci :

jobsform

Ne vous inquiétez pas, je vais vous venir en aide pour certains points que nous n’avons pas vu ensemble.

Récupération des options customs dans une grid et dans un menu déroulant

Pour le statut, nous avons besoin de ce genre de tableau :
[0 => 'Disabled', 1 => 'Enabled']

Premièrement il faut que l’on se charge de l’affichage de la colonne.
Créez le fichier :
app/code/Maxime/Jobs/Model/Source/Job/Status.php

Avec ce contenu :

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

Et modifiez la classe Job pour ajouter la méthode getAvailableStatuses
app/code/Maxime/Jobs/Model/Job.php

Je vous donne le code complet de la classe au cas où 😉

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

Pour la colonne dans l’uiComponent nous aurons ce type de XML (Je ne vous le donne pas entier de suite par contre 😉 )

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

Et pour le filtre voici comment utiliser un select :

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

Récupération des options customs en BDD pour la grid et les filtres de l’admin

Pour les departments, ca sera grosso-modo le même principe.
Sauf que le toOptionArray retournera un tableau récupéré depuis la BDD 🙂

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

Mettez le contenu suivant :

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

On met à jour l’affichage de la colonne et du filtre dans l’uiComponent.
Ca je vous laisse faire comme des grands 🙂

Concernant l’affichage des dates, je vous donne juste une ligne importante qui est celle-ci :

<item name="dateFormat" xsi:type="string" translate="true">MMM dd, YYYY</item>

A vous de jouer !

Reprenez les deux précédents chapitres, ainsi que les éléments que je vous ai fourni ci-dessus.
Vous ne devriez pas rencontrer de grandes difficultés, mais il faut quand même réfléchir un minimum !












Alors vous avez fini ?

Correctif

Voici les fichiers un par un et leur contenu !

app/code/Maxime/Jobs/Model/Source/Job/Status.php

Afficher

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

Afficher

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

Afficher

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

Afficher

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

Afficher

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

Afficher

<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

Afficher

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

Afficher

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

Afficher

<?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',
                ['job' => $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

Afficher

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

Afficher

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

Afficher

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

Afficher

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

Afficher

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

Afficher

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


Voilà je pense que tous les fichiers sont présents 🙂

Si j’en ai oublié un ou si vous remarquez une erreur, n’hésitez pas à en faire part dans les commentaires.
Si vous avez des questions également 😉

Maintenant que nous avons tous les éléments en back office pour notre module, nous allons nous occuper de la partie front !

Continuer la formation
Revenir à la partie précédente
TP : Créer la grid, les formulaires et les controllers pour l’objet Job
Taggé sur :

19 thoughts on “TP : Créer la grid, les formulaires et les controllers pour l’objet Job

  • 02/03/2016 à 17:17
    Permalink

    Merci beaucoup pour ce tutorial ! ll est très bien fait.

    Répondre
  • 16/03/2016 à 15:17
    Permalink

    Super ton tuto, mais j’ai eu une erreur sur le di.xml après avoir fini ce TP.
    La ligne

    Maxime\Jobs\Model\ResourceModel\Grid\Job\Collection

    doit être déplacée après notre première déclaration faite pour Department

    Maxime\Jobs\Model\ResourceModel\Grid\Department\Collection

    Sinon à l’exécution j’ai une erreur du type : « ‘More than one node matching the query: /config/type[@name=’Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory’]’ in /var/www/html/magento2/vendor/magento/framework/Config/Dom.php on line 256 » le noeud de type ‘Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory’ semble pouvoir être déclaré 1 seule fois !

    Encore bravo pour ce tuto très bien détaillé (c’est assez déroutant ce volume de fichiers/code pour créer 3 colonnes… (en exagérant un peu 🙂 ))!

    Répondre
    • 16/03/2016 à 15:22
      Permalink

      Edit :
      La ligne

      Maxime\Jobs\Model\ResourceModel\Grid\Job\Collection

      doit être déplacée après notre première déclaration faite pour Department

      Maxime\Jobs\Model\ResourceModel\Grid\Department\Collection

      Répondre
      • 16/03/2016 à 15:31
        Permalink

        Je n’ai pas tout saisi, peux-tu envoyer le di.xml complet (via http://pastebin.com/ par exemple)

        Sinon l’erreur « More than one node … » est renvoyée par ton IDE ?

        Répondre
      • 16/03/2016 à 15:44
        Permalink

        Je pense que j’ai saisi 😉
        J’ai mis a jour le fichier di.xml dans le tuto !
        Plus d’erreur dans l’IDE et le module fonctionne toujours correctement 🙂

        Vu le nombre de fichiers et de lignes de code je m’en doutais qu’une erreur se glissait quelque part !
        Merci pour ton retour en tout cas 😉

        Certes les fichiers sont conséquents, mais avec Magento 1 il fallait créer plein de classes pour obtenir ce résultat.
        Ici c’est déjà plus « flexible » et quasiment tout est dans l’uiComponent

        Répondre
        • 17/03/2016 à 14:12
          Permalink

          Oui, c’est ça ! Désolé, pour les explications, le code a été enlevé dans mon commentaire, ce qui rend en effet mon message difficilement compréhensible !

          Répondre
          • 17/03/2016 à 14:13
            Permalink

            Oui je m’en suis aperçu je vais voir pour corriger ça rapidement 😉

  • 24/03/2016 à 17:05
    Permalink

    Bonjour,

    Très bonne formation, elle me sauve face à cet univers mystérieux qu’est magento pour les novices ^^
    J’ai une petite question pour les dates. Dans le formulaire d’édition, les dates s’affichent ainsi "Friday, January 1, 2016 at 12:00:00 AM Central European Standard Time". J’ai essayé tout un tas de choses au niveau du Form.php rien à faire, ca ne prend rien en compte. Comment puis-je forcer un affichage du genre "2016-01-01" ?

    Merci de votre aide !

    Répondre
    • 25/03/2016 à 14:15
      Permalink

      Cela se passe bien dans le Form.php :
      app/code/Maxime/Jobs/Block/Adminhtml/Job/Edit/Form.php

      C’est le code :

      // 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']
      );

      Qui permet de mettre la date du jour au format YYYY-MM-DD si c’est vide.
      Et dans les paramètres du addField on a : ‘date_format’ => ‘Y-MM-dd’

      Apparemment le format que l’on met en paramètre dans date_format est différent du format de la méthode date() de PHP. Je n’ai pas regardé le pourquoi du comment mais faites quelques tests pour voir la différence

      Répondre
  • 20/05/2016 à 12:22
    Permalink

    La partie save Job ne fonctionne pas corectement, quand on clique sur save ça génére l’erreur suivante:

    SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`sampdatmag`.`msdmaxime_job`, CONSTRAINT `MSDMAXIME_JOB_DEPARTMENT_ID_MAXIME_DEPARTMENT_ENTITY_ID` FOREIGN KEY (`department_id`) REFERENCES `maxime_department` (`entity_id`) ON DELETE CASCADE), query was: UPDATE `msdmaxime_job` SET `title` = ?, `type` = ?, `location` = ?, `date` = ‘2016-01-02′, `status` = ?, `description` = ?, `department_id` = ? WHERE (entity_id=’4’) »;i:1;s:7603: »

    Répondre
    • 20/05/2016 à 13:38
      Permalink

      Essayes-tu de sauvegarder un job qui a un department qui n’existe plus ?
      Logiquement ca ne devrait pas arriver avec le delete cascade

      Répondre
      • 20/05/2016 à 14:58
        Permalink

        Non il s’agit d’un job qui a un département existant, en plus c’est reproductible aussi en cas de modification.
        Est quand j’exécute une requête SQLpour ajouter un job dans phpMyAdmin j’ai le même comportement

        Répondre
        • 21/05/2016 à 19:09
          Permalink

          Étrange j’ai testé mon module même après la mise à jour de Magento en 2.0.6 et tout fonctionne parfaitement. N’hésite pas à debugger en profondeur pour voir d’où peut venir le souci

          Répondre
          • 01/06/2016 à 12:33
            Permalink

            Magento Version 2.07

            La source du problème :

            [2016-06-01 11:30:57] main.CRITICAL: Exception message: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`magento2`.`mag2_maxime_job`, CONSTRAINT `MAG2_MAXIME_JOB_DEPARTMENT_ID_MAXIME_DEPARTMENT_ENTITY_ID` FOREIGN KEY (`department_id`) REFERENCES `maxime_department` (`entity_id`) ON DELETE CASCAD), query was: UPDATE `mag2_maxime_job` SET `title` = ?, `type` = ?, `location` = ?, `date` = ‘2016-02-02′, `status` = ?, `description` = ?, `department_id` = ? WHERE (entity_id=’3’)
            Trace: #0 /var/www/html/magento2/lib/internal/Magento/Framework/DB/Statement/Pdo/Mysql.php(95): Zend_Db_Statement_Pdo->_execute(Array)
            #1 /var/www/html/magento2/vendor/magento/zendframework1/library/Zend/Db/Statement.php(303): Magento\Framework\DB\Statement\Pdo\Mysql->_execute(Array)
            #2 /var/www/html/magento2/vendor/magento/zendframework1/library/Zend/Db/Adapter/Abstract.php(480): Zend_Db_Statement->execute(Array)
            #3 /var/www/html/magento2/vendor/magento/zendframework1/library/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query(‘UPDATE `mag2_ma…’, Array)
            #4 /var/www/html/magento2/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php(444): Zend_Db_Adapter_Pdo_Abstract->query(‘UPDATE `mag2_ma…’, Array)
            #5 /var/www/html/magento2/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php(499): Magento\Framework\DB\Adapter\Pdo\Mysql->_query(‘UPDATE `mag2_ma…’, Array)
            #6 /var/www/html/magento2/vendor/magento/zendframework1/library/Zend/Db/Adapter/Abstract.php(635): Magento\Framework\DB\Adapter\Pdo\Mysql->query(‘UPDATE `mag2_ma…’, Array)
            #7 /var/www/html/magento2/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php(797): Zend_Db_Adapter_Abstract->update(‘mag2_maxime_job’, Array, ‘entity_id=’3 »)
            #8 /var/www/html/magento2/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php(403): Magento\Framework\Model\ResourceModel\Db\AbstractDb->updateObject(Object(Maxime\Jobs\Model\Job))
            #9 /var/www/html/magento2/lib/internal/Magento/Framework/Model/AbstractModel.php(615): Magento\Framework\Model\ResourceModel\Db\AbstractDb->save(Object(Maxime\Jobs\Model\Job))
            #10 /var/www/html/magento2/app/code/Maxime/Jobs/Controller/Adminhtml/Job/Save.php(60): Magento\Framework\Model\AbstractModel->save()
            #11 /var/www/html/magento2/var/generation/Maxime/Jobs/Controller/Adminhtml/Job/Save/Interceptor.php(24): Maxime\Jobs\Controller\Adminhtml\Job\Save->execute()
            #12 /var/www/html/magento2/lib/internal/Magento/Framework/App/Action/Action.php(102): Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor->execute()
            #13 /var/www/html/magento2/app/code/Magento/Backend/App/AbstractAction.php(226): Magento\Framework\App\Action\Action->dispatch(Object(Magento\Framework\App\Request\Http))
            #14 [internal function]: Magento\Backend\App\AbstractAction->dispatch(Object(Magento\Framework\App\Request\Http))
            #15 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Interceptor.php(74): call_user_func_array(Array, Array)
            #16 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Chain/Chain.php(70): Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor->___callParent(‘dispatch’, Array)
            #17 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Maxime\Jobs\Con…’, ‘dispatch’, Object(Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor), Array, ‘adminAuthentica…’)
            #18 /var/www/html/magento2/app/code/Magento/Backend/App/Action/Plugin/Authentication.php(143): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
            #19 [internal function]: Magento\Backend\App\Action\Plugin\Authentication->aroundDispatch(Object(Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
            #20 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Chain/Chain.php(68): call_user_func_array(Array, Array)
            #21 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Maxime\Jobs\Con…’, ‘dispatch’, Object(Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor), Array, ‘designLoader’)
            #22 /var/www/html/magento2/lib/internal/Magento/Framework/App/Action/Plugin/Design.php(39): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http))
            #23 [internal function]: Magento\Framework\App\Action\Plugin\Design->aroundDispatch(Object(Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
            #24 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Chain/Chain.php(68): call_user_func_array(Array, Array)
            #25 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Interceptor.php(136): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Maxime\Jobs\Con…’, ‘dispatch’, Object(Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor), Array, ‘adminMassaction…’)
            #26 /var/www/html/magento2/app/code/Magento/Backend/App/Action/Plugin/MassactionKey.php(33): Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
            #27 [internal function]: Magento\Backend\App\Action\Plugin\MassactionKey->aroundDispatch(Object(Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
            #28 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Interceptor.php(141): call_user_func_array(Array, Array)
            #29 /var/www/html/magento2/var/generation/Maxime/Jobs/Controller/Adminhtml/Job/Save/Interceptor.php(39): Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor->___callPlugins(‘dispatch’, Array, Array)
            #30 /var/www/html/magento2/lib/internal/Magento/Framework/App/FrontController.php(55): Maxime\Jobs\Controller\Adminhtml\Job\Save\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
            #31 [internal function]: Magento\Framework\App\FrontController->dispatch(Object(Magento\Framework\App\Request\Http))
            #32 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Interceptor.php(74): call_user_func_array(Array, Array)
            #33 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Chain/Chain.php(70): Magento\Framework\App\FrontController\Interceptor->___callParent(‘dispatch’, Array)
            #34 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Interceptor.php(136): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Framewo…’, ‘dispatch’, Object(Magento\Framework\App\FrontController\Interceptor), Array, ‘install’)
            #35 /var/www/html/magento2/lib/internal/Magento/Framework/Module/Plugin/DbStatusValidator.php(69): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
            #36 [internal function]: Magento\Framework\Module\Plugin\DbStatusValidator->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
            #37 /var/www/html/magento2/lib/internal/Magento/Framework/Interception/Interceptor.php(141): call_user_func_array(Array, Array)
            #38 /var/www/html/magento2/var/generation/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins(‘dispatch’, Array, Array)
            #39 /var/www/html/magento2/lib/internal/Magento/Framework/App/Http.php(115): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
            #40 /var/www/html/magento2/lib/internal/Magento/Framework/App/Bootstrap.php(258): Magento\Framework\App\Http->launch()
            #41 /var/www/html/magento2/index.php(39): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http))
            #42 {main} [] []
            [2016-06-01 11:30:58] main.CRITICAL: Broken reference: the ‘header’ tries to reorder itself towards ‘global.notices’, but their parents are different: ‘page.wrapper’ and ‘notices.wrapper’ respectively. [] []
            [2016-06-01 11:30:58] main.CRITICAL: Broken reference: the ‘page.breadcrumbs’ tries to reorder itself towards ‘notifications’, but their parents are different: ‘page.wrapper’ and ‘notices.wrapper’ respectively. [] []
            [2016-06-01 11:30:58] main.CRITICAL: Broken reference: the ‘global.search’ tries to reorder itself towards ‘notification.messages’, but their parents are different: ‘header.inner.right’ and ‘header’ respectively. [] []

          • 01/06/2016 à 15:10
            Permalink

            I found the source of the problem

            If you use Database tables prefix, you will get a problem with the foreign keys

            Thank you

  • 01/06/2016 à 14:48
    Permalink

    I solved the problem by excuting this sql requry :

    ALTER TABLE `mag2_maxime_job` ADD FOREIGN KEY ( `department_id` ) REFERENCES `magento2`.`mag2_maxime_department` (
    `entity_id`
    ) ON DELETE CASCADE ON UPDATE NO ACTION ;

    Répondre
  • 21/10/2016 à 14:05
    Permalink

    Bonjour Maxime,

    Merci beaucoup pour votre excellent tuto, chapeau bas ! Grâce à vous jeu réaliser mon projet Magento2 et je pense qu’il a sauvé plus d’une personne …

    Je vous écrit juste pour vous dire que je pense qu’il y a une erreur dans le fichier app/code/Maxime/Jobs/Controller/Adminhtml/Job/Save.php à la ligne 56. Vous avez écrit « deparment » à la place de « department » et d’ailleurs je crois deviner que c’est même plutôt « job » qu’il faut mettre.

    Merci encore et bon courage pour la suite de ce tuto.

    Répondre
  • 16/07/2018 à 10:18
    Permalink

    Salut Maxime, je vous remercie pour ce super tuto et je veux poser une question à propos de l’affichage de loader dans la page de Job parce que j’ai pas eu ce loader pourtant tout fonctionne bien pour le department ? !! et il n’ a y pas des erreurs !!!! avez vous une idée à propos de ce pb ? et merci d’avance

    Répondre

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous utilisons des cookies afin de nous assurer de vous proposer la meilleure expérience sur ce site.
Ok