Table of Contents for
Magento 2 Development Quick Start Guide

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Magento 2 Development Quick Start Guide by Branko Ajzele Published by Packt Publishing, 2018
  1. Magento 2 Development Quick Start Guide
  2. Title Page
  3. Copyright and Credits
  4. Magento 2 Development Quick Start Guide
  5. Packt Upsell
  6. Why subscribe?
  7. Packt.com
  8. Contributors
  9. About the author
  10. About the reviewer
  11. Packt is searching for authors like you
  12. Table of Contents
  13. Preface
  14. Who this book is for
  15. What this book covers
  16. To get the most out of this book
  17. Download the example code files
  18. Code in Action
  19. Conventions used
  20. Get in touch
  21. Reviews
  22. Understanding the Magento Architecture
  23. Technical requirements
  24. Installing Magento
  25. Modes
  26. Areas
  27. Request flow processing
  28. Modules
  29. Creating the minimal module
  30. Cache
  31. Dependency injection
  32. Argument injection
  33. Virtual types
  34. Proxies
  35. Factories
  36. Plugins
  37. The before plugin
  38. The around plugin
  39. The after plugin
  40. Events and observers
  41. Console commands
  42. Cron jobs
  43. Summary
  44. Working with Entities
  45. Technical requirements
  46. Understanding types of models
  47. Creating a simple model
  48. Methods worth memorizing
  49. Working with setup scripts
  50. The InstallSchema script
  51. The UpgradeSchema script
  52. The Recurring script
  53. The InstallData script
  54. The UpgradeData script
  55. The RecurringData script
  56. Extending entities
  57. Creating extension attributes
  58. Summary
  59. Understanding Web APIs
  60. Technical requirements
  61. Types of users
  62. Types of authentication
  63. Types of APIs
  64. Using existing web APIs
  65. Creating custom web APIs
  66. Understanding search criteria
  67. Summary
  68. Building and Distributing Extensions
  69. Technical requirements
  70. Building a shipping extension
  71. Distributing via GitHub
  72. Distributing via Packagist
  73. Summary
  74. Developing for Admin
  75. Technical requirements
  76. Using the listing component
  77. Using the form component
  78. Summary
  79. Developing for Storefront
  80. Technical requirements
  81. Setting up the playground
  82. Calling and initializing JS components
  83. Meet RequireJS
  84. Replacing jQuery widget components
  85. Extending jQuery widget components
  86. Creating jQuery widgets components
  87. Creating UI/KnockoutJS components
  88. Extending UI/KnockoutJS components
  89. Summary
  90. Customizing Catalog Behavior
  91. Technical requirements
  92. Creating the size guide
  93. Creating the same day delivery
  94. Flagging new products
  95. Summary
  96. Customizing Checkout Experiences
  97. Technical requirements
  98. Passing data to the checkout
  99. Adding order notes to the checkout
  100. Summary
  101. Customizing Customer Interactions
  102. Technical requirements
  103. Understanding the section mechanism
  104. Adding contact preferences to customer accounts
  105. Adding contact preferences to the checkout
  106. Summary
  107. Other Books You May Enjoy
  108. Leave a review - let other readers know what you think

Using the form component

The form is a basic component responsible for performing CRUD operations on an entity. The listingElements group referenced under vendor/magento/module-ui/etc/ui_configuration.xsd file provides a nice list of both primary and secondary form components:

bookmark dataProvider fileUploader massaction range
boolean date form modal radioset
button dynamicRows hidden multiline select
checkbox email htmlContent multiselect tab
checkboxset exportButton input nav text
component field insertForm number textarea
container fieldset insertListing paging wysiwyg
dataSource file listing price

 

Moving forward, we will use the form component, and a few of its secondary components to create the Stock Resupply screen as shown:

