Magento 2 Training : Unit 7 – Lesson A

On the last practical, we created the department view page. We put the list of department’s job inside.
We will create a configuration to display, or not, this list.

Create custom configuration field with Magento 2

You have to go on Stores > Configuration :

link_store_config

We will add a tab, with one element inside. When you will click on this element, you will have a configuration page which the custom field.

Create the file :
app/code/Maxime/Jobs/etc/adminhtml/system.xml

With this content :

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="jobs" translate="label" sortOrder="1000">
            <label>Jobs</label>
        </tab>
        <section id="jobs" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Jobs</label>
            <tab>jobs</tab>
            <resource>Maxime_Jobs::jobs</resource>
            <group id="department" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Department configuration</label>
                <field id="view_list" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Show job list</label>
                    <comment>Show department's job list of the viewing department</comment>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
            </group>
       </section>
    </system>
</config>

– “tab” node create a new tab. The attribute “sortOrder” allow you to change it’s position on the list. You can try from yourself and check on Magento core files the values for the native configuration nodes.
– “section” node add a new element to our tab. We associate it with the “tab” node. You can change the text with the label value. Finally, the resource define the right to show the element.
– “group” node create a group of field on the form. We have only one group at this moment with the lebel “Department Configuration”.
– To finish, we have our “field”. We set the “select” type, and define the label and the comment. The “source_model” allow you to retrieve the array of values for the select. We use a native object from Magento which return a “Yes/No” array.

You can add some section / group / field in Magento native tabs / sections / groups. For example, you can add a new group with some new field inside the “Catalog” tab and “Catalog” section.

Manage config scope

You notice the attributes : showInDefault, showInWebsite, showInStore
We define the element’s scope.

A Magento store has got website, store and store view :
store_admin

We will learn the behaviour later on this training. You only have to know that a Magento store can have many websites, with many stores, with many store views.

We have only one store view, but our field must be customizable on each store view, so these 3 attributes are set with “1” value.

Magento will retrieve the config value like this :
– If the field has got store view scope (showInStore), and a value is defined, we return it
– Else if the field has got website scope (showInWebsite), and a value is defined, we return it
– Else we return the default scope value (showInDefault)

It’s an example of the configuration creation. You can change it if you want, and have fields only with the store view scope !

When you edit configuration on Magento admin, you can change the current scope with this menu :

scope_store

During this lesson, we stay with “Default Scope”.
Now, refresh the admin stores configuration page and our menu… is not displayed !

ACL creation

It is not displayed because we haven’t created the access (ACL) to show it.

Take the file :
app/code/Maxime/Jobs/etc/acl.xml

And replace the content with it :

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <!-- Admin menu -->
                <resource id="Maxime_Jobs::job_head" title="Jobs" sortOrder="100" >
                    <resource id="Maxime_Jobs::department" title="Departments" sortOrder="10">
                        <resource id="Maxime_Jobs::department_save" title="Save Department" sortOrder="10" />
                        <resource id="Maxime_Jobs::department_delete" title="Delete Department" sortOrder="20" />
                    </resource>
                    <resource id="Maxime_Jobs::job" title="Jobs" sortOrder="20">
                        <resource id="Maxime_Jobs::job_save" title="Save Job" sortOrder="10" />
                        <resource id="Maxime_Jobs::job_delete" title="Delete Job" sortOrder="20" />
                    </resource>
                </resource>

                <!-- Admin config -->
                <resource id="Magento_Backend::stores">
                    <resource id="Magento_Backend::stores_settings">
                        <resource id="Magento_Config::config">
                            <resource id="Maxime_Jobs::jobs" title="Jobs Section" />
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

We add a new node : Magento_Backend::stores
The 3 first levels are native ACL.
The last node is our module node : Maxime_Jobs::jobs
This “id” is equal to the “resource” node of the previous file.

If you refresh the page, the menu is now visible, and you can click to display the new configuration page :

edit_job_config

Don’t save it, we will set default value on our module.

Add default config value on Magento 2 module

Create the file :
app/code/Maxime/Jobs/etc/config.xml

And put this content inside :

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <jobs>
            <department>
                <view_list>1</view_list>
            </department>
        </jobs>
    </default>
