Fundamentals of Magento 2 Development v2 - 1 Exercise Solutions PDF
Fundamentals of Magento 2 Development v2 - 1 Exercise Solutions PDF
magento.com/training/overview v. 2.1
[email protected]
magento.com/training/overview
magento.com/training/catalog/certification
The content provided in this student guide and all other intellectual property related to this course, including electronic knowledge products
delivered via the Internet (“Training Materials”), are copyrighted by Magento. Reproduction, copying or translation of Training Materials is
strictly prohibited. All Training Materials shall be used only for the private use of an individual participating in the specific training course for
which the materials are intended. Any other use or repurposing of the Training Materials, including use of the Training Materials for training
outside of Magento, is strictly prohibited. Offenders may be liable for payment of damages.
Contents
Contents
Unit One. Preparation & Configuration ................................................................................................................... 6
Module 3. Magento 2 Overview .......................................................................................................................... 6
1.3.1. Create a new module. Make a mistake in its config. Create a second module dependent on the first. .... 6
Module 5. Development Operations .................................................................................................................. 7
1.5.1: Mode........................................................................................................................................................... 7
1.5.2: Cache ......................................................................................................................................................... 7
Module 6. Dependency Injection & Object Manager ....................................................................................... 7
1.6.1: Dependency Injection ................................................................................................................................. 7
1.6.2: Object Manager .......................................................................................................................................... 8
Module 7. Plugins ................................................................................................................................................ 9
1.7.1: Plugins 1 ..................................................................................................................................................... 9
1.7.2: Plugins 2 ..................................................................................................................................................... 9
Module 8. Events ................................................................................................................................................. 9
1.8.1. In your module, create an observer to the event controller_action_predispatch. Get the URL from the
request object request->getPathInfo(). Log it into the file. ................................................................................... 9
Module 9. Module Configuration ..................................................................................................................... 11
1.9.1. In the empty module you created in Exercise 1.3.1, add custom configuration xml/xsd files. ................. 11
Unit Two. Request Flow .......................................................................................................................................... 16
Module 2. Request Flow Overview .................................................................................................................. 16
2.2.1. Find a place in the code where output is flushed to the browser. Create an extension that captures and
logs the file-generated page HTML. (“Flushing output” means a “send” call to the response object.) .............. 16
Module 3. Request Routing .............................................................................................................................. 17
2.3.1. Create an extension that logs into the file list of all available routers into a file. ...................................... 17
2.3.2. Create a new router which “understands” URLs like /frontName-actionPath-action and converts them to
/frontName/actionPath/action ............................................................................................................................. 18
2.3.3. Modify Magento so a “Not Found” page will forward to the home page. ................................................. 19
Module 5. Working with Controllers ................................................................................................................ 20
2.5.1. Create a frontend controller that renders “HELLO WORLD” ................................................................... 20
2.5.2. Customize the catalog product view controller using plugins and preferences. ...................................... 20
2.5.3. Create an adminhtml controller that allows access only if the GET parameter “secret” is set. ............... 21
2.5.4. Make the “Hello World” controller you just created redirect to a specific category page. ........................ 22
Module 6. URL Rewrites ................................................................................................................................... 22
2.6.1. Create a URL rewrite for the “Hello World” controller. ............................................................................. 22
Unit Three. Rendering ............................................................................................................................................. 23
Module 3. Rendering Flow ................................................................................................................................. 23
3.3.1. In the core files, find and print out the layout XML for the product view page. ........................................ 23
Module 5. Block Architecture & Life Cycle ..................................................................................................... 23
3.5.1. Create a block extending AbstractBlock and implement the _toHtml() method. Render that block in the
new controller. .................................................................................................................................................... 23
3.5.2. Create and render a text block in the controller. ...................................................................................... 24
3.5.3. Customize the Catalog\Product\View\Description block, implement the _beforeToHtml() method, and set
a custom description for the product here. ......................................................................................................... 24
Module 6. Templates ......................................................................................................................................... 25
3.6.1. Define which template is used in Catalog\Block\Product\View\Attributes. ............................................... 25
3.6.2. Create a template block and a custom template file for it. Render the block in the controller. ................ 25
3.6.3. Customize the Catalog\Block\Product\View\Description block and assign a custom template to it. ....... 25
1.3.1. Create a new module. Make a mistake in its config. Create a second
module dependent on the first.
Solution
1. Create a folder app/code/Training/Test.
Create a file app/code/Training/Test/etc/module.xml:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Training_Test" setup_version="2.0.0">
</module>
</config>
2. Register your module with app/code/Training/Test/ registration.php
<?php
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Training_Test',
__DIR__
);
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Training_Test" setup_version="2.0.0">
<sequence>
<module name="Magento_Test"/>
</sequence>
</module>
</config>
1.5.1: Mode
Then, go into the lib\internal\Magento\Framework\App\Bootstrap.php file and throw an exception in the method,
run().
See whether the exception is displayed on your screen. If it is, you have successfully set the mode. If not, review your steps.
(It is possible that cache settings may need to be adjusted, but that topic is taught later in the course).
1.5.2: Cache
Under what circumstances will cleaning the var/cache folder not work?
Answer: When cache storage is different (separate) from the var/cache folder. For example, it could be memory caching like
Redis or Memcached.
Pattern: The constructor has a list of objects assigned to protected properties, and then used inside a class. This is what DI
looks like in Magento 2
Go back to the two modules created you created in Exercise 1.3.1, Unit1_FirstModule and Unit1_SecondModule*.
In Unit1_FirstModule, create the folder “MagentoU”. In this folder, create the class “Test”. The code to be used is given
below.
Note that the word “module” is not usually used in a module name. This is only used here for learning purposes.
--------------------
namespace Unit1\FirstModule\MagentoU;
class Test
{
public function __construct(
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
\Magento\Catalog\Model\ProductFactory $productFactory,
\Magento\Checkout\Model\Session $session,
\Unit1\FirstModule\Api\ProductRepositoryInterface $unit1ProductRepository,
$justAParameter = false,
array $data
) {
}
}
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Unit1\FirstModule\Api\ProductRepositoryInterface"
type="Magento\Catalog\Model\ProductRepository" />
</config>
<type name="Unit1\FirstModule\MagentoU\Test">
<arguments>
<argument name="justAParameter" xsi:type="string”> Hello world! </argument>
<argument name=”data” type=”array”>
<item name=”test-array-item”>Test Array Item!!!</item>
</argument>
</arguments>
</type>
Finally, create a di.xml file in the SecondModule and add another item to the $data array from there.
Module 7. Plugins
1.7.1: Plugins 1
Although you do not commonly interact with Interceptors, it is useful to understand how plugins work. This can be helpful in the
debugging process.
In your Magento installation, take a look at a couple of interceptors and note how similar they are to each other – for example,
var/generation/Magento/Catalog/Model/Product/Interceptor.php.
1.7.2: Plugins 2
For the class... Magento\Catalog\Model\Product and the method... getPrice():
1. Customize Magento\Theme\Block\Html\Footer class, to replace the body of the getCopyright() method with your
implementation. Return a hard-coded string: “Customized copyright!”
Module 8. Events
Note that if you decide to extend the XML config file, you will also need to update the XSD schema as well.
Solution
1. Create an event declaration in events.xml:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="controller_action_predispatch">
<observer name="training_test"
instance="Training\Test\Observer\RedirrectToProductView" shared="false" />
</event>
</config>
2. Create an Observer:
<?php
namespace Training\Test\Observer;
use Magento\Framework\Event\ObserverInterface;
/**
* @var \Magento\Framework\App\ActionFlag
*/
protected $_actionFlag;
/**
* @param \Magento\Framework\App\Response\RedirectInterface $redirect
*/
public function __construct(
\Magento\Framework\App\Response\RedirectInterface $redirect,
\Magento\Framework\App\ActionFlag $actionFlag
) {
$this->redirect = $redirect;
$this->_actionFlag = $actionFlag;
}
/**
*
* @param \Magento\Framework\Event\Observer $observer
* @return $this
*
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function execute(\Magento\Framework\Event\Observer $observer) {
$request = $observer->getEvent()->getData('request');
if ($request->getModuleName() != 'catalog' || $request->getControllerName() !=
'product') {
$controller = $observer->getControllerAction();
$this->_actionFlag->set('',
\Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true);
$this->redirect->redirect($controller->getResponse(),
'catalog/product/view/id/1');
}
}
}
1.9.1. In the empty module you created in Exerci se 1.3.1, add custom
configuration xml/xsd files.
To create new xml/xsd files, we have to take the following steps:
Phase 1: Create test.xml and test.xsd files.
Phase 2: Create PHP files to process them: Config, ConfigInterface, Convertor, Reader, SchemaLocator.
Phase 3: Define a preference for ConfigInterface.
Phase 4: Test: In this example we will create a new controller to test this functionality.
Let’s follow through each step.
Phase 1
1.1) Create etc/test.xml:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="test.xsd">
<mynode>HELLO</mynode>
<mynode>HELLO 2</mynode>
</config>
1.2) Create etc/test.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="config">
<xs:complexType>
<xs:sequence>
<xs:element name="mynode" type="xs:string" maxOccurs="10"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Phase 2
2.1) Create an interface:
<?php
namespace Training\Test\Model\Config;
interface ConfigInterface {
public function getMyNodeInfo();
}
2.2) Create Config class:
<?php
namespace Training\Test\Model;
/**
* @param \Magento\Framework\Config\FileResolverInterface $fileResolver
* @param \Magento\Catalog\Model\ProductOptions\Config\Converter $converter
* @param \Magento\Catalog\Model\ProductOptions\Config\SchemaLocator
* $schemaLocator
* @param \Magento\Framework\Config\ValidationStateInterface $validationState
* @param string $fileName
* @param array $idAttributes
* @param string $domDocumentClass
* @param string $defaultScope
*/
$domDocumentClass = 'Magento\Framework\Config\Dom',
$defaultScope = 'global'
) {
parent::__construct(
$fileResolver,
$converter,
$schemaLocator,
$validationState,
$fileName,
$idAttributes,
$domDocumentClass,
$defaultScope
);
}
}
2.4) Create schemaLocator class:
<?php
namespace Training\Test\Model\Config;
/**
* Path to corresponding XSD file with validation rules for separate config
* files
* @var string
*/
protected $_perFileSchema = null;
/**
* @param \Magento\Framework\Module\Dir\Reader $moduleReader
*/
public function __construct(\Magento\Framework\Module\Dir\Reader $moduleReader)
{
$etcDir = $moduleReader->getModuleDir('etc', 'Training_Test');
$this->_schema = $etcDir . '/test.xsd';
$this->_perFileSchema = $etcDir . '/test.xsd';
}
/**
* Get path to merged config schema
*
* @return string|null
*/
public function getSchema()
{
return $this->_schema;
}
/**
* Get path to pre file validation schema
*
* @return string|null
*/
public function getPerFileSchema()
{
return $this->_perFileSchema;
}
}
2.5) Create converter class:
<?php
namespace Training\Test\Model\Config;
Phase 3
Set a preference in the di.xml:
<preference for="Training\Test\Model\Config\ConfigInterface"
type="Training\Test\Model\Config" />
Phase 4
4.1) Create a controller file (assuming you’ve set up routes.xml already):
<?php
/**
* Product controller.
*
* @copyright Copyright (c) 2014 X.commerce, Inc. (http://www.magentocommerce.com)
*/
namespace Training\Test\Controller\Action;
2.2.1. Find a place in the code where output is flushed to the browser. Create
an extension that captures and logs the file-generated page HTML. (“Flushing
output” means a “send” call to the response object.)
Solution
1. Declare an event in the file etc/frontend/events.xml:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="controller_front_send_response_before">
<observer name="training_test" instance="Training\Test\Observer\LogPageOutput"
shared="false" />
</event>
</config>
use Magento\Framework\Event\ObserverInterface;
/**
* @param \Psr\Log\LoggerInterface $logger
*/
public function __construct(\Psr\Log\LoggerInterface $logger)
{
$this->_logger = $logger;
}
/**
*
* @param \Magento\Framework\Event\Observer $observer
* @return $this
*
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function execute(\Magento\Framework\Event\Observer $observer) {
$response = $observer->getEvent()->getData('response');
$body = $response->getBody();
$this->_logger->addDebug("--------\n\n\n BODY \n\n\n ". $body);
}
2.3.1. Create an extension that logs into the file list of all available routers into
a file.
Solution
1. Create a preference in the di.xml:
<preference for="Magento\Framework\App\FrontControllerInterface"
type="Training\Test\App\FrontController" />
/**
* @var \Magento\Framework\App\Response\Http
*/
protected $response;
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @param \Magento\Framework\App\RouterList $routerList
* @param \Magento\Framework\App\Response\Http $response
* @param \Psr\Log\LoggerInterface $logger
*/
public function __construct(
\Magento\Framework\App\RouterList $routerList,
\Magento\Framework\App\Response\Http $response,
\Psr\Log\LoggerInterface $logger)
{
$this->routerList = $routerList;
$this->response = $response;
$this->logger = $logger;
}
/**
* @param \Magento\Framework\App\RequestInterface $request
* @return
\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
*/
public function dispatch(\Magento\Framework\App\RequestInterface $request) {
foreach ($this->routerList as $router) {
$this->logger->addDebug(get_class($router));
}
return parent::dispatch($request);
}
}
namespace Training\Test\Controller;
In this example, the router only “understands” urls that start with “test”. To make it work with every url, remove the
line:
if (preg_match("%^/(test)-(.*?)-(.*?)$%", $info, $m)) {
2.3.3. Modify Magento so a “Not Found” page will forward to the home page.
Solution
There are many different ways to do this. The easiest is to change the config option /web/default/noroute. This will
change the 404 page for all requests. To make the code more flexible, you can create a new NoRouteHandler. To
do this:
1. Declare your handler in the di.xml:
<type name="Magento\Framework\App\Router\NoRouteHandlerList">
<arguments>
<argument name="handlerClassesList" xsi:type="array">
<item name="default" xsi:type="array">
<item name="class"
xsi:type="string">Training\Test\Controller\NoRouteHandler</item>
<item name="sortOrder" xsi:type="string">200</item>
</item>
</argument>
</arguments>
</type>
$request
->setModuleName($moduleName)
->setControllerName($controllerName)
->setActionName($actionName);
return true;
}
}
2.5.2. Customize the catalog product view controller using plugins and
preferences.
Solution
1. To add a plugin or preference, use the following code in di.xml:
<preference for="Magento\Catalog\Controller\Product\View"
type="Training\Test\Controller\Product\View" />
or
<type name="Magento\Catalog\Controller\Product\View">
<plugin name="product-view-controller-plugin"
type="Training\Test\Controller\Product\View" sortOrder="10"/>
</type>
namespace Training\Test\Controller\Product;
3. Uncomment the appropriate method for testing: Uncomment “execute” for preferences, and “beforeExecute”,
“afterExecute” for plugins.
2.5.3. Create an adminhtml controller that allows access only if the GET
parameter “secret” is set.
Solution
1. Create a file etc/adminhtml/routes.xml:
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="admin">
<route id="adminhtml">
<route id="test" frontName="test">
<module name="Training_Test" before="Magento_Adminhtml" />
</route>
</route>
</router>
</config>
/**
* Check if admin has permissions to visit related pages
*
* @return bool
*/
protected function _isAllowed() {
$secret = $this->getRequest()->getParam('secret');
return isset($secret) && (int)$secret==1;
}
}
2.5.4. Make the “Hello World” controller you just created redirect to a specific
category page.
Solution
Put a line $this->_redirect(‘catalog/category/view/id/_CATEGORY_ID_’) into the execute method (but
replace _CATEGORY_ID_ with the real category_id).
3.3.1. In the core files, find and print out the layou t XML for the product view
page.
Solution
\Magento\Framework\View\Layout::generateXml()
namespace Training\Test\Controller\Block;
Module 6. Templates
3.6.2. Create a template block and a custom template file for it. Render the
block in the controller.
Solution
1. Create the block:
<?php
namespace Training\Test\Block;
Note: You cannot create your own block. You must use Magento\Framework\View\Element\Template,
since it is not an abstract.
2. Create a template file Training/Test/view/frontend/test.phtml:
“Hello from template”.
3. Create an action class:
<?php
/**
* Product controller.
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Training\Test\Controller\Action;
}
}
style="background_color:
<?= $this->getData('background_color') ?>;"
3.8.4. Change the block color to orange on the product detail pages only.
3.8.5. On category pages, move the exercise block to the bottom of the left
column.
3.8.6. On the custom action you just added, remove the custom block from the
content.top container. (See Exercise 3.8.1.)
3.8.7. Using layout XML, add a new link for the custom page you just created
to the set of existing links at the top of every page.
4.2.1. Echo the list of all store views and associated root categories.
Magento\Store\Model\Store::getRootCategoryId()
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
4.4.1. Create a table with an install script for the module Training_Vendor.
1. Give Training_Vendor a setup_version of 0.0.1.
2. Create the Setup folder.
3. Create the InstallSchema class.
4. Create a training_vendor_entity table using DDL methods.
5. Execute the installation using the console tool.
6. Verify that it works by checking the setup_module table.
4.7.1. Create a text input attribute (1) from the Admin interface.
1. Add it to an attribute set.
2. Check that it appears on the product edit page.
3. Make it visible on the storefront product view page.
Solution
Note that this exercise does not require any coding. It must be completed using the browser to access the
Magento Admin backend and the storefront only.
4.7.2. Create a text input attribute (2) from an install data method.
Follow the steps in the previous exercise and create a text input attribute that is visible on the storefront, but this
time create it from an install data method.
Solution
<?php
namespace Training\Orm\Setup;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute as CatalogAttribute;
use Magento\Catalog\Setup\CategorySetup;
use Magento\Catalog\Setup\CategorySetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
/**
* Installs data for a module
*
* @param ModuleDataSetupInterface $setup
* @param ModuleContextInterface $context
* @return void
*/
public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
}
}
2. In the install method, create an instance of the CategorySetup class and call addAttribute on it.
4. Open a product in the Admin interface and confirm that the new attribute exists and can be set on a store view
level.
5. Visit a product on the storefront and confirm that the new attribute is visible there, too.
Solution
1. Expand the Training_Orm module by adding a new section to the UpgradeData setup class.
'example_multiselect',
[
'frontend_model' =>
\Training\Orm\Entity\Attribute\Frontend\HtmlList::class,
'is_html_allowed_on_front' => 1,
]
);
}
}
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/modu
le.xsd">
<module name="Training_Orm" setup_version="0.1.2">
<sequence>
<module name="Magento_Eav"/>
</sequence>
</module>
</config>
<?php
namespace Training\Orm\Entity\Attribute\Frontend;
/**
* @param \Magento\Framework\Object $object
* @return string
*/
/**
* @param \Magento\Framework\Object $object
* @return string[]
*/
private function getOptions(\Magento\Framework\Object $object)
{
$optionId = $object->getData($this->getAttribute()->getAttributeCode());
$option = $this->getOption($optionId);
return $this->isSingleValue($option) ? [$option] : $option;
}
/**
* @param string[]|string $option
* @return bool
*/
private function isSingleValue($option)
{
return !is_array($option);
}
}
Solution
// ...
use Magento\Customer\Setup\CustomerSetup;
use Magento\Customer\Setup\CustomerSetupFactory;
// ...
public function __construct(
CategorySetupFactory $categorySetupFactory,
CustomerSetupFactory $customerSetupFactory,
StoreManager $storeManager
) {
$this->catalogSetupFactory = $categorySetupFactory;
$this->customerSetupFactory = $customerSetupFactory;
$this->storeManager = $storeManager;
}
// ...
);
$customerSetup->getEavConfig()->getAttribute('customer', 'priority')
->setData('used_in_forms', ['adminhtml_customer'])
->save();
}
}
3. Note that the system property must be set to 0 for Magento to recognize the custom attribute when saving a
customer through the Admin interface.
4. Note that customer attributes must be added to the customer_form_attribute explicitly to be visible in the
browser.
5. Create the custom source model.
<?php
namespace Training\Orm\Entity\Attribute\Source;
use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
Preparation
1. Create a new module Training_Registry for the exercise solutions.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/rout
es.xsd">
<router id="standard">
<route id="training_repository" frontName="training_repository">
<module name="Training_Repository"/>
</route>
</router>
</config>
Solution
1. Create an action controller to output the exercise result.
<?php
namespace Training\Repository\Controller\Repository;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
private $productRepository;
/**
* @var SearchCriteriaBuilder
*/
private $searchCriteriaBuilder;
$products = $this->getProductsFromRepository();
/**
* @return ProductInterface[]
*/
private function getProductsFromRepository()
{
$criteria = $this->searchCriteriaBuilder->create();
$products = $this->productRepository->getList($criteria);
return $products->getItems();
}
If there is no output when testing the action in this stage, check the PHP error logs for out-of-memory
exceptions.
/**
* @return ProductInterface[]
*/
private function getProductsFromRepository()
{
$this->setProductTypeFilter();
$criteria = $this->searchCriteriaBuilder->create();
$products = $this->productRepository->getList($criteria);
return $products->getItems();
}
3. Add another filter that is applied using a logical AND operator by adding the following method and calling it
from the getProductsFromRepository() method.
$this->searchCriteriaBuilder->addFilter([$nameFilter]);
}
4. Add a sort order instruction by adding the SortOrderBuilder and SearchCriteriaInterface to the class
dependencies and adding the following method (called from the getProductsFromRepository() method).
5. Limit the number of product to 6. This can be done by renaming the new method setProductOrder() to
setProductPaging() and adding the new 2 lines at the end of the method.
Solution
1. Add a new action controller, Customer.php, to the training_repository/repository route created in the previous
exercise.
2. In the execute method of the controller, use the customer repository to get a list of customers and print some
data.
$customers = $this->getCustomersFromRepository();
$this->getResponse()->appendBody(
sprintf("List contains %s\n\n", get_class($customers[0]))
);
foreach ($customers as $customer) {
$this->outputCustomer($customer);
}
}
/**
* @return \Magento\Customer\Api\Data\CustomerInterface[]
*/
private function getCustomersFromRepository()
{
$criteria = $this->searchCriteriaBuilder->create();
$customers = $this->customerRepository->getList($criteria);
return $customers->getItems();
}
$customers = $this->getCustomersFromRepository();
$this->getResponse()->appendBody(
sprintf("List contains %s\n\n", get_class($customers[0]))
);
foreach ($customers as $customer) {
$this->outputCustomer($customer);
}
}
4. Add two filters with a logical OR condition by specifying them as a filter group.
namespace Training\Repository\Controller\Repository;
use Magento\Customer\Api\CustomerRepositoryInterface
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\Search\FilterGroupBuilder;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
/**
* @var SearchCriteriaBuilder
*/
private $searchCriteriaBuilder;
/**
* @var FilterGroupBuilder
*/
private $filterGroupBuilder;
/**
* @var FilterBuilder
*/
private $filterBuilder;
$this->addEmailFilter();
$this->addNameFilter();
$customers = $this->getCustomersFromRepository();
$this->getResponse()->appendBody(
sprintf("List contains %s\n\n", get_class($customers[0]))
);
/**
* @return \Magento\Customer\Api\Data\CustomerInterface[]
*/
private function getCustomersFromRepository()
{
$this->searchCriteriaBuilder->setFilterGroups(
[$this->filterGroupBuilder->create()]
);
$criteria = $this->searchCriteriaBuilder->create();
$customers = $this->customerRepository->getList($criteria);
return $customers->getItems();
}
Solution
1. Create a new flat table entity called Example with a model, resource model, and collection. Refer to the ORM
unit of the training or the exercise solution code archive for details. The model, resource model, and collection
code is boilerplate and is not included in this document.
Repository
├── Model
│ ├── Example.php
│ └── Resource
│ ├── Example
│ │ └── Collection.php
│ └── Example.php
└── Setup
├── InstallSchema.php
└── UpgradeData.php
2. Use a module install class to create the matching table, and a module data upgrade class to create a couple of
example records in the table.
<?php
namespace Training\Repository\Setup;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\DB\Ddl\Table as DdlTable;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
'name',
DdlTable::TYPE_TEXT,
255,
['nullable' => false]
)->addColumn(
'created_at',
DdlTable::TYPE_TIMESTAMP,
null,
['nullable' => false, 'default' => DdlTable::TIMESTAMP_INIT]
)->addColumn(
'updated_at',
DdlTable::TYPE_TIMESTAMP,
null,
['nullable' => false, 'default' => DdlTable::TIMESTAMP_INIT]
)->addIndex(
$setup->getIdxName(
$tableName,
['name'],
AdapterInterface::INDEX_TYPE_UNIQUE
),
['name'],
['type' => AdapterInterface::INDEX_TYPE_UNIQUE]
);
$setup->getConnection()->createTable($ddlTable);
$setup->endSetup();
}
}
<?php
namespace Training\Repository\Setup;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
]
);
}
}
}
<?php
namespace Training\Repository\Api;
use Magento\Framework\Api\SearchCriteriaInterface;
interface ExampleRepositoryInterface
{
/**
* @return Data\ExampleSearchResultsInterface
*/
public function getList(SearchCriteriaInterface $searchCriteria);
}
4. Add an interface for the API data model Training\Repository\Api\Data\ExampleInterface with getters and
setters for all the properties that should be accessible from the outside.
<?php
namespace Training\Repository\Api\Data;
interface ExampleInterface
{
/**
* @param int $id
* @return $this
*/
public function setId($id);
/**
* @return int
*/
public function getId();
/**
* @return string
*/
/**
* @param string $name
* @return $this
*/
public function setName($name);
/**
* @return string
*/
public function getCreatedAt();
/**
* @param string $createdAt
* @return $this
*/
public function setCreatedAt($createdAt);
/**
* @return string
*/
public function getModifiedAt();
/**
* @param string $modifiedAt
* @return $this
*/
public function setModifiedAt($modifiedAt);
}
It can inherit all methods, or specify getItems() and setItems() to provide more specific phpdoc type hints.
<?php
namespace Training\Repository\Api\Data;
interface ExampleSearchResultsInterface
extends \Magento\Framework\Api\SearchResultsInterface
{
/**
* @api
* @return \Training\Repository\Api\Data\ExampleInterface[]
*/
/**
* @api
* @param \Training\Repository\Api\Data\ExampleInterface[] $items
* @return $this
*/
public function setItems(array $items = null);
}
6. Specify the preferences configuration for these three new interfaces in an etc/di.xml file.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/e
tc/config.xsd">
<preference for="Training\Repository\Api\ExampleRepositoryInterface"
type="Training\Repository\Model\ExampleRepository"/>
<preference for="Training\Repository\Api\Data\ExampleInterface"
type="Training\Repository\Model\Example"/>
<preference for="Training\Repository\Api\Data\ExampleSearchResultsInterface"
type="Magento\Framework\Api\SearchResults"/>
</config>
<?php
namespace Training\Repository\Model;
use Magento\Framework\Model\AbstractModel;
use Training\Repository\Api\Data\ExampleInterface;
The getList() method creates an example collection instance and applies the SearchCriteria using the
appropriate methods on the collection.
Then the collection is loaded, and all entities are converted into the configured object implementation for the
Api\Data\ExampleInterface.
For this implementation it means that the conversion happens to the same instance, but should the DI
configuration for the interface change, this ensures that change will take effect.
<?php
namespace Training\Repository\Model;
use Magento\Framework\Api\Search\FilterGroup;
use Magento\Framework\Api\SearchCriteriaInterface;
use Training\Repository\Api\Data\ExampleInterface;
use Training\Repository\Api\Data\ExampleInterfaceFactory as ExampleDataFactory;
use Training\Repository\Api\Data\ExampleSearchResultsInterface;
use Training\Repository\Api\Data\ExampleSearchResultsInterfaceFactory;
use Training\Repository\Api\ExampleRepositoryInterface;
use Training\Repository\Model\Example as ExampleModel;
use Training\Repository\Model\Resource\Example\Collection as ExampleCollection;
/**
* @var ExampleFactory
*/
private $exampleFactory;
/**
* @var ExampleDataFactory
*/
private $exampleDataFactory;
/**
* @return ExampleSearchResultsInterface
*/
public function getList(SearchCriteriaInterface $searchCriteria)
{
/** @var ExampleCollection $collection */
$collection = $this->exampleFactory->create()->getCollection();
$this->applySearchCriteriaToCollection($searchCriteria, $collection);
$examples = $this->convertCollectionToDataItemsArray($collection);
$searchResults->setTotalCount($collection->getSize());
$searchResults->setItems($examples);
return $searchResults;
}
<?php
namespace Training\Repository\Controller\Repository;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Training\Repository\Api\ExampleRepositoryInterface;
/**
* @var SearchCriteriaBuilder
*/
private $searchCriteriaBuilder;
/**
* @var FilterBuilder
*/
private $filterBuilder;
}
}
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
Solution
5.6.3. Create your own Data API class and make it available through the Web
API.
Solution
1. In order to perform a REST API call to the Customer module, perform the following two http calls:
di.xml
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework\
/ObjectManager/etc/config.xsd">
<preference for="Training\Api\Api\Data\HelloInterface" type="Training\Api\Model\Hello" />
</config>
webapi.xml
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../app/code/Magento/Webapi/etc/webapi.xsd">
Api/Api/Data/HelloInterface.php
<?php
namespace Training\Api\Api\Data;
interface HelloInterface {
/**
* Hello method
*
* @return string|null
*/
public function sayHello();
}
Api/Model/Hello.php
<?php
namespace Training\Api\Model;
use Training\Api\Api\Data;
<?xml version="1.0"?>
<response>HELLO WORLD!</response>
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
1. game_id 6. Simulator
2. name (varchar) 7. Shooter
3. type (RPG) 8. trial_period (period in days)
4. RTS 9. release_date
5. MMO
For this table, create a grid with columns that correspond to the fields.
Make release_date column optional (not visible by default).
Create filters that correspond to fields (text for name, dropdown for type, from-to for trial_period, date
for release_date).
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
name (text)
type (dropdown: RPG, RTS, MMO, Simulator, Shooter)
trial_period (integer)
release_date (date)
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
Name it “test”.
Put it into the General section.
Make it a “yes/no” select.
Create a new element in the system configuration with custom code that renders “Hello World”.
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
6.6.2. Menu
Create a submenu in the Catalog/Product menu called “Games”. It should lead to the games grid created earlier.
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.
6.6.3. ACL
Create a new page in Admin that renders “Hello World”.
Create a new role for this page.
Create a new user and assign the user access to the page. (Verify that the user does have access.)
Solution
You will be provided with a code archive containing the solutions for the exercises in this module.