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 :
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 :
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 :
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 :
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 :
You have to replace storeviewcode with the store view code :
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 :
You can have multiple lines if many values for many scopes are saved (Default, website, store view) :
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 :
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 !
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?