</config>

– The “default” node say it’s the default scope.
– The next nodes are “section”, “group” and field name set on system.xml
– On the field node, we put our default value : 1

If you refresh the admin page, the select will be selected with the “Yes” value.

You can set default value for website and store view scope :

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <jobs>
            <department>
                <view_list>1</view_list>
            </department>
        </jobs>
    </default>

    <websites>
        <websitecode>
            <jobs>
                <department>
                    <view_list>1</view_list>
                </department>
            </jobs>
        </websitecode>
    </websites>

    <stores>
        <storeviewcode>
            <jobs>
                <department>
                    <view_list>1</view_list>
                </department>
            </jobs>
        </storeviewcode>
    </stores>
</config>

You have to replace websitecode with the website code :
website_conf

You have to replace storeviewcode with the store view code :
store_view_conf

Now, you can change the value to “No” and save config.
The XML default value is ignored and the custom value is taken.

If cofig was saved on Magento admin, the value is stored on the table :
core_config_data

You can find our config with it’s path :
bdd_conf

You can have multiple lines if many values for many scopes are saved (Default, website, store view) :

bdd_socpes_values

If you delete the line on DB, the default XML value will be taken.

But, if I want to update the config when I put my code in production, how can I do ?

Update programatically config value with Magento 2

We will use setup to do that !

Change the module version :
app/code/Maxime/Jobs/etc/module.xml

Put the “setup_version” to “1.0.0.3”

Take the setup UpgradeData :
app/code/Maxime/Jobs/Setup/UpgradeData.php

And change it’s code with this :

<?php
/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Maxime\Jobs\Setup;

use Maxime\Jobs\Model\Department;
use Maxime\Jobs\Model\Job;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Config\Model\ResourceModel\Config;

/**
 * @codeCoverageIgnore
 */
class UpgradeData implements UpgradeDataInterface
{

    protected $_department;
    protected $_job;

    protected $_resourceConfig;

    public function __construct(Department $department, Job $job, Config $resourceConfig){
        $this->_department = $department;
        $this->_job = $job;
        $this->_resourceConfig = $resourceConfig;
    }

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $installer = $setup;
        $installer->startSetup();

        // Action to do if module version is less than 1.0.0.1
        if (version_compare($context->getVersion(), '1.0.0.1') < 0) {
            $departments = [
                [
                    'name' => 'Marketing',
                    'description' => 'Sed cautela nimia in peiores haeserat plagas, ut narrabimus postea,
                aemulis consarcinantibus insidias graves apud Constantium, cetera medium principem sed
                siquid auribus eius huius modi quivis infudisset ignotus, acerbum et inplacabilem et in
                hoc causarum titulo dissimilem sui.'
                ],
                [
                    'name' => 'Technical Support',
                    'description' => 'Post hanc adclinis Libano monti Phoenice, regio plena gratiarum et
                venustatis, urbibus decorata magnis et pulchris; in quibus amoenitate celebritateque
                nominum Tyros excellit, Sidon et Berytus isdemque pares Emissa et Damascus saeculis condita
                priscis.'
                ],
                [
                    'name' => 'Human Resource',
                    'description' => 'Duplexque isdem diebus acciderat malum, quod et Theophilum insontem atrox
                interceperat casus, et Serenianus dignus exsecratione cunctorum, innoxius, modo non reclamante publico vigore,
                discessit.'
                ]
            ];

            /**
             * Insert departments
             */
            $departmentsIds = array();
            foreach ($departments as $data) {
                $department = $this->_department->setData($data)->save();
                $departmentsIds[] = $department->getId();
            }


            $jobs = [
                [
                    'title' => 'Sample Marketing Job 1',
                    'type' => 'CDI',
                    'location' => 'Paris, France',
                    'date'  => '2016-01-05',
                    'status' => $this->_job->getEnableStatus(),
                    'description' => 'Duplexque isdem diebus acciderat malum, quod et Theophilum insontem atrox
                interceperat casus, et Serenianus dignus exsecratione cunctorum, innoxius, modo non reclamante publico vigore,
                discessit.',
                    'department_id' => $departmentsIds[0]
                ],
                [
                    'title' => 'Sample Marketing Job 2',
                    'type' => 'CDI',
                    'location' => 'Paris, France',
                    'date'  => '2016-01-10',
                    'status' => $this->_job->getDisableStatus(),
                    'description' => 'Duplexque isdem diebus acciderat malum, quod et Theophilum insontem atrox
                interceperat casus, et Serenianus dignus exsecratione cunctorum, innoxius, modo non reclamante publico vigore,
                discessit.',
                    'department_id' => $departmentsIds[0]
                ],
                [
                    'title' => 'Sample Technical Support Job 1',
                    'type' => 'CDD',
                    'location' => 'Lille, France',
                    'date'  => '2016-02-01',
                    'status' => $this->_job->getEnableStatus(),
                    'description' => 'Duplexque isdem diebus acciderat malum, quod et Theophilum insontem atrox
                interceperat casus, et Serenianus dignus exsecratione cunctorum, innoxius, modo non reclamante publico vigore,
                discessit.',
                    'department_id' => $departmentsIds[1]
                ],
                [
                    'title' => 'Sample Human Resource Job 1',
                    'type' => 'CDI',
                    'location' => 'Paris, France',
                    'date'  => '2016-01-01',
                    'status' => $this->_job->getEnableStatus(),
                    'description' => 'Duplexque isdem diebus acciderat malum, quod et Theophilum insontem atrox
                interceperat casus, et Serenianus dignus exsecratione cunctorum, innoxius, modo non reclamante publico vigore,
                discessit.',
                    'department_id' => $departmentsIds[2]
                ]
            ];

            foreach ($jobs as $data) {
                $this->_job->setData($data)->save();
            }
        }