The form itself is to consist of Stock and +Qty fields. The Stock field will be a read-only field consisting of an SKU + current qty string. The Back button will take us back to the Micro Inventory listing, whereas the Save button will post the form to a special Resupply controller action, which will then increase the stock by a given +Qty amount. The Actions selector in the upper left corner is to consist of two custom actions, allowing for fixed product stock increases.

We start off by defining the <MODULE_DIR>/Controller/Adminhtml/Product/Resupply.php as follows:

use \Magento\Framework\Controller\ResultFactory;
class Resupply extends \Magelicious\Minventory\Controller\Adminhtml\Product {
protected $stockRegistry;
protected $productRepository;
protected $resupply;

public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
\Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
\Magelicious\Minventory\Model\Resupply $resupply
) {
parent::__construct($context);
$this->productRepository = $productRepository;
$this->stockRegistry = $stockRegistry;
$this->resupply = $resupply;
}

public function execute() {
if ($this->getRequest()->isPost()) {
$this->resupply->resupply(
$this->getRequest()->getParam('id'),
$_POST['minventory_product']['qty']
);
$this->messageManager->addSuccessMessage(__('Successfully resupplied'));
$redirectResult = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $redirectResult->setPath('minventory/product/index');
} else {
$resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE);
$resultPage->getConfig()->getTitle()->prepend((__('Stock Resupply')));
return $resultPage;
}
}
}

Given the simplicity of our form, using the isPost() check on the request object, we allow ourselves to use the same controller action for rendering the Stock Resupply screen, as well as submitting the save action to it.

With controller action in place, we then define the <MODULE_DIR>/view/adminhtml/layout/minventory_product_resupply.xml as follows:

<page>
<update handle="styles"/>
<body>
<referenceContainer name="content">
<uiComponent name="minventory_resupply_form"/>
</referenceContainer>
</body>
</page>

Much like with the form listing, this layout file merely calls the minventory_resupply_form component, which is where all our visual elements of the Stock Resupply screen reside.

We then define the <MODULE_DIR>/view/adminhtml/ui_component/minventory_resupply_form.xml as follows:

<form>
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">minventory_resupply_form.minventory_resupply_form_data_source</item>
<item name="deps" xsi:type="string">minventory_resupply_form.minventory_resupply_form_data_source</item>
</item>
<item name="layout" xsi:type="array">
<item name="type" xsi:type="string">tabs</item>
</item>
</argument>
<settings>
<buttons>
<button name="save" class="Magelicious\Minventory\Block\Adminhtml\Product\Edit\Button\Save"/>
<button name="back" class="Magelicious\Minventory\Block\Adminhtml\Product\Edit\Button\Back"/>
</buttons>
</settings>
<!-- dataSource -->
<!-- fieldset -->
</form>

Much like the listing component, the form component also requires a data provider.

We then modify the minventory_resupply_form.xml by replacing the <!-- dataSource --> with following:

<dataSource name="minventory_resupply_form_data_source">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
</item>
</argument>
<dataProvider class="Magelicious\Minventory\Ui\DataProvider\Product\Form\ProductDataProvider" name="minventory_resupply_form_data_source">
<settings>
<requestFieldName>id</requestFieldName>
<primaryFieldName>entity_id</primaryFieldName>
</settings>
</dataProvider>
</dataSource>

Here we set the data provider, which points to our custom class, Magelicious\Minventory\Ui\DataProvider\Product\Form\ProductDataProvider.

We further modify the minventory_resupply_form.xml by replacing the <!-- fieldset --> with the following:

<fieldset name="minventory_product">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string" translate="true">General</item>
</item>
</argument>
<field name="stock">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string">Stock</item>
<item name="visible" xsi:type="boolean">true</item>
<item name="dataType" xsi:type="string">text</item>
<item name="formElement" xsi:type="string">input</item>
<item name="disabled" xsi:type="string">true</item>
</item>
</argument>
</field>
<field name="qty">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string">+Qty</item>
<item name="visible" xsi:type="boolean">true</item>
<item name="dataType" xsi:type="string">text</item>
<item name="formElement" xsi:type="string">input</item>
<item name="focused" xsi:type="string">true</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">true</item>
<item name="validate-zero-or-greater" xsi:type="boolean">true</item>
</item>
</item>
</argument>
</field>
</fieldset>