        // Action to do if module version is less than 1.0.0.3
        if (version_compare($context->getVersion(), '1.0.0.3') < 0) {
            $this->_resourceConfig->saveConfig('jobs/department/view_list', 1, 'default', 0);
        }

        $installer->endSetup();
    }
}

– We add $_resourceConfig to our class
– We inject it on the construct
– On the upgrade we do a saveConfig

This method take 4 parameters:
– Config path
– Config value
– Config scope
– Website or store ID, 0 if scope is default.

The scope can be :
– default : default scope
– stores : store view scope
– websites : website scope

Launch the upgrade method on the magento root folder :
./bin/magento setup:upgrade

If you chek on database, you will see this :
config_saved_update

Get config value on frontend with Magento 2

Take the file:
app/code/Maxime/Jobs/Block/Department/View.php

Replace the content with :

<?php
namespace Maxime\Jobs\Block\Department;
class View extends \Magento\Framework\View\Element\Template
{
    protected $_jobCollection = null;

    protected $_department;

    protected $_job;

    const LIST_JOBS_ENABLED = 'jobs/department/view_list';

    /**
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param \Maxime\Jobs\Model\Department $department
     * @param \Maxime\Jobs\Model\Job $job
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Maxime\Jobs\Model\Department $department,
        \Maxime\Jobs\Model\Job $job,
        array $data = []
    ) {
        $this->_department = $department;

        $this->_job = $job;

        parent::__construct(
            $context,
            $data
        );
    }

    /**
     * @return $this
     */
    protected function _prepareLayout()
    {
        parent::_prepareLayout();

        // Get department
        $department = $this->getLoadedDepartment();

        // Title is department's name
        $title = $department->getName();
        $description = __('Look at the jobs we have got for you');
        $keywords = __('job,hiring');

        $this->getLayout()->createBlock('Magento\Catalog\Block\Breadcrumbs');

        if ($breadcrumbsBlock = $this->getLayout()->getBlock('breadcrumbs')) {
            $breadcrumbsBlock->addCrumb(
                'jobs',
                [
                    'label' => __('We are hiring'),
                    'title' => __('We are hiring'),
                    'link' => $this->getListJobUrl() // No link for the last element
                ]
            );
            $breadcrumbsBlock->addCrumb(
                'job',
                [
                    'label' => $title,
                    'title' => $title,
                    'link' => false // No link for the last element
                ]
            );
        }

        $this->pageConfig->getTitle()->set($title);
        $this->pageConfig->setDescription($description);
        $this->pageConfig->setKeywords($keywords);


        $pageMainTitle = $this->getLayout()->getBlock('page.main.title');
        if ($pageMainTitle) {
            $pageMainTitle->setPageTitle($title);
        }

        return $this;
    }

    protected function _getDepartment()
    {
        if (!$this->_department->getId()) {
            // our model is already set in the construct
            // but I put this method to load in case the model is not loaded
            $entityId = $this->_request->getParam('id');
            $this->_department = $this->_department->load($entityId);
        }
        return $this->_department;
    }

    public function getLoadedDepartment()
    {
        return $this->_getDepartment();
    }

    public function getListJobUrl(){
        return $this->getUrl('jobs/job');
    }

    protected function _getJobsCollection(){
        if($this->_jobCollection === null && $this->_department->getId()){
            $jobCollection = $this->_job->getCollection()
                ->addFieldToFilter('department_id', $this->_department->getId())
                ->addStatusFilter($this->_job, $this->_department);
            $this->_jobCollection = $jobCollection;
        }
        return $this->_jobCollection;
    }

    public function getLoadedJobsCollection()
    {
        return $this->_getJobsCollection();
    }

    public function getJobUrl($job){
        if(!$job->getId()){
            return '#';
        }

        return $this->getUrl('jobs/job/view', ['id' => $job->getId()]);
    }

    public function getConfigListJobs() {
        return $this->_scopeConfig->getValue(
            self::LIST_JOBS_ENABLED,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
    }
}

– We add the method getConfigListJobs
– We define a constant with the config path : LIST_JOBS_ENABLED

Last thing to do, update the template :
app/code/Maxime/Jobs/view/frontend/templates/jobs/department/view.phtml

Replace with this code inside :

<?php
$department = $this->getLoadedDepartment();

if($this->getConfigListJobs()){
    $jobCollection = $this->getLoadedJobsCollection();
    $iterator = 1;
    $total = $jobCollection->count();
} else {
    $total = 0;
}
?>
<?php if($department->getId()) : ?>
    <div class="department-view-wrapper">
        <div class="description"><?php echo $department->getDescription(); ?></div>
    </div>
    <?php if($total): ?>
        <h2><?php echo __('Jobs for this department'); ?></h2>
        <?php foreach($jobCollection AS $job): ?>
            <ol class="jobs list">
                <li class="item<?php echo ($iterator == 1) ? ' first' : ''; ?><?php echo ($total == $iterator) ? ' last' : ''; ?>">
                    <div class="title">
                        <a href="<?php echo $this->getJobUrl($job); ?>" title="<?php echo $job->getTitle(); ?>">
                            <?php echo $job->getTitle(); ?>
                        </a>
                    </div>
                    <div class="department_name">
                        <?php echo __('Department : '); ?>
                        <a href="<?php echo $this->getDepartmentUrl($job); ?>" title="<?php echo $job->getDepartmentName(); ?>">
                            <?php echo $job->getDepartmentName(); ?>
                        </a>
                    </div>
                    <div class="type"><?php echo $job->getType(); ?></div>
                    <div class="location"><?php echo $job->getLocation(); ?></div>
                    <div class="date"><?php echo $this->formatDate($job->getDate()); ?></div>
                    <div class="description"><?php echo $job->getDescription(); ?></div>
                </li>
            </ol>
            <?php $iterator++; ?>
        <?php endforeach; ?>
    <?php endif; ?>
<?php else : ?>
    <?php echo __('This department does not exist'); ?>
<?php endif; ?>

I put the condition at the beginning of the template file in order to not load the jobs collection if parameter is disabled.

Now our list is displayed or not with the config you set on Magento admin.

Next time, we will create a cron task !

Continue training
Return to previous lesson
Configuration creation on Magento 2 admin
Tagged on:         

One thought on “Configuration creation on Magento 2 admin

  • 10/14/2016 at 04:54
    Permalink

    I am developing an extension where in the ADMIN store>>configuration my extension setting i want to put attribute draggable and sortable list and therefore i need to set custom template for this particular field, so is there any way i can put custom template phtml file inside system.xml?

    Reply

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