Here we defined fieldset with a General title, and two fields: stock and qty. The stock field was defined as disabled, as its purpose will be merely to merge the <SKU> | <qty> values for informational purposes. The structure of the individual field definition might seem overwhelming at first, but we can easily determine available arguments by observing the <component name="column" definition under the <MAGENTO_DIR>/module-ui/view/base/ui_component/etc/definition.map.xml.

We then define <MODULE_DIR>/Ui/DataProvider/Product/Form/ProductDataProvider.php as follows:

class ProductDataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider {
protected $loadedData;
protected $productRepository;
protected $stockRegistry;
protected $request;

public function __construct(
string $name,
string $primaryFieldName,
string $requestFieldName,
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory,
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
\Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
\Magento\Framework\App\RequestInterface $request,
array $meta = [], array $data = []
) {
parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
$this->collection = $collectionFactory->create();
$this->productRepository = $productRepository;
$this->stockRegistry = $stockRegistry;
$this->request = $request;
}

public function getData() {
if (isset($this->loadedData)) {
return $this->loadedData;
}
$id = $this->request->getParam('id');
$product = $this->productRepository->getById($id);
$stockItem = $this->stockRegistry->getStockItemBySku($product->getSku());
$this->loadedData[$product->getId()]['minventory_product'] = [
'stock' => __('%1 | %2', $product->getSku(), $stockItem->getQty()),
'qty' => 10
];
return $this->loadedData;
}
}

Our data provider is expected to implement the getData method. This returns an array of data that feeds the form with proper values. The structure of the array might be difficult to grasp at first, so it helps to gloss over some of Magento's data providers. The stock and qty entries here will provide values for the fields defined via minventory_resupply_form.xml.

We then define <MODULE_DIR>/Block/Adminhtml/Product/Edit/Button/Back.php as follows:

class Back extends \Magento\Backend\Block\Template implements \Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface
{
public function getButtonData() {
return [
'label' => __('Back'),
'on_click' => sprintf("location.href = '%s';", $this->getBackUrl()),
'class' => 'back',
'sort_order' => 10
];
}

public function getBackUrl() {
    return $this->getUrl('*/*/');
}
}

The ButtonProviderInterface requires the getButtonData method implementation. The structure of the return array is somewhat blurry until we gloss over some of the other buttons that are defined across Magento. This renders our Back button as follows:

<button id="back" title="Back" type="button" class="action- scalable back" onclick="location.href = '... stripped away ...';" data-ui-id="back-button">
<span>Back</span>
</button>

The Back button provides a go back to previous page functionality, which in our case is determined by the value of the getBackUrl method response.

We then define <MODULE_DIR>/Block/Adminhtml/Product/Edit/Button/Save.php as follows:

class Save extends \Magento\Backend\Block\Template implements \Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface
{
public function getButtonData() {
return [
'label' => __('Save'),
'class' => 'save primary',
'data_attribute' => [
'mage-init' => ['button' => ['event' => 'save']],
'form-role' => 'save',
],
'sort_order' => 20,
];
}
}

Much like with the previous button, we use a similar array structure for our button here. The difference is that this time we are passing the data_attribute as well. This renders our Save button as follows:

<button id="save" title="Save" type="button" class="action- scalable save primary ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" onclick="location.href = '... stripped away ...';" data-form-role="save" data-ui-id="save-button" role="button" aria-disabled="false"><span class="ui-button-text">
<span>Save</span>
</span></button>

The mage-init part might seem confusing at the moment. Suffice it to say that it's a way of initializing a JS component, which is something we will address in more detail in the next chapter. Our Save essentially triggers the form's submission.

With this we have finished our form component definition, making the whole Stock Resupply screen functional.