0% found this document useful (0 votes)
171 views128 pages

Combined MD

The document provides detailed instructions on customizing the 'My Account' section in Magento, including adding menu items and modifying customer attributes. It explains the structure and registration process for Magento modules, emphasizing the importance of modularity and dependency management. Additionally, it outlines Magento areas and their roles in organizing code for optimized request processing.

Uploaded by

ajay.tiwari
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
171 views128 pages

Combined MD

The document provides detailed instructions on customizing the 'My Account' section in Magento, including adding menu items and modifying customer attributes. It explains the structure and registration process for Magento modules, emphasizing the importance of modularity and dependency management. Additionally, it outlines Magento areas and their roles in organizing code for optimized request processing.

Uploaded by

ajay.tiwari
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

File: /home/hb/Downloads/magento2-exam-notes-master/combined.

md
Page 1 of 128

## 10.1 Demonstrate ability to customize My Account

Describe how to customize the “My Account” section.

*How do you add a menu item?*


- Create A theme or use an existing one
- Create a folder in the theme Magento_Customer
- Create a folder inside the theme Magento_Customer called layout
- Create a file inside the theme/Magento_Customer/layout/customer_account.xml
- Add similar xml
```xml
<?xml version="1.0"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See [Link] for license details.
*/
-->
<page xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/
page_configuration.xsd">
<body>
<referenceBlock name="customer_account_navigation">
<block class="Magento\Customer\Block\Account\SortLinkInterface"
name="customer-account-navigation-address-link">
<arguments>
<argument name="label" xsi:type="string" translate="true">Russell
Special</argument>
<argument name="path" xsi:type="string">special/link</argument>
<argument name="sortOrder" xsi:type="number">165</argument>
</arguments>
</block>
</referenceBlock>
</body>
</page>
```

*How would you customize the “Order History” page?*


## 10.2 Demonstrate ability to customize customer functionality

Describe how to add or modify customer attributes.

Describe how to extend the customer entity.

*How would you extend the customer entity using the extension attributes mechanism?*

Describe how to customize the customer address.

*How would you add another field into the customer address?*

Describe customer groups and their role in different business processes.

*What is the role of customer groups?*

*What functionality do they affect?*

Describe Magento functionality related to VAT.

*How do you customize VAT functionality?*


# Magento module overview
>A module is a logical group – that is, a directory containing blocks, controllers,
helpers, models – that are related to a specific business feature. In keeping with
Magento’s commitment to optimal modularity, a module encapsulates one feature and has
minimal dependencies on other modules. - [Magento DevDocs - Module overview](http://
[Link]/guides/v2.2/architecture/archi_perspectives/components/modules/
mod_intro.html)
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 2 of 128

>
>Modules and themes are the units of customization in Magento. Modules provide business
features, with supporting logic, while themes strongly influence user experience and
storefront appearance. Both components have a life cycle that allows them to be
installed, deleted, and disabled. From the perspective of both merchants and extension
developers, modules are the central unit of Magento organization. - [Magento DevDocs -
Module overview]([Link]
components/modules/mod_intro.html)
>
>The Magento Framework provides a set of core logic: PHP code, libraries, and the basic
functions that are inherited by the modules and other components. - [Magento DevDocs -
Module overview]([Link]
components/modules/mod_intro.html)

Modules can be installed with Composer allowing their version management. All modules
installed in such way are placed in the `vendor/` folder and have next base structure
`vendor/<vendor>/<type>-<module-name>`. In this case `<type>` can be:
1. `module` - Magento module
1. `theme` - admin or frontend themes
1. `language` - language packs

In a case when you have a very specific functionality or customisation which is related
to a specific project and there is no need to share it with other projects it should be
created in the `app/code/<vendor>/<type>-<module-name>` directory and the required
directories within it.

### Module registration

>Magento components, including modules, themes, and language packages, must be registered
in the Magento system through the Magento ComponentRegistrar class. - [Magento DevDocs -
Register your component]([Link]
[Link])

>Each component must have a file called [Link] in its root directory. For
example, here is the [Link] file for Magento’s AdminNotification module.
Depending on the type of component, registration is performed through [Link] by
adding to it as follows: - [Magento DevDocs - Register your component](http://
[Link]/guides/v2.2/extension-dev-guide/build/[Link])

- How to register modules - [Link] [1]


```php
<?php

use \Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdminNotification',
__DIR__);
```

- how to register language packages - [Link] [2]


```php
<?php

use \Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, '<VendorName>_<packageName>',
__DIR__);
```

- how to register themes - [Link] [3]


```php
<?php

use \Magento\Framework\Component\ComponentRegistrar;
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 3 of 128

ComponentRegistrar::register(ComponentRegistrar::THEME, '<area>/<vendor>/<theme name>',


__DIR__);
```

- [Link] autoload/files[] = "[Link]" [4]


```json
{
"name": "Acme-vendor/bar-component",
"autoload": {
"psr-4": { "AcmeVendor\\BarComponent\\": "" },
"files": [ "[Link]" ]
} }
```

- at what stage - when including vendor/[Link] [5]


```php
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See [Link] for license details.
*/

\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Magento_Backend',
__DIR__
);
```

- how registered when not in composer - project [Link] autoload/files[] = [app/etc/


[Link]]([Link]
etc/[Link]) [6]
```json
"autoload": {
"files": [
"app/etc/[Link]"
],
}

```

Examples [1],[2],[3],[4],[5],[6] taken from [Magento DevDocs - Register your component]


([Link]
[Link])

##### [Link] search paths


app/code/*/*/cli_commands.php, [Link]
app/design/*/*/*/[Link]
app/i18n/*/*/[Link]
lib/internal/*/*/[Link]
lib/internal/*/*/*/[Link]

##### [Link] load flow


- pub/[Link]
- app/[Link]
- app/[Link]
- vendor/[Link]
- vendor/module[]/[Link] -- last step in Composer init
- `Magento\Framework\Component\ComponentRegistrar::register(type='module',
name='Prince_Productattach', path='/var/www/...')`

# Describe module limitations.


Minimization of software dependencies is a cornerstone of Magento architecture. Based on
this principle there are several logical limitations:
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 4 of 128

1. One module is responsible only for one feature.


1. Module dependencies on other modules must be declared explicitly.
1. Excluding or disabling a module should not disable another module.

# How do different modules interact with each other?


Magento 2 is [PSR-4]([Link] compliant. As a main principle
of module interaction Magento 2 declares a dependency injection pattern and service
contracts.
One of the important parts of module interaction is a Magento area. All components
operate with the system and other components in the scope of default areas.

### Module dependencies


Modules can contain dependencies upon these software components:
1. Other Magento modules.
1. PHP extensions.
1. Third party libraries.

Module dependencies can be managed by:


>1. Name and declare the module in the [Link] file.
>1. Declare any dependencies that the module has (whether on other modules or on a
different component) in the module’s [Link] file.
>1. (Optional) Define the desired load order of config files and .css files in the
[Link] file.
>
> -- [Magento DevDocs - Module dependencies]([Link]
architecture/archi_perspectives/components/modules/mod_depend.html)

> Example: Module A declares a dependency upon Module B. Thus, in Module A’s [Link]
file, Module B is listed in the <sequence> list, so that B’s files are loaded before A’s.
Additionally, you must declare a dependency upon Module B in A’s [Link] file.
Furthermore, in the deployment configuration, Modules A and B must both be defined as
enabled. - [Magento DevDocs - Module dependencies]([Link]
architecture/archi_perspectives/components/modules/mod_depend.html)

### Dependency types

##### Hard dependency


> Modules with a hard dependency on another module cannot function without the module it
depends on.
Specifically:
>1. The module contains code that directly uses logic from another module (for example,
the latter module’s instances, class constants, static methods, public class properties,
interfaces, and traits).
>1. The module contains strings that include class names, method names, class constants,
class properties, interfaces, and traits from another module.
>1. The module deserializes an object declared in another module.
>1. The module uses or modifies the database tables used by another module.
>
> -- [Magento DevDocs - Module dependency types]([Link]
architecture/archi_perspectives/components/modules/mod_depend_types.html)

##### Soft dependency


>Modules with a soft dependency on another module can function properly without the other
module, even if it has a dependency upon it. Specifically:
>The module directly checks another module’s availability.
>The module extends another module’s configuration.
>The module extends another module’s layout.
>
> -- [Magento DevDocs - Module dependency types]([Link]
architecture/archi_perspectives/components/modules/mod_depend.html)

Magento module install order flow:


>1. The module serving as a dependency for another module
>2. The module dependent on it

Following dependencies should not be created:


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 5 of 128

>1. Circular (both direct and indirect)


>1. Undeclared
>1. Incorrect
>
> -- [Magento DevDocs - Module dependency types]([Link]
architecture/archi_perspectives/components/modules/mod_depend.html)

>You can build dependencies between classes in the application layer, but these classes
must belong to the same module. Dependencies between the modules of the application layer
should be built only by the service contract or the service provider interface (SPI). -
[Magento DevDocs - Module dependency types]([Link]
architecture/archi_perspectives/components/modules/mod_depend.html)

### Magento areas


A Magento area organizes code for optimized request processing by loading components
parts which are related only to the specific area. Areas are registered in the `[Link]`
file.

>Modules define which resources are visible and accessible in an area, as well as an
area’s behavior. The same module can influence several areas. For instance, the RMA
module is represented partly in the adminhtml area and partly in the frontend area.
If your extension works in several different areas, ensure it has separate behavior and
view components for each area.
Each area declares itself within a module. All resources specific for an area are located
within the same module as well.
You can enable or disable an area within a module. If this module is enabled, it injects
an area’s routers into the general application’s routing process.
If this module is disabled, Magento will not load an area’s routers and, as a result, an
area’s resources and specific functionality are not available.
>
> -- [Magento DevDocs - Modules and areas]([Link]
architecture/archi_perspectives/components/modules/mod_and_areas.html)

Magento has 5 areas types:


> 1. Magento Admin (`adminhtml`): entry point for this area is [Link] or pub/
[Link]. The Admin panel area includes the code needed for store management. The /app/
design/adminhtml directory contains all the code for components you’ll see while working
in the Admin panel.
> 1. Storefront (`frontend`): entry point for this area is [Link] or pub/[Link].
The storefront (or frontend) contains template and layout files that define the
appearance of your storefront.
> 1. Basic (`base`): used as a fallback for files absent in adminhtml and frontend areas.
> 1. Cron (`crontab`): In [Link], the [`\Magento\Framework\App\Cron`](https://
[Link]/magento/magento2/blob/2.2/lib/internal/Magento/Framework/App/[Link]#L68-L70)
class always loads the 'crontab' area.
>
> You can also send requests to Magento using the SOAP and REST APIs. These two areas:
> 1. Web API REST (`webapi_rest`): entry point for this area is [Link] or pub/
[Link]. The REST area has a front controller that understands how to do URL lookups
for REST-based URLs.
> 1. Web API SOAP (`webapi_soap`): entry point for this area is [Link] or pub/
[Link].
>
> -- [Magento DevDocs - Modules and areas]([Link]
architecture/archi_perspectives/components/modules/mod_and_areas.html)

Not documented but used in code [Magento/Framework/App/[Link]](


[Link]
[Link]#L18-L25):
1. Documentation (doc). Deprecated.
1. Admin (admin). Deprecated.

# What side effects can come from this interaction?


- error when module is missing or disabled
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 6 of 128

- error when injecting missing class


- (?) null or error when using object manager for missing class
ReflectionException - Class MissingClass does not exist
objectManager->create() = new $type() or new $type(...args) --> PHP Warning: Uncaught
Error: Class 'MissingClass' not found
# Determine how to locate different types of files in Magento.

>One of the first things you can do to get started with component development is to
understand and set up the file system. Each type of component has a different file
structure, although all components require certain files.
In addition, you can choose the component root directory to start development. The
following sections have more information. - [Magento DevDocs - About component file
structure]([Link]
prepare_file-[Link])

### Where are the files containing JavaScript, HTML, and PHP located?
- view/frontend/web/js
- view/frontend/[Link]
- view/frontend/layout
- view/frontend/templates

### Root directory location

>A component’s root directory is the top-level directory for that component under which
its folders and files are located. Depending on how your Magento development environment
was installed, your component’s root directory can be located in two places: - [Magento
DevDocs - Create your component file structure]([Link]
extension-dev-guide/build/[Link])

##### `<Magento install directory>/app:`


* Modules - app/code.
* Storefront themes - app/design/frontend.
* Admin themes - app/design/adminhtml.
* Language packages - use app/i18n.

##### `<Magento install directory>/vendor`

>This location is found in the alternative setups where the composer create-project
command was used to get a Magento 2 metapackage (which downloads the CE or EE code), or a
compressed Magento 2 archive was extracted in order to install Magento.
>
>Any third party components (and the Magento application itself) are downloaded and
stored under the vendor directory. If you are using Git to manage project, this directory
is typically added to the .gitignore file. Therefore, we recommend you do your
customization work in app/code, not vendor.
>
> -- [Magento DevDocs - Create your component file structure]([Link]
guides/v2.2/extension-dev-guide/build/[Link])

### Required files

>`[Link]`: Among other things, this file specifies the directory in which the
component is installed by vendors in production environments. By default, composer
automatically installs components in the <Magento root dir>/vendor directory. For more
information, see Component registration.
>
>`etc/[Link]`: This file specifies basic information about the component such as the
components dependencies and its version number. This version number is used to determine
schema and data updates when bin/magento setup:upgrade is run.
>
>`[Link]`: Specifies component dependencies and other metadata. For more
information, see Composer integration.
>
> -- [Magento DevDocs - About component file structure]([Link]
guides/v2.2/extension-dev-guide/prepare/prepare_file-[Link])
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 7 of 128

Class [Magento\Framework\Module\ModuleList\Loader]([Link]
blob/2.3/lib/internal/Magento/Framework/Module/ModuleList/[Link]#L78) load `etc/
[Link]` files and sort modules by sequence.
The sequence use for sorting events, plugins, preferences and layouts.

### Common directories

- `Api` - Any PHP classes exposed to the API.


- `Block` - PHP view classes as part of Model View Controller(MVC) vertical
implementation of module logic.
- `Console` - Console commands
- `Controller` - PHP controller classes as part of MVC vertical implementation of module
logic.
- `Controller/Adminhtml` - Admin controllers
- `Cron` - Cron job classes
- `etc` - Configuration files; in particular, [Link], which is required.
- `Helper` - Helpers
- `i18n` - Localization files in CSV format
- `Model` - PHP model classes as part of MVC vertical implementation of module logic.
- `Model/ResourceModel` - Database interactions
- `Observer` - Event listeners
- `Plugin` - Contains any needed plug-ins.
- `Setup` - Classes for module database structure and data setup which are invoked when
installing or upgrading.
- `Test` - Unit tests
- `Ui` - UI component classes
- `view` - View files, including static view files, design templates, email templates,
and layout files.
- view/{area}/email
- view/{area}/layout
- view/{area}/templates
- view/{area}/ui_component
- view/{area}/ui_component/templates
- view/{area}/web
- view/{area}/web/template
- view/{area}/[Link]

see [Magento DevDocs - Create your component file structure]([Link]


guides/v2.2/extension-dev-guide/build/[Link])

### Service contract

> The service contract of a module is defined by the set of interfaces in the module’s `/
Api` directory.
>
> This directory contains:
>
> * Service interfaces in the `Api` namespace of the module ([Catalog API](https://
[Link]/magento/magento2/tree/2.0/app/code/Magento/Customer/Api)).
>
> * Data (or entity) interfaces in the `Api/Data` directory ([Catalog API/Data](https://
[Link]/magento/magento2/tree/2.0/app/code/Magento/Customer/Api/Data)). Data entities*
are data structures passed to and returned from service interfaces. Files in the data
directory contain get() and set() methods for entries in the entity table and extension
attributes.
>
> -- [Service contract anatomy]([Link]
archi_perspectives/service_layer.html#service-contract-anatomy)

### Theme file structure


Example #1
```
├── [Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 8 of 128

├── [Link]
├── etc
│ └── [Link]
├── i18n
│ └── en_US.csv
├── LICENSE_AFL.txt
├── [Link]
├── media
│ └── [Link]
├── [Link]
└── web
├── css
│ ├── [Link]
│ ├── [Link]
│ ├── source
│ │ ├── _actions-[Link]
│ │ ├── _breadcrumbs.less
│ │ ├── _buttons.less
│ │ ├── components
│ │ │ └── _modals_extend.less
│ │ ├── _icons.less
│ │ ├── _layout.less
│ │ ├── _theme.less
│ │ ├── _tooltips.less
│ │ ├── _typography.less
│ │ └── _variables.less
│ ├── _styles.less
│ ├── [Link]
│ └── [Link]
├── images
│ └── [Link]
└── js
├── [Link]
├── [Link]
└── [Link]
```

### Language package file structure


Example #2
```
├── de_DE
│ ├── [Link]
│ ├── [Link]
│ ├── LICENSE_AFL.txt
│ ├── [Link]
│ └── [Link]
├── en_US
│ ├── [Link]
│ ├── [Link]
│ ├── LICENSE_AFL.txt
│ ├── [Link]
│ └── [Link]
├── pt_BR
│ ├── [Link]
│ ├── [Link]
│ ├── LICENSE_AFL.txt
│ ├── [Link]
│ └── [Link]
```

Examples #1, #2 - - [Magento DevDocs - Create your component file structure](https://


[Link]/guides/v2.2/extension-dev-guide/build/[Link])
# Determine how to use configuration files in Magento. Which configuration files
correspond to different features and functionality?

### List of existed *.xml configs


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 9 of 128

- `[Link]` - resource title, sort


- `adminhtml/rules/payment_{country}.xml` - paypal
- `address_formats.xml`
- `address_types.xml` - format code and title only
- `[Link]` - name, instance - e.g. full_page=Page Cache
- `catalog_attributes.xml` - catalog_category, catalog_product, unassignable,
used_in_autogeneration, quote_item [*]([Link]
custom-catalog-attributes/)
- `[Link]`
- `[Link]` - defaults
- `[Link]` - group[], job instance, method, schedule [*]([Link]
notes/magento2-exam-notes/blob/master/
1.%20Magento%20Architecture%20and%20Customization%20Techniques/
6.%20Configure%20event%20observers%20and%20scheduled%[Link]#crontabxml)
- `cron_groups.xml` [*]([Link]
1.%20Magento%20Architecture%20and%20Customization%20Techniques/
6.%20Configure%20event%20observers%20and%20scheduled%[Link]#cron-groups)
- `[Link]` - preference, plugins, virtual type [*]([Link]
v2.2/extension-dev-guide/build/[Link])
- `eav_attributes.xml` - locked entity attributes (global, unique etc.)
- `email_templates.xml` - id label file type module -- view/frontend/email/[Link]
- `[Link]` - observers, shared, disabled [*]([Link]
magento2-exam-notes/blob/master/
1.%20Magento%20Architecture%20and%20Customization%20Techniques/
6.%20Configure%20event%20observers%20and%20scheduled%[Link]#demonstrate-how-to-
configure-observers)
- `[Link]`
- `extension_attributes.xml` - for, attribute code, attribute type
- `[Link]`
- `[Link]`
- `[Link]` - class, view_id, title, description
- `[Link]`
- `integration/[Link]`
- `integration/[Link]`
- `[Link]` - admin menu
- `[Link]` - version, sequence
- `[Link]` - scheduled updates, subscribe to table changes, indexer model
- `page_types.xml`
- `[Link]` - groups, method allow_multiple_address
- `[Link]` - renders by type (invoice, shipment, creditmemo) and product type
- `product_types.xml` - label, model instance, index priority, (?) custom attributes, (!)
composable types
- `product_options.xml`
- `[Link]`
- `[Link]`
- `[Link]` - collectors (quote, order, invoice, credit memo)
- `search_engine.xml`
- `search_request.xml` - index, dimensions, queries, filters, aggregations, buckets
- `[Link]` - action route placeholder -> invalidate customer sections
- `[Link]` - adminhtml config
- `[Link]` - entity, rules, constraints -> class
- `[Link]` - vars by module
- `[Link]` - route, method, service class and method, resources
- `[Link]` - class, email compatible, image, ttl (?), label, description, parameters
- `zip_codes.xml`

# Interfaces for work with configs

#### [\Magento\Framework\Config\Reader\Filesystem]([Link]
blob/2.3/lib/internal/Magento/Framework/Config/Reader/[Link]) ->
[\Magento\Framework\Config\ReaderInterface]([Link]
lib/internal/Magento/Framework/Config/[Link])
Gets .xsd names from schema locator, gets full .xml file list from file resolver, merges
all files, validates, runs converter to get resulting array.
- read(scope)
+ `fileResolver->get(_filename)`
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 10 of 128

+ merge and validate each file (if mode developer)


+ validate merged DOM
+ converter->convert(dom) => array
- `_idAttributes`, `_fileName`, `_schemaFile` (from schemaLocator), `_perFileSchema`
(from schemaLocator), filename ([Link])
- schemaFile from schemaLocator

#### [\Magento\Framework\Config\ConverterInterface]([Link]
blob/2.3/lib/internal/Magento/Framework/Config/[Link])
Convert an array to any format
- `convert(\DOMDocument $source)`

#### [\Magento\Framework\Config\SchemaLocatorInterface]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/Config/[Link]) -
full path to .xsd
- `getPerFileSchema` - per file before merge
- `getSchema` - merged file

#### [\Magento\Framework\Config\ValidationStateInterface]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/Config/[Link])

This interface retrieves the validation state.


- `isValidationRequired()`

[\Magento\Framework\App\Arguments\ValidationState]([Link]
blob/2.3/lib/internal/Magento/Framework/App/Arguments/[Link]) is default
implementation, that require validation only in developer mode.

#### [\Magento\Framework\Config\ScopeListInterface]([Link]
blob/2.3/lib/internal/Magento/Framework/Config/[Link])

This interface the list of all scopes.


- `getAllScopes()`

#### [\Magento\Framework\Config\Data]([Link]
internal/Magento/Framework/Config/[Link]) -> [\Magento\Framework\Config\DataInterface]
([Link]
[Link])

Helps to get the configuration data in a specified scope.


- `merge(array $config);`
- `get($key, $default = null)`

###### Links and examples:


- Product types configs model to read data from [product_types.xml]([Link]
magento/magento2/blob/2.3/app/code/Magento/Catalog/etc/product_types.xml):
[\Magento\Catalog\Model\ProductTypes]([Link]
code/Magento/Catalog/Model/ProductTypes)
- Implementation via virtual types to read data from [Link]: [Magento/Theme/etc/
[Link]]([Link]
[Link]#L42)
- [Link]

### Configuration load and merge flow

#### Loading order


1. First step is a `app/etc/[Link]` loading
1. Next step is collecting configuration files from all modules and merging them:
`vendor_name/component_name/etc/*.xml`
1. At the last step Magento will collect all configs from `vendor_name/component_name/
etc/<area>/*.xml`

#### Merge flow


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 11 of 128

>Nodes in configuration files are merged based on their fully qualified XPaths, which has
a special attribute defined in $idAttributes array declared as its identifier. This
identifier must be unique for all nodes nested under the same parent node.
>
> * If node identifiers are equal (or if there is no identifier defined), all underlying
content in the node (attributes, child nodes, and scalar content) is overridden.
> * If node identifiers are not equal, the node is a new child of the parent node.
> * If the original document has multiple nodes with the same identifier, an error is
triggered because the identifiers cannot be distinguished.
> * After configuration files are merged, the resulting document contains all nodes from
the original files.
>
> -- [Magento DevDocs - Module configuration files]([Link]
v2.2/config-guide/config/[Link])

config merger = \Magento\Framework\Config\Dom


- when merging each file
+ createConfigMerger|merge
+ \Magento\Framework\Config\Dom::_initDom(dom, perFileSchema)
+ \Magento\Framework\Config\Dom::validateDomDocument
+ $dom->schemaValidate
- after all merged
+ \Magento\Framework\Config\Dom::validate(mergedSchema)
+ \Magento\Framework\Config\Dom::validateDomDocument

## Sensitive and environment settings

This scope is a very huge part which includes a lot of things and there is a short list
of useful links
to the official Magento DevDocs documentation:

- [Set configuration values]([Link]


[Link])
- [Sensitive and system-specific]([Link]
prod/[Link])
- [Magento Enterprise B2B Extension configuration paths reference](https://
[Link]/guides/v2.2/config-guide/prod/[Link])
- [Other configuration paths reference]([Link]
guide/prod/[Link])

### Example of how to set sensitive settings

- shared config app/etc/[Link]


- sensitive or system-specific app/etc/[Link]:

```xml
<type name="Magento\Config\Model\Config\TypePool">
<arguments>
<!-- sensitive config items -->
<argument name="sensitive" xsi:type="array">
<item name="payment/paypal_express/merchant_id" xsi:type="string">1</item>
<!-- keys, password, emails, personally identifiable information -->
</argument>
<!-- environment specific config items -->
<argument name="environment" xsi:type="array">
<item name="payment/paypal_express/debug" xsi:type="string">1</item>
<!-- URLs, IPs, hosts, modes sandbox/live, email recipients -->
</argument>
</arguments>
</type>
```

Sensitive info doesn't get exported with `bin/magento app:config:dump`. use env. params,
e.g.
`CONFIG__DEFAULT__PAYMENT__TEST__PASWORD` for `payment/test/password`
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 12 of 128

`bin/magento app:config:dump`:

- system-specific > app/etc/[Link]


- shared > app/etc/[Link]
- sensitive - skipped

`bin/magento config:sensitive:set`:

- writes to app/etc/[Link]
# Demonstrate how to use dependency injection

Magento 2 uses constructor injection, where all injected objects or factories are
provided when the object is constructed.

Magento loads [Link] files and merges them all together from the following stages:
* Initial (app/etc/[Link])
* Global ({moduleDir}/etc/[Link])
* Area-specific ({moduleDir}/etc/{area}/[Link])

### Object manager

Under the hood, the [ObjectManager]([Link]


internal/Magento/Framework/ObjectManager/[Link])
will use PHP reflection features to look at a class’s __construct type hints/parameters,
automatically instantiate the object for us, and then pass it into the constructor as an
argument.

[AbstractFactory]([Link]
Framework/ObjectManager/Factory/[Link]) >
[\Magento\Framework\ObjectManager\FactoryInterface]([Link]
blob/2.3/lib/internal/Magento/Framework/ObjectManager/[Link])
and their implementations use to resolve arguments and create new objects.

By default, all objects created via automatic constructor dependency injection are
singleton objects,
because they created via ObjectManager::get() method.

```
if ($isShared) {
$argument = $this->objectManager->get($argumentType);
} else {
$argument = $this->objectManager->create($argumentType);
}
```
[\Magento\Framework\ObjectManager\Factory::resolveArgument()]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/ObjectManager/Factory/
[Link]#L143-L147)

### Arguments

```
<argument xsi:type="object">{typeName}</argument>
<argument xsi:type="object" shared="{shared}">{typeName}</argument>

<argument xsi:type="string">{strValue}</argument>
<argument xsi:type="string" translate="true">{strValue}</argument>

<argument xsi:type="boolean">{boolValue}</argument>
<argument xsi:type="number">{numericValue}</argument>
<argument xsi:type="init_parameter">{Constant::NAME}</argument>
<argument xsi:type="const">{Constant::NAME}</argument>
<argument xsi:type="null"/>

<argument xsi:type="array">
<item name="someKey" xsi:type="<type>">someVal</item>
</argument>
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 13 of 128

```

###### Links
- [Dependency injection]([Link]
[Link])
- [The [Link] file]([Link]
[Link])
- [ObjectManager]([Link]
[Link])
- [Proxies]([Link]
- [Factories]([Link]
- [Alan Storm, The Magento 2 Object System]([Link]
#magento-2-object-system)
- [Alan Kent, Magento 2 Dependency injection]([Link]
dependency-injection-the-m2-way-to-replace-api-implementations/)
# Demonstrate ability to use plugins

There are three types of plugins in magento: around, before and after.

__Important__: Classes, abstract classes and interfaces that are implementations of or


inherit from classes that have plugins will also inherit plugins from the parent class.
For example, if you create a plugin for [\Magento\Catalog\Block\Product\AbstractProduct]
([Link]
[Link]),
plugin methods will be called for all child classes, such as
\Magento\Catalog\Block\Product\View, \Magento\Catalog\Block\Product\ProductList\Upsell
etc.

### Limitations

Plugins only work on __public methods__ for any __classes__ or __interfaces__.


So plugins can not be used on following:

> 1. Final methods


> 1. Final classes
> 1. Non-public methods
> 1. Class methods (such as static methods)
> 1. __construct
> 1. Virtual types
> 1. Objects that are instantiated before Magento\Framework\Interception is bootstrapped

@since 2.2 *after* plugin has access to *arguments*!

### Prioritizing plugins

The `sortOrder` property controls how your plugin interacts with other plugins on the
same class.

> The prioritization rules for ordering plugins:


>
> * Before the execution of the observed method, Magento will execute plugins from lowest
to greatest `sortOrder`.
>
> * During each plugin execution, Magento executes the current plugin's before method.
> * After the before plugin completes execution, the current plugin's around method
will wrap and execute the next plugin or observed method.
>
> * Following the execution of the observed method, Magento will execute plugins from
greatest to lowest `sortOrder`.
>
> * During each plugin execution, the current plugin will first finish executing its
around method.
> * When the around method completes, the plugin executes its after method before
moving on to the next plugin.
>
> -- [Magento DevDocs - Plugins]([Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 14 of 128

guide/[Link]#prioritizing-plugins)

Plugin sortOrder:

- before sortOrder=10, before sortOrder=20, before sortOrder=30 ...


- before and around (first half) called together for same plugin!
- around (second half) and after called together for same plugin!

Example:

- [Link], [Link] first half


- [Link], [Link] first half
- [Link], `____________________
- ____________________, [Link] first half
- method()
- ____________________, [Link] second half
- [Link] , ________________________________
- [Link] second half, [Link]
- [Link] second half, [Link]

### Identify strengths and weaknesses of plugins.

> The greatest weakness is exploited in the hands of a developer who is either not
> experienced or not willing to take the time to evaluate the fallout. For example,
> used improperly, an around plugin can prevent the system from functioning.
> They can also make understanding what is going on by reading source code hard
> (spooky action at a distance).
>
> -- [Swiftotter Developer Study Guide]([Link]
magento-2-certified-developer-study-guide)

Using around plugins:


> Avoid using around method plugins when they are not required because they increase
stack traces and affect performance.
> The only use case for around method plugins is when you need to terminate the execution
of all further plugins and original methods.
>
> -- [Magento DevDocs - Programming Best Practices]([Link]
v2.2/ext-best-practices/extension-coding/[Link]#using-around-plugins)

### In which cases should plugins be avoided?

> Plugins are useful to modify the input, output, or execution of an existing method.
> Plugins are also best to be avoided in situations where an event observer will work.
> Events work well when the flow of data does not have to be modified.
>
> -- [Swiftotter Developer Study Guide]([Link]
magento-2-certified-developer-study-guide)

Info from Magento technical guidelines:


> `4. Interception`
>
> 4.1. Around-plugins SHOULD only be used when behavior of an original method is supposed
to be substituted in certain scenarios.
>
> 4.2. Plugins SHOULD NOT be used within own module .
>
> 4.3. Plugins SHOULD NOT be added to data objects.
>
> 4.4. Plugins MUST be stateless.
>
> ...
>
> `14. Events`
>
> 14.1. All values (including objects) passed to an event MUST NOT be modified in the
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 15 of 128

event observer. Instead, plugins SHOULD BE used for modifying the input or output of a
function.
>
> -- [Magento DevDocs - Technical guidelines]([Link]
coding-standards/[Link])

Also check:
- [Yireo - Magent 2 observer or Plugin?]([Link]
observer-or-plugin)
- [Magento DevDocs - Observers Best Practices]([Link]
ext-best-practices/extension-coding/[Link])

### how it works?

Magento automatically generates `Interceptor` class for the plugin target and store it in
the `generated\code` directory.

```
namespace \Notes;

class Interceptor extends \Notes\MyBeautifulClass implements


\Magento\Framework\Interception\InterceptorInterface
{
use \Magento\Framework\Interception\Interceptor;

public function __construct($specificArguments1, $someArg2 = null)


{
$this->___init();
parent:__construct($specificArguments1, $someArg2);
}

public function sayHello()


{
pluginInfo = pluginList->getNext('MyBeautifulClass', 'sayHello')
__callPlugins('sayHello', [args], pluginInfo)
}
}
```

[\Magento\Framework\Interception\Interceptor]([Link]
2.3/lib/internal/Magento/Framework/Interception/[Link]):

- $pluginList = [\Magento\Framework\Interception\PluginListInterface]([Link]
magento/magento2/blob/2.3/lib/internal/Magento/Framework/Interception/
[Link])
- $subjectType = 'MyBeautifulClass'
- `___init` - called in in constructor, pluginList = get from object manager, subjectType
= class name
- pluginList->getNext
- `___callPlugins`
- `___callParent`

### how generated?

\Magento\Framework\App\Bootstrap::create
\Magento\Framework\App\Bootstrap::__construct
\Magento\Framework\App\ObjectManagerFactory::create
\Magento\Framework\ObjectManager\DefinitionFactory::createClassDefinition
\Magento\Framework\ObjectManager\DefinitionFactory::getCodeGenerator
\Magento\Framework\Code\Generator\Io::__construct
\Magento\Framework\Code\Generator::__construct
spl_autoload_register([new \Magento\Framework\Code\Generator\Autoloader, 'load']);

\Magento\Framework\App\ObjectManagerFactory::create
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 16 of 128

\Magento\Framework\Code\Generator::setGeneratedEntities
\Magento\Framework\App\ObjectManager\Environment\Developer::configureObjectManager

\Magento\Framework\Code\Generator\Autoloader::load
\Magento\Framework\Code\Generator::generateClass

### Decide how to generate based on file suffix - generator


\Magento\Framework\Code\Generator\EntityAbstract
```
array (
'extensionInterfaceFactory' => '\\Magento\\Framework\\Api\\Code\\Generator\
\ExtensionAttributesInterfaceFactoryGenerator',
'factory' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Factory',
'proxy' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Proxy',
'interceptor' => '\\Magento\\Framework\\Interception\\Code\\Generator\\Interceptor',
'logger' => '\\Magento\\Framework\\ObjectManager\\Profiler\\Code\\Generator\\Logger',
- logs all public methods call
- Magento\Framework\ObjectManager\Factory\Log -- missing?
'mapper' => '\\Magento\\Framework\\Api\\Code\\Generator\\Mapper',
- extractDto() = $this->{$name}Builder->populateWithArray()->create
'persistor' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Persistor',
- getConnection, loadEntity, registerDelete, registerNew, registerFromArray,
doPersist, doPersistEntity
'repository' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Repository', --
deprecated
'convertor' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Converter',
- Extract data object from model
- getModel(AbstractExtensibleObject $dataObject) = getProductFactory()->create()-
>setData($dataObject)->__toArray()
'searchResults' => '\\Magento\\Framework\\Api\\Code\\Generator\\SearchResults',
- extends \Magento\Framework\Api\SearchResults
'extensionInterface' => '\\Magento\\Framework\\Api\\Code\\Generator\
\ExtensionAttributesInterfaceGenerator',
'extension' => '\\Magento\\Framework\\Api\\Code\\Generator\
\ExtensionAttributesGenerator',
- extension_attributes.xml
- extends \Magento\Framework\Api\AbstractSimpleObject
- implements {name}\ExtensionInterface
- for every custom attribute, getters and setters
'remote' => '\\Magento\\Framework\\MessageQueue\\Code\\Generator\
\RemoteServiceGenerator',
)
```

```
Magento\Framework\App\ResourceConnection\Proxy -> type Proxy, name
Magento\Framework\App\ResourceConnection
Magento\Framework\Code\Generator::shouldSkipGeneration - type not detected, or class
exists
\Magento\Framework\Code\Generator::createGeneratorInstance -- new for every file
```

### \Magento\Framework\Code\Generator\EntityAbstract - code generation


```
\Magento\Framework\Code\Generator\EntityAbstract::generate
\Magento\Framework\Code\Generator\EntityAbstract::_validateData - class not existing etc.
\Magento\Framework\Code\Generator\EntityAbstract::_generateCode
- \Magento\Framework\Code\Generator\ClassGenerator extends
\Zend\Code\Generator\ClassGenerator
- \Magento\Framework\Code\Generator\EntityAbstract::_getDefaultConstructorDefinition
- \Magento\Framework\Code\Generator\EntityAbstract::_getClassProperties
- \Magento\Framework\Code\Generator\EntityAbstract::_getClassMethods
```

###### Links
- [Magento DevDocs - Plugins (Interceptors)]([Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 17 of 128

extension-dev-guide/[Link])
- [Alan Storm - Magento 2 Object Manager Plugin System]([Link]
magento_2_object_manager_plugin_system/)# Configure event observers and scheduled jobs

## Observers

Events are dispatched by modules when certain actions are triggered.


In addition to its own events, Magento allows you to create your own events that can be
dispatched in your code.
When an event is dispatched, it can pass data to any observers configured to watch that
event.

Best practices:
- Make your observer efficient
- Do not include business logic
- Declare observer in the appropriate scope
- Avoid cyclical event loops
- Do not rely on invocation order

>
> `14. Events`
>
> 14.1. All values (including objects) passed to an event MUST NOT be modified in the
event observer. Instead, plugins SHOULD BE used for modifying the input or output of a
function.
>
> -- [Magento DevDocs - Technical guidelines]([Link]
coding-standards/[Link])

### Demonstrate how to configure observers


- can define observer in global area, then disable in specific area

Observers can be configured in the `[Link]` file.

Properties:
- name (required) - Name of the observer for the event definition
- instance (required) - Class name of the observer
- disabled - Is observer active or not (Default: false)
- shared - Class lifestyle (Default: false)

Example:
```xml
<config xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/[Link]">
<event name="cms_block_save_before">
<observer name="myObserverName"
instance="MyCompany\MyModule\Observer\MyObserver" />
</event>
<event name="cms_block_save_commit_after">
<observer name="myObserverName2"
instance="MyCompany\MyModule\Observer\AnotherObserver" disabled="true" shared="true"/>
</event>
</config>
```

Observer class should be placed in the <module-root>/Observer directory and implement


[Magento\Framework\Event\ObserverInterface]([Link]
lib/internal/Magento/Framework/Event/[Link])
interface.

```php
<?php

class MyObserver implements \Magento\Framework\Event\ObserverInterface


{
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 18 of 128

public function execute(\Magento\Framework\Event\Observer $observer)


{
$order = $observer->getEvent()->getData('order'); // $observer->getEvent()-
>getOrder();
// observer code...
}
}
```

### Dispatching events

Events dispatch by [Magento\Framework\Event\Manager]([Link]


blob/2.3/lib/internal/Magento/Framework/Event/[Link]) class
that implement [Magento\Framework\Event\ManagerInterface]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/Event/[Link]) interface:
> dispatch($eventName, array $data = []);

Example:
> $this->eventManager->dispatch('cms_page_prepare_save', ['page' => $model, 'request' =>
$this->getRequest()]);

###### Links
- [Magento DevDocs - Events and observers]([Link]
extension-dev-guide/[Link])
- [Magento DevDocs - Observers Best Practices]([Link]
ext-best-practices/extension-coding/[Link])

## Scheduled jobs

### Demonstrate how to configure a scheduled job

##### Cron groups

A cron group is a logical group that enables you to easily run cron for more than one
process at a time.
Most Magento modules use the default cron group.

To declare new group and specify settings, create `<module>/etc/cron_groups.xml` file


(store view scope):

```xml
<config xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/cron_groups.xsd">
<group id="default">
<schedule_generate_every>15</schedule_generate_every>
<schedule_ahead_for>20</schedule_ahead_for>
<schedule_lifetime>15</schedule_lifetime>
<history_cleanup_every>10</history_cleanup_every>
<history_success_lifetime>10080</history_success_lifetime>
<history_failure_lifetime>10080</history_failure_lifetime>
<use_separate_process>0</use_separate_process>
</group>
</config>
```

Existing groups:
- default (no separate process)
- index - mview, targetrule
- catalog_event - catalog_event_status_checker - mark event open/closed
- consumers - consumers_runner if configured to run by cron. `bin/magento
queue:consumers:start`. PID file var/{$consumer}.pid
- staging - staging_apply_version, staging_remove_updates,
staging_synchronize_entities_period
- ddg_automation (dotmailer)
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 19 of 128

##### [Link]

`[Link]` file is used to execute an action on schedule.


This file always located in the <module>/etc/ folder (not in <module>/etc/{area}/)!

```xml
<config xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/[Link]">
<group id="GROUP_NAME">
<job name="CRON_JOB_UNIQUE_ID" instance="CLASS" method="METHOD">
<config_path>some/config/path</config_path>
</job>
<job name="indexer_clean_all_changelogs"
instance="Magento\Indexer\Cron\ClearChangelog" method="execute">
<schedule>0 * * * *</schedule>
</job>
</group>
</config>
```

run:

- magento cron:run [–group=”"]


- pub/[Link]?[group=<name>] in a web browser, protect with basic auth

Stack trace:
```
\Magento\Cron\Console\Command\CronCommand::execute
\Magento\Framework\App\Cron::launch
`default` event
\Magento\Cron\Observer\ProcessCronQueueObserver
check for specific group
cleanup
generate
check for standalone process
```

[\Magento\Cron\Model\Config\Data]([Link]
Magento/Cron/Model/Config/[Link]) extends [\Magento\Framework\Config\Data](https://
[Link]/magento/magento2/blob/2.3/lib/internal/Magento/Framework/Config/[Link])
- merges [\Magento\Cron\Model\Config\Reader\Db::get]([Link]
blob/2.3/app/code/Magento/Cron/Model/Config/Reader/[Link]#L51) from Database

Sample DB structure:
```
default/crontab/GROUP/jobs/JOB/schedule/cron_expr = '* * * * *'
default/crontab/GROUP/jobs/JOB/schedule/config_path = 'some/config/path' -- try to read
schedule from this config, store view scope
default/crontab/GROUP/jobs/JOB/run/model = 'class::method'
```
DB config usage example: [ProductAlert, [Link]]([Link]
blob/2.3/app/code/Magento/ProductAlert/etc/adminhtml/[Link]#L38:L45), [ProductAlert,
[Link]]([Link]
etc/[Link]), backend model: [Magento\Cron\Model\Config\Backend\Product\Alert]
([Link]
Product/[Link])

`bin/magento cron:install` example:


```
#~ MAGENTO START 4d557a63fe1eac8a2827a4eca020c6bb
* * * * * /usr/bin/php7.0 /var/www/m22ee/bin/magento cron:run 2>&1 | grep -v "Ran jobs by
schedule" >> /var/www/m22ee/var/log/[Link]
* * * * * /usr/bin/php7.0 /var/www/m22ee/update/[Link] >> /var/www/m22ee/var/log/
[Link]
* * * * * /usr/bin/php7.0 /var/www/m22ee/bin/magento setup:cron:run >> /var/www/m22ee/var/
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 20 of 128

log/[Link]
#~ MAGENTO END 4d557a63fe1eac8a2827a4eca020c6bb
```

how is separate process ran?


`bin/magento cron:run --group=NAME --bootstrap=standaloneProcessStarted=1`

what is update/[Link]?
TODO: find out

what is setup:cron:run?
TODO: find out

###### Links
- [Magento DevDocs - Configure a custom cron job and cron group (tutorial)](https://
[Link]/guides/v2.2/config-guide/cron/[Link])
- [Magento DevDocs - Custom cron job and cron group reference](https://
[Link]/guides/v2.2/config-guide/cron/[Link])

### Identify the function and proper use of automatically available events

Model events [\Magento\Framework\Model\AbstractModel]([Link]


blob/2.3/lib/internal/Magento/Framework/Model/[Link]):

- `model_load_before`, `{$_eventPrefix}_load_before`
- `model_load_after`, `{$_eventPrefix}_load_after`
- `model_save_commit_after`, `{$_eventPrefix}_save_commit_after`
- `model_save_before`, `{$_eventPrefix}_save_before`
- `model_save_after`, `{$_eventPrefix}_save_after`
- `model_delete_before`, `{$_eventPrefix}_delete_before`
- `model_delete_after`, `{$_eventPrefix}_delete_after`
- `model_delete_commit_after`, `{$_eventPrefix}_delete_commit_after`

Flat collection events


[\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection](https://
[Link]/magento/magento2/blob/2.3/lib/internal/Magento/Framework/Model/ResourceModel/
Db/Collection/[Link]):

- `core_collection_abstract_load_before`, `{_eventPrefix}_load_before`
- `core_collection_abstract_load_after`, `{_eventPrefix}_load_after`

only if `_eventPrefix` and `_eventObject` defined: `{prefix}_load_before`, `{prefix}


_load_after`

EAV collection events [\Magento\Eav\Model\Entity\Collection\AbstractCollection](https://


[Link]/magento/magento2/blob/2.3/app/code/Magento/Eav/Model/Entity/Collection/
[Link]):

- eav_collection_abstract_load_before

[\Magento\Framework\Model\AbstractModel]([Link]
internal/Magento/Framework/Model/[Link]):

- _eventObject = 'object'
- _eventPrefix = 'core_abstract', e.g. 'catalog_category'
- _getEventData() - 'data_object' + $_eventObject
- `model_load_before` (object, field=null, value=ID)
- `{_eventPrefix}_load_before`, e.g. `catalog_category_load_before` (object, field,
value, data_object, category)

###### Links
- [Cyrill Schumacher's Blog - List of all dispatched events]([Link]
magento-2.2-list-of-all-dispatched-events/)
# Utilize the CLI
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 21 of 128

### Describe the usage of bin/magento commands in the development cycle.

### Demonstrate an ability to create a deployment process.


Modes: *default*, *developer*, *production*. MAGE-MODE env variable

Commands:

- `bin/magento deploy:mode:show`
- `magento deploy:mode:set {mode} [-s|--skip-compilation]` -- skip compilation when
changing to production

cannot switch to default mode, only developer or production

Default:

- errors logged in var/report, not displayed


- static created dynamically - copied! changes not visible. cached

Developer:

- exceptions displayed, not logged


- exception thrown if bad event subscriber.
- var/report detailed
- static created dynamically - symlinked???, changes visible immediately
- error handler - throws exception instead of logging (notice etc.)

Production - max speed, no errors, no file generation:

- admin can't enable/disable cache types


- errors logged, not displayed
- static not created dynamically, must be deployed
- not need for www-data to write, pub/static can be read-only

### Commands explaination


#### setup:di:compile
1. [Code compilation includes the following (in no particular order):](https://
[Link]/guides/v2.2/config-guide/cli/[Link])
* Application code generation (factories, proxies)
* Area configuration aggregation (optimized dependency injection configurations per area)

see [generated/metadata]([Link]
[Link]#codegen-om)
* Interceptor generation (optimized code generation of interceptors)
* Interception cache generation
* Repositories code generation (generated code for APIs)
* Service data attributes generation (generated extension classes for data objects)
* [Running setup:di:compile places the app into a special mode]([Link]
[Link]/2017/07/magento-2-run-setupdicompile/)

### How to add CLI commands (off-topic)

Magento 2 CLI is based on the Symfony Console component.

To create new CLI command you need:


- Create command class (the recommended location is {module}/Console/Command)
This class must extends from [\Symfony\Component\Console\Command\Command](https://
[Link]/symfony/console/blob/master/Command/[Link]) and have 2 methods:

`configure` - to set the name, description, command line arguments etc

`execute` - is the place where you write your code

```php
<?php
namespace Vendor\Module\Console\Command;
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 22 of 128

use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

class ExampleCommand extends \Symfony\Component\Console\Command\Command


{
protected function configure()
{
$this->setName('example:hello')
->setDescription('Hello world command');

// Positional argument
$this->addArgument(
'myargument',
InputArgument::REQUIRED,
'Positional required argument example'
);

// Not required option


$this->addOption(
'myoption',
null,
InputOption::VALUE_OPTIONAL,
'Option example',
ScopeConfigInterface::SCOPE_TYPE_DEFAULT
);

parent::configure();
}

protected function execute(\Symfony\Component\Console\Input\InputInterface $input,


\Symfony\Component\Console\Output\OutputInterface $output)
{
$output->writeln('hello world');
}
}
```

- Declare your command in [\Magento\Framework\Console\CommandListInterface](https://


[Link]/magento/magento2/blob/2.3/lib/internal/Magento/Framework/Console/
[Link]) using dependency injection ({module}/etc/[Link]).
See also CommandListInterface implementation: [\Magento\Framework\Console\CommandList]
([Link]
[Link])

{module}/etc/[Link]:
```xml
<type name="Magento\Framework\Console\CommandListInterface">
<arguments>
<argument name="commands" xsi:type="array">
<item name="exampleHello"
xsi:type="object">Vendor\Module\Console\Command\ExampleCommand</item>
</argument>
</arguments>
</type>
```

- Clean the cache and compiled code directories

rm -rf cache/* page_cache/* di/* generation/*

###### Links
- [Magento DevDocs - How to add CLI commands]([Link]
extension-dev-guide/cli-cmds/[Link])
- [Magento DevDocs - Command naming guidelines]([Link]
extension-dev-guide/cli-cmds/[Link])
- [Symfony Documentation - The Console Component]([Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 23 of 128

[Link])
- [Magento - sample-module-command]([Link]
master/sample-module-command)
# Demonstrate the ability to manage the cache

### Describe cache types and the tools used to manage caches.

- config
- layout
- block_html
- collections
- db_ddl
- eav
- full_page
- reflection
- translate
- config_integration
- config_integration_api
- config_webservice

Description for most cache type can be found on the [Magento DevDocs - Manage the cache]
([Link]
[Link]#config-cli-subcommands-cache-clean-over)

Commands:

- `magento setup:db-schema:upgrade`
- `magento cache:status`, `magento cache:enable`, `magento cache:disable`
- `magento cache:clean`, `magento cache:flush`

### Init:

frontend:
```
\Magento\Framework\App\ObjectManager\ConfigLoader::load
cacheType = config, frontend = default
\Magento\Framework\App\Cache\Frontend\Pool::_initialize
\Magento\Framework\App\Cache\Frontend\Factory::create
\Zend_Cache::_makeFrontend
\Zend_Cache_Core::__construct
```

backend:

- \Zend_Cache_Backend
- \Zend_Cache_Backend_File
- \Magento\Framework\Cache\Backend\Database

How do you add dynamic content to pages served from the full page cache?

1. Mark any block `cacheable="false"` in layout xml - whole page is uncacheable. Example -
checkout
1. Disable caching in controller using *headers*:
`$page->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0',
true);`
1. Marking block property isScopePrivate - will be loaded via AJAX - deprecated
1. ESI when Varnish enabled, set TTL - Example - menu block
1. Configure page variations - extend *http context*, more cached versions of same page -
store view, customer group, language, currency, is logged in
`\Magento\Framework\App\Http\Context::getVaryString`

Only _GET_ and _HEAD_ are cached

Clear cache
\Magento\Framework\DataObject\IdentityInterface
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 24 of 128

### when giving product page, must somehow send varnish tags
Any block can implement IdentityInterface. After rendering layout and before sending
output,
all blocks are examined for implementing this interface. Cache tags are collected as
merge of all blocks
getIdentities() tags.

```
\Magento\PageCache\Model\Layout\LayoutPlugin::afterGetOutput
X-Magento-Tags = merge(all [Link])
```

block ListProduct:

- every product[].getIdentities
- cat_p_{productId}
- *if changed categories* - cat_p_c_{categoryId}
- *if changed status* - every category[] cat_p_c_{categoryId}
- *if frontend* - 'cat_p'
- cat_c_p_{categoryId}

block product/view:

- \Magento\Catalog\Model\Product::getIdentities:
- cat_p_{productId}
- *if changed categories* - cat_p_c_{categoryId}
- *if changed status* - every category[] cat_p_c_{categoryId}
- *if frontend* - 'cat_p'
- *if current_category* - cat_c_{categoryId}

### after reindex, must somehow clean cache

- any [Link] -- by MView


- any [Link]
- \Magento\Framework\Indexer\CacheContext::registerTags

plugin \Magento\Indexer\Model\Processor:

\Magento\Indexer\Model\Processor\CleanCache::afterUpdateMview

- event `clean_cache_after_reindex`
- clean cache cacheContext->getIdentities()

\Magento\Indexer\Model\Processor\CleanCache::afterReindexAllInvalid

- event `clean_cache_by_tags`
- clean cache cacheContext->getIdentities()

module-cache-invalidate observer `clean_cache_after_reindex`


\Magento\CacheInvalidate\Observer\InvalidateVarnishObserver::execute
\Magento\CacheInvalidate\Model\PurgeCache::sendPurgeRequest

### Describe how to operate with cache clearing.

How would you clean the cache? In which case would you refresh cache/flash cache storage?

> To purge out-of-date items from the cache, you can clean or flush cache types:
>
> - Cleaning a cache type deletes all items from enabled Magento cache types only. In
other words, this option does not affect other processes or applications because it
cleans only the cache that Magento uses.
>
> Disabled cache types are not cleaned.
>
> - Flushing a cache type purges the cache storage, which might affect other processes
applications that are using the same storage.
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 25 of 128

>
> Flush cache types if you’ve already tried cleaning the cache and you’re still having
issues that you cannot isolate.
>
> -- [Magento DevDocs - Manage the cache]([Link]
guide/cli/[Link]#config-cli-subcommands-cache-clean-over)

Sessions and caching data should never be stored in one database in Redis. In that case
you'll not have problems with flushing the cache.

### Describe how to clear the cache programmatically.

To clear the cache programmatically you neeed to call next the methods:
- [\Magento\Framework\App\CacheInterface::remove($identifier)]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/App/[Link]#L48) - remove
cached data by identifier
- [\Magento\Framework\App\CacheInterface::clean($tags = [])]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/App/[Link]#L56) - clean
cached data by specific tag

##### What mechanisms are available for clearing all or part of the cache?

Dispatch a `clean_cache_by_tags` event with parameter of the object you want to clear
from the cache.

Example: [\Magento\Framework\Model\AbstractModel]([Link]
blob/2.3/lib/internal/Magento/Framework/Model/[Link]#L817) (afterSave,
afterDelete methods)

```php
<?php
public function afterSave()
{
$this->cleanModelCache();
$this->_eventManager->dispatch('model_save_after', ['object' => $this]);
$this->_eventManager->dispatch('clean_cache_by_tags', ['object' => $this]);
$this->_eventManager->dispatch($this->_eventPrefix . '_save_after', $this-
>_getEventData());
$this->updateStoredData();
return $this;
}

public function cleanModelCache()


{
$tags = $this->getCacheTags();
if ($tags !== false) {
$this->_cacheManager->clean($tags); // \Magento\Framework\App\CacheInterface
}
return $this;
}
```

Default `clean_cache_by_tags` event observers are:


- [Magento\PageCache\Observer\FlushCacheByTags]([Link]
2.3/app/code/Magento/PageCache/Observer/[Link]#L57) - if Built-In caching
is enabled
- [Magento\CacheInvalidate\Observer\InvalidateVarnishObserver]([Link]
magento2/blob/2.3/app/code/Magento/CacheInvalidate/Observer/
[Link]#L50)- if Varnish caching is enabled

###### Links
- [Magento DevDocs - Magento cache overview]([Link]
frontend-dev-guide/cache_for_frontdevs.html)
- [Magento DevDocs - Configure caching]([Link]
guide/[Link])
- [Magento DevDocs - Partial caching]([Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 26 of 128

dev-guide/cache/[Link])
- [Magento DevDocs - Full Page caching]([Link]
dev-guide/cache/[Link])
- [Magento DevDocs - Private content]([Link]
dev-guide/cache/page-caching/[Link])
# 1-Magento Architecture and Customization Techniques

## 1.1 Describe Magento’s module-based architecture


- [Link]
- [Link] autoload/files[] = "[Link]"
- at what stage - when including vendor/[Link]
- how registered when not in composer - project [Link] autoload/files[] = app/etc/
[Link]

app/code/*/*/cli_commands.php, [Link]
app/design/*/*/*/[Link]
app/i18n/*/*/[Link]
lib/internal/*/*/[Link]
lib/internal/*/*/*/[Link]

pub/[Link]
app/[Link]
app/[Link]
vendor/[Link]
vendor/module[]/[Link] -- last step in Composer init
Magento\Framework\Component\ComponentRegistrar::register(type='module',
name='Prince_Productattach', path='/var/www/...')

How do different modules interact with each other?


- composer require
- [Link] sequence - hard requirement, affects load order
- DI require instance

What side effects can come from this interaction?


- error when module is missing or disabled
- error when injecting missing class
- (?) null or error when using object manager for missing class
ReflectionException - Class MissingClass does not exist
objectManager->create() = new $type() or new $type(...args) --> PHP Warning: Uncaught
Error: Class 'MissingClass' not found

## 1.2 Describe Magento’s directory structure

- app/code/Vendor/Module
- app/design/frontend/VendorName/theme_name_lowercase
- app/i18n/VendorName/en_US
- vendor/vendor-name/module-theme-package/

Where are the files containing JavaScript, HTML, and PHP located?
- view/frontend/web/js
- view/frontend/[Link]
- view/frontend/layout
- view/frontend/templates

How do you find the files responsible for certain functionality?

## 1.3 Utilize configuration XML and variables scope

Determine how to use configuration files in Magento. Which configuration files correspond
to different features and
functionality?

- [Link] - resource title, sort


- adminhtml/rules/payment_{country}.xml - paypal
- address_formats.xml
- address_types.xml - format code and title only
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 27 of 128

- [Link] - name, instance - e.g. full_page=Page Cache


- catalog_attributes.xml - catalog_category, catalog_product, unassignable,
used_in_autogeneration, quote_item
- [Link]
- [Link] - defaults
- [Link] - group[], job instance, method, schedule
- cron_groups.xml
- [Link] - preference, plugins, virtual type
- eav_attributes.xml - locked entity attributes (global, unique etc.)
- email_templates.xml - id label file type module -- view/frontend/email/[Link]
- [Link] - observers, shared, disabled
- [Link]
- extension_attributes.xml - for, attribute code, attribute type
- [Link]
- [Link]
- [Link] - class, view_id, title, description
- [Link] - API resources + pre-configure the integration (After Magento 2.2)
- integration/[Link] - file defines which API resources the integration has access
to(Deprecated in Magento 2.2 and all config are moved in [Link])
- integration/[Link] - pre-configure the integration, the values cannot be edited
from the admin panel ((Deprecated in Magento 2.2 and all config are moved in
[Link])
- [Link]
- [Link] - version, sequence
- [Link] - scheduled updates, subscribe to table changes, indexer model
- page_types.xml
- [Link] - groups, method allow_multiple_address
- [Link] - renders by type (invoice, shipment, creditmemo) and product type
- product_types.xml - label, model instance, index priority, (?) custom attributes, (!)
composable types
- product_options.xml
- [Link]
- [Link] - Routes, Frontend name, module and extending routes
- [Link] - collectors (quote, order, invoice, credit memo)
- search_engine.xml - Add custom search engine
- search_request.xml - index, dimensions, queries, filters, aggregations, buckets
- [Link] - action route placeholder -> invalidate customer sections
- [Link] - adminhtml config
- [Link] - entity, rules, constraints -> class
- [Link] - vars by module
- [Link] - route, method, service class and method, resources
- [Link] - class, email compatible, image, ttl (?), label, description, parameters
- zip_codes.xml

### \Magento\Framework\Config\Reader\Filesystem, \Magento\Framework\Config\ReaderInterface


Gets .xsd names from schema locator, gets full .xml file list from file resolver, merges
all files, validates, runs converter to get resulting array.

- read(scope)
+ fileResolver->get(_filename)
+ merge and validate each file (if mode developer)
+ validate merged dom
+ converter->convert(dom) => array
- _idAttributes, _fileName, _schemaFile (from schemaLocator), _perFileSchema (from
schemaLocator), filename ([Link])
- schemaFile from schemaLocator

### \Magento\Catalog\Model\ProductTypes\Config\Converter,
\Magento\Framework\Config\ConverterInterface - array in any format
- convert(\DOMDocument $source)

### \Magento\Framework\Config\SchemaLocatorInterface - full path to .xsd


- getPerFileSchema - per file before merge
- getSchema - merged file

### config merger = \Magento\Framework\Config\Dom


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 28 of 128

- when merging each file


+ createConfigMerger|merge
+ \Magento\Framework\Config\Dom::_initDom(dom, perFileSchema)
+ \Magento\Framework\Config\Dom::validateDomDocument
+ $dom->schemaValidate
- after all merged
+ \Magento\Framework\Config\Dom::validate(mergedSchema)
+ \Magento\Framework\Config\Dom::validateDomDocument

## Sensitive and environment settings


- shared config app/etc/[Link]
- sensitive or system-specific app/etc/[Link]:

```
<type name="Magento\Config\Model\Config\TypePool">
<arguments>
<!-- sensitive config items -->
<argument name="sensitive" xsi:type="array">
<item name="payment/paypal_express/merchant_id" xsi:type="string">1</item>
<!-- keys, password, emails, personally identifiable information -->
</argument>
<!-- environment specific config items -->
<argument name="environment" xsi:type="array">
<item name="payment/paypal_express/debug" xsi:type="string">1</item>
<!-- URLs, IPs, hosts, modes sandbox/live, email recipients -->
</argument>
</arguments>
</type>
```

sensitive info doesn't get exported with `bin/magento app:config:dump`. use env. params,
e.g.
`CONFIG__DEFAULT__PAYMENT__TEST__PASWORD` for `payment/test/password`

`bin/magento app:config:dump`:

- system-specific > app/etc/[Link]


- shared > app/etc/[Link]
- sensitive - skipped

`bin/magento config:sensitive:set`:

- writes to app/etc/[Link]

## 1.4 Demonstrate how to use dependency injection

```
<argument xsi:type="object">{typeName}</argument>
<argument xsi:type="object" shared="{shared}">{typeName}</argument>

<argument xsi:type="string">{strValue}</argument>
<argument xsi:type="string" translate="true">{strValue}</argument>
```

* Initial (app/etc/[Link])
* Global (<moduleDir>/etc/[Link])
* Area-specific (<moduleDir>/etc/<area>/[Link])

## 1.5 Demonstrate ability to use plugins


- can't use before Magento\Framework\Interception\... is initialized
- *after* plugin has access to *arguments*! @since 2.2

```
class MyBeautifulClass
{
use \Magento\Framework\Interception\Interceptor;
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 29 of 128

public function __construct($specificArguments1, $someArg2 = null)


{
$this->___init();
parent:__construct($specificArguments1, $someArg2);
}

public function sayHello()


{
pluginInfo = pluginList->getNext('MyBeautifulClass', 'sayHello')
__callPlugins('sayHello', [args], pluginInfo)
}
}
```

Magento\Framework\Interception\Interceptor:

- $pluginList = \Magento\Framework\Interception\PluginListInterface
- $subjectType = 'MyBeautifulClass'
- `___init` - called in in constructor, pluginList = get from object manager, subjectType
= class name
- pluginList->getNext
- `___callPlugins`
- `___callParent`

### how generated?

\Magento\Framework\App\Bootstrap::create
\Magento\Framework\App\Bootstrap::__construct
\Magento\Framework\App\ObjectManagerFactory::create
\Magento\Framework\ObjectManager\DefinitionFactory::createClassDefinition
\Magento\Framework\ObjectManager\DefinitionFactory::getCodeGenerator
\Magento\Framework\Code\Generator\Io::__construct
\Magento\Framework\Code\Generator::__construct
spl_autoload_register([new \Magento\Framework\Code\Generator\Autoloader, 'load']);

\Magento\Framework\App\ObjectManagerFactory::create
\Magento\Framework\Code\Generator::setGeneratedEntities
\Magento\Framework\App\ObjectManager\Environment\Developer::configureObjectManager

\Magento\Framework\Code\Generator\Autoloader::load
\Magento\Framework\Code\Generator::generateClass

### Decide how to generate based on file suffix - generator


\Magento\Framework\Code\Generator\EntityAbstract
```
array (
'extensionInterfaceFactory' => '\\Magento\\Framework\\Api\\Code\\Generator\
\ExtensionAttributesInterfaceFactoryGenerator',
'factory' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Factory',
'proxy' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Proxy',
'interceptor' => '\\Magento\\Framework\\Interception\\Code\\Generator\\Interceptor',
'logger' => '\\Magento\\Framework\\ObjectManager\\Profiler\\Code\\Generator\\Logger',
- logs all public methods call
- Magento\Framework\ObjectManager\Factory\Log -- missing?
'mapper' => '\\Magento\\Framework\\Api\\Code\\Generator\\Mapper',
- extractDto() = $this->{$name}Builder->populateWithArray()->create
'persistor' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Persistor',
- getConnection, loadEntity, registerDelete, registerNew, registerFromArray,
doPersist, doPersistEntity
'repository' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Repository', --
deprecated
'convertor' => '\\Magento\\Framework\\ObjectManager\\Code\\Generator\\Converter',
- Extract data object from model
- getModel(AbstractExtensibleObject $dataObject) = getProductFactory()->create()-
>setData($dataObject)->__toArray()
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 30 of 128

'searchResults' => '\\Magento\\Framework\\Api\\Code\\Generator\\SearchResults',


- extends \Magento\Framework\Api\SearchResults
'extensionInterface' => '\\Magento\\Framework\\Api\\Code\\Generator\
\ExtensionAttributesInterfaceGenerator',
'extension' => '\\Magento\\Framework\\Api\\Code\\Generator\
\ExtensionAttributesGenerator',
- extension_attributes.xml
- extends \Magento\Framework\Api\AbstractSimpleObject
- implements {name}\ExtensionInterface
- for every custom attribute, getters and setters
'remote' => '\\Magento\\Framework\\MessageQueue\\Code\\Generator\
\RemoteServiceGenerator',
)
```

```
Magento\Framework\App\ResourceConnection\Proxy -> type Proxy, name
Magento\Framework\App\ResourceConnection
Magento\Framework\Code\Generator::shouldSkipGeneration - type not detected, or class
exists
\Magento\Framework\Code\Generator::createGeneratorInstance -- new for every file
```

### \Magento\Framework\Code\Generator\EntityAbstract - code generation


```
\Magento\Framework\Code\Generator\EntityAbstract::generate
\Magento\Framework\Code\Generator\EntityAbstract::_validateData - class not existing etc.
\Magento\Framework\Code\Generator\EntityAbstract::_generateCode
- \Magento\Framework\Code\Generator\ClassGenerator extends
\Zend\Code\Generator\ClassGenerator
- \Magento\Framework\Code\Generator\EntityAbstract::_getDefaultConstructorDefinition
- \Magento\Framework\Code\Generator\EntityAbstract::_getClassProperties
- \Magento\Framework\Code\Generator\EntityAbstract::_getClassMethods
```

## 1.6 Configure event observers and scheduled jobs

### Demonstrate how to configure observers


- can define observer in global area, then disable in specific area

Observer sortOrder:

- before sortOrder=10, before sortOrder=20, before sortOrder=30 ...


- before and around (first half) called together for same plugin!
- around (second half) and after called together for same plugin!

Example:

- [Link], [Link] first half


- [Link], [Link] first half
- [Link], `____________________
- ____________________, [Link] first half
- method()
- ____________________, [Link] second half
- [Link] , ________________________________
- [Link] second half, [Link]
- [Link] second half, [Link]

### Demonstrate how to configure a scheduled job

cron_groups.xml - store view scope:

- default (no separate process)


- index - mview, targetrule
- catalog_event - catalog_event_status_checker - mark event open/closed
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 31 of 128

- consumers - consumers_runner if configured to run by cron. `bin/magento


queue:consumers:start`. PID file var/{$consumer}.pid
- staging - staging_apply_version, staging_remove_updates,
staging_synchronize_entities_period
- ddg_automation (dotmailer)

```
<group id="NAME">
<job name="NAME" instance="CLASS" method="METHOD">
<config_path>some/config/path</config_path>
</job>
<job name="NAME" instance="CLASS" method="METHOD">
<schedule>* * * * *</config_path>
</job>
</group>
```

run:

- magento cron:run [–group=”"]


- pub/[Link]?[group=<name>] in a web browser, protect with basic auth

```
\Magento\Cron\Console\Command\CronCommand::execute
\Magento\Framework\App\Cron::launch
`default` event
\Magento\Cron\Observer\ProcessCronQueueObserver
check for specific group
cleanup
generate
check for standalone process
```

`\Magento\Cron\Model\Config\Data` extends `\Magento\Framework\Config\Data`


- merges \Magento\Cron\Model\Config\Reader\Db::get from Database

Sample DB structure:
```
default/crontab/GROUP/jobs/JOB/schedule/cron_expr = '* * * * *'
default/crontab/GROUP/jobs/JOB/schedule/config_path = 'some/config/path' -- try to read
schedule from this config, store view scope
default/crontab/GROUP/jobs/JOB/run/model = 'class::method'
```

`bin/magento cron:install` example:


```
#~ MAGENTO START 4d557a63fe1eac8a2827a4eca020c6bb
* * * * * /usr/bin/php7.0 /var/www/m22ee/bin/magento cron:run 2>&1 | grep -v "Ran jobs by
schedule" >> /var/www/m22ee/var/log/[Link]
* * * * * /usr/bin/php7.0 /var/www/m22ee/update/[Link] >> /var/www/m22ee/var/log/
[Link]
* * * * * /usr/bin/php7.0 /var/www/m22ee/bin/magento setup:cron:run >> /var/www/m22ee/var/
log/[Link]
#~ MAGENTO END 4d557a63fe1eac8a2827a4eca020c6bb
```

how is separate process ran?


`bin/magento cron:run --group=NAME --bootstrap=standaloneProcessStarted=1`

what is update/[Link]?
TODO: find out

what is setup:cron:run?
TODO: find out
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 32 of 128

### Identify the function and proper use of automatically available events

Model events \Magento\Framework\Model\AbstractModel:

- `model_load_before`, `{$_eventPrefix}_load_before`
- `model_load_after`, `{$_eventPrefix}_load_after`
- `model_save_commit_after`, `{$_eventPrefix}_save_commit_after`
- `model_save_before`, `{$_eventPrefix}_save_before`
- `model_save_after`, `{$_eventPrefix}_save_after`
- `model_delete_before`, `{$_eventPrefix}_delete_before`
- `model_delete_after`, `{$_eventPrefix}_delete_after`
- `model_delete_commit_after`, `{$_eventPrefix}_delete_commit_after`

Flat collection events


\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection:

- `core_collection_abstract_load_before`, `{_eventPrefix}_load_before`
- `core_collection_abstract_load_after`, `{_eventPrefix}_load_after`

only if `_eventPrefix` and `_eventObject` defined: `{prefix}_load_before`, `{prefix}


_load_after`

EAV collection events \Magento\Eav\Model\Entity\Collection\AbstractCollection:

- eav_collection_abstract_load_before

\Magento\Framework\Model\AbstractModel:

- _eventObject = 'object'
- _eventPrefix = 'core_abstract', e.g. 'catalog_category'
- _getEventData() - 'data_object' + $_eventObject
- `model_load_before` (object, field=null, value=ID)
- `{_eventPrefix}_load_before`, e.g. `catalog_category_load_before` (object, field,
value, data_object, category)

## 1.7 Utilize the CLI


### Describe the usage of bin/magento commands in the development cycle.

### Demonstrate an ability to create a deployment process.


Modes: *default*, *developer*, *production*. MAGE-MODE env variable

Commands:

- `bin/magento deploy:mode:show`
- `magento deploy:mode:set {mode} [-s|--skip-compilation]` -- skip compilation when
changing to production

cannot switch to default mode, only developer or production

Default:

- errors logged in var/report, not displayed


- static created dynamically - copied! changes not visible. cached

Developer:

- exceptions displayed, not logged


- exception thrown if bad event subscriber.
- var/report detailed
- static created dynamically - symlinked???, changes visible immediately
- error handler - throws exception instead of logging (notice etc.)

Production - max speed, no errors, no file generation:

- admin can't enable/disable cache types


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 33 of 128

- errors logged, not displayed


- static not created dynamically, must be deployed
- not need for www-data to write, pub/static can be read-only

## 1.8 Demonstrate the ability to manage the cache

### Describe cache types and the tools used to manage caches.

- config
- layout
- block_html
- collections
- db_ddl
- eav
- full_page
- reflection
- translate
- config_integration
- config_integration_api
- config_webservice

Commands:

- `magento setup:db-schema:upgrade`
- `magento cache:status`, `magento cache:enable`, `magento cache:disable`
- `magento cache:clean`, `magento cache:flush`

### Init:

frontend:
```
\Magento\Framework\App\ObjectManager\ConfigLoader::load
cacheType = config, frontend = default
\Magento\Framework\App\Cache\Frontend\Pool::_initialize
\Magento\Framework\App\Cache\Frontend\Factory::create
\Zend_Cache::_makeFrontend
\Zend_Cache_Core::__construct
```

backend:

- \Zend_Cache_Backend
- \Zend_Cache_Backend_File
- \Magento\Framework\Cache\Backend\Database

How do you add dynamic content to pages served from the full page cache?

1. Mark any block `cacheable="false"` in layout xml - whole page is uncacheable. Example -
checkout
1. Disable caching in controller using *headers*:
`$page->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0',
true);`
1. Marking block property isScopePrivate - will be loaded via AJAX - deprecated
1. ESI when Varnish enabled, set TTL - Example - menu block
1. Configure page variations - extend *http context*, more cached versions of same page -
store view, customer group, language, currency, is logged in
`\Magento\Framework\App\Http\Context::getVaryString`

Only _GET_ and _HEAD_ are cached

Clear cache
\Magento\Framework\DataObject\IdentityInterface

### when giving product page, must somehow send varnish tags
Any block can implement IdentityInterface. After rendering layout and before sending
output,
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 34 of 128

all blocks are examined for implementing this interface. Cache tags are collected as
merge of all blocks
getIdentities() tags.

```
\Magento\PageCache\Model\Layout\LayoutPlugin::afterGetOutput
X-Magento-Tags = merge(all [Link])
```

block ListProduct:

- every product[].getIdentities
- cat_p_{productId}
- *if changed categories* - cat_p_c_{categoryId}
- *if changed status* - every category[] cat_p_c_{categoryId}
- *if frontend* - 'cat_p'
- cat_c_p_{categoryId}

block product/view:

- \Magento\Catalog\Model\Product::getIdentities:
- cat_p_{productId}
- *if changed categories* - cat_p_c_{categoryId}
- *if changed status* - every category[] cat_p_c_{categoryId}
- *if frontend* - 'cat_p'
- *if current_category* - cat_c_{categoryId}

### after reindex, must somehow clean cache

- any [Link] -- by MView


- any [Link]
- \Magento\Framework\Indexer\CacheContext::registerTags

plugin \Magento\Indexer\Model\Processor:

\Magento\Indexer\Model\Processor\CleanCache::afterUpdateMview

- event `clean_cache_after_reindex`
- clean cache cacheContext->getIdentities()

\Magento\Indexer\Model\Processor\CleanCache::afterReindexAllInvalid

- event `clean_cache_by_tags`
- clean cache cacheContext->getIdentities()

module-cache-invalidate observer `clean_cache_after_reindex`


\Magento\CacheInvalidate\Observer\InvalidateVarnishObserver::execute
\Magento\CacheInvalidate\Model\PurgeCache::sendPurgeRequest

### Describe how to operate with cache clearing.

How would you clean the cache? In which case would you refresh cache/flash cache storage?

### Describe how to clear the cache programmatically.

What mechanisms are available for clearing all or part of the cache?
# Utilize modes and application initialization

## Identify the steps for application initialization.


[app/[Link]]([Link]

- composer autoloader, functions, umask, timezone UTC, php precision

[\Magento\Framework\App\Bootstrap]([Link]
internal/Magento/Framework/App/[Link])::*create*
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 35 of 128

- configure autoloader - PSR-4 prepend generation\Magento

[\Magento\Framework\App\Bootstrap]([Link]
internal/Magento/Framework/App/[Link])::*createApplication*

- just call object manager->create

[\Magento\Framework\App\Bootstrap]([Link]
internal/Magento/Framework/App/[Link])::*run*

- set error handler


- assert maintenance
- assert installed
- response = application->*launch*()
- response->*sendResponse*()
- on error: application->catchException
- on missed error:
- dev mode: print exception trace
- normal mode: log, message "An error has happened during application run. See
exception log for details."

### Application class

[bootstrap->createApplication()]([Link]
internal/Magento/Framework/App/[Link]#L230)

- [\Magento\Framework\App\Http]([Link]
internal/Magento/Framework/App/[Link]) - [Link], pub/[Link]
load config area by front name
front controller->dispatch
event `controller_front_send_response_before`

- [\Magento\Framework\App\Cron]([Link]
internal/Magento/Framework/App/[Link]) - pub/[Link]
config area `crontab`
load translations
dispatch event `default`

- [\Magento\MediaStorage\App\Media]([Link]
code/Magento/MediaStorage/App/[Link]) - pub/[Link]
access /media/* when using DB image storage and physical file doesn't exist

- [\Magento\Framework\App\StaticResource]([Link]
lib/internal/Magento/Framework/App/[Link]) - pub/[Link]
404 in production
/$area/$resource/$file/... params, load config by params
sends file in response
assetRepo->createAsset - \Magento\Framework\View\Asset\File
assetPublisher->publish - materialize (copy/symlink) file if doesn't exist

- [\Magento\Indexer\App\Indexer]([Link]
Magento/Indexer/App/[Link]) - module-indexer, unused?
- [\Magento\Backend\App\UserConfig]([Link]
code/Magento/Backend/App/[Link]) - module-backend, unused?

Notes:

- Responsibility - launch() and return response


- Roughly different application class matches different physical file entry points
([Link], [Link], [Link], [Link], [Link])
- Front controller exists only in Http application
- There's no CLI application, Symfony command is used

### HTTP application


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 36 of 128

[\Magento\Framework\App\Http::launch]([Link]
internal/Magento/Framework/App/[Link]#L128)
1. detect config area by front name

```php
<?php
$areaCode = $this->_areaList->getCodeByFrontName($this->_request->getFrontName());
$this->_state->setAreaCode($areaCode);
$this->_objectManager->configure($this->_configLoader->load($areaCode));
```

`\Magento\Framework\App\AreaList` - areas from argument [Link] ([AreaList](https://


[Link]/magento/magento2/blob/2.3/lib/internal/Magento/Framework/App/[Link]))

- frontend = [frontname null, router "standard"] --- *default when nothing matched*
- adminhtml - [frontNameResolver=..., router "admin"]
[\Magento\Backend\App\Area\FrontNameResolver::getFrontName(checkhost)](https://
[Link]/magento/magento2/blob/2.3/app/code/Magento/Backend/App/Area/
[Link]#L83)
system config `admin/url/use_custom`, `admin/url/custom`
- crontab = null
- webapi_rest = [frontName `/rest`]
- webapi_soap = [frontname `/soap`]

1. [ObjectManagerInterface->configure()]([Link]
internal/Magento/Framework/ObjectManager/[Link]#L82) - selected area code
1. result = FrontControllerInterface->dispatch()
1. [ResultInterface->renderResult()]([Link]
internal/Magento/Framework/Controller/[Link]#L122) into response object
1. event `controller_front_send_response_before` (request, response)

### How would you design a customization that should act on every request and capture
output data regardless of the controller?
- event `controller_front_send_response_before`

## Describe front controller responsibilities

*Front controller* exists only in Http application (pub/[Link])

- Same entry point, how to execute different logic?


Via different DI preference depending on detected config area (areaList-
>getCodeByFrontName)
- *Default* global preference app/etc/[Link] - Magento\Framework\App\FrontController
- "frontend", "adminhtml", "crontab" area code - no preference, use default
*App\FrontController*
- "webapi_rest (frontName `/rest`) - preference module-webapi/etc/webapi_rest/[Link] -
*\Magento\Webapi\Controller\Rest*
- "webapi_soap" (frontname `/soap`) - preference module-webapi/etc/webapi_soap/[Link] -
*\Magento\Webapi\Controller\Soap*

### [\Magento\Framework\App\FrontController]([Link]
lib/internal/Magento/Framework/App/[Link]):

- routerList
- action = router[].match
- result = [Link]() or [Link]()
- noroute action fallback

### Router match - action can be:

- generic \Magento\Framework\App\ActionInterface::execute - not used?


- [\Magento\Framework\App\Action\AbstractAction]([Link]
2.3/lib/internal/Magento/Framework/App/Action/[Link])::dispatch - context,
request, response, result factory, result redirect factory
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 37 of 128

### Dispatch/execute action - result can be:

- [\Magento\Framework\Controller\ResultInterface]([Link]
blob/2.3/lib/internal/Magento/Framework/Controller/[Link]) - renderResult,
setHttpResponseCode, setHeader

Implementations:
- [Result\Raw]([Link]
Framework/Controller/Result/[Link]) -> Result\AbstractResult
- [Result\Json]([Link]
Framework/Controller/Result/[Link]) -> Result\AbstractResult
- [Result\Forward]([Link]
Framework/Controller/Result/[Link]) -> Result\AbstractResult
- [\Magento\Framework\View\Result\Layout]([Link]
2.3/lib/internal/Magento/Framework/View/Result/[Link]) -> Result\AbstractResult
- [\Magento\Framework\View\Result\Page]([Link]
lib/internal/Magento/Framework/View/Result/[Link]) ->
\Magento\Framework\View\Result\Layout
- [Result\Redirect]([Link]
Framework/Controller/Result/[Link]) -> Result\AbstractResult

- [\Magento\Framework\App\ResponseInterface]([Link]
lib/internal//Magento/Framework/App/[Link]) - sendResponse

Implementations:
- [Console\Response]([Link]
Magento/Framework/App/Console/[Link])
- [\Magento\MediaStorage\Model\File\Storage\FileInterface]([Link]
magento2/blob/2.3/app/code/Magento/MediaStorage/Model/File/Storage/[Link]) ->
\Magento\Framework\App\Response\Http
- [\Magento\Framework\HTTP\PhpEnvironment\Response]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/HTTP/PhpEnvironment/[Link]) ->
\Zend\Http\PhpEnvironment\Response
- [\Magento\Framework\Webapi\Response]([Link]
lib/internal/Magento/Framework/Webapi/[Link]) ->
\Magento\Framework\HTTP\PhpEnvironment\Response
- [\Magento\Framework\Webapi\Rest\Response]([Link]
2.3/lib/internal/Magento/Framework/Webapi/Rest/[Link]) ->
\Magento\Framework\Webapi\Response

### [\Magento\Webapi\Controller\Rest]([Link]
code/Magento/Webapi/Controller/[Link]) ->
\Magento\Framework\App\FrontControllerInterface:

- preference for FrontController set in [etc/webapi_rest/[Link]]([Link]


magento/magento2/blob/2.3/app/code/Magento/Webapi/etc/webapi_rest/[Link]#L32)
- process path [/$store]/... - specific store, [/all]/... - admin store (0), /... -
default store
- a. process schema request /schema
- b. or process api request (resolve route, invoke route -> service class with params)

### [\Magento\Webapi\Controller\Soap]([Link]
code/Magento/Webapi/Controller/[Link]) ->
\Magento\Framework\App\FrontControllerInterface:

- process path (same as REST)


- a. generate WSDL ?wsdl
- b. or generate WSDL service list ?wsdl_list
- c. or handle SOAP (native PHP)

### In which situations will the front controller be involved in execution, and how can
it be used in the scope of customizations?

- Front controller is only used in HTTP application - pub/[Link]


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 38 of 128

- involved in `frontend` and `adminhtml` (HTTP\App\FrontController), `webapi_rest`


(Controller\Rest), `webapi_soap` (Controller\Soap) areas
- HTTP customization - register router and place custom code in match() method
# Demonstrate ability to process URLs in Magento
## Describe how Magento processes a given URL.

urlBuilder - [\Magento\Framework\UrlInterface]([Link]
2.3/lib/internal/Magento/Framework/[Link]): getUrl

Instances:

- [\Magento\Framework\Url]([Link]
Magento/Framework/[Link])
- [\Magento\Backend\Model\Url]([Link]
Magento/Backend/Model/[Link])

1. preprocess route params

\Magento\Framework\Url\*RouteParamsPreprocessorInterface*::execute
- composite - delegates to many instances
- staging - Preview\RouteParamsPreprocessor
* if frontend and preview, adds `?___version`
* if store code in URL, adds `?___store`

1. Disable caching if object found in route params


1. *createUrl* (may be cached in memory)

- stash all parameters `_fragment`, `_escape`, `_escape_params`, `_query`, `_nosid`


- \Magento\Framework\Url::*getRouteUrl* - without any system params
- then compile ?query and #fragment params

1. run modifier

\Magento\Framework\Url\ModifierInterface::execute - mode entire/base


- composite - delegates to many instances
- staging - Url\BaseUrlModifier - only base mode
In frontend and preview, replaces host part with `$_SERVER[HTTP_HOST]`

*getRouterUrl*:

- `_direct` option = baseUrl + `_direct`


- $routeName/$controllerName/$actionName/$param1/$value1/...

### How do you identify which module and controller corresponds to a given URL?

- first part is route name. Search [Link] for route with matching *ID*
- modules for this ID are sorted with "before" and "after"
- controller/action classes is searched in matched modules

### What is necessary to create a custom URL structure?

- register custom router, e.g. [Magento\Robots\Controller\Router]([Link]


magento/magento2/blob/2.3/app/code/Magento/Robots/Controller/[Link])
- create rewrite record for specific URL

## Describe the URL rewrite process and its role in creating user-friendly URLs.

Router `urlrewrite `:
- `?____from_store` param, redirect to new URL if necessary.

Example:
- on English store category page /shoes switching to Norwegian store
- _/no/shoes?____from_store=1_
- find new rewrite for norwegian store
- 302 redirect to _/no/sko_
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 39 of 128

- find rewrite by request path


- redirect if necessary
- return forward action - mark request not dispatched, force continue router loop

### How is getUrl('catalog/product/view/id/1') replaced with rewrite?

- Product->getProductUrl
- Product\Url->getProductUrl
- Product\Url->getUrl
- UrlFinderInterface->findOneByData
- new Url->getUrl -- `_direct` if rewrite found = baseUrl . requestPath

Rewrite is not used with regular getUrl, only when module uses explicitly (catalog, CMS).

### CMS URL rewrite

- On event `cms_page_save_after`, if identifier or store changed, deletes and creates new


rewrites.
- Doesn't create redirect rewrite for renamed redirects.
- CMS page opens with UrlRewrite router (priority 20), not CMS router (priority 60).

### How are user-friendly URLs established, and how are they customized?

Module UrlRewrite:
- \Magento\UrlRewrite\Model\UrlPersistInterface::deleteByData
- \Magento\UrlRewrite\Model\UrlPersistInterface::replace

Product:

- event `catalog_product_save_before` - generate URL key by product name (if url key
wasn't provided)
* [ProductUrlKeyAutogeneratorObserver]([Link]
code/Magento/CatalogUrlRewrite/Observer/[Link])
* [\Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::getUrlKey](https://
[Link]/magento/magento2/blob/2.3/app/code/Magento/CatalogUrlRewrite/Model/
[Link]#L125)

- event `catalog_product_save_after` - generate and replace URL rewrites (when changed


url_key, categories, websites or visibility)
* [ProductProcessUrlRewriteSavingObserver]([Link]
app/code/Magento/CatalogUrlRewrite/Observer/[Link])
* [\Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::generate](https://
[Link]/magento/magento2/blob/2.3/app/code/Magento/CatalogUrlRewrite/Model/
[Link]#L128)
* deleteByData, replace

Category:

- event `catalog_category_save_before` - generate URL key, update child categories


* [CategoryUrlPathAutogeneratorObserver]([Link]
app/code/Magento/CatalogUrlRewrite/Observer/[Link])
*
\Magento\CatalogUrlRewrite\Observer\CategoryUrlPathAutogeneratorObserver::updateUrlPathForChildren
*
\Magento\CatalogUrlRewrite\Observer\CategoryUrlPathAutogeneratorObserver::updateUrlPathForCategory
* \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator::getUrlPath
* child category.url_path

- event `catalog_category_save_after` - when changed (key, anchor, products)


* [CategoryProcessUrlRewriteSavingObserver]([Link]
2.3/app/code/Magento/CatalogUrlRewrite/Observer/
[Link]#L90)
* [\Magento\CatalogUrlRewrite\Observer\UrlRewriteHandler::generateProductUrlRewrites]
([Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 40 of 128

Observer/[Link]#L124)
* ... lots of logic

## Describe how action controllers and results function.

[App\Action\Action::dispatch]([Link]
Magento/Framework/App/Action/[Link]#L91):

- event `controller_action_predispatch`
- event `controller_action_predispatch_$routeName`, e.g. `..._checkout`
- event `controller_action_predispatch_$fullActionName`, e.g. `..._checkout_cart_index`
- stop if FLAG_NO_DISPATCH
- *execute* - all action controllers implement this
- stop if FLAG_NO_POST_DISPATCH
- event `controller_action_postdispatch_$fullActionName`
- event `controller_action_postdispatch_$routeName`
- event `controller_action_postdispatch`
- if action doesn't return result, response object is returned -- action can just modify
response object

### How do controllers interact with another?

- Controller\Response\Forward - changes request params, marks request not dispatched, so


front controller
will match again and new controller will be executed
- Controller\Response\Redirect - another controller URL

### How are different response types generated?


[\Magento\Framework\Controller\ResultInterface]([Link]
2.3/lib/internal/Magento/Framework/Controller/[Link]):
- renderResult
- setHttpResponseCode
- setHeader

[Controller\AbstractResult]([Link]
Magento/Framework/Controller/[Link]):

- *renderResult* - required by interface - applies headers and calls *render*. children


must implement this
- setHttpResponseCode
- setHeader
- setStatusHeader

[Controller\Result\Raw]([Link]
Framework/Controller/Result/[Link]):

- setContents
- *render* - set response body

[Controller\Result\Json]([Link]
Magento/Framework/Controller/Result/[Link]):

- setData - array
- setJsonData - string
- *render* - processes inline translations, sets application/json header and response
body json string

[Controller\Result\Forward]([Link]
Magento/Framework/Controller/Result/[Link]):

- setModule, setController, setParams


- *forward* - does the trick, modifies request object, marks request not dispatched
- *render* - does nothing, forward must be called manually
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 41 of 128

[Controller\Result\Redirect]([Link]
Magento/Framework/Controller/Result/[Link]):

- setUrl, setPath - custom address


- setRefererUrl, setRefererOrBaseUrl - go back function

[View\Result\Layout]([Link]
Framework/View/Result/[Link]): - renders layout without `default` handle and page
layout (1-column etc.)

- *renderResult*
* event `layout_render_before`
* event `layout_render_before_$fullActionName`, e.g. `..._checkout_cart_index`
* render

- *render* - layout->getOutput, translate inline, set response body


- addDefaultHandle = $fullActionName, e.g. `checkout_cart_index`
- addHandle, addUpdate

[View\Result\Page]([Link]
Framework/View/Result/[Link]): - wraps layout into page layout
- same events as above
- *render* - renders layout, assigns vars and renders outer page template
- assign - values into viewVars property. default
Default: requireJs, headContent, headAdditional, htmlAttributes, headAttributes,
bodyAttributes, loaderIcon, layoutContent
- addDefaultHandle = $fullActionName + `default`
# 2.3 Demonstrate ability to customize request routing

## Describe request routing and flow in Magento.

[Frontend routers]([Link]
[Link]):

- robots (10)
- urlrewrite (20)
- standard (30) - module/controller/action
* get modules by front name registered in [Link]
* find action class
* if action not found in all modules, searches "noroute" action in *last* module

- cms (60)
- default (100) - noRouteActionList - 'default' noroute handler

Adminhtml routers:

- admin (10) - extends base router - module/controller/action, controller path prefix


"adminhtml"
- default (100) - noRouteActionList - 'backend' noroute handler

Default router (frontend and backend):

- noRouteHandlerList. process
+ backend (10)
*Default admin 404 page* "adminhtml/noroute/index" when requested path starts with
admin route.

+ default (100)
*Default frontend 404 page* "cms/noroute/index" - admin config option `web/default/
no_route`.

- always returns forward action - just mark request not dispatched - this will continue
match loop

### When is it necessary to create a new router or to customize existing routers?


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 42 of 128

Create new router when URL structure doesn't fit into module/controller/action template,
e.g. fixed [Link] or dynamic arbitraty rewrites from DB.

If you want to replace controller action, you can register custom module in controller
lookup sequence -
reference route by ID and add own module "before" original module.

### How do you handle custom 404 pages?

1. If [front controller]([Link]
Magento/Framework/App/[Link]#L61-L65) catches
[\Magento\Framework\Exception\NotFoundException]([Link]
2.3/lib/internal/Magento/Framework/Exception/[Link]), it changes action
name *"noroute"* and continues loop.
E.g. catalog/product/view/id/1 throws NotFoundException. catalog/product/noroute is
checked.

1. If standard router recognizes front name but can't find controller, it tries to find
*"noroute"*
action from last checked module.
E.g. catalog/brand/info controller doesn't exist, so catalog/brand/noroute will be
checked.

[\Magento\Framework\App\Router\Base::getNotFoundAction]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/App/Router/[Link]#L237)

1. If all routers didn't match, default controller provides two opportunities:


- set default 404 route in admin config `web/default/no_route` (see:
[\Magento\Framework\App\Router\NoRouteHandler::process]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/App/Router/[Link]#L34))
- register custom handler in [noRouteHandlerList]([Link]
blob/2.3/lib/internal/Magento/Framework/App/Router/[Link]):
* backend (sortOrder: 10) [Magento\Backend\App\Router\NoRouteHandler](https://
[Link]/magento/magento2/blob/2.3/app/code/Magento/Backend/App/Router/
[Link]#L44) -> `adminhtml/noroute/index`
* default (sortOrder: 100) [Magento\Framework\App\Router\NoRouteHandler](https://
[Link]/magento/magento2/blob/2.3/lib/internal/Magento/Framework/App/Router/
[Link])
# Determine the layout initialization process

## Determine how layout is compiled.

View\Layout::*build* = View\Layout\Builder::*build* - once

1. loadLayoutUpdates - reads layout xml files and DB updates by *current handles*, result
in `_updates` array

* event `layout_load_before`

1. generateLayoutXml - joins `_updates` into XML string, loads XML object, initiailzes
`_elements` = []

* [Link]
* no events

1. generateLayoutBlocks - [Link]

* event `layout_generate_blocks_before`
* readerPool->interpret - every reader checks for matching xml node name (attribute,
body, head etc.), prepares page structure
* generatorPool->process
* add top-level element as outputElement. default "root"
* event `layout_generate_blocks_after`

```
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 43 of 128

[Link] -> [Link]


readerPool.*interpret* -- schedules
nodeReaders[type].interpret each element -- Layout\ReaderInterface
- html, move
- body - own [Link], but without 'body' reader
- head - css,script,link,remove,meta,title,attribute
- 'container', 'referenceContainer' --> Layout\Reader\Container
- 'block', 'referenceBlock' --> Layout\Reader\Block
- uiComponent
View\Layout\ReaderInterface::interpret -- html, body, head, ui component, reader pool,
container, move, block

View\Layout\[Link] -- generates blocks


buildStructure
generator[].*process*
- head, body
- block - creates blocks, sets layout, event `core_layout_block_create_after`, block
'actions'
- container - sets tag, id, class, label, display
- uiComponent - creates wrapper element, prepareComponent recursively etc.

getOutput
- renderElement(root)
- renderNonCachedElement(root) -- hardcoded switch
- is uiComponent -> toHtml
- is block -> toHtml
- is container -> renderElement(child[])
- event `core_layout_render_element`
```

### How would you debug your [Link] files and verify that the right layout
instructions are used?

When XML object is created from string XML updates, this is a good place to examine
resuls.
View\Layout\[Link] or View\[Link] is a good place to dump
structure.

## Determine how HTML output is rendered.

First external code calls [Link](name) to register top level elements


for output generation.
When layout.*getOutput* is called, it renders each outputElement.

- View\Layout::getOutput
- for every registered `_output` element
- View\Layout::renderElement

When layout is build initially, it finds top level container and registers it as
addOutputElement - default "root".

### How does Magento flush output, and what mechanisms exist to access and customize
output?

Render response:

- action controller returns ResponseInterface object, e.g. View\Response\Page


- front controller returns this response
- App\[Link] application renders result, calling [Link]
- controller response renders contents and assigns to [Link]
- App\Http event `controller_front_send_response_before` allows to modify response before
returning
- App\Bootstrap object gets response object from App\Http
- [Link] - Zend implementation - send headers, send content

Customize:
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 44 of 128

1. modify any response in event `controller_front_send_response_before`


1. View\Element\AbstractBlock::toHtml event `view_block_abstract_to_html_after`

## Determine module layout XML schema.

Module layouts:

- module/view/base/layout/
- module/view/frontend/layout/
- module/view/adminhtml/layout/

### layout_generic.xsd

Generic layout - useful for returning Ajax response. Doesn't have body, head, css etc.,
only pure structure:

```xml
<layout xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/
layout_generic.xsd">
<container
name="" as="" after="" before="" label=""
output=""
htmlTag="" htmlClass="" htmlId=""
/>
<referenceContainer
name="" label=""
htmlTag="" htmlClass="" htmlId=""
display="true/false"
remove="true/false"
/>
<block
class="" name="" as="" template="" before="" after="" group=""
acl="" aclResource="" ifconfig=""
output="" cacheable="bool" ttl="int"
/>
<referenceBlock name="" template="" display="" remove="" />
<update handle="name" />
<move element="" destination="" as="" after="" before="" />
<uiComponent
component="" name="" as="" before="" after="" group=""
aclResource="" ifconfig=""
output="" cacheable="" ttl=""
/>
</config>
```

### page_configuration.xsd

Same as layout plus:

- top level layout selection


- structure is wrapped in body node
- additional nodes - html attribute, head title, attribute and scripts

```xml
<page
layout="1column"
label=""
design_abstraction="custom / page_layout"

xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/
page_configuration.xsd"
>
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 45 of 128

<html>
<attribute name="" value="" />
</html>
<head>
<title>Title</title>
<css src defer ie_condition charset hreflang media rel rev sizes target type
src_type />
<link src defer ie_condition charset hreflang media rel rev sizes target type
src_type />
<meta content charset http-equiv name scheme />
<script src defer ie_condition async charset type src_type />
<remove src="" />
<attribute name="" value="" />
</head>
<update handle="" />
<body>
<attribute name="" value="" />
<!-- container/block structure same as in generic layout -->
</body>
</page>
```

### [Link] - declare available page layouts

- <module_dir>/view/<area_code>/[Link]
- <theme_dir>/<Namespace>_<Module>/[Link]

- base:
* empty

- frontend:
* 1column - extends empty
* 2columns-left - extends 1column
* 2columns-right - same as 2columns-left
* 3columns - same as 2columns-left

- adminhtml:
* admin-empty
* admin-1column
* admin-2columns-left

### page_layout.xsd

Only containers

```xml
<layout xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_layout.xsd">
<referenceContainer />
<container />
<update />
<move />
</layout>
```

### layout [Link]:

- [Link]
- [Link]
- View\Layout\[Link]:

* loadLayoutUpdates
* generateLayoutXml
* generateLayoutBlocks
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 46 of 128

- create result page - adds default handles


- page [Link]
- all normal layout XML is collected and is about to create blocks
- custom step before generating layout blocks

### page [Link]:


- View\Result\[Link]
- View\Result\Page::addDefaultHandle - `default`, `$fullActionName`

- View\Page\[Link] = build
- View\Page\[Link] - extends View\Layout\Builder, custom readPageLayout on step
generateLayoutBlocks

* (inherit) loadLayoutUpdates
* (inherit) generateLayoutXml
* generateLayoutBlocks - additionally *readPageLayout*

+ right before creating actual blocks, reads and merges page layout xml files
+ new instance of View\Model\Layout\Merge::load handles = '1column' -- uses
subDir 'page_layout'
+ *interprets* found page nodes - schedules blocks. page layout instructions
(root page template) are interpreted before others
+ original generateLayoutBlocks *interprets*, then runs *generators*

- check View\Page\[Link], e.g. "1column"


- adds default body classes - `$fullActionName`, `page-layout-$layout`
- renders block '[Link]', '[Link]'
- assigns vars 'requireJs', 'headContent', 'headAdditional', 'htmlAttributes',
'headAttributes', 'bodyAttributes', 'loaderIcon'
- [Link] -- already built
- renderPage

* [Link] defined via app/etc/[Link]


* include Magento_Theme::[Link]

### In your custom controller don't forget to add entity-specific IDs:

$page->addPageLayoutHandles(['id' => $category->getId()]);

- adds handle `$fullActionName_$param_$value` like `catalog_category_view_id_17`


- informs full page cache of *entity specific page*
\Magento\Framework\View\EntitySpecificHandlesList::addHandle

* \Magento\PageCache\Model\Layout\MergePlugin::beforeValidateUpdate - entity-specific
layout handles must not contain TTL

e.g. catalog_category_view_id_15.xml: <block ttl="3600"> ... will throw error

* \Magento\PageCache\Observer\ProcessLayoutRenderElement::_wrapEsi - exclude entity-


specific handles from ESI URL

View\Layout\[Link]

- loadLayoutUpdates
- generateLayoutXml
- generateLayoutBlocks

View\Page\[Link]

- extend generateLayoutBlocks
- readPageLayout
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 47 of 128

### File Collector

- View\File\CollectorInterface
- View\File\Collector\Decorator\ModuleDependency - Decorator that sorts view files
according to dependencies between modules they belong to
- View\File\Collector\Decorator\ModuleOutput - Decorator that filters out view files that
belong to modules, output of which is prohibited
- View\File\Collector\Base - Source of base files introduced by modules

Collector\Base - Source of base files introduced by modules

- view/base/, view/{$area}/

Collector\Override\Base - Source of view files that explicitly override base files


introduced by modules

- theme/{$namespace}_{$module}/

Collector\Theme - Source of view files introduced by a theme

- theme/

Collector\Override\ThemeModular - Source of view files that explicitly override modular


files of ancestor themes

- theme/{$namespace}_{$module}/{$themeVendor}/{$themeName}/

Collector\Library - Source of base layout files introduced by modules

- lib_web/
- each inherited theme[]: theme/web/

### How do you add new elements to the pages introduced by a given module?

```
<block class="module\block\class" template="new module template" ... />
```

## Demonstrate the ability to use layout fallback for customizations and debugging.

- View\Element\Template::getTemplateFile
- View\Element\Template\File\Resolver::getTemplateFileName(template, [module, area]) --
just caches in memory
- View\FileSystem::getTemplateFileName -- detects module by Module_Name::[Link],
adds default params
* View\Asset\Repository::updateDesignParams - adds default missing params [object
themeModel, locale]

- View\Design\FileResolution\Fallback\[Link](area, ) -- minify html


- View\Design\FileResolution\Fallback\[Link] -- calls resolver type='file'
- View\Design\FileResolution\Fallback\Resolver\[Link]
- View\Design\Fallback\[Link] - by type: file, locale file, template file

*File fallback rule*:


- when missing 'module_name':
* <theme_dir>
- when set 'module_name':
* <theme_dir>/<module_name>/templates
* <module_dir>/view/<area_code>/templates
* <module_dir>/view/base/templates

- View\Design\FileResolution\Fallback\Resolver\Simple::resolveFile(fileRule)
- search file in each directory

### How do you identify which exact [Link] file is processed in a given scope?
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 48 of 128

## How does Magento treat layout XML files with the same names in different modules?

They are merged in module sequence order. Additionally, modules marked as "disable module
output"
are skipped (though deprecated).

View\File\Collector\Decorator\ModuleDependency - Decorator that sorts


view files according to dependencies between modules they belong to

View\File\Collector\Decorator\ModuleOutput - Decorator that filters out


view files that belong to modules, output of which is prohibited

View\File\Collector\Base - Source of base files introduced by modules

## Identify the differences between admin and frontend scopes.

- customized layout builder and page builders - automatically init messages block
- custom parent \Magento\Backend\Block\AbstractBlock
* auto inject AuthorizationInterface

- custom parent \Magento\Backend\Block\Template:


* available some properties - authorization, mathRandom, backendSession, formKey, class
nameBuilder
* event `adminhtml_block_html_before` - fired only when non-cached render.
To compare, event `view_block_abstract_to_html_after` fires even when block loaded
from cache

- customized "block" layout reader (runs interpret) -- reads "acl" block attribute
- customzied "block" layout generator - default block
"class=Magento\Backend\Block\Template"
- controller result object with additional methods:
* setActiveMenu
* addBreadcrumb
* addContent(block) - moves to 'content'
* addLeft(block)
* addJs
* moveBlockToContainer

### What differences exist for layout initialization for the admin scope?

*Custom layout and page builders* - automatically initializes message block from message
manager:
\Magento\Backend\Model\View\Layout\Builder and \Magento\Backend\Model\View\Page\Builder:
- afterGenerateBlock - called in the end of generateLayoutBlocks, the end of generation -
`layout->initMessages()`
- getBlock('messages') or create new one
- addMessages from Message\ManagerInterface
- addStorageType('default')
# Demonstrate ability to utilize themes and the template structure

app/design/frontend/Vendor/themename/
```
- [Link]
- [Link]
- [Link] - name, parent, logo
- etc/[Link] - currently catalog images configuration
- i18n/
- media/ - pub logo here
- Magento_Checkout/ - extend specific module
---- layout/ - normally layouts are extended
------- override/base/ - special case: completely replace base layout for module
------- override/theme/Magento/luma/ - special case: completely replace specific theme
layout
---- templates/ - replace module files
---- web/ - replace module files
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 49 of 128

- web/ - replace theme files


---- js/
---- css/
------- source/
---- fonts/
---- images/
---- i18n/en_US/ - locale-specific file replacement (without module)
---- i18n/en_US/Magento_Checkout/ - locale-specific module file replacement
```

- Magento\Theme\Model\View\Design implements View\DesignInterface


- View\Model\Layout\Merge implements View\Layout\ProcessorInterface

## Layouts:

Normally layouts are merged, extending parent ones.

To completely replace *original module layout* file, e.g. to replace


vendor/magento/module-checkout/view/frontend/layout/[Link] place new file in
Magento_Checkout/layout/override/base/[Link]

To completely replace some *parent theme's layout* customization, e.g. to replace


vendor/magento/theme-frontend-luma/Magento_Checkout/layout/[Link] place new file in
Magento_Checkout/layout/override/Magento/luma/[Link]

It is *not possible* to use override trick inside *module*, only in theme. You cannot
create
new module app/code/MyVendor/MyModule/view/frontend/layout/override/... and replace
layout files.

## Locale:

You can replace static files with locale-specific - JS, logo, font etc. You have 2 places:
- non-module specific, e.g. theme asset - web/i18n/en_US/
- from some module - web/i18n/en_US/Magento_Checkout/

### how is theme loaded


- plugin magento-store/etc/[Link]
- App\Action\[Link]
- View\DesignLoader::load
- App\Area::load('design')
- App\Area::_initDesign
* singleton View\DesignInterface = \Magento\Theme\Model\View\Design
* [Link]('frontend')
* [Link]
* [Link]
+ get config `design/theme/theme_id` (website scope if single store mode, otherwise
store view scope)
+ EE only: if theme is not configured, use default from DI argument Magento/luma
* [Link] - area, theme model. loads theme model from DB
+ View\Design\Theme\FlyweightFactory::create
+ View\Design\Theme\ThemeProviderInterface::getThemeByFullPath
Has 2 implementations, real DB and fake data collection - Can be used to deploy
static or
in other setup commands, even if Magento is not installed yet.

singleton *View\DesignInterface* = \Magento\Theme\Model\View\Design - holds current area,


locale and theme model
- get/setDesignTheme = theme
- get/setArea
- getLocale
- getConfigurationDesignTheme
- setDefaultDesignTheme
- getThemePath
- getDesignParams - area, theme, locale
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 50 of 128

*View\Design\ThemeInterface* = \Magento\Theme\Model\Theme - this is also DB model


- getArea
- getCode
- getParentTheme
- getInheritedThemes
- getId
- getThemePath
- getFullPath
- isPhysical

*Theme type*: physical, virtual, staging

_Physical themes_

Physical refers to the fact that those themes are defined by files.
For example, the blank and luma theme are physically defined under app/design/frontend/

_Virtual themes_

This is yet unclear but I think virtual themes refer to themes you can create in the
backend which extends existing physical themes but it seems like it's not fully
implemented yet.
You can see that there's two tabs available in the edit section only for virtual themes
which let
you provides custom CSS and JS for a virtual theme.
I reckon a virtual theme would be something you setup temporarily (like Christmas theme)
for a short
period of theme and which requires only few design changes compared to the physical theme
it extends.

_Staging_

Must be something about EE campaign.

### how loadLayoutUpdates works

- Loads ALL layout update files using file name as layout handle name - as XML object.
- Selects only currently relevant handles (e.g. 'default', 'checkout_cart_index').
- Finds <update handle=name> instructions, merges them recursively.
- Selected file contents as added to `_updates[]` array as pieces of string - they will
be converted to XML object later in generateLayoutXml.

Trace:

View\Layout\Builder::loadLayoutUpdates
* event `layout_load_before`
* View\Model\Layout\Merge::load
+ Collects loaded layout files contents into `updates[]` array.
+ Loads from cache when possible, XML contents (e.g. '<block
name="someBlock" ....><referenceBlock....>')
and separately pageLayout (e.g. '1column')

[Link](handle)
- merge._merge(handle)

* Called for every page handle, e.g. 'default', 'checkout_cart_index'.


* Checks cyclic dependency - logs error in dev mode
* _fetchPackageLayoutUpdates
* _fetchDbLayoutUpdates

- merge._validateMergedLayout
* View\Model\Layout\Update\Validator::isValid with `urn:magento:framework:View/Layout/
etc/layout_merged.xsd`
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 51 of 128

*_fetchPackageLayoutUpdates*
* [Link] - loads ALL layouts regardless of handle
* merge._loadFileLayoutUpdatesXml
- get physical theme, checking parents until found
- update files = theme file [Link] + page layout file [Link]
- read files one by one
- merge._substitutePlaceholders - replaces in raw XML string - {{baseUrl}} and
{{baseSecureUrl}}
- adds file contents to result as one of:

```xml
<layout id="{$fileName}" {$attributes}>{$fileContents}</layout>
<handle id="{$fileName}" {$attributes}>{$fileContents}</handle>
```

- all are wrapped in outer tag

```xml
<layouts xmlns:xsi="[Link]
<layout id="default" {$attributes}>{$fileContents}</layout>
<handle id="checkout_cart_index" {$attributes}>{$fileContents}</handle>
<handle id="checkout_cart_index">{$fileContents}</handle>
...
</layouts>
```

* finds all <layout> and <handle> with ID *matching handle* that is being loaded
* merge._fetchRecursiveUpdates - finds xpath <update handle="any">, calls _merge
recursively
* [Link]
plugin \Magento\PageCache\Model\Layout\MergePlugin::beforeValidateUpdate checks
that entity-specific
update handles don't have `ttl` declarations
* [Link] -- main goal, adds content to `updates[]`

*_fetchDbLayoutUpdates:*
* does nothing itself, but there's a plugin
\Magento\Widget\Model\ResourceModel\Layout\Plugin::aroundGetDbUpdateString - loads
updates from DB by theme/store
* merge._fetchRecursiveUpdates - finds xpath <update handle="any">, calls _merge
recursively
* [Link]
* [Link]

### Layout file source

merge.*fileSource*:
View\Layout\File\Collector\Aggregated\Proxy - Source of layout files aggregated from a
theme
and its parents according to merging and overriding conventions

- add *base files* - modules

* View\File\Collector\Decorator\ModuleDependency as layoutFileSourceBaseSorted
* View\File\Collector\Decorator\ModuleOutput as layoutFileSourceBaseFiltered
* *View\File\Collector\Base* as layoutFileSourceBaseFiltered, subDir = 'layout'
* Component\DirSearch::collect(type='module', pattern) - uses component registrar to
get modules, then searches by pattern

```
<modules>/view/base/layout/*.xml --- isBase = true, shared between adminhtml and
frontend themes
<modules/view/frontend/layout/*.xml
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 52 of 128

```

- get inherited themes

for each theme (e.g. Magento/blank, then Magento/luma):


- add theme files

* View\File\Collector\Decorator\ModuleDependency as layoutFileSourceThemeSorted
* View\File\Collector\Decorator\ModuleOutput as layoutFileSourceThemeFiltered
* *View\File\Collector\ThemeModular* as layoutFileSourceTheme, subDir = 'layout'

```
<theme>/*_*/layout/*.xml -- sets module context, e.g. Magento_Checkout
```

- replace override base files

* View\File\Collector\Decorator\ModuleDependency as layoutFileSourceOverrideBaseSorted
* View\File\Collector\Decorator\ModuleOutput as layoutFileSourceOverrideBaseFiltered
* *View\File\Collector\Override\Base* as layoutFileSourceOverrideBase, subDir = 'layout/
override/base'

```
<theme>/*_*/layout/override/base/*.xml
```

* View\File\FileList::replace
* View\File\FileList\Collator::collate - replaces by matching keys: is base, theme,
module, filename

- replace override theme files

* View\File\Collector\Decorator\ModuleDependency as layoutFileSourceOverrideThemeSorted
* View\File\Collector\Decorator\ModuleOutput as layoutFileSourceOverrideThemeFiltered
* *View\File\Collector\Override\ThemeModular* as layoutFileSourceOverrideTheme, subDir
= 'layout/override/theme'

```
<theme>*_*/layout/override/theme/*/*/*.xml
```

### Page layout file source

All same as layout, but subDir 'page_layout':

```
<modules>/view/base/page_layout/*.xml --- isBase = true, shared between adminhtml and
frontend themes
<modules/view/frontend/page_layout/*.xml
<theme>/*_*/page_layout/*.xml -- sets module context, e.g. Magento_Checkout
<theme>/*_*/page_layout/override/base/*.xml
<theme>*_*/page_layout/override/theme/*/*/*.xml
```

View\Layout\ProcessorInterface = View\Model\Layout\Merge
- $updates - array of all found string XML file contents E.g.
```
updates[] = '<body><block name="someBlock"...'
updates[] = '<referenceBlock name="someBlock">...'
```
- addUpdate(string) - string XML from file

## Demonstrate the ability to customize the Magento UI using themes.


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 53 of 128

### When would you create a new theme?

- create new theme from scratch without parent when design is very different from existing
- inherit new theme to add smaller customizations - move, hide, reorder elements, change
block arguments, html attributes
- new theme can be assigned to specific store view, for example for b2b store
- theme can apply dynamically based on browser user agent as exception - enter regexp in
_Content > Design > Implementation > [Edit] > Design Rule > User Agent Rules_
Full page cache and design exception:
```
plugin magento-store/etc/[Link]:
Magento\Framework\App\Action\[Link]:
\Magento\Framework\View\DesignLoader::load
\Magento\Framework\App\Area::load('design')
\Magento\Framework\App\Area::_initDesign
\Magento\Framework\App\Area::detectDesign
\Magento\Framework\App\Area::_applyUserAgentDesignException
\Magento\Framework\View\DesignExceptions::getThemeByRequest

plugin \Magento\PageCache\Model\App\CacheIdentifierPlugin::afterGetValue - checks


rule design exception
View\DesignExceptions::getThemeByRequest
```

- etc/[Link] file *doesn't merge* with parent themes, it either inherits completely
from parent
or your theme has new version of it

### How do you define theme hierarchy for your project?

[Link] - parent

Determine theme hierarchy of existing project:

- Go to _Content > Design > Configuration_


- Check "Theme Name" column on row with store view under question
- find paths to all available themes in app/etc/design/frontend/* or vendor/*/theme-*
(conventionally)
or find programmatically:
```php
(new \Magento\Framework\Component\ComponentRegistrar())->getPaths('theme');
```
- open theme path/[Link], check `<parent>` node and so on

## Demonstrate the ability to customize/debug templates using the template fallback


process.

### How do you identify which exact theme file is used in different situations?

### How can you override native files?

- theme layout override module file: <theme>/layout/override/base/*.xml


- theme layout override one of parent theme layouts: <theme>/layout/override/Magento/luma/
*.xml
- static asset for theme, e.g. <theme>/web/js/[Link]
- static asset for module, e.g. <theme>/Magento_Checkout/web/js/view/[Link]
- static asset for locale:
```
<theme>/web/i18n/en_US/Magento_Checkout/
<theme>/web/i18n/en_US/
```
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 54 of 128

## Demonstrate an understanding of block architecture and its use in development.

### [View\Element\AbstractBlock]([Link]
internal/Magento/Framework/View/Element/[Link]):
- data automatically assigns to _data, can access data arguments later, e.g. in _construct
- `jsLayout` data argument
- toHtml:
* event `view_block_abstract_to_html_before`
* disable module output still works
* if not in cache:
+ _beforeToHtml
+ _toHtml
+ save cache
* _afterToHtml - always, even when cached
* event `view_block_abstract_to_html_after`

### [View\Element\Template]([Link]
internal/Magento/Framework/View/Element/[Link]):
- `template` data argument
- `_viewVars` property, assign()
- `_toHtml` renders template when defined
* getTemplateFile = [View\Element\Template\File\Resolver]([Link]
magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Element/Template/File/
[Link])::getTemplateFileName
* fetchView
+ [View\Element\Template\File\Validator]([Link]
develop/lib/internal/Magento/Framework/View/Element/Template/File/[Link])::isValid
- checks allowed directory (view_preprocessed/module/theme) or symlink
+ if bad file, log and throw error in DEV mode
+ View\TemplateEnginePool::get by file extension
- phtml - [View\TemplateEngine\Php]([Link]
develop/lib/internal/Magento/Framework/View/TemplateEngine/[Link]) - extract vars,
include file
- xhtml - [View\TemplateEngine\Xhtml]([Link]
develop/lib/internal/Magento/Framework/View/TemplateEngine/[Link])
- \Magento\Developer\Model\TemplateEngine\Decorator\DebugHints
+ [Link](block, template, viewVars)
- default `getCacheKeyInfo` - store code, template, base URL. By default *store aware*
- helper methods:
* getMediaDirectory
* getRootDirectory
* getObjectData
* getBaseUrl

admin configuration `dev/template/allow_symlink`, default false

#### Which objects are accessible from the block?

- $this = \Magento\Framework\View\TemplateEngine\Php
- $this->something() is proxied to $block->something() via magic `__call` function - only
public available!
- isset($this->something) -> isset($block->something)
- $this->property = $block->property
- $this->helper() gets singleton of AbstractHelper

#### What is the typical block’s role?

As much business logic as possible should be moved out of template, and blocks provide
access to processed
data for templates. Actual data crunching can (and should) be proxied further to models.

## Identify the stages in the lifecycle of a block.

- block is generated
* [Link]
* [Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 55 of 128

* [Link] - View\Layout\Reader\Block::interpret - resolves params,


schedules structure element
+ `<action ifconfig method>`
+ `<arguments>`
+ attributes: class, group, template, ttl, display, acl
+ visibilityConditions: attribute `ifconfig`, attribuet `aclResource`, child
`<visibilityCondition name="" className=""><arguments.../></visibilityCondition>`
* [Link] - View\Layout\Generator\Block::process - actually creates block
+ generate all blocks
`__construct`, `_construct`
+ set layout to all blocks, event after each
`[Link]`, `block._prepareLayout`
event `core_layout_block_create_after`
+ call all actions

[Link]:
- [Link] -> `__construct()` -> `_construct` -> check argument 'template' -
> setTemplate
* [Link]
* [Link](classname)
* [Link]
* [Link]([Link])
- [Link] (when available)
- [Link] (when available)

### Arguments

Block arguments are treated like data-arguments. Arguments values set in a layout file
can be accessed in templates using the get{ArgumentName}() and has{ArgumentName}()
methods.

Arguments values set in a layout file can be accessed in templates using the
get{ArgumentName}()
and has{ArgumentName}() methods. The latter returns a boolean defining whether there's
any value set.
{ArgumentName} is obtained from the name attribute the following way: for getting the
value of
`<argument name="some_string">` the method name is getSomeString().

[Magento docs - Layout instructions]([Link]


guide/layouts/[Link]#argument)

### Block viewModel concept

Magento 2.2 suggests moving block business logic to separate viewModel classes.

Avoid extending this template class, because with inheriting the template block is the
constructor is very large.

If you need custom presentation logic in your blocks, use this class as block, and declare
custom view models in block arguments in layout handle file.

View model parameter can have any name.


Also, one block can have multiple view models injected via layout.

Example:

```xml
<block name="[Link]" class="Magento\Backend\Block\Template"
template="My_Module::[Link]" >
<arguments>
<argument name="viewModel" xsi:type="object">My\Module\ViewModel\Custom</
argument>
</arguments>
</block>
```
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 56 of 128

In template, access your model instead of the block:

```php
$viewModel = $block->getData('viewModel');
// or
$viewModel = $block->getViewModel();
```

[ViewModels in Magento 2]([Link]

### In what cases would you put your code in the _prepareLayout(), _beforeToHtml(), and
_toHtml() methods?

`_prepareLayout` - most commonly extended:

At this stage all blocks in layout are generated, but _prepareLayout is only running,
so some blocks might still be not initialized completely.

- set page config title

```php
$this->pageConfig->getTitle()->set(__('Address Book'));
```

- edit head block, e.g. add RSS


- add breadcrumbs
- create new block, set as child - e.g. grid

`_beforeToHtml`:
- can't change page title?
- some blocks are rendered, can't change them
- assign additional template values
- delay computation to the latest point until render. If block is not rendered we save
computation

`_toHtml`:
- block without template, put custom rendering here, e.g. calling external API
- suppress template output if should not render by condition - return empty string

#### How would you use events fired in the abstract block?

`view_block_abstract_to_html_before`:
- edit cache params - lifetime, ttl, tags
- add column to grid
- edit template params - e.g. set disable edit flag

`view_block_abstract_to_html_after`:
- edit html - replace, add content, add wrappers

## Describe how blocks are rendered and cached.

toHtml, load cache, [when not in cache: _beforeToHtml, _toHtml], _afterToHtml, save cache

- data attribute `cache_lifetime` must be set


- cache key one of:
* data attribute `cache_key` = BLOCK_`$cache_key`
* default getCacheKeyInfo: [name in layout]
* or BLOCK_`getCacheKeyInfo()` - by default name in layout. By default *same for all
stores*!
- cache tags for automatic invalidation:
* data attribute `cache_tags`
* + implicit tag 'block_html'
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 57 of 128

* if instanceof DataObject\IdentityInterface, `getIdentities`

## Identify the uses of different types of blocks.

- View\Element\AbstractBlock - custom logic


- View\Element\Template - any template
- View\Element\Text - set text programmatically
- etc.

### When would you use non-template block types?

Something like CMS page - admin controls block content and layout. Another example -
dynamic [Link]
whose content is stored in DB.

### In what situation should you use a template block or other block types?

Use template block whenever possible to allow for theme markup customization.
# Demonstrate ability to use layout and XML schema
## Describe the elements of the Magento layout XML schema, including the major XML
directives.

### Xsi:type niceties

*string*:

```xml
<argument xsi:type="string" translate="bool"></argument>
```

- true values: [true, 1, 'true', '1']


- false values: [false, 0, 'false', '0']

[*options*]([Link]
Framework/View/Layout/Argument/Interpreter/[Link]):

```xml
<argument name="options" xsi:type="options"
model="Magento\Tax\Model\TaxClass\Source\Customer"/>
```

- Model class instance of Data\OptionSourceInterface - toOptionArray.


- Result = [Link], slightly processed, optgroup will probably not work.

[*url*]([Link]
Framework/View/Layout/Argument/Interpreter/[Link]):

```xml
<argument xsi:type="url" path="">
<param name="_current">1</param>
</argument>
```
```xml
<argument name="configuration" xsi:type="array">
<item name="recently_viewed_product" xsi:type="array">
<item name="requestConfig" xsi:type="array">
<item name="syncUrl" xsi:type="url" path="catalog/product/
frontend_action_synchronize"/>
</item>
</item>
</argument>
```

[*helper*]([Link]
Framework/View/Layout/Argument/Interpreter/[Link]):
```xml
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 58 of 128

<action method="setTemplate">
<argument name="template" xsi:type="helper"
helper="Magento\Paypal\Helper\Hss::getReviewButtonTemplate">
<param name="name">Magento_Paypal::hss/review/[Link]</param>
</argument>
</action>
```

### Major directives:

*Node readers*:
- `<html>`
- `<head>`
- `<body>`
- `<move>`
- `<container>`
- `<block>`
- `<uiComponent>`

*Generators*:
- `<head>`
- `<body>`
- `<container>`
- `<block>`
- `<uiComponent>`

*Scheduled Structure* - View\Page\Config\Structure:


- `scheduledPaths[$blockName] = $parent1/$parent2/$blockName`
- `scheduledStructure[$blockName] = $row`
- `scheduledData[$blockName] = [attributes actions arguments]`
- `elementAttributes[element][]`
- `removeElementAttributes[element][attribute]`
- `assets[/original/link/src] = attributes`
- `removeAssets[/some/src/] = /some/src`
- `title = __('Value')`
- `metadata[name] = content`
- `bodyClasses[]`

### `<HTML>` node reader [View\Page\Config\Reader\Html]([Link]


magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Page/Config/Reader/[Link])

Html reader is used for collecting attributes of html in to the scheduled page structure.

```xml
<html>
<attribute name="html_attribute_name" value="html_attribute_value" />
</html>
```

*structure*:

`elementAttributes['html']['html_attribute_name'] = 'html_attribute_value'`

*render*:

- View\Result\Page::render
- `'htmlAttributes' => $this->pageConfigRenderer-
>renderElementAttributes($config::ELEMENT_TYPE_HTML),`
- View\Page\Config\Renderer::renderElementAttributes - join(' ', name-value pairs)
- [Link]
```html
<!doctype html>
<html <?= /* @escapeNotVerified */ $htmlAttributes ?>>
```
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 59 of 128

### `<HEAD>` node reader [View\Page\Config\Reader\Head]([Link]


magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Page/Config/Reader/[Link])

- `<css src="" ...attributes>`, `<script src="" ...attributes>`, `<link


src="" ...attributes>`
View\Page\Config\Structure::addAssets:
structure `assets['path/to/src'] = attributes`

- `<remove src="">`
[Link]:
structure `removeAssets['path/to/src'] = 'path/to/src'`

- `<title>`
[Link]
`title = __($title)`

- `<meta name="" content="">` or `<meta property="" content="">`


[Link]:
`metadata[name] = content`

- `<attribute name value />`


[Link]
`elementAttributes[head][attribute]=value`

render:
* View\Result\Page::render
* `'headAttributes' => $this->pageConfigRenderer-
>renderElementAttributes($config::ELEMENT_TYPE_HEAD),`
* [Link]: `<head <?= /* @escapeNotVerified */ $headAttributes ?>>` - key1=value1
key2=value2

### `<HEAD>` generator [View\Page\Config\Generator\Head]([Link]


magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Page/Config/Generator/
[Link])

- processRemoveAssets - structure.`assets` vs structure.`removeAssets`


- processRemoveElementAttributes - `elementAttributes` vs `removeElementAttributes`
- processAssets - `assets`
* add *remote* assets, attribute src_type='controller' or src_type='url'
- example `<css src="" src_type="controller" content_type="link" />`
- Page\Config::addRemotePageAsset
* add normal assets View\Page\Config::addPageAsset
- processTitle - copy from structure to page config
- processMetadata - copy
- processElementAttributes - copy: html, head etc.

### `<BODY>` node reader [View\Page\Config\Reader\Body]([Link]


magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Page/Config/Reader/[Link])

```xml
<body>
<attribute name="id" value="html-body"/>
<attribute name="class" value="preview-window"/>
</body>
```

- "class" attributes - structure.`bodyClasses[] = class-name`


tip: empty class clears all
- other attributes - structure.`elementAttributes[body][attribute-name] = value`
* View\Result\Render `'bodyAttributes' => $this->pageConfigRenderer-
>renderElementAttributes($config::ELEMENT_TYPE_BODY),`
* [Link] `<body ... <?= /* @escapeNotVerified */ $bodyAttributes ?>>`
- calls readerPool to process `<body>` contents recursively

### `<BODY>` generator [View\Page\Config\Generator\Body]([Link]


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 60 of 128

magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Page/Config/Generator/
[Link])

Copies structure.`bodyClasses[]` to View\Page\Config::addBodyClass


- adds to pageConfig.`elements[body][class] = '...'`

### `<MOVE>` node reader [View\Layout\Reader\Move]([Link]


blob/2.2-develop/lib/internal/Magento/Framework/View/Layout/Reader/[Link])

```xml
<move element='' destination='' as='' (after=''|before='') />
```

structure.`scheduledMoves[elementName] = [destination, siblingName, isAfter, alias]`

All moves are processed in View\Layout\GeneratorPool before all other generators:


- [Link]
* scheduleElement - creates array structure `_elements`
- `_elements[parentId]['children'][childId] = alias`
- `_elements[childId]['parent'][parentId] = alias`
- bad relations `brokenParent[elementId] = 1`
* reorderElements
* moveElementInStructure -- here
* removeElement - broken parents
* remove elements when 'visibilityConditions' doesn't match

### `<CONTAINER>`, `<REFERENCECONTAINER>` node reader [View\Layout\Reader\Container]


([Link]
Layout/Reader/[Link])

- `<container>`:
* `<container name as before/after>` - schedule structure
structure.`scheduledStructure[name] = [container alias parent sibling is_after]`
* [Link]
structure.`scheduledData[name][attributes] = [tag id class label display]`

- `<referenceContainer name remove ...attributes>`


* possible to un-remove
* merges normal container attributes

- calls readerPool to process contents recursively, just like `<body>`

### `<BLOCK>` node reader [View\Layout\Reader\Block]([Link]


blob/2.2-develop/lib/internal/Magento/Framework/View/Layout/Reader/[Link])

- schedules structure View\Layout\ScheduledStructure\Helper::scheduleStructure


* generates name if needed - like `{$parent_name}_schedule_block1`
* [type='block', alias='$as', parent_name='$parent', sibling_name='$beforeAfterName',
is_afer=true/false]
* `scheduledPaths[$blockName] = $parent1/$parent2/$blockName`
* `scheduledStructure[$blockName] = $row`
- data['attributes'] - [class, group, template, ttl, display, acl]
- data['actions'][] = [method, arguments, ifconfig, scope=STORE]
- data['arguments'] = parse `<arguments>`:
* `<argument name="$name">...</argument>`
View\Layout\Argument\Parser::parse
Config\Converter\Dom\Flat::convert

```xml
<arguments>
<argument>Text node</argument>
<argument>
<item>
<item>...parsed recursively</item>
</item>
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 61 of 128

</argument>
</arguments>
```

- [Link]
* Data\Argument\InterpreterInterface::evaluate
* Data\Argument\Interpreter\Composite as layoutArgumentReaderInterpreter

```xml
<argument name="interpreters" xsi:type="array">
<item name="options"
xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Options</item>
<item name="array" xsi:type="object">layoutArrayArgumentReaderInterpreterProxy</item>
<item name="boolean"
xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Boolean</item>
<item name="number"
xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Number</item>
<item name="string"
xsi:type="object">Magento\Framework\Data\Argument\Interpreter\StringUtils</item>
<item name="null"
xsi:type="object">Magento\Framework\Data\Argument\Interpreter\NullType</item>
<item name="object"
xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item>
<item name="url"
xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item>
<item name="helper"
xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Passthrough</item>
</argument>
```

Scheduled Structure:
- `scheduledPaths[$blockName] = $parent1/$parent2/$blockName`
- `scheduledStructure[$blockName] = $row`
- `scheduledData[$blockName] = [attributes actions arguments]`

### `<BLOCK>` generator [View\Layout\Generator\Block]([Link]


blob/2.2-develop/lib/internal/Magento/Framework/View/Layout/Generator/[Link])

View\Layout\Generator\Block::process: - creates block instance, evaluates arguments


- finds scheduled elements with type = 'block'
- [Link]
* Data\Argument\InterpreterInterface::evaluate

View\Layout\Argument\Interpreter\Decorator\Updater as layoutArgumentGeneratorInterpreter
- modifies result via registered [View\Layout\Argument\UpdaterInterface](https://
[Link]/magento/magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Layout/
Argument/[Link])

```xml
<argument name="testArrayWithUpdater" xsi:type="array">
<updater>Magento\Framework\View\Layout\Argument\UpdaterInterface</updater>
<item name="add" xsi:type="array">
<item name="label" xsi:type="string" translate="true">Move to Archive</item>
<item name="url" xsi:type="string">*/sales_archive/massAdd</item>
</item>
</argument>
<argument name="dataSource" xsi:type="object">
<updater>Magento\Sales\Model\Grid\CollectionUpdater</updater>
</argument>
```

Data\Argument\Interpreter\Composite as layoutArgumentGeneratorInterpreterInternal

```xml
<argument name="interpreters" xsi:type="array">
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 62 of 128

<item name="options"
xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Options</item>
<item name="array" xsi:type="object">layoutArrayArgumentGeneratorInterpreterProxy</
item>
<item name="boolean"
xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Boolean</item>
<item name="number"
xsi:type="object">Magento\Framework\Data\Argument\Interpreter\Number</item>
<item name="string"
xsi:type="object">Magento\Framework\Data\Argument\Interpreter\StringUtils</item>
<item name="null"
xsi:type="object">Magento\Framework\Data\Argument\Interpreter\NullType</item>
<item name="object" xsi:type="object">layoutObjectArgumentInterpreter</item>
<item name="url"
xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\Url</item>
<item name="helper"
xsi:type="object">Magento\Framework\View\Layout\Argument\Interpreter\HelperMethod</item>
</argument>
```

- every block:
* [Link] -> _prepareLayout
* event `core_layout_block_create_after`
- every scheduled action:
* [Link] - evaluate arguments and call_user_func_array

### `<UI_COMPONENT>` node reader [View\Layout\Reader\UiComponent]([Link]


magento/magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Layout/Reader/
[Link])

```xml
<ui_component name group aclResource ifconfig>
<visibilityCondition name="" className="">
<arguments>...</arguments>
</visibilityCondition>
</ui_component>
```

- scheduleStructure - structure.`scheduledStructure[name] = [uiComponent alias parent


sibling is_after]`
- attributes: group, component, aclResource + visibilityConditions
structure.`scheduledData[name] = [attributes]`
- read ui config, merge definition
+ _magic here_
- ui component generated layout elements -> - calls readerPool to process contents
recursively

### `<UI_COMPONENT>` generator [View\Layout\Generator\UiComponent]([Link]


magento/magento2/blob/2.2-develop/lib/internal/Magento/Framework/View/Layout/Generator/
[Link])

- finds all scheduled elements of type uiComponent


- generateComponent - creates components, prepares recursively, wraps in layout block
* View\Element\UiComponentFactory::create - View\Element\UiComponentInterface
compatible with BlockInterface
* prepare component and its children recursively - [Link] - providers,
buttons, form data etc.
* creates layout block wrapper for component -
\Magento\Ui\Component\Wrapper\UiComponent - just renders component

### How do you use layout XML directives in your customizations?


- move elements
- referenceContainer - change attributes, add children
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 63 of 128

- referenceBlock - change template, position before/after, add arguments, add children,


set no display
- add HEAD scripts/styles

## Describe how to create a new layout XML file.

- Place layout in one of directories:


* custom-module/view/base/layout/
* custom-module/view/frontend/layout/
* custom-theme/Magento_Checkout/layout/

Or the same with page_layout directory.

- When extending page layout, don't copy-paste layout="..." attribute if not intended.
- Use standard XML namespaces:

```xml
<layout xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/
layout_generic.xsd">
```
```xml
<page
xmlns:xsi="[Link]
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/
page_configuration.xsd"
>
```

## Describe how to pass variables from layout to block.

- use block arguments:

```xml
<block class="Magento\Customer\Block\Account\Navigation" name="[Link]">
<arguments>
<argument name="css_class" xsi:type="string">header links</argument>
</arguments>
</block>
```

[Magento docs]([Link]
[Link]#argument)

- substitute dynamic argument value as result of helper execution

```xml
<action method="setTemplate">
<argument name="template" xsi:type="helper"
helper="Magento\Paypal\Helper\Hss::getReviewButtonTemplate">
<param name="name">Magento_Paypal::hss/review/[Link]</param>
</argument>
</action>
```
# Utilize JavaScript in Magento
## Describe different types and uses of JavaScript modules. Which JavaScript modules are
suited for which tasks?

- plain requirejs module

Good for executing regular JS code, maybe some legacy code that doesn't require
active interactions
with other existing JS components on the page.

```html
<script type="text/x-magento-init">
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 64 of 128

{
"*": {
"mage/cookies": {
"expires": null,
"path": "/",
"domain": ".[Link]",
"secure": false,
"lifetime": "3600"
}
}
}
</script>
```
Element selector syntax is good for passing container element, so module can bind
event listeners only
within given element instead of querying DOM directly. Makes code tidier.

```html
<script type="text/x-magento-init">
{
"[data-gallery-role=gallery-placeholder]": {
"mage/gallery/gallery": {
"mixins":["magnifier/magnify"],
"magnifierOpts":
{"fullscreenzoom":"20","top":"","left":"","width":"","height":"","eventType":"hover","enabled":"false"},
"data": [/*...*/],
"options": {
// ...
},
"fullscreen": {
// ...
},
"breakpoints": {"mobile":{"conditions":{"max-width":"767px"},"options":
{"options":{"nav":"dots"}}}} }
}
}
</script>
```

- action - set some data, call AJAX


* Magento_Checkout/js/action/[Link]
* Magento_Customer/js/action/[Link] - when called, calls AJAX, when done executes
callbacks and adds messages

- shared model for data access - [Link] properties


* Magento_Customer/js/model/customer
* Magento_Checkout/js/model/quote
* some state - is loading etc. Magento_Checkout/js/model/shipping-address/form-popup-
[Link]
* validation rules Magento_Ui/js/lib/validation/[Link]

- view - ui component
* form Magento_Ui/js/form/form
* grid Magento_Ui/js/grid/listing
* minicart Magento_Checkout/js/view/minicart

- jQuery widget

```JS
{
$.widget('[Link]', {
... // [Link], [Link]
})
return $.[Link];
}
$(element).shippingCart(...)
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 65 of 128

```

## Describe UI components.

Base class hierarchy:

- uiClass
- uiElement + uiEvents + links
- uiCollection

### *uiClass* - Magento_Ui/js/lib/core/class

[About the uiClass library]([Link]


ui_comp_uiclass_concept.html)

The uiClass is an abstract class from which all components are extended. The uiClass is
a low-level class and is rarely used as direct parent for UI components’ classes.

Adds support for extending classes `extend()` and calling parent methods `_super()`.

methods:
- initialize, initConfig

properties:
- [Link]

### *uiElement* - Magento_Ui/js/lib/core/element/[Link]

[About the uiElement class]([Link]


ui_comp_uielement_concept.html)

Powers most of UI components internals. Adds support for features:


- automatic data linking between models
- automatic module loading
- working with observable properties
- stateful properties - stored automatically
- events - on/off/trigger

methods:
- initialize, initObservable, initModules
properties:
- tracks, modules, source, provider,

initLinks:
- imports
- exports
- links == same value in imports and exports

```
imports: {
totalRecords: '${ $.provider }:[Link]'
}
```
same as
```
imports: {
totalRecords: 'example.example_data_source:[Link]'
}
```

```
exports: {'visible': '${ $.provider }:visibility'}
imports: {'visible': '${ $.provider }:visibility'}
links: {'visible': '${ $.provider }:visibility'}
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 66 of 128

links: {value: '${ $.provider }:${ $.dataScope }'}


```

```
listens: {'${ $.namespace }.${ $.namespace }:responseData': 'setParsed'}
listens: {'${ $.provider }:[Link]': 'overload reset validate'} -- call multiple
callbacks
listens:
'index=create_category:responseData' => 'setParsed',
'newOption' => 'toggleOptionSelected'
```

$.someProperty - property of the UI component in the scope of this component

conditions in string literals:

'${ $.provider }:${ $.customScope ? $.customScope + "." : ""}[Link]': 'validate'

### initObservable
- observe([{Boolean} isTracked,] {String|Array|Object} listOfProperties)
- observe('var1 var2 var3')
- observe(['var1', 'var2', 'var3'])
- observe(true, 'var1 var2 var3') -- isTracked, use property accessors
- observe(false, 'var1 var2 var3') -- not tracked, use observable properties
- observe({var1: 'value', var2: true}) -- initial values from object

### initModules
```JS
defaults: {
modules: {
'%myProperty%': '%linkToTheComponent%',
externalSource: '${ $.externalProvider }',
street: '${ $.parentName }.street',
city: '${ $.parentName }.city',
country: '${ $.parentName }.country_id',
productForm: 'product_form.product_form'
}
}
```

## uiCollection - Magento_Ui/js/lib/core/[Link]

[About the uiCollection class]([Link]


concepts/ui_comp_uicollection_concept.html)

Enables components to have nested child components:


- `elems` observable property
- initElement
- getChild, insertChild, removeChild, destroyChildren
- regions

## Module_Ui/js/core/app, uiLayout - Module_Ui/js/core/renderer/layout

[The uiLayout service object]([Link]


concepts/ui_comp_uilayout_concept.html)

Renders UI collection components structure, instantiates classes, sets children etc.

- app -> layout()


- run
- iterator
- process
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 67 of 128

- addChild - current node to parent


- manipulate - appendTo, prependTo, insertTo
- initComponent
* loadDeps
* loadSource (source='uiComponent')
* initComponent
+ long async -> global function var component = new Constr(_.omit(node,
'children'));

## In which situation would you use UiComponent versus a regular JavaScript module?

[Overview of UI components]([Link]
ui_comps.html)

UI components work well together: they communicate with each other via the uiRegistry
service that
tracks their asynchronous initialization.

Therefore, if we need to extend something that has already been implemented as a hierarchy
of UI components or add a new feature that should interact with other UI components,
it’s easier and more effective to use a UI component.

## Describe the use of [Link], x-magento-init, and data-mage-init.

### [Link]

We are interested in:


- map as alias - same as defining virtual type preference in DI
```js
map: {
'*': {
alias: 'Vendor_Module/js/complex/path/amd-module'
}
}
```
Now we can use our 'Vendor_Module/js/complex/path/module' using `alias` in any RequireJS
module or config file without needing to type the entire path. For example, in Magento,
`catalogAddToCart` is mapped to 'Magento_Catalog/js/catalog-add-to-cart' and can be used
anywhere as a RequireJS module name.

- map as preference - same as preference in DI, replace one class with another
```js
var config = {
"map": {
"*": {
"<default_component>": "<custom_component>"
}
}
};
```
* <default_component>: the name of the default component you replace
* <custom_component>: the name of the custom component

- mixins - same as around plugins in DI. This is Magento customization over requireJS
```js
var config = {
config: {
mixins: {
'Magento_Checkout/js/action/place-order': {
'Magento_CheckoutAgreements/js/model/place-order-mixin': true
}
}
}
};
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 68 of 128

```

Use `wrapper` model to implement around functionality for functions.

Place order mixin body:


```
define([
'jquery',
'mage/utils/wrapper',
'Magento_CheckoutAgreements/js/model/agreements-assigner'
], function ($, wrapper, agreementsAssigner) {
'use strict';

return function (placeOrderAction) {

/** Override default place order action and add agreement_ids to request */
return [Link](placeOrderAction, function (originalAction, paymentData,
messageContainer) {
agreementsAssigner(paymentData);

return originalAction(paymentData, messageContainer);


});
};
});

```

Plain requirejs objects can be extended directly.

Example - mixin over Magento_Checkout/js/model/quote:

```
define([
'jquery',
'ko'
], function($, ko) {
'use strict';

var checkoutMethod = [Link](null);

return function(quote) {
return $.extend(quote, {
checkoutMethod: checkoutMethod
});
};
});
```

### Text/x-magento-init and Data-mage-init

[Alan Storm - Javascript Init Scripts]([Link]


magento_2_javascript_init_scripts)

Following 3 snippets are completely equivalent:


```html
<div class="selector" data-mage-init="{'Custom_Module/js/something': {option1: 'value1',
option2: 'value2'}}"></div>
```

```html
<div class="selector"></div>
<script type="text/x-magento-init">
{
'.selector': {
'Custom_Module/js/something': {
option1: 'value1',
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 69 of 128

option2: 'value2'
}
}
}
</script>
```

```html
<div class="selector"></div>
<script>
require(['Custom_Module/js/something'], function(fn) {
fn({option1: 'value1', option2: 'value2'}, DOMElement('.selector'));
});
</script>
```

How Magento executes scripts:


- mage/[Link]
- lib/web/mage/apply/[Link]
* parses init scripts `[Link]('script[type="text/x-magento-init"]')`
* converts to data-attribute format on according element, e.g. `<div data-mage-
init="...">`
* scripts with `*` selector (virtuals, no node to assign to) are collected separately
- mage/apply/[Link]
* parses data-attribute init scripts `[Link]('[data-mage-init]')` -
including converted `<script type="text/x-magento-init">`
* merges virtuals from above (`*` selector)
* creates components from parsed definitions

In other words:
- we return component-function
- magento gets it via require and invokes with arguments. E.g. uiComponent(config, node)
- node '*' -> false
- selector matching multiple nodes - multiple module instances (like jQuery plugin)

Benefits:
- top-level selector is defined outside js module
- initial values from server are defined outside modules

Example:
```html
<div class="selector-one" data-mage-init="{'Custom_Module/js/something': {option1:'',
option2:''}}"></div>
<div id="selector-two"></div>

<script type="text/x-magento-init">
{
'#selector-two': {
'Custom_Module/js/component': {
mixins: [],
option1: value1,
option2: value2
}
}
}
</script>

<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app": {
"components": {
"customer": {
"component": "Magento_Customer/js/view/customer"
}
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 70 of 128

}
}
}
}
</script>
```

Transforms into following:


```html
<div class="selector-one" data-mage-init="{'Custom_Module/js/something': {option1:'',
option2:''}}"></div>
<div id="selector-two" data-mage-init="{'Custom_Module/js/component': {mixins: [],
option1: value1, option2: value2}}"></div>
<script>
// somewhere in closures...
virtuals[] = {
el: false,
data: '{"Magento_Ui/js/core/app": {"components": {"customer": {"component":
"Magento_Customer/js/view/customer"}}}}'
}
<script>

```

And then JS is executed like this:


```JS
require(['Custom_Module/js/something'], function(fn) {
// your component should return function-constructor with 2 params: (config, $element)
fn({option1:'', option2:''}, DOMElement('.selector-one'));
});
require(['Custom_Module/js/something'], function(fn) {
fn({option1: value1, option2: value2}, DOMElement('#selector-two'));
});
require(['Magento_Ui/js/core/app'], function(appFn) {
// element = false when selector '*'
appFn({"components": {"customer": {"component": "Magento_Customer/js/view/customer"}}},
false);
})
```

There's interesting possibility to return jQuery widget instead of contructor function.


Magento detects this and calls widget accordingly.

Here's how:
```js
require([component], function (fn) {
if (typeof fn === 'object') {
fn = fn[component].bind(fn);
}

if (_.isFunction(fn)) {
fn(config, el);
} else if ($(el)[component]) {
$(el)[component](config);
}
});
```

### Mixins example

```HTML
<script type="text/x-magento-init">
{
"[data-gallery-role=gallery-placeholder]": {
"mage/gallery/gallery": {
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 71 of 128

"mixins":["magnifier/magnify"],
"magnifierOpts": ...,
"data": ...,
"options": ...
}
}
}
</script>
```

```JS
element = $('[data-gallery-role=gallery-placeholder]');
config = {magnifierOpts:..., data:..., options:...}

require(['mage/gallery/gallery'], function(gallery) {
require(["magnifier/magnify"], function(magnify) {
var result = magnify(config, element); // call for each mixin. Magnify will use
[Link]
extend(config, result); // magnify returns same config, config not changed
})
gallery(config, element);
});
```
# Demonstrate ability to use data-related classes

## Describe repositories and data API classes.

[Magento - Searching with Repositories]([Link]


dev-guide/[Link])

Magento 2.2 changed repository getList approach. Before you applied filters, sorts and
pagination manually
right in the `getList` method itself.

Magento 2.2 takes this boilerplate routine off developer's shoulders. Just call
`[Link]()`.

[Api\SearchCriteria\CollectionProcessorInterface]([Link]
blob/2.2-develop/lib/internal/Magento/Framework/Api/SearchCriteria/
[Link])
[Api\SearchCriteria\CollectionProcessor]([Link]
develop/lib/internal/Magento/Framework/Api/SearchCriteria/[Link])
- this is a composite and default preference

Default collection processors - always applied:


- [FilterProcessor]([Link]
Magento/Framework/Api/SearchCriteria/CollectionProcessor/[Link]#L45):
* DI configurable *custom filters* by field name. E.g. store_id
* *field mapping*
* addFilterGroupToCollection when no custom filter defined - like before
- [SortingProcessor]([Link]
Magento/Framework/Api/SearchCriteria/CollectionProcessor/[Link]#L45):
* DI configurable *default orders*
* *field mapping*
- [PaginationProcessor]([Link]
Magento/Framework/Api/SearchCriteria/CollectionProcessor/[Link]):
* setCurPage, setPageSize

Additional processors - add them via DI for your EAV repository:


- EAV [FilterProcessor]([Link]
Magento/Eav/Model/Api/SearchCriteria/CollectionProcessor/[Link]#L45)
* same as normal filter processor, slightly different condition apply
* *field mapping*
* *custom filters*
- [JoinProcessor]([Link]
Magento/Framework/Api/SearchCriteria/CollectionProcessor/[Link]) - used only
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 72 of 128

in tax rule repository

For EAV, inject virtual collection processor


`Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor`

## How do you obtain an object or set of objects from the database using a repository?

*Loading single object*

While model->load() is deprecated, your repository is the place to work with resource
model.
- create empty model object with factory
- call `[Link]($object)` just like `model->load()` would.

Example:
```xml
public function getById($blockId)
{
$block = $this->blockFactory->create();
$this->resource->load($block, $blockId);
if (!$block->getId()) {
throw new NoSuchEntityException(__('CMS Block with id "%1" does not exist.',
$blockId));
}
return $block;
}
```

## How do you configure and create a SearchCriteria instance using the builder?

- Api\SearchCriteriaBuilder
- Api\SearchCriteria

To build a searchCriteria object, use $searchCriteriaBuilder. You don't need a factory,


this type is declared as `shared="false"`. Once `create()` method is caled, its data
empties.

## How do you use Data/Api classes?

Api/:
- repository interfaces
- put operational interfaces - helpers etc. business logic API
- implementation lies in models
- usually available via WebAPI

Api/Data - entity container interfaces, usually extend AbstractSimpleObject

In your repository, you should return data interfaces. Some models directly implement
data interfaces (catalog product), while others are completely separate - there's
customer model and customer data object.

Example of converting collection models to data objects in customer repository:


```xml
$customers = [];
/** @var \Magento\Customer\Model\Customer $customerModel */
foreach ($collection as $customerModel) {
$customers[] = $customerModel->getDataModel();
}
$searchResults->setItems($customers);
```

`getDataModel` is not a standard method, you implement it yourself.

```xml
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 73 of 128

public function getDataModel()


{
// ...
$customerDataObject = $this->customerDataFactory->create();
$this->dataObjectHelper->populateWithArray(
$customerDataObject,
$customerData, // $customer->getData()
\Magento\Customer\Api\Data\CustomerInterface::class
);
// ...
return $customerDataObject;
}
```

## populateWithArray helper

Based on interface, calls all SETTERS with given data to fill data object

[Api\DataObjectHelper::populateWithArray]([Link]
lib/internal/Magento/Framework/Api/[Link]#L80)
- calls data object SETTERS `set*`, `setIs*` with given raw data
- handles `custom_attributes` - data [Link]

Example: [Magento\Customer\Controller\Account\Edit::execute]([Link]
magento2/blob/2.3/app/code/Magento/Customer/Controller/Account/[Link]#L78)

## buildOutputDataArray

Based on given interface, calls all GETTERS to make resulting data array.

[Reflection\DataObjectProcessor::buildOutputDataArray]([Link]
magento2/blob/2.3/lib/internal/Magento/Framework/Reflection/[Link]#L81):
- \Magento\Framework\Reflection\MethodsMap::getMethodsMap - method name and getter return
types
- filter only getters: is..., has..., get...
- get data using interface getter, e.g. $object->getSku() - based on Interface definition
- deduce field name by getter name, e.g. 'sku'
- process custom_attributes
\Magento\Framework\Reflection\CustomAttributesProcessor::buildOutputDataArray
- process extension_attributes
\Magento\Framework\Reflection\ExtensionAttributesProcessor::buildOutputDataArray
- process return object: build return value objects with their return type annotation
- process return array: cast each element to type, e.g. int[] => (int) each value
- cast element to type

Example: [Magento\Customer\Model\Address::updateData]([Link]
blob/2.3/app/code/Magento/Customer/Model/[Link]#L145)
# Demonstrate ability to use EAV entity load and save

Types of resource models:


- flat resource - `Magento\Framework\Model\ResourceModel\AbstractResource`
- EAV entity resource - `Magento\Eav\Model\Entity\AbstractEntity`
- version controll aware EAV entity resource -
`Magento\Eav\Model\Entity\VersionControl\AbstractEntity`.
Used only by customer and customer address.

New Entity Manager stands aside, you don't need to extend your resource model from it,
but rather manually call its methods in your resource - load, save, delete.
\Magento\Framework\EntityManager\EntityManager is suitable for both flat and EAV tables.

## Entity Manager

Entity manager is a new way of saving both flat tables and EAV entities.
Separates individual operations into own class - read, create, update, delete.
This give a lot of freedom for extensions.
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 74 of 128

Each operation still has a lot to do - save EAV data and run extensions. Default
operations
split these activities into smaller actions:
- save main table - both for flat and EAV
- save EAV attributes - only for EAV entities. see *attribute pool*
- run extensions - see *extension pool*

Entity manager is concept separate from regular flat and EAV resource models saving, it
doesn't
call them under the hood. It does the same things but in own fashion with extensibility
in mind.

This means that you can still build your code on regular resource models:
- flat resource Magento\Framework\Model\ResourceModel\Db\AbstractDb
- EAV entity resource Magento\Eav\Model\Entity\AbstractEntity

Entity manager is currently used by:


- bundle option
- bundle selection
- catalog rule
- product
- category
- cms block
- cms page
- sales rule
- gift card amount
- and some others (staging etc.)

Terms:
- *entity manager* - calls appropiate operations. Holds type resolver and operations pool
- *metadata pool* - DI injectable. Register Api Data interface and [`entityTableName`,
`identifierField`]
```xml
<item name="Magento\Cms\Api\Data\PageInterface" xsi:type="array">
<item name="entityTableName" xsi:type="string">cms_page</item>
<item name="identifierField" xsi:type="string">page_id</item>
<!-- eavEntityType - flag is eav -->
<!-- connectionName -->
<!-- sequence -->
<!-- entityContext -->
</item>
```

- *operation pool* - `checkIfExists`/`read`/`create`/`update`/`delete` operations per


entity type. Operaton does ALL work.
*Default operations - can extend in DI:*
- checkIfExists - Magento\Framework\EntityManager\Operation\CheckIfExists - decides
if entity is new or updated
- read - Magento\Framework\EntityManager\Operation\Read
- create - Magento\Framework\EntityManager\Operation\Create
- update - Magento\Framework\EntityManager\Operation\Update
- delete - Magento\Framework\EntityManager\Operation\Delete

- *attribute pool* - read/create/update attributes in separate tables per entity type. DI


injectable, has default, register only to override.
```xml
<item name="Magento\Catalog\Api\Data\CategoryInterface" xsi:type="array">
<item name="create"
xsi:type="string">Magento\Catalog\Model\ResourceModel\CreateHandler</item>
<item name="update"
xsi:type="string">Magento\Catalog\Model\ResourceModel\UpdateHandler</item>
</item>
```
*Default attribute pool actions - Module_Eav/etc/[Link]:*
- read - Magento\Eav\Model\ResourceModel\ReadHandler
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 75 of 128

- create - Magento\Eav\Model\ResourceModel\CreateHandler
- update - Magento\Eav\Model\ResourceModel\UpdateHandler

- *extension pool* - custom modifications on entity read/create/update. Put your


extensions here, good for extension attributes
+ product gallery read/create
+ product option read/save
+ product website read/save
+ configurable product links read/save
+ category link read/save
+ bundle product read/save - set options
+ downloadable link create/delete/read/update
+ gift card amount read/save
+ cms block, cms page - store read/save

### *Entity [Link]:*


- resolve entity type - implementing data interface `\Api\Data` when possible, or
original class
- check `has(entity)` - detect create new or update
* operation [Link](`checkIfExists`)
- if new, operation [Link](`create`).execute
- if existing, operation [Link](`update`).execute
- run callbacks

### `create` operation


- event `entity_manager_save_before`
- event `{$lower_case_entity_type}_save_before`
- apply sequence - \Magento\Framework\EntityManager\Sequence\SequenceApplier::apply
* entity[id] = [Link]
- *create main*
* EntityManager\Db\CreateRow::execute - insert into main table by existing column
- *create attributes*
* attribute pool actions `create`[].execute
* default action handler `Magento\Eav\Model\ResourceModel\CreateHandler`:
+ only if entity is EAV type, insert all attributes
- \Magento\Eav\Model\ResourceModel\AttributePersistor::registerInsert
- \Magento\Eav\Model\ResourceModel\AttributePersistor::flush
- *create extensions* - \Magento\Framework\EntityManager\Operation\Create\CreateExtensions
* extension pool actions `create`[].execute - save many-to-many records, custom columns
etc.
- event `{$lower_case_entity_type}_save_after`
- event `entity_manager_save_after`

## Object converters
\Magento\Framework\Reflection\DataObjectProcessor::buildOutputDataArray(object, interface)
\Magento\Framework\Reflection\CustomAttributesProcessor::buildOutputDataArray
\Magento\Framework\Reflection\ExtensionAttributesProcessor::buildOutputDataArray

\Magento\Framework\Api\\`SimpleDataObjectConverter`:
- toFlatArray:
* data object [Link]
* ConvertArray::toFlatArray
- convertKeysToCamelCase - used by Soap API, supports `custom_attributes`

\Magento\Framework\Api\\`ExtensibleDataObjectConverter`:
- toNestedArray(entity, skip custom attributes list, interface) - used by entity
[Link]:
* data object [Link]
* add `custom_attributes`
* add `extension_attributes`
- toFlatArray:
* toNestedArray
* ConvertArray::toFlatArray
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 76 of 128

- (static) convertCustomAttributesToSequentialArray

\Magento\Framework\Reflection\DataObjectProcessor::buildOutputDataArray:
- \Magento\Framework\Reflection\MethodsMap::getMethodsMap - method name and getter return
types
- filter only getters: is..., has..., get...
- get data using interface getter, e.g. $object->getSku() - based on Interface definition
- deduce field name by getter name, e.g. 'sku'
- process custom_attributes
\Magento\Framework\Reflection\CustomAttributesProcessor::buildOutputDataArray
- process extension_attributes
\Magento\Framework\Reflection\ExtensionAttributesProcessor::buildOutputDataArray
- process return object: build return value objects with their return type annotation
- process return array: cast each element to type, e.g. int[] => (int) each value
- cast element to type

[Link]:
- buildOutputDataArray
- product data = call all getters + original data
- product = initializeProductData. create new product/get existing by SKU. set product
data
- process links, unless product data `ignore_links_flag`
- abstract [Link]
- product [Link]
- entity [Link]
# Describe common structure/architecture

## Some URLs in backend start with "admin/admin/.." - 2 times admin, some only 1 -
"admin/...". How?

Sample router:
```xml
<router id="admin">
<route id="adminhtml"> <!-- frontName="admin" -->
<module name="Magento_Theme" />
</route>
<route id="theme" frontName="theme">
<module name="Magento_Theme" />
</route>
</router>
```

Sample menu items:


- menu add action="adminhtml/system_design_theme"
[Link]
\Magento\Theme\Controller\Adminhtml\System\Design\Theme\Index
* /admin/admin/system_design_theme/index
* App\[Link]('admin')
* \Magento\Backend\App\Area\FrontNameResolver::getFrontName
* default front name = deployment config `backend/frontName` - from [Link]
* if config `admin/url/use_custom_path`, admin front name = config `admin/url/
custom_path`

- menu add action="theme/design_config"


[Link]
\Magento\Theme\Controller\Adminhtml\Design\Config\Index

Fun fact: same "Content > Design > Themes" link can open by 2 different URLs:
- [Link]
- [Link]

First link "hides" behind Magento_Backend original route "adminhtml"="admin" frontName.


Second link has own frontName="theme" and is more beautiful.
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 77 of 128

Admin app flow:


- [Link](application)
- [Link]
- front [Link]
- admin routerList:
* `admin`
* `default`
- Magento\Backend\App\[Link] - same as frontend, but admin parses *4* sections:
* parseRequest
+ `_requiredParams = ['areaFrontName', 'moduleFrontName', 'actionPath', 'actionName']`
+ "admin/admin/system_design_theme/index"

`router id="admin"`

Admin router - Base, same as frontend, BUT:


- `_requiredParams = ['areaFrontName', 'moduleFrontName', 'actionPath', 'actionName']` -
*4* sections, rest is params.
Example: "kontrollpanel/theme/design_config/index/key/3dd89..7f6e/":
* moduleFrontName = "theme", actionPath = "design_config", actionName = "index",
* parameters = [key: 3dd89..7f6e]
* fromt name "theme" - search adminhtml/[Link] for ....?
Example 2 "/kontrollpanel/admin/system_design_theme/index/key/4bd2...13/:
* moduleFrontName = "admin", actionPath = "system_design_theme", actionName = "index"
* parameters = [key: 4bd2...13]

- `pathPrefix = 'adminhtml'` -- all controller classes are searched in


`Module\Controller\Adminhtml\...`

When in *adminhtml area*, ALL controllers will reside in Controller/Adminhtml/ modules


path.

Module_Backend registers router `<route name="adminhtml" frontName="admin">` => `<module


name="Magento_Backend"/>`.
This means that `getUrl("adminhtml/controller/action")`:
- Magento\Backend\Model\Url adds prefix `$areaFrontName/` in `_getActionPath`
- *route name* "adminhtml", frontName will always be "admin/"
- default controllers search module is *Magento_Backend*
- declaring other modules before "Magento_Backend" you risk overriding some system
backend controllers:
+ ajax/translate, auth/login, dashboard/index, index/index, system/store etc.

You can in turn *register own router* `<route name="theme" frontName="theme">` =>
`<module name="Module_Alias"/>`.
You will generate links with `getUrl("theme/controller/action")`:
- backend URL model._getActionPath adds prefix areaFrontName
- *route name* "theme", frontName "theme"
- controlles are searched *only in your module* Module_Alias, no interference with
Magento_Backend controllers.

### What does `router id="admin"`, `router id="standard"` mean in [Link]?


```xml
<config>
<router id="admin">
<routes>
<route name="adminhtml" frontName="admin">...</route>
</routes>
</router>
<router id="standard">
<routes>
<route name="checkout" frontName="checkout">...</route>
</routes>
</router>
</config>
```
We know that frontend and admin areas main *router* is almost the same -
\Magento\Framework\App\Router\Base.
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 78 of 128

It simply finds *routes* by matching `frontName`.

Key difference is matched *area* in *area list* defined `router` name:


- `adminhtml` area - `admin` *router*.
- `frontend` area - `standard` *router*.
All routes and frontNames are read only from router by ID.

This means that admin routes always lie in etc/adminhtml/[Link] and have `router
id="admin"`.
Frontend routes - etc/frontend/[Link] and always have `router id="standard"`.
This looks like redundancy to me.

*Admin controller load summary (one more time)*:


- match backend route name
- match adminhtml area, router = 'admin'
- load adminhtml configuration
- adminhtml/[Link] - routerList = [admin, default]
- admin router = extends frontend Base router
- parse params - 4 parts: [front area from [Link], frontName, controllerPath, actionName]
- search [Link]/router id="admin"/route by frontName=$frontName - second parameter
- matched route has 1+ ordered module names, e.g. [Module_Theme, Module_Backend]
- search controller class in each Module_Name/Controller/Adminhtml/[Controller/Path]/
[ActionName]

*URL generation*:

In *frontend* URLs are generated with `\Magento\Framework\[Link]`:


- getUrl() -> createUrl() = getRouterUrl + ?query + #fragment
- getRouterUrl() = getBaseUrl() + `_getRoutePath`
- getBaseUrl() example '[Link]
- `_getRoutePath` = `_getActionPath` + params pairs. For example: 'catalog/product/view/
id/47'
- `_getActionPath` example: 'catalog/product/view'

In *backend* use customized version `\Magento\Backend\Model\Url`:


- `_getActionPath` *prepends* area front name, e.g. 'kontrollpanel/catalog/product/view'.
Now all backend links have this prefix.
- getUrl() adds secret key /key/hash
- can disable secret key generation with turnOffSecretKey() and revert with
turnOnSecretKey().
Global admin config `admin/security/use_form_key`.

*Secret key* = `hash($routeName . $controllerName . $actionName . $sessionFormKey)`

## Describe the difference between Adminhtml and frontend. What additional tools and
requirements exist in the admin?
- ACL permissions - `_isAllowed`, change static const `ADMIN_RESOURCE`
- base controller Magento\Backend\App\Action = Magento\Backend\App\AbstractAction
- custom *URL model* - secret key `key`
- layout `acl` block attribute
- ...
# Define form and grid widgets

## UI Component generation

UI component scheme - `urn:magento:module:Magento_Ui:etc/ui_configuration.xsd`


Magento_Ui, name just like page_configuration

Generation flow:
- \Magento\Framework\View\Layout\Generator\UiComponent::generateComponent - create,
prepare, wrap
- create component \Magento\Framework\View\Element\UiComponentFactory::create
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 79 of 128

* \Magento\Framework\View\Element\UiComponentFactory::mergeMetadata -- components
definitions in array format + [Link]
+ metadata = ['cert_form' => ['children' => [Link]]]
* for each child[],
\Magento\Framework\View\Element\UiComponentFactory::createChildComponent recursively:
+ create PHP class for component, e.g. `new
Magento\Ui\Component\DataSource($components = [])`;
* create PHP class for component, e.g. `new Magento\Ui\Component\Form($components =
[children...])`;
- prepare component recursively
* getChildComponents[].prepare - update data, js_config etc.
- wrap to *UiComponent\Container* as child 'component'
* toHtml = render -> renderEngine->render (template + '.xhtml')

## AbstractComponent

*component PHP class common arguments:*


- name
- template -> .xhtml
- layout: generic - default/tabs. DI config Magento\Framework\View\Layout\Pool
- config:
* value
- js_config:
* component - JS component class
* extends - by default [Link] - top level UI component instance name,
e.g. 'cms_block_form'
* provider - [Link] - adds to "types"
- actions - [Link]. Broken?
- html_blocks - [Link]
- buttons - [Link]
- observers

*What is the difference between component data `config`, `js_config` and others?*
- looks like js_config is special - goes to `types` definition
- config - normal JS component config overriding `defaults`?
- other values - only for PHP component class usage?

#### `provider`, `extends`


- PHP [Link] - all components automatically get `data.js_config.extends =
[top level ui component name]`.
- PHP Component.`prepare`:
* every component data.js_config is registered in `types` by constant type (_see
below_).
Definitions for multiple same types (component `column` occurres many times) are
merged.
* when data.js_config.`provider` is set:
+ `extends` is removed. This is TOP LEVEL component.
+ this TOP LEVEL compoment data.js_config is registered by personal name - e.g.
"cms_block_form" instead of "form".

This makes sense - ALL components inherit same top level `provider` via `extends`.

Example:
```
"Magento_Ui/js/core/app": {
"types": {
// [component constant type]: [data.js_config],
// TOP LEVEL specific js_config and required provider
cms_block_form: {
provider: 'cms_block_form_data_source'
},
// below are generic types by constant type
form: {
extends: 'cms_block_form'
},
fieldset: {
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 80 of 128

component: 'Magento_Ui/js/form/components/fieldset',
extends: 'cms_block_form'
},
input: {
extends: 'cms_block_form'
}

},
"components": ...
}
```

## DataSource, DataProvider

DataProvider interface is NOT in UI module, but in framework.


Magento\Framework\View\Element\Uicomponent\DataProvider\DataProviderInterface:
- getName - why???
- getData - [items, totalItems]
- setConfig, getConfigData
- addFilter, addOrder, setLimit
- getPrimaryFieldName - used e.g. to delete row by ID
- getRequestFieldName ???
- getMeta -- extends/overrides ui component structure - converted array.
Meta format example:

```
[
'' => [
attributes => [
class => 'Magento\Ui\Component\Form',
name => 'some_name',
],
arguments => [
data => [
js_config => [
component => 'Magento_Ui/js/form/form',
provider => 'cms_block_form.block_form_data_source',
],
template => 'templates/form/collapsible',
],
],
children => [ ...nested components... ]
]
]
```
- getFieldMetaInfo, getFieldsMetaInfo, getFieldsetMetaInfo -- wtf?
- getSearchCriteria, getSearchResult ???

You don't have to implement data provider from scratch, choose from 2 implementations:
- in framework next to interface -
`Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider`.
This provider sets collects all input filters and sorts into *search criteria*.
Data is returned like `searchResult = $this->reporting->search($searchCriteria)`.
Nice and tidy.
* static properties are passed in ui component XML in `listing/dataSource/dataProvider/
settings`:
+ name ??? - `[YourComponentName]_data_source`
+ primaryFieldName, e.g. `entity_id`
+ requestFieldName, e.g. `id`
* addFilter, addOrder, setLimit - proxy to search criteria builder

But who will process our searchCriteria?


* `\Magento\Framework\View\Element\UiComponent\DataProvider\Reporting` is responsible
for
returning SearchResult by SearchCriteria.
* `\Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory` creates
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 81 of 128

collection
instance by *data provider name*.
* You register your collection as DI arguments for CollectionFactory:
```xml
<type
name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="sales_order_grid_data_source"
xsi:type="string">Magento\Sales\Model\ResourceModel\Order\Grid\Collection</item>
</argument>
<arguments>
</type>
```

- in Magento_Ui - `\Magento\Ui\DataProvider\AbstractDataProvider`
This provider works directly with *collection*, you define collection in constructor
when extending.
* as usual, static properties are passed in ui component XML as arguments or settings:
+ name
+ primaryFieldName
+ requestFieldName
+ meta
+ data - data['config'] for your own usage, you can pass some settings, preferences
etc.
* addFilter = [Link]
* addOrder = [Link]
* etc...
* getData = [Link]

## Source of confusion - unknown UI [Link] structure, node names.


`[Link]`

`[Link]` maps what values come from where! This explains custom node names
and attributes that are
magically inserted in resulting JS configuration.

`Magento_Ui/view/base/ui_component/etc/[Link]`

E.g. component `dataSource` > argument `dataProvider` -> primaryFieldName =


`xpath(dataProvider/settings/requestFieldName)`
Example from `[Link]`:

```xml
<component name="dataSource">
<schema name="current">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="submit_url" type="url" xsi:type="converter">settings/
submitUrl</item>
...
<item name="clientConfig" type="item"
xsi:type="converter">dataProvider/settings/clientConfig</item>
<item name="provider" type="string" xsi:type="xpath">@provider</
item>
...
</item>
<item name="js_config" xsi:type="array">
<item name="deps" type="deps" xsi:type="converter">settings/deps</
item>
</item>
<item name="layout" xsi:type="array">
<item name="type" type="string" xsi:type="xpath">settings/layout/
type</item>
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 82 of 128

<item name="navContainerName" type="string"


xsi:type="xpath">settings/layout/navContainerName</item>
</item>
</argument>
<argument name="class" type="string" xsi:type="xpath">dataProvider/
@class</argument>
<argument name="name" type="string" xsi:type="xpath">dataProvider/@name</
argument>
...
```
And `[Link]`:
```xml
<dataSource class="Magento\Ui\Component\DataSource"/>
```

Using [Link] we can deduce full `<dataSource>` definition and what parameter
will end up where:
```xml
<dataSource class="Magento\Ui\Component\DataSource">
<aclResource/>
<settings>
<submitUrl />
<validateUrl />
<updateUrl/>
<filterUrlParams/>
<storageConfig/>
<statefull/>
<imports/>
<exports/>
<links/>
<listens/>
<ns/>
<componentType/>
<dataScope/>
<deps/>
<layout>
<type/>
<navContainerName/>
</layout>
</settings>
<dataProvider name="some_name" class="SomeDataProviderClass">
<settings>
<primaryFieldName>entity_id</primaryFieldName>
<requestFieldName>id</requestFieldName>
</settings>
</dataProvider>
</dataSource
```

Fun fact - some parameters are scattered in `settings`, another in `dataProvider/


settings`, but in fact ALL
of them will be used only for dataProvider object data.

PHP data source creation equivalent:


```php
$dataProvider = $objectManager->create(`SomeDataProviderClass`, [
'name' => 'some_name',
'primaryFieldName' => 'entity_id',
'requestFieldName' => 'id',
'data' => [
'config' => [
'submit_url' => '',
'validate_url' => '',
'update_url' => '',
'filter_url_params' => '',
'clientConfig' => '',
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 83 of 128

'provider' => '',


'component' => '',
'template' => '',
'sortOrder' => '',
'displayArea' => '',
'storageConfig' => '',
'statefull' => '',
'imports' => '',
'exports' => '',
'links' => '',
'listens' => '',
'ns' => '',
'componentType' => '',
'dataScope' => '',
'aclResource' => '',
],
'js_config' => [
'deps' => [],
],
'layout' => [
'type' => '',
'navContainerName' => '',
],
],
]);
$dataSource = $objectManager->create('Magento\Ui\Component\DataSource', [
'dataProvider' => $dataProvider,
]);
```

With this sacred knowledge we can add/override data provider parameters explicitly in
addition to normal config:
```xml
<dataSource name="...">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="referer_url" xsi:type="url" path="sales/archive/shipments/
index"/>
</item>
</argument>
</argument>
<dataProvider class="..." name="..">
<settings>...</settings>
</dataProvider>
</dataSource>
```

Okay, ui component *data source* doesn't get any configuration, it all goes to *data
provider* - 'submit_url' etc.
Data provider can only return data and modify meta, how does it pass these JS values for
JS components?

Here's how:
- View\Layout\Generic::build (initial page open) or
View\Element\UiComponent\ContentType\Json::render (load AJAX data)
- [Link] -- merges
* [Link]
* data [Link] -- returns data['config'] -- all the params we set as
arguments

## UI Form

*Caveats:*
- default form [Link] is `templates/form/default`. But its spinner works only with
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 84 of 128

`tabs` layout.
`<div data-role="spinner" data-component="{{getName()}}.areas"`. Normal form does not
have `areas`, and
so this spinner will work forever.
Solution:
* either set [Link] = `templates/form/collapsible`:
`<div data-role="spinner" data-component="{{getName()}}.{{getName()}}"` - this
matches normal form
* or set [Link] = `tabs`. It will init `areas` suitable for default
template.
See \Magento\Ui\Component\Layout\Tabs::initAreas.

*component [Link]*:
- read ID from request, e.g. current entity ID
- filter data provider by primaryField = $requestedValue
- get row data by requested primary ID
* note that normal collections don't return rows indexed by ID and you will receive an
error
* Magento data provider override getData and index by ID manually, e.g.
\Magento\Cms\Model\Page\DataProvider::getData

### *Working with form fields*:

Example: `<field name="something" formElement="input">`.

Magic unraveled:
- see [Link] `<field class="Magento\Ui\Component\Form\Field"/>`
- \Magento\Ui\Component\Form\Field::prepare - creates new child ui component
`wrappedComponent`
with type = `$this->getData('config/formElement')`.
- how to set config/formElement? See [Link], search for `component
name="field"`:
`<item name="formElement" type="string" xsi:type="xpath">@formElement</item>`.

`formElement` creates arbitrary ui component and inherits same data as main `field`.

*Suitable `formElement` types:*


- input
- textarea
- fileUploader
- date
- email
- wysiwyg
- checkbox, prefer radio/toggle
```xml
<formElements>
<checkbox>
<settings>
<valueMap>
<map name="false" xsi:type="number">0</map>
<map name="true" xsi:type="number">1</map>
</valueMap>
<prefer>toggle</prefer>
</settings>
</checkbox>
</formElements>
```
- select:
```xml
<formElements>
<select>
<settings>
<options class="Magento\Config\Model\Config\Source\Design\Robots"/>
</settings>
</select>
</formElements>
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 85 of 128

```
- multiselect

## UI Listing
`listing` structure:
- argument data.js_config.provider
- `dataSource`
* `dataProvider`
- `filterbar`
* `filters`
* `bookmark`
* `export`
* `massaction`
- `columns`
* `column`

- `listing` - just container, server template 'templates/listing/[Link]'


- `columns` - no server processing, all juice here:
* JS component Magento_Ui/js/grid/listing
- `column`
* JS component Magento_Ui/js/grid/columns/column
* don't forget to include config/dataType = settings/dataType

Implementing listing checklist:


- set settings/spinner = "columns"
* Listing template 'templates/listing/[Link]' has spiner `data-
component="{{getName()}}.{{getName()}}.{{spinner}}"`
* `columns` js component - `Magento_Ui/js/grid/listnig` - hides data when loaded -
`[Link]([Link]).hide()`.
- dataSource must specify provider in order to load data propertly
`<dataSource provider="Magento_Ui/js/grid/provider"/>`
This provider handles loading data via AJAX.
- set `dataSource/settings/updateUrl` = `mui/index/render`. This will return grid data in
JSON format.
When this parameter is missing, AJAX is made to current URL

```xml
<column>
<settings>
<dataType></dataType> <!-- creates wrapped UI component, column config =
merge([Link] + [Link]) -->
</settings>
</column>
```

DataType:
- text - default

Listing toolbar:
```xml
<listingToolbar>
<bookmark />
<columnsControls />
<filterSearch />
<filters />
<massaction>
<action name="delete">
<settings>
<label/>
<type/> - delete/edit/...
<url path=""/>
<confirm>
<message/>
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 86 of 128

<title/>
</confirm>
<callback>
<target />
<provider />
</callback>
</settings>
</action>
</massaction>
<paging />
</listingToolbar>
```

Column types:
- selection
```xml
<selectionsColumn name="ids">
<settings>
<indexField>block_id</indexField>
</settings>
</selectionsColumn>
```
- select
```xml
<column name="is_active" component="Magento_Ui/js/grid/columns/select">
<settings>
<options class="Magento\Cms\Model\Block\Source\IsActive"/>
<filter>select</filter>
<editor>
<editorType>select</editorType>
</editor>
<dataType>select</dataType>
<label translate="true">Status</label>
</settings>
</column>
```
- ...

column/settings/filter:
- textRange
- text
- select
- dateRange
- ...

## UNSORTED

Magento\Framework\Config\DataInterfaceFactory = \Magento\Ui\Config\Data::get('cert_form')
-
evil class that converts all the obscure UI component conventions into real
understandable data array.
E.g. `<settings>`, `<formElement>` and the like.

Magento_Ui/view/base/ui_component/etc/[Link]
Magento_Ui/view/base/ui_component/etc/[Link]

- config [Link]
- new config reader \Magento\Ui\Config\Reader('cert_form.xml')
- config [Link]:
* find all UI files with this name
* [Link]
+ convert file contents to DOM \Magento\Ui\Config\Reader\Dom(file content) - new
\[Link]
+ convert DOM to array - \Magento\Ui\Config\Converter::convert(DOMDocument)
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 87 of 128

+ `config [Link]` -- all conventions start here


- merges default data by node name from `[Link]`

\Magento\Ui\Component\Filters: text, textRange, select, dateRange

page layout - `urn:magento:framework:View/Layout/etc/page_configuration.xsd`


# Utilize ACL to set menu items and permissions

## Describe how to set up a menu item and permissions.

[Link] - `urn:magento:module:Magento_Backend:/etc/[Link]` - flat structure


```xml
<menu>
<add id="Module_Alias::item_name" title="Title" translate="title"
module="Module_Alias" sortOrder="30"
parent="Magento_Backend::system_design" action="adminhtml/system_design"
resource="Magento_Backend::schedule"
dependsOnConfig="" dependsOnModule="" sortOrder="" target="" toolTip=""
/>
<update id="Module_Alias::item_name" [all normal params] />
<remove id="Module_Alias::item_name" />
</menu>
```

*How would you add a new tab in the Admin menu?*


- don't specify parent and action

*How would you add a new menu item in a given tab?*


- just set one of top level parents, e.g. 'Magento_Backend::system'

*How do menu items relate to ACL permissions?*

## Describe how to check for permissions in the permissions management tree structures.
*How would you add a new user with a given set of permissions?*

- System > Permissions > User Roles has all the differnet roles and associated
permissions. Each role can be scoped to Website level and granular permissions based on
resource options.
- System > Permissions > All Users to view and create new users and associate to a role.
There are two tabs 1 is user info the other is User Role where you define the Role for
this user. You can only select 1 role per user.

*How can you do that programmatically?*


- You can leverage [\Magento\Authorization\Model\Acl\AclRetriever]([Link]
magento/magento2/blob/2.2-develop/app/code/Magento/Authorization/Model/Acl/
[Link]). That as a few methods that will help

```php
/**
* Get a list of available resources using user details
*
* @param string $userType
* @param int $userId
* @return string[]
* @throws AuthorizationException
* @throws LocalizedException
*/
public function getAllowedResourcesByUser($userType, $userId)
{
if ($userType == UserContextInterface::USER_TYPE_GUEST) {
return [self::PERMISSION_ANONYMOUS];
} elseif ($userType == UserContextInterface::USER_TYPE_CUSTOMER) {
return [self::PERMISSION_SELF];
}
try {
$role = $this->_getUserRole($userType, $userId);
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 88 of 128

if (!$role) {
throw new AuthorizationException(
__('We can\'t find the role for the user you wanted.')
);
}
$allowedResources = $this->getAllowedResourcesByRole($role->getId());
} catch (AuthorizationException $e) {
throw $e;
} catch (\Exception $e) {
$this->logger->critical($e);
throw new LocalizedException(
__(
'Something went wrong while compiling a list of allowed resources. '
. 'You can find out more in the exceptions log.'
)
);
}
return $allowedResources;
}

/**
* Get a list of available resource using user role id
*
* @param string $roleId
* @return string[]
*/
public function getAllowedResourcesByRole($roleId)
{
$allowedResources = [];
$rulesCollection = $this->rulesCollectionFactory->create();
$rulesCollection->getByRoles($roleId)->load();
$acl = $this->aclBuilder->getAcl();
/** @var \Magento\Authorization\Model\Rules $ruleItem */
foreach ($rulesCollection->getItems() as $ruleItem) {
$resourceId = $ruleItem->getResourceId();
if ($acl->has($resourceId) && $acl->isAllowed($roleId, $resourceId)) {
$allowedResources[] = $resourceId;
}
}
return $allowedResources;
}
```

However the actual code to set privilage permission may look like this in the core code
vendor/magento/magento2-base/setup/src/Magento/Setup/Fixtures/[Link]

In particular this section:

```php
$adminUser = $this->userFactory->create();
$adminUser->setRoleId($role->getId())
->setEmail('admin' . $i . '@[Link]')
->setFirstName('Firstname')
->setLastName('Lastname')
->setUserName('admin' . $i)
->setPassword('123123q')
->setIsActive(1);
$adminUser->save();
```

```php
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See [Link] for license details.
*/
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 89 of 128

namespace Magento\Setup\Fixtures;

use Magento\Authorization\Model\Acl\Role\Group;
use Magento\Authorization\Model\RoleFactory;
use Magento\Authorization\Model\RulesFactory;
use Magento\Authorization\Model\UserContextInterface;
use Magento\Framework\Acl\RootResource;
use Magento\User\Model\ResourceModel\User\CollectionFactory as UserCollectionFactory;
use Magento\User\Model\UserFactory;

/**
* Generate admin users
*
* Support the following format:
* <!-- Number of admin users -->
* <admin_users>{int}</admin_users>
*/
class AdminUsersFixture extends Fixture
{
/**
* @var int
*/
protected $priority = 5;

/**
* @var UserFactory
*/
private $userFactory;

/**
* @var RoleFactory
*/
private $roleFactory;

/**
* @var UserCollectionFactory
*/
private $userCollectionFactory;

/**
* @var RulesFactory
*/
private $rulesFactory;

/**
* @var RootResource
*/
private $rootResource;

/**
* @param FixtureModel $fixtureModel
* @param UserFactory $userFactory
* @param UserCollectionFactory $userCollectionFactory
* @param RoleFactory $roleFactory
* @param RulesFactory $rulesFactory
* @param RootResource $rootResource
*/
public function __construct(
FixtureModel $fixtureModel,
UserFactory $userFactory,
UserCollectionFactory $userCollectionFactory,
RoleFactory $roleFactory,
RulesFactory $rulesFactory,
RootResource $rootResource
) {
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 90 of 128

parent::__construct($fixtureModel);
$this->userFactory = $userFactory;
$this->roleFactory = $roleFactory;
$this->userCollectionFactory = $userCollectionFactory;
$this->rulesFactory = $rulesFactory;
$this->rootResource = $rootResource;
}

/**
* {@inheritdoc}
*/
public function execute()
{
$adminUsersNumber = $this->fixtureModel->getValue('admin_users', 0);
$adminUsersStartIndex = $this->userCollectionFactory->create()->getSize();

if ($adminUsersStartIndex >= $adminUsersNumber) {


return;
}

$role = $this->createAdministratorRole();

for ($i = $adminUsersStartIndex; $i <= $adminUsersNumber; $i++) {


$adminUser = $this->userFactory->create();
$adminUser->setRoleId($role->getId())
->setEmail('admin' . $i . '@[Link]')
->setFirstName('Firstname')
->setLastName('Lastname')
->setUserName('admin' . $i)
->setPassword('123123q')
->setIsActive(1);
$adminUser->save();
}
}

/**
* {@inheritdoc}
*/
public function getActionTitle()
{
return 'Generating admin users';
}

/**
* {@inheritdoc}
*/
public function introduceParamLabels()
{
return [
'admin_users' => 'Admin Users'
];
}

/**
* Create administrator role with all privileges.
*
* @return \Magento\Authorization\Model\Role
*/
private function createAdministratorRole()
{
$role = $this->roleFactory->create();
$role->setParentId(0)
->setTreeLevel(1)
->setSortOrder(1)
->setRoleType(Group::ROLE_TYPE)
->setUserId(0)
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 91 of 128

->setUserType(UserContextInterface::USER_TYPE_ADMIN)
->setRoleName('Example Administrator');
$role->save();

/** @var \Magento\Authorization\Model\Rules $rule */


$rule = $this->rulesFactory->create();
$rule->setRoleId($role->getId())
->setResourceId($this->rootResource->getId())
->setPrivilegies(null)
->setPermission('allow');
$rule->save();

return $role;
}
}

```
# Demonstrate ability to use products and product types

## Identify/describe standard product types (simple, configurable, bundled, etc.).

Product type model - [Magento\Catalog\Model\Product\Type\AbstractType]([Link]


magento/magento2/blob/2.2-develop/app/code/Magento/Catalog/Model/Product/Type/
[Link])

7 product types:

- [*virtual*]([Link]
Catalog/Model/Product/Type/[Link])

Not tangible products - services, memberships, warranties, and subscriptions. No


weight, can't be shipped.

- [*simple*]([Link]
Catalog/Model/Product/Type/[Link])

Single SKU, has weight and can be shipped.

- [*downloadable*]([Link]
Downloadable/Model/Product/[Link])

Same as virtual + downloadable links

- [*configurable*]([Link]
ConfigurableProduct/Model/Product/Type/[Link])

Add 1 composite product, consisting of 1 selected variation. All options are


connected.
Child product inventory tracked separately.

- [*grouped*]([Link]
GroupedProduct/Model/Product/Type/[Link])

Groups multiple products on same page. Customer can buy all of some of them, edit qty.
After adding to cart, they are separate, same as if you added each of them one by one.

- [*bundle*]([Link]
Model/Product/[Link])

Add 1 composite product, consisting of many selected variations, options independent.


SKU dynamic/fixed, weight dynamic/fixed, price dynamic/fixed. Each qty x total qty.
Inventory is tracked separately for each child.

- *gift card (Commerce edition)*

Like downloadable, purchase gift card code instead of link.


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 92 of 128

Can be virtual/physical/both. Thus can be shipped.

## product_types.xml - maximum configuration:


```xml
<type name="mytype" label="My Type Product" modelInstance="..." composite="false"
isQty="false" canUseQtyDecimals="true" indexPriority="10" sortOrder="10">
<priceModel instance="...\Model\Product\Type\Mytype\Price" />
<indexerModel instance="...\Model\ResourceModel\Product\Indexer\Price\Mytype" />
<stockIndexerModel instance="...\Model\ResourceModel\Indexer\Stock\Mytype" />
<customAttributes>
<attribute name="is_real_product" value="true"/>
<attribute name="refundable" value="true/false"/>
<attribute name="taxable" value="true"/>
<attribute name="is_product_set" value="false"/>
</customAttributes>
<allowedSelectionTypes>
<type name="simple" />
<type name="virtual" />
</allowedSelectionTypes>
</type>
<composableTypes>
<type name="simple"/>
<type name="virtual"/>
<type name="downloadable"/>
</composableTypes>
```

### How would you obtain a product of a specific type?

Product type code is stored in catalog_product_entity.type_id. Thus, we can use


collection:
`$productCollection->addFieldToFilter('type_id', 'simple');`

### What tools (in general) does a product type model provide?

*_prepareProduct* - given add to cart request, returns product items that will be
converted to quote items

When product is added to cart:


- [\Magento\Checkout\Controller\Cart\Add::execute]([Link]
blob/2.2-develop/app/code/Magento/Checkout/Controller/Cart/[Link]#L80)
- [\Magento\Checkout\Model\Cart::addProduct]([Link]
develop/app/code/Magento/Checkout/Model/[Link]#L354)
- [\Magento\Quote\Model\Quote::addProduct]([Link]
develop/app/code/Magento/Quote/Model/[Link]#L1583)
- `$cartCandidates = $product->getTypeInstance()->prepareForCartAdvanced($request,
$product, $processMode);`
- [`prepareForCartAdvanced`]([Link]
code/Magento/Catalog/Model/Product/Type/[Link]#L455) simply calls
[`typeInstance._prepareProduct`]([Link]
code/Magento/Catalog/Model/Product/Type/[Link]#L371)
- type analyzes request and initializes one or many product items
- quote converts product items to quote_items

Standard types:
- `simple._prepareProduct` - just returns [product]. one record will be saved in
quote_item
- `configurable._prepareProduct` - prepares and returns [parentProduct, childProduct].
Configures parent, configures child, then adds some useful linking info to their custom
options.

* adds parent configurable product normally


* based on param `super_attribute` selections (color=red, size=XL), loads sub product
* [Link]:
+ `attributes = {color: 12, size: 35}`,
+ `product_qty_{$[Link]} = 1`
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 93 of 128

+ `simple_product = $subProduct`
+ recursively calls [Link]._prepareProduct. this will configure
child product as well
* [Link](`parent_product_id`, ID)
- `grouped._prepareProduct`
* loads sub products individually from `catalog_product_link` by type id
LINK_TYPE_GROUPED = 3
* subProduct[].getTypeInstance._prepareProduct
* some meta info is added to custom options:
+ `product_type = 'grouped'`
* all configured products are returned as separate items, not linked to any parent
- `bundle._prepareProduct` - [parentProduct, selectedChild1, ..., selectedChildN]
- `downloadable._prepareProduct` - same as simple, plus additionally generates product
links
* shows error if required links not selected
* [Link](`downloadable_link_ids`, '1,2,...')

`_prepareProduct` is mostly where product type customization are placed. Here you analyze
request (`$_POST` values),
validate input, can set prices, custom option.
Primary goal is to return configured products with custom options
(`[Link]`).
Each product will be converted to `quote_item` records, custom options will be converted
to `quote_item_option` records.

*processBuyRequest()*

Convert buyRequest back to options to configure when you added product to cart and click
Configure.

*checkProductBuyState()*

Check product has all required options. read [Link], throws exception on
error.
Used by quote_item.checkData() - checks if product in cart is healthy.

*getOrderOptions()*

Prepare additional options/information for order item which will be created from this
product.

- product_calculations - parent/child
- shipment_type - together/separately

*getSku()*, *getOptionSku()*

Glues SKU parts for custom options, bundle selections etc.

*getRelationInfo()* - [table, parent_field_name, child_field_name, where]

Used in:
- fulltext search to get attributes of product+children
- product flat indexer - insert children into flat

*getWeight()*

- configurable product used simple product's weight


- bundle product composes sum weight of components (if weight type dynamic)
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 94 of 128

*isSalable()*

- by default true if product status is enabled


- data['is_salable'] can force not salable (data attribute set by inventory from stock
index?)
- configurable - at least one enabled child product must be in stock
- bundle - all required options are salable
- downloadable - must have links

*processBuyRequest()*

Used for setting product preconfigured values - color, size, bundle selections - when
configuring added product.

[Helper\[Link]]([Link]
develop/app/code/Magento/Catalog/Helper/[Link]#L485):
```php
$optionValues = $product->processBuyRequest($buyRequest);
// $type->processBuyRequest
$optionValues->setQty($buyRequest->getQty());
$product->setPreconfiguredValues($optionValues);
```

- configurable - assigns super_attribute selection.


- gift card - assigns amount, messages, emails etc. - all form data from remembered buy
request.
# Describe price functionality

## Identify the basic concepts of price generation in Magento.

*Terms:*
- price info factory - creates *price info* and passes new *price collection* for
specific product instance and qty
- *price info* - holds *adjustment collection* and *price collection*. same across all
product types
- *price collection* - holds price pool
- *price pool* - holds price interfaces for every type of price - regular, special, final
etc.
Class is same, but a lot of copies are created by di configuration.
- *adjustment collection* - holds *adjustment pool* and adjustment codes - `tax`, `wee`,
`wee_tax`
- *adjustment pool* - holds adjustments by code - `tax` sort 20, `wee` sort 25, `wee_tax`
sort 35
- *price interface* - holds float value in display currency, price amount obj., can
return custom amount without specific adjustments
- *amount interface* - in display currency. base, full amount, adjustments

What's `wee_tax`:
- fixed product tax (FPT) can be configured to be taxed
- tax percentage over fixed tax

Flow:
- product implements Pricing\SaleableInterface
- [Link]
- \Magento\Catalog\Model\Product\Type::getPriceInfo
- Pricing\PriceInfo\Factory::create - by product instance and qty
* di injectable type configurations: product_type_id => [infoClass, prices]
* creates *price collection* = [$type_id]['prices']
+ separate *copy* of `Pricing\Price\Collection` with according *copy* of
`Pricing\Price\Pool` with
separate set of price info classes by type - regular, final etc.
+ ALL product types have personal collection and pool instance
* creates *price info* = `Pricing\PriceInfo\Base`
+ MOST product types use SAME instance `Pricing\PriceInfo\Base` with default
`Pricing\Adjustment\Collection`
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 95 of 128

+ Only bundle product holds custom *copy* with custom instance of


`Adjustment\Collection` - but has NO changes.
So it's just a separate copy.

- Pricing\PriceInfoInterface
- Pricing\Price\Collection - separate pool by product type
- Pricing\Price\Pool - iterate/access by code prices. A lot of pools per product type.
`target` argument means inherit prices from other type
* Magento\Catalog\Pricing\Price\Pool
* Magento\Bundle\Pricing\Price\Pool
* Magento\ConfigurableProduct\Pricing\Price\Pool
* Magento\Downloadable\Pricing\Price\Pool
* Magento\GroupedProduct\Pricing\Price\Pool
* Magento\GiftCard\Pricing\Price\Pool

## product type -> price collection -> price pool -> prices:
- `default` -> Magento\Catalog\Pricing\Price\Collection ->
Magento\Catalog\Pricing\Price\Pool:
* regular_price = Magento\Catalog\Pricing\Price\RegularPrice
* final_price = Magento\Catalog\Pricing\Price\FinalPrice
* tier_price = Magento\Catalog\Pricing\Price\TierPrice
* special_price = Magento\Catalog\Pricing\Price\SpecialPrice
* base_price = Magento\Catalog\Pricing\Price\BasePrice
* custom_option_price = Magento\Catalog\Pricing\Price\CustomOptionPrice
* configured_price = Magento\Catalog\Pricing\Price\ConfiguredPrice
* wishlist_configured_price = Magento\Catalog\Pricing\Price\ConfiguredPrice
* catalog_rule_price = Magento\CatalogRule\Pricing\Price\CatalogRulePrice
* (staging) catalog_rule_price =
Magento\CatalogRuleStaging\Pricing\Price\CatalogRulePrice

- `downloadable` -> Magento\Downloadable\Pricing\Price\Collection ->


Magento\Downloadable\Pricing\Price\Pool:
* link_price = Magento\Downloadable\Pricing\Price\LinkPrice
* wishlist_configured_price = Magento\Wishlist\Pricing\ConfiguredPrice\Downloadable
* _inherit rest from_ `default`

- `configurable` -> Magento\ConfigurableProduct\Pricing\Price\Collection ->


Magento\ConfigurableProduct\Pricing\Price\Pool:
* regular_price = Magento\ConfigurableProduct\Pricing\Price\ConfigurableRegularPrice
* final_price = Magento\ConfigurableProduct\Pricing\Price\FinalPrice
* wishlist_configured_price =
Magento\Wishlist\Pricing\ConfiguredPrice\ConfigurableProduct
* _inherit rest from_ `default`

- `bundle` -> Magento\Bundle\Pricing\Price\Collection ->


Magento\Bundle\Pricing\Price\Pool:
* regular_price = Magento\Bundle\Pricing\Price\BundleRegularPrice
* final_price = Magento\Bundle\Pricing\Price\FinalPrice
* tier_price = Magento\Bundle\Pricing\Price\TierPrice
* special_price = Magento\Bundle\Pricing\Price\SpecialPrice
* custom_option_price = Magento\Catalog\Pricing\Price\CustomOptionPrice
* base_price = Magento\Catalog\Pricing\Price\BasePrice
* configured_price = Magento\Bundle\Pricing\Price\ConfiguredPrice
* bundle_option = Magento\Bundle\Pricing\Price\BundleOptionPrice
* catalog_rule_price = Magento\CatalogRule\Pricing\Price\CatalogRulePrice
* wishlist_configured_price = Magento\Bundle\Pricing\Price\ConfiguredPrice

- `grouped` => Magento\GroupedProduct\Pricing\Price\Collection ->


Magento\GroupedProduct\Pricing\Price\Pool:
* final_price = Magento\GroupedProduct\Pricing\Price\FinalPrice
* configured_price = Magento\GroupedProduct\Pricing\Price\ConfiguredPrice
* wishlist_configured_price = Magento\GroupedProduct\Pricing\Price\ConfiguredPrice
* _inherit rest from_ `default`

- `giftcard` => Magento\GiftCard\Pricing\Price\Collection =>


Magento\GiftCard\Pricing\Price\Pool:
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 96 of 128

* regular_price = Magento\Catalog\Pricing\Price\RegularPrice
* final_price = Magento\GiftCard\Pricing\Price\FinalPrice
* tier_price = Magento\Catalog\Pricing\Price\TierPrice
* special_price = Magento\Catalog\Pricing\Price\SpecialPrice
* msrp_price = Magento\Msrp\Pricing\Price\MsrpPrice
* custom_option_price = Magento\Catalog\Pricing\Price\CustomOptionPrice
* base_price = Magento\Catalog\Pricing\Price\BasePrice
* configured_price = Magento\GiftCard\Pricing\Price\ConfiguredPrice
* bundle_option = Magento\Bundle\Pricing\Price\BundleOptionPrice
* wishlist_configured_price = Magento\GiftCard\Pricing\Price\ConfiguredPrice

Default *regular price* example:


- getValue - *[Link]*, convert to display currency. This is raw float value
without adjustments
- getAmount = Pricing\Adjustment\Calculator::getAmount:
* [Link]
* adjustment can be included in base price, included in display price
* if included in base price:
+ extractAdjustment - get base price without adjustment.
+ applyAdjustment - add adjustment back
* if included in display price:
+ applyAdjustment - add adjustment over base price

Default *final price* example:


- `final_price` delegates calculation to `base_price`
- gets all prices info, checks implementing `Pricing\Price\BasePriceProviderInterface`
* regular_price, catalog_rule_price, special_price, tier_price
- takes min value

And so on. Most classes customize PriceInterface `getValue`, while `getAmount` and
adjustment adding is the same.

*Adjustment*:
- isIncludedInBasePrice
* tax = true if admin prices are including tax
* weee = false, they are always added on top of base price
- isIncludedInDisplayPrice
* tax = display prices incl. tax
- *extractAdjustment* - return base price without adjustment (taxes)
- *applyAdjustment* - base price + adjustment
- getSortOrder

### How would you identify what is composing the final price of the product?
`default` final price:
- `final_price` delegates calculation to `base_price`
- gets all prices info, checks implementing `Pricing\Price\BasePriceProviderInterface`
* regular_price, catalog_rule_price, special_price, tier_price
- takes min value

`configurable` final price:


- price resolver interface
\Magento\ConfigurableProduct\Pricing\Price\PriceResolverInterface
- ConfigurableFinalPriceResolver ->
Magento\ConfigurableProduct\Pricing\Price\ConfigurablePriceResolver
- gets products with lowest prices by *lowest price options provider*, one lowest product
from each type:
* base price
* tier price
* index price
* catalog rule price
- when selecting, lowest product is also processed by *select processors*:
* status enabled
* assigned website match
* in stock (`cataloginventory_stock_status`) -- when hide out of stock
* in stock -- when show out of stock (?)
- get final price of each individual lowest product
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 97 of 128

- configurable final price is lowest of their values

`grouped` final price:


- lowest final price of all individual products in group

`bundle` final price:


- same as `default` final price (min of classes implementing interface
`BasePriceProviderInterface`)
plus `bundle_option` price - check price type fixed/dynamic, all required salable
options * qty

`giftcard` final price:


- first giftcard amount

### How can you customize the price calculation process?


1. Create price adjustment:
- Implements Pricing\Adjustment\AdjustmentInterface
- DI configuration:

```xml
<type name="Magento\Framework\Pricing\Adjustment\Collection">
<arguments>
<argument name="adjustments" xsi:type="array">
<item name="somecode"
xsi:type="const">Custom\Module\Pricing\Adjustment::ADJUSTMENT_CODE</item>
</argument>
</arguments>
</type>
```
```xml
<type name="Magento\Framework\Pricing\Adjustment\Pool">
<arguments>
<argument name="adjustments" xsi:type="array">
<item name="somecode" xsi:type="array">
<item name="className"
xsi:type="string">Custom\Module\Pricing\Adjustment</item>
<item name="sortOrder" xsi:type="string">40</item>
</item>
</argument>
</arguments>
</type>
```
- implement isIncludedInBasePrice, isIncludedinDisplayPrice, extractAdjustment,
applyAdjustment

1. Register some new price type with `default` price pool, implement interface
`Pricing\Price\BasePriceProviderInterface`.
My new price can influence final price if returns lowest price.
1. Create plugin over necessary native price classes, e.g. around getValue
1. Replace price class via DI for specific virtual pool, e.g.
```xml
<virtualType name="Magento\Bundle\Pricing\Price\Pool">
<arguments>
<argument name="prices" xsi:type="array">
<item name="regular_price"
xsi:type="string">Custom\Module\Pricing\Price\BundleRegularPrice</item>
</argument>
<arguments>
</virtualType>
```
1. Replace price class via DI preference for specific class.

## Describe how price is rendered in Magento

Terms:
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 98 of 128

- *global render* - shared block `Magento\Framework\Pricing\Render` declared in


`[Link]` layout.
Data-arguments contain global defaults.
- *local render* - specific instance block `Magento\Catalog\Pricing\Render` to render
specific price
in designated location. Can be multiple on the same page. Its block arguments overwrite
arguments
from *global renderer*. At least must set argument `price_type_code`.
- *price layout* - simple wrapper that holds separate layout instance and renders only
one layout handle `catalog_product_prices`.
- *renderer pool* - shared block `Pricing\Render\RendererPool` defined in
`catalog_product_prices.xml`.
Data-arguments describe price box *class*, *template*, *amount renderer* and
*adjustment render* for each
combination of *product type* and *price type*, or defaults when not customized.
- *price box* - `Pricing\Render\PriceBoxInterface` - renders template, can render
multiple prices like old price and special price
- *amount render* - `Pricing\Render\Amount` - renders one specific price inside price box
- *display value* and *adjustments html*
- *adjustment render* - `Pricing\Render\AbstractAdjustment` - whatever custom adjustment
markup. has access to amount render and amount

- include *local render* block in layout:


```xml
<block class="Magento\Catalog\Pricing\Render" name="[Link]">
<arguments>
<argument name="price_render" xsi:type="string">[Link]</
argument>
<argument name="price_type_code" xsi:type="string">final_price</argument>
<argument name="zone" xsi:type="string">item_view</argument>
<argument name="display_msrp_help_message" xsi:type="string">1</argument>
<argument name="id_suffix_some" xsi:type="string">copy-</argument>
</arguments>
</block>
```
- gets layout block by name in `price_render`
- *global render* - block `[Link]` = Framework\Pricing\Render
is declared in `default` handle with *default arguments* - present for all pages.
```xml
<block class="Magento\Framework\Pricing\Render" name="[Link]">
<arguments>
<argument name="price_render_handle" xsi:type="string">catalog_product_prices</
argument>
<argument name="use_link_for_as_low_as" xsi:type="boolean">true</argument>
<!-- set "override" configuration settings here -->
</arguments>
</block>
```
- *global render*.`_prepareLayout`:
* has *price layout* - mini-copy of layout. it had only one block *renderer pool*,
every product type add own arguments by type.
* [Link] from arguments `price_render_handle`, by default
`catalog_product_prices`
* [Link] - full layout processing - load, generateXml, generateElements.
- *local render*._toHtml - could be cached
- *local arguments* are merged with default arguments, additional argument `render_block`
= *local render*
- *global render*.render
- get *renderer pool* from *price layout* (`catalog_product_prices` handle):
- Pricing\Render\RendererPool::createPriceRender by *product type* and requested *price
code*:
Searches `renderer_class` in data arguments by patterns:
* `$type/prices/$priceCode/render_class`
* `$type/default_render_class`
* `default/prices/$priceCode/render_class`
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 99 of 128

* `default/default_render_class`
- creates new found *price box* block instance of `Pricing\Render\PriceBoxRenderInterface`
- finds and sets template from data arguments by patterns:
* `$type/prices/$priceCode/render_template`
* `$type/default_render_template`
* `default/prices/$priceCode/render_template`
* `default/default_render_template`
- all *price boxes* are cached: `store-template-baseUrl-template-
[price_id_prefix.productId.price_id_suffix]-priceCode`

E.g. `simple` product type renders `final_price` price box:


- price box class `Magento\Catalog\Pricing\Render\FinalPriceBox`
- template `Magento_Catalog::product/price/final_price.phtml`
- if has special price, shows 2 prices - '.special-price' and '.old-price':

has special = regular price ([Link]) < final price (lowest of regular_price,
catalog_rule_price, special_price, tier_price)

- shows as low as if applicable


- amount = [Link]() = [Link] + adjustments in display currency
- price [Link](amount)
- price [Link]
- renderer [Link] - from data-arguments by patterns:
* `$type/prices/$priceCode/amount_render_class`
* `$type/default_amount_render_class`
* `default/prices/$priceCode/amount_render_class`
* `default/default_amount_render_class`
- amount render template:
* `$type/prices/$priceCode/amount_render_template`
* `$type/default_amount_render_template`
* `default/prices/$priceCode/amount_render_template`
* `default/default_amount_render_template`

E.g. `simple` product renders `final_price` amount:


- amount render class `Pricing\Render\Amount`
- amount render template `Magento_Catalog::product/price/amount/[Link]` - '.price-
container'
- amount [Link] = [Link]() = [Link]() + adjustments
- amount [Link]
* renderer [Link] - finds adjustment = [class, template] by patterns
- can be multiple!
+ `$type/adjustments/$priceCode`
+ `$type/adjustments/default`
+ `default/adjustments/$priceCode`
+ `default/adjustments/default`
E.g. \Magento\Tax\Pricing\Render\Adjustment with `Magento_Tax::pricing/
[Link]`
- amount [Link](array)
* can suppress adjustments by setting data `skip_adjustments`
* set css classes from adjustment codes
* check that adjustment applies: [Link](code)
* adjustment [Link]
* adjustment [Link] - must implement abstract method, e.g. call `$this->toHtml`.
Can set back data-attributes to amount render, e.g. classes and labels.

### How would you render price in a given place on the page
Include new copy block type `Magento\Catalog\Pricing\Render`, pass required value for
`price_type_code` argument.

### ... and how would you modify how the price is rendered?
- use data-arguments to customize data for *price box*, *amount renders* and *adjustment
renders*:
* css_classes, price_id, price_id_prefix, price_id_suffix
- create layout handle `catalog_product_prices.xml`, reference block
`[Link]` and customize arguments.
* (product type/price type or default type) *price box* class and template, *amount
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 100 of 128

render* class and template


* (product type/price type or default type) specific adjustment class and template

# Demonstrate ability to use and customize categories

## Describe category properties and features.

Category features:
- can be anchored/non-anchored. When anchored, all products from subcategories "bubble
up" and show up in
the category listing even when _not assigned_ explicitly.
- include in menu switch
- display type - products only, cms block only, both
- you can assign direct category products and set their individual positions. Directly
assigned products
always show before "bubbled up" for anchor.

## How do you create and manage categories?


- as always, prefer using repository over directly saving model
- [Magento\Catalog\Model\CategoryRepository]([Link]
develop/app/code/Magento/Catalog/Model/[Link])
- [Magento\Catalog\Model\CategoryManagement]([Link]
develop/app/code/Magento/Catalog/Model/[Link]) - getTree, move, getCount
- [[Link]]([Link]
Catalog/Model/[Link]#L384)(newParent, afterId) -- old approach

## [Category model]([Link]
Catalog/Model/[Link]) - few changes since M1
- getTreeModel, getTreeModelInstance
- move - event `catalog_category_move_before`, `catalog_category_move_after`, event
`category_move`,
[Link], clean cache by tag
- getProductCollection
- getStoreIds

- getPathIds, getLevel
- getParentId, getParentIds - without self ID
- getParentCategory
- getParentDesignCategory - Category
- isInRootCategoryList

- getAllChildren - = by default joined ids ("2,3,4...") + parent id, recursive, only


active
- getChildren - joined ids ("2,3,4..."), only immediate, only active
- hasChildren - recursive, only active
- getAnchorsAbove - array of ids - parents that are anchor
- getProductCount - only directly assigned, all

- getCategories - tree|collection. recursive, only active, only include_in_menu, rewrites


joined
- getParentCategories - Category[], only active
- getChildrenCategories - collection, only active, only immediate, include_in_menu*,
rewrites joined

## Describe the category hierarchy tree structure implementation (the internal structure
inside the database).
- Every category has hierarchy fields: parent_id, level, path
- To avoid recursive parent-child traversing, flat category path is saved for every
category. It includes
all parents IDs. This allows to optimize children search with single query. E.g. to
search children of
category id 2 and knowing its path '1/2', we can get children:

`select * from catalog_category_entity where path like "1/2/%"`:

```
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 101 of 128

+-----------+-----------+--------------+-------+
| entity_id | parent_id | path | level |
+-----------+-----------+--------------+-------+
| 3 | 2 | 1/2/3 | 2 |
| 4 | 3 | 1/2/3/4 | 3 |
| 5 | 3 | 1/2/3/5 | 3 |
| 6 | 3 | 1/2/3/6 | 3 |
| 7 | 2 | 1/2/7 | 2 |
| 8 | 7 | 1/2/7/8 | 3 |
....
```

## What is the meaning of parent_id 0?


Magento\Catalog\Model\Category constants:
- ROOT_CATEGORY_ID = 0 -- looks like some reserved functionality, has only one child
- TREE_ROOT_ID = 1 -- all store group root categories are assigned to this node

- These categories are not visible in admin panel in category management.


- When creating root category from admin, parent_id is always TREE_ROOT_ID = 1.
- [Magento\Store\Model\Store::getRootCategoryId]([Link]
2.2-develop/app/code/Magento/Store/Model/[Link]#L998):
* TREE_ROOT_ID = 1 when store group is not selected
* store group root category ID
- store group root category allows only selecting from categoryes with parnet_id =
TREE_ROOT_ID = 1

## How are paths constructed?

Slash-separates parents and own ID.

## Which attribute values are required to display a new category in the store?

- is_active = 1
- include_in_menu = 1, but even if not displayed in menu, can be opened by direct link
- url_key can be created automatically based on name

## What kind of strategies can you suggest for organizing products into categories?
# Determine and manage catalog rules

*Primary tables*:
- `catalogrule` - dates from/to, conditions serialized, actions serialized, simple
action, discount amount
- `catalogrule_website` - rule-website
- `catalogrule_customer_group` - rule-customer group

*Index tables*:
- `catalogrule_product` - time from/to, customer group, action operator/amount
- `catalogrule_product_price` - customer group, rule date, rule price, latest start date,
earlier end date
- `catalogrule_group_website` - rule to customer group, website.
Just unique rule/customer group/websites that currently exist in index table
`catalogrule_product` for *today*.

*Replica tables* - used to fast switch new index data:


- `catalogrule_product_replica`
- `catalogrule_product_price_replica`
- `catalogrule_group_website_replica`

## Apply rules
- [*Cron job*]([Link]
CatalogRule/Cron/[Link]) 01:00 am invalidates `catalogrule_rule` indexer.
- Clicking ["Apply rules"]([Link]
Magento/CatalogRule/Model/Rule/[Link]#L54), ["Save and apply"]([Link]
magento/magento2/tree/2.2-develop/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/
Catalog/[Link]#L97) in admin panel invalidates indexer as well.
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 102 of 128

- Cron job `indexer_reindex_all_invalid`, running every minute, will pick it up and call
indexer
* Magento\CatalogRule\Model\Indexer\AbstractIndexer::executeFull
* [Magento\CatalogRule\Model\Indexer\IndexBuilder::reindexFull]([Link]
magento/magento2/tree/2.2-develop/app/code/Magento/CatalogRule/Model/Indexer/
[Link]#L280)

index builder.[doReindexFull]([Link]
code/Magento/CatalogRule/Model/Indexer/[Link]#L297):
- clear tables `catalogrule_product`, `catalogrule_product_price`
- for every rule, reindex rule product.
"one day, maybe today, maybe in a year, this rule will match these products"

* this is regardless of dates, dates are simply written in table for later uage
* get matching product IDs
* insert into `catalogrule_product` (`_replica`) combinations: product/website/customer
group
- reindex product prices `catalogrule_product_price` (`_replica`).
For yesterday, today and tomorrow reads "planned" products, and executes price update
starting from initial [Link] attribute value. If multiple rules match in same
date,
they will all apply (unless stop flag).

* dates = [yesterday; today; tomorrow]


* for each website:
* read ALL planned products from `catalogrule_product` from all rules, join `price`
product attribute
+ \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder::build
* programmatically filter dates (from/to)
* calculates price
+ \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator::calculate
+ to fixed/to percent/by fixed/by percent
* groups by [date, product id, website id, customer group id]
* persists to `catalogrule_product_price`
+ \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor::execute
- reindex *rule relations* with *customer groups and websites*
`catalogrule_group_website` (`_replica`)
* `catalogrule_group_website` - distinct combinations of customer group and website
matched for *today*
- switches tables from replica to active

## Catalog rule price info


[Magento\CatalogRule\Pricing\Price\CatalogRulePrice::getValue]([Link]
magento2/tree/2.2-develop/app/code/Magento/CatalogRule/Pricing/Price/
[Link]#L88)
- [Link]('catalog_rule_price') can be preselected by collection from index.
Used only by bundle and is deprecated.
* \Magento\CatalogRule\Model\ResourceModel\Product\CollectionProcessor::addPriceData -
join catalogrule_product_price
- [Magento\CatalogRule\Model\ResourceModel\Rule::getRulePrice]([Link]
magento2/tree/2.2-develop/app/code/Magento/CatalogRule/Model/ResourceModel/[Link]#L162)
by (date, website, customer group, product)
- [Link]
- loads directly from DB `catalogrule_product_price`

## Identify how to implement catalog price rules.


Matching products - conditions and actions serialized, are implemented on
foundation of abstract rules [Magento\Rule\Model\AbstractModel]([Link]
magento/magento2/tree/2.2-develop/app/code/Magento/Rule/Model/[Link]):
- conditions - [Magento\Rule\Model\Condition\AbstractCondition]([Link]
magento/magento2/tree/2.2-develop/app/code/Magento/Rule/Model/Condition/
[Link]), [combine]([Link]
app/code/Magento/Rule/Model/Condition/[Link])
- actions - [Magento\Rule\Model\Action\AbstractAction]([Link]
magento2/tree/2.2-develop/app/code/Magento/Rule/Model/Action/[Link]),
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 103 of 128

collection

Admin form:
- \Magento\CatalogRule\Block\Adminhtml\Promo\Catalog\Edit\Tab\Conditions
- conditions renderer \Magento\Rule\Block\Conditions
- \Magento\Rule\Model\Condition\AbstractCondition::asHtmlRecursive:
* TypeElementHtml
* AttributeElementHtml
* OperatorElementHtml
* ValueElementHtml
* RemoveLinkHtml
* ChooserContainerHtml

Matching products:
- [Magento\CatalogRule\Model\Rule::getMatchingProductIds]([Link]
magento2/tree/2.2-develop/app/code/Magento/CatalogRule/Model/[Link]#L294)
- get product collection
- for each product, run [rule::callbackValidateProduct]([Link]
magento2/tree/2.2-develop/app/code/Magento/CatalogRule/Model/[Link]#L329)
- check if product matches conditions [Magento\Rule\Model\Condition\Combine::validate]
([Link]
Condition/[Link]#L331)

## When would you use catalog price rules?


- set up discount 30% on Helloween that starts in a month
- for b2b website, discount 10% from all prices
- VIP customer group members get

## How do they impact performance?


- one SQL query on every final price request
- reindexing seems not optimized, dates match in PHP and not SQL

## How would you debug problems with catalog price rules?


- Check indexes `catalogrule_product` - find row by product id, ensure found - rule
matched
- Ensure needed rule matched and unwanted rules didn't match, check sort_order, check
from_time and to_time, ensure correct
- check `catalogrule_product_price` contains product within +-1 day
# Demonstrate ability to use quote, quote item, address, and shopping cart rules in
checkout

Interesting *quote* fields or methods:


- `ext_shipping_info` - TEXT 64k, existed in M1, not used. Good place to keep shipping
API response?
- `trigger_recollect`
* [Link] - Mark quotes that depend on catalog
price rules
to be recollected on demand. Called on event `catalogrule_before_apply`. Good way to
update carts
after "hacking" prices?
* [Link] - by product IDs. Mark recollect contain product(s)
quotes
+ global plugin after product save, _if price or tier price changed_, trigger
recollect
+ adminhtml event after product save, _if status disabled_.
- `is_changed` - not used
* before quote save - see temporary data-attribute quote.`changed_flag` - not used
- `orig_order_id` - not used
- `items_count` - INT "Display number of items in cart"
- `items_qty` - FLOAT "Display item quantities", e.g. 1.5 kg, 3 same chairs etc.
- `converted_at` - not used
- getItemsByProduct - matching product id and custom options. good to use right after add
to cart
+ [Link]
+ after withlist aded to cart, finds and changes store id
+ withlist add all to cart, on error find and remove from cart
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 104 of 128

Quote extends Model\AbstractExtensibleModel - has support of `extension_attributes`.


Register extensions in `extension_attributes.xml`:
```xml
<extension_attributes for="Magento\Quote\Api\Data\CartInterface">
<attribute code="shipping_assignments"
type="Magento\Quote\Api\Data\ShippingAssignmentInterface[]" />
</extension_attributes>
```
Use quote repository plugins afterLoad, beforeSave or whenever needed to populate your
values.
Quote does not utilize `custom_attributes` as it is not EAV by nature.

Quote address custom attributes:


* community \Magento\Quote\Model\Quote\Address\CustomAttributeList - empty, use plugin to
add
* EE
\Magento\CustomerCustomAttributes\Model\Quote\Address\CustomAttributeList::getAttributes
+ customer address attributes + customer attributes

Quote Item shares 2 models, quote item and quote address item.

*Quote item* interesting methods:


- checkData - called after adding to cart and updating options
* quote [Link] - triggers stock validation
* product type [Link]
- `custom_price`
- getCalculationPrice - `custom_price` or converted price
- isChildrenCalculated - [Link] = CALCULATE_CHILD
- isShipSeparately - [Link] = SHIPMENT_SEPARATELY
- `additional_data` - TEXT
- getOptions
- getQtyOptions:
* for composite products in cart when sub-products are added, returns array something
like
`[$subProductId = qty (from option `product_qty_{$subProductId}`), otherSubId =
qty, ...]`
+ used in \Magento\CatalogInventory\Model\Quote\Item\QuantityValidator::validate
- compare - \Magento\Quote\Model\Quote\Item\Compare::compare - merge items and add qty
instead of new item
+ \Magento\Quote\Model\Quote::updateItem
- representProduct - compares quote item with some new product, checks product id and
custom options
+ [Link]
- compareOptions
+ representProduct

## Describe how to modify these models and effectively use them in customizations.
- All support extension attributes
- Quote address supports custom_attributes

## Inventory validation
Qty can be updated from:
- cart [Link] by:
* checkout/cart/updatePost with update_cart_action='update_qty'
- [Link] by:
* admin/sales/order/create
* advanced checkout
* checkout/cart/updateItemOptions
* admin/cart_product_composite_cart/update
* quote [Link] via Savehandler and CartItemPersister

on qty update:
- quote [Link]
- event `sales_quote_item_qty_set_after`
- \Magento\CatalogInventory\Observer\QuantityValidatorObserver::execute
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 105 of 128

- \Magento\CatalogInventory\Model\Quote\Item\QuantityValidator::validate
* all validation errors can set [Link], quote [Link]
* check out of stock, parent out of stock
* when there's sub products with qty options:
+ check qty increments
+ for each sub product - initializer [Link]

\Magento\CatalogInventory\Model\Quote\Item\QuantityValidator\Initializer\Option::initialize
- multiply parent qty`*`sub qty, check resulting min sale qty, max sale qty, qty
increments, in stock, backorders
\Magento\CatalogInventory\Model\StockState::checkQuoteItemQty,
\Magento\CatalogInventory\Model\StockStateProvider::checkQuoteItemQty
* when no sub products qty:
+ initializer stock [Link]

\Magento\CatalogInventory\Model\Quote\Item\QuantityValidator\Initializer\StockItem::initialize
\Magento\CatalogInventory\Model\StockState::checkQuoteItemQty
\Magento\CatalogInventory\Model\StockStateProvider::checkQuoteItemQty

## Add to cart
cart model - deprecated.

- checkout/cart/add [product, qty, related_product]


* cart [Link]
+ filter request here, can register own via argument for
\Magento\Checkout\Model\Cart\RequestInfoFilter
+ by default filtered out `form_key` and `custom_price`
* [Link]
* product type [Link]
* get same quote item or create new
* \Magento\Quote\Model\Quote\Item\Processor::prepare
+ quote [Link]
+ support [Link] -> quote [Link]
* event `checkout_cart_product_add_after`
* checkout [Link]
* [Link]
+ quote collect totals
+ event `checkout_cart_save_after`
+ reinitialize state - remove addresses and payments
* event `checkout_cart_add_product_complete`

- checkout/cart/updatePost
* [Link] =>
\Magento\CatalogInventory\Model\StockStateProvider::suggestQty - qty increments, min/max
qty
* [Link]
+ events `checkout_cart_update_items_before`, `checkout_cart_update_items_after`
+ quote [Link] -- triggers stock validation
* [Link]

## Describe how to customize the process of adding a product to the cart.


- plugin over product type `prepareForCartAdvanced`
- event `catalog_product_type_prepare_full_options` - custom options in `_prepareOptions`
- plugin over \Magento\Quote\Model\Quote\Item\Processor::prepare - qty, custom price
- event `checkout_cart_product_add_after`

## Which different scenarios should you take into account?


- add to cart from catalog
- add to cart from wishlist
- move all wishlist to cart
- merge quote when existing customer has quote, then shops as a guest and logs in
- admin create order
- admin reorder
- configure added product - change custom options
# Demonstrate ability to use totals models
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 106 of 128

[Link]
```xml
<section name="quote">
<group name="totals">
<item name="tax" instance="Magento\Tax\Model\Sales\Total\Quote\Tax"
sort_order="450">
<renderer name="adminhtml"
instance="Magento\Sales\Block\Adminhtml\Order\Create\Totals\Tax"/>
<renderer name="frontend" instance="Magento\Tax\Block\Checkout\Tax"/>
</item>
</group>
</section>
```

Classes:
- Quote\TotalsCollector - does the job, `collect` and `collectAddressTotals`.
(collectQuoteTotals not used)

## Quote totals
- [Link]
- total = [Link] - crunch numbers, return data to set on quote
* create *quote total object* quote\address\total. all you set here will be copied to
quote
* event `sales_quote_collect_totals_before`
* collect item qtys - [Link], [Link], [Link]
* zero total object amounts - subtotal, grand total etc.
* collect each *address totals*:
+ *collect address totals* into new address total object
- address total = quote.*collectAddressTotals* _(see below)_
+ *add* to quote total object: shipping amount, shipping description, subtotal,
subtotal with discount, grant total
(+ base versions when applicable)
* validate max grand total amount 99999999
* validate coupon code - check that at least one address has it, otherwise reset to ''
- code is invalid
* event `sales_quote_collect_totals_after`
- [Link]([Link])

## Address totals
[Link] for each address (billing, shipping) - set address fields,
return some fields for quote
- new shipping assignment obj:
+ shipping = [method, address]
+ items = address items. empty for normal checkout?
- create new *address total object* quote\address\total. all you set here will be copied
to address
- event `sales_quote_address_collect_totals_before`
- get collectors:
+ _initModels -> _initModelInstance -> [Link] -- tax collector can
change its sort programmaticaly
+ sales/totals_sort/* - admin configurable *totals retrievers* ordering - display order
- *every collector[].collect*
+ all data set on address total object will be copied to address. Main amounts will
also copy to quote total object.
+ has direct access to quote
+ has access to address via shipping [Link]
- event `sales_quote_address_collect_totals_after`
- [Link](address [Link])

## Display totals
Totals rendered in cart in UI-component fashion with JS configuration.

Totals JS models extend `Magento_Checkout/js/view/summary/abstract-total` and implement:


- getPureValue - raw float
- getValue - formatted via `Magento_Catalog/js/price-utils` in format
`[Link]`.
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 107 of 128

Models get their values from model [Link](), e.g. `[Link]()().subtotal`.


Initial totals values are in `[Link]`, any extended attributes
are merged into main totals.

`[Link]` values are provided by


`\Magento\Checkout\Model\CompositeConfigProvider`.
Default config provider `\Magento\Checkout\Model\DefaultConfigProvider`.
Register your classes via arguments.

Default config [Link] and REST API reuse same repository.


\Magento\Quote\Model\Cart\CartTotalRepository::get:
- billing or shipping [Link]
* \Magento\Quote\Model\Quote\TotalsReader::fetch
* each collector[].*fetch*(quote, data)
+ can return \Magento\Quote\Model\Quote\Address\Total or array [code, title, value]
+ title can be object with method *render()* - by default this is just __ Phrase
translation
+ can return multiple totals as once
+ can overwrite other totals by code
- convert address data to interface Cart\Totals - totals by code + quote visible items
- *totals segments* - totals by code as return from fetch:
* \Magento\Quote\Model\Cart\TotalsConverter::process - convert address totals to total
segment
* title = [Link]
- add other info - coupon code, grand total, items, items qty, currency code

js model [Link]:
```
[
{address data fields = populate \Magento\Quote\Api\Data\TotalsInterface with
[Link]},
items = quote visible items,
total_segments = as returned from totals [Link]
]
```

## Invoice totals, credit memo totals


```xml
<section name="order_invoice">
<group name="totals">
<item name="subtotal" instance="Magento\Sales\Model\Order\Invoice\Total\Subtotal"
sort_order="50"/>
</group>
</section>
<section name="order_creditmemo">
<group name="totals">
<item name="subtotal"
instance="Magento\Sales\Model\Order\Creditmemo\Total\Subtotal" sort_order="50"/>
</group>
</section>
```

Invoice totals:
- \Magento\Sales\Model\Order\Invoice::collectTotals
- every total - \Magento\Sales\Model\Order\Invoice\Total\AbstractTotal::collect(invoice)
+ directly update invoice object

Credit memo totals:


- \Magento\Sales\Model\Order\Creditmemo::collectTotals
- every total - \Magento\Sales\Model\Order\Creditmemo\Total\AbstractTotal::collect
+ directly update credit memo object

## Render totals in admin area:


Example - admin invoice totals:
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 108 of 128

handle `sales_order_invoice_view`:
```xml
<block class="Magento\Sales\Block\Adminhtml\Order\Invoice\Totals" name="invoice_totals"
template="Magento_Sales::order/[Link]">
```

- block totals.`_beforeToHtml`
- block order totals.`_initTotals` - add hardcoded totals - subtotal, shipping, discount,
grand_total, base_grandtotal
- every child [Link]
- child blocks call block order [Link](total, after={'first'|'last'|$code}) or
addTotalBefore(total, before)
+ total = DataObject [code, value, base_value, label, optional: {area, block_name,
is_formated, strong}]
- block order totals template
+ totals with area = 'footer'
+ totals with empty area
+ if 'block_name', getChildHtml - simply renders child block
+ otherwise print [label, value], value will be price formated unless flag 'is_formated'

## Describe how to modify the price calculation process in the shopping cart.
- when preparing product in product type model, can set `product.custom_price`
- register quote totals collector in [Link]. Edit given address_total.
Examples:
* `$total->setGrandTotal($total->getGrandTotal() - $pointsCurrencyAmountUsed);`
* call `$total->addTotalAmount(code, value)`. Grand total will then be sum of all total
amounts - they can be negative.

## How can you add a custom totals model or modify existing totals models?
- add quote totals collector - declare in [Link], implement `collect` and `fetch`
+ in collect can modify address totals object (preferred), or edit directly quote or
quote address via shipping assignment
+ fetch can return null, or one or many declarations in array format or ready
Address\Total objects
+ fetch can rewrite other totals with same 'code'
+ in cart block JS config, add view model and implement getPureValue, getValue. Can
read values from model [Link]().totals_segments
- Render totals in admin:
+ sales_invoice_view, referenceBlock 'invoice_totals', add block with method
`initTotals`, call parentBlock.`addTotal`
# Demonstrate ability to customize the shopping cart

## Describe how to implement shopping cart rules.


DB tables:
- `salesrule`
* name, description, is_active, is_rss
* from_date, to_date, conditions_serialized, is_advanced, sort_order, product_ids
* actions_serialized, simple_action, discount_amount, discount_qty, discount_step,
apply_to_shipping
* stop_rules_processing, uses_per_customer, times_used
* coupon_type, use_auto_generation
- `salesrule_label` - label translation
* rule_id, store_id, label
- `salesrule_product_attribute`
* rule_id, website_id, customer_group_id, attribute_id

Multiple selection:
- `salesrule_website`
- `salesrule_customer_group`

- `salesrule_coupon`
* rule_id, code, usage_limit, usage_per_customer, times_used, expiration_date,
is_primary, created_at, type

Usage tracking:
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 109 of 128

- `salesrule_coupon_usage`
* coupon_id, customer_id, times_used
- `salesrule_customer`
* rule_id, customer_id, times_used
- `salesrule_coupon_aggregated`, `salesrule_coupon_aggregated_updated`,
`salesrule_coupon_aggregated_order` - reporting stuff

Notes:
- ui component `sales_rule_form`
- conditions - html content block - *generic form* Edit\Tab\Conditions
- conditions fieldset renderer \Magento\Rule\Block\[Link]
- `$element->getRule()->getConditions()->asHtmlRecursive();`
- Magento\SalesRule\Model\Rule extends Magento\Rule\Model\AbstractModel
+ store labels
+ coupon generation
- Conditions model instance - \Magento\SalesRule\Model\Rule\Condition\Combine, extends
getNewChildSelectOptions:
+ \Magento\SalesRule\Model\Rule\Condition\Product\Found - Product attribute combination
+ \Magento\SalesRule\Model\Rule\Condition\Product\Subselect - Products subselection
+ Conditions combination - self, recursion
+ \Magento\SalesRule\Model\Rule\Condition\Address - Cart Attribute:
base_subtotal, total_qty, weight, shipping_method, postcode, region, region_id,
country_id

what is:
- `is_advanced` column - not used
- `product_ids` column - not used
- `salesrule_product_attribute` table - attributes currently used in conditions and
actions of rule.

Used in plugin to \Magento\Quote\Model\Quote\Config::`getProductAttributes` to ensure


all needed
attributes are loaded with quote.
* [Link]()
* quoteResource.`_assignProducts`
* product collection `->addAttributeToSelect($this->_quoteConfig-
>getProductAttributes())`.

## What is the difference between sales rules and catalog rules?


- Catalog rules update product final price, they are visible in all catalog - category
listing, product page.
- Sales rule is visible only in checkout and can require a coupon.
- Sales rules can affect shipping price
- Sales rules can trigger on products combination

## How do sales rules affect performance?


Performance is not affected, all calculation happens in totals collectors.

## What are the limitations of the native sales rules engine?


Only one coupon can be applied.

Implementing rules:
- Rule model
* getConditionsInstance - will be used in form as `$model->getConditions()-
>asHtmlRecursive()`.
Usually starting conditions are composite - selector [all/any] and sub-conditions
selection.
- Composite conditions model
* getNewChildSelectOptions - used by Consition\[Link]

## Describe add-to-cart logic in different scenarios.


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 110 of 128

### Add to cart


cart model - used only on frontend, marked deprecated.

- checkout/cart/add [product, qty, related_product]


* cart [Link]
+ filter request here, can register own via argument for
\Magento\Checkout\Model\Cart\RequestInfoFilter
+ by default filtered out `form_key` and `custom_price`
* [Link]
* product type instance.`prepareForCartAdvanced`
* get same quote item or create new
* \Magento\Quote\Model\Quote\Item\Processor::prepare
+ quote [Link]
+ support [Link] -> quote [Link]
* event `checkout_cart_product_add_after`
* checkout [Link]
* [Link]
+ quote collect totals
+ event `checkout_cart_save_after`
+ reinitialize state - remove addresses and payments
* event `checkout_cart_add_product_complete`

- checkout/cart/updatePost
* [Link] =>
\Magento\CatalogInventory\Model\StockStateProvider::suggestQty - qty increments, min/max
qty
* [Link]
+ events `checkout_cart_update_items_before`, `checkout_cart_update_items_after`
+ quote [Link] -- triggers stock validation
* [Link]

### *controller checkout/cart/add*:


- [Link]:
* [Link]: type.`prepareForCartAdvanced`, event `sales_quote_product_add_after`
* event `checkout_cart_product_add_after`
- event `checkout_cart_add_product_complete` -- this event only when normal add to cart
* withlist observer, marked to be deleted

### *add to withlist* `wishlist/index/add`:


- same buy request
- cart candidates = type [Link] -> `_prepareProduct`. Same as with
add to cart.
- create/update qty withlist item, just like quote item
- event `wishlist_product_add_after`
- event `wishlist_add_product`

### *add to cart from wishlist* `withlist/index/cart`:

Can override original buyRequest when adding to cart.


NO event _checkout_cart_add_product_complete_.

- load withlist item and options


- merge buy request - saved and current.
- wishlist [Link]
- `[Link]` with merged buyRequest:
* events `sales_quote_product_add_after`, `checkout_cart_product_add_after`
- [Link]
* event `checkout_cart_save_before`
* event `checkout_cart_save_after`
- [Link]
- \Magento\Wishlist\Helper\Data::calculate
* write to customer session
* event `wishlist_items_renewed`

### *merge quotes* controller `customer/account/loginPost`:


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 111 of 128

Cart model is not used at all.

- \Magento\Customer\Model\Session::setCustomerDataAsLoggedIn
- event `customer_login`
- \Magento\Checkout\Observer\LoadCustomerQuoteObserver
- checkout session.`loadCustomerQuote`:
* event `load_customer_quote_before`
* finds existing customer quote
* detects that guest quote <> customer quote
* customer quote.`merge`(guest quote):
+ event `sales_quote_merge_before`
+ for each quote item.`compare` - product id, options same
+ if items same, qty++
+ if guest item is new, clone quote item, [Link], event `sales_quote_add_item`
+ event `sales_quote_merge_after`
* *delete guest quote*

### *reorder* controller `sales/order/reorder`:

Like normal add to cart, but NO event _checkout_cart_add_product_complete_.

- load order, ensure can view


- for each order item, `[Link]` - converts order item to quote item:
* load product
* get item option buyRequest
* `[Link]` with old buy request
+ events `sales_quote_product_add_after`, `checkout_cart_product_add_after`

## Describe how to customize the process of adding a product to the cart.


- plugin over product type `prepareForCartAdvanced`
- event `catalog_product_type_prepare_full_options` - custom options in `_prepareOptions`
- plugin over \Magento\Quote\Model\Quote\Item\Processor::prepare - qty, custom price
- event `checkout_cart_product_add_after`

## Which different scenarios should you take into account?


- add to cart from catalog
- add to cart from wishlist
- move all wishlist to cart
- merge quote when existing customer has quote, then shops as a guest and logs in
- admin create order
- admin reorder
- configure added product - change custom options

## Render product types


checkout cart index:
- `<update handle="checkout_cart_item_renderers"/>`
- `<update handle="checkout_item_price_renderers"/>`

- block \Magento\Checkout\Block\Cart\AbstractCart::getItems - quote visible items


* can limit with *pagination*
* *custom_items* can override
- abstract cart.`getItemHtml`(quote item)
- render via abstract cart.`getItemRenderer` by product type
* renderer child block `[Link]` \Magento\Framework\View\Element\RendererList
* renderer list [Link](type, template)
* finds child block by alias = product type
* \Magento\Checkout\Block\Cart\Item\Renderer
*

Customizations:
- provide cart block data arguments `<referenceBlock name="[Link]">`:
* `renderer_list_name` - use custom renderer list
* `overriden_templates` - array by product type
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 112 of 128

* `renderer_template` - regardless of product type


- in `checkout_cart_item_renderers`, `<referenceBlock name='[Link]'>`, add child
blocks by alias = product type. E.g.

```xml
<referenceBlock name="[Link]">
<block class="Magento\Checkout\Block\Cart\Item\Renderer"
name="[Link]" as="default" template="Magento_Checkout::cart/
item/[Link]">
<block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions"
name="[Link]" as="actions">
<block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Edit"
name="[Link]" template="Magento_Checkout::cart/
item/renderer/actions/[Link]"/>
<block class="Magento\Checkout\Block\Cart\Item\Renderer\Actions\Remove"
name="[Link]"
template="Magento_Checkout::cart/item/renderer/actions/[Link]"/>
</block>
</block>
</referenceBlock>
```

## Describe the available shopping cart operations.

### Configure product in cart - controller `checkout/cart/configure`


Product view and product configure are rendered via same helper
\Magento\Catalog\Helper\Product\[Link].

The only difference is params:


- category_id - can be `false`
- configure_mode - optional
- buy_request - optional
- specify_options - error message if missing options

*Normal product view*:


- params[category_id] = current
- params[specify_options] - from product type
- helper product [Link]
* \Magento\Catalog\Helper\Product::initProduct - load product, check visible/enabled
+ event `catalog_controller_product_init_before`
+ event `catalog_controller_product_init_after`
* event `catalog_controller_product_view`

*Configure product*:
- params[`buyRequest`] = quote [Link]
- helper product [Link]
* product [Link]
* product helper.`prepareProductOptions` - set default selections from buy request,
e.g. selected size=XXL
+ [Link], `product [Link]`, `product
[Link]`
+ product.`setPreconfiguredValues`
* [Link] -- used to show all hidden customization options instantly
in edit mode
* event `catalog_controller_product_view`

[Link] is later used:


- default product qty = [Link] [Link]
- custom option values = [Link] values.option_{$id}

### How do you add a field to the shipping address?

Quote address extends AbstractExtensibleModel, and so should implement


`getCustomAttributesCodes()`
to add custom attributes. Custom attributes should load and save automatically, assuming
you added
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 113 of 128

a column in migration and registered custom attribute in plugin (see below).

Quote address custom attributes:


* community \Magento\Quote\Model\Quote\Address\CustomAttributeList - empty, use plugin to
add
* EE
\Magento\CustomerCustomAttributes\Model\Quote\Address\CustomAttributeList::getAttributes
+ customer address attributes + customer attributes

Alternatively, you can always register extension attribute and load/save it manually.
# Demonstrate ability to customize shipping and payment methods

[Customize checkout]([Link]
checkout_overview.html)

## Describe how to troubleshoot payment methods.

Awesome Magento documentation - [Payment Provider Gateway]([Link]


guides/v2.2/payments-integrations/[Link])

Following is brief digest of official documentation.

Magento 2 replaces abstract payment method that you would normally extend with payment
adapter.
Where there used to be protected methods for every aspect - validate, set data,
authorize, invoice, refund etc. -
now it moves to separate classes. Just like with controllers where each action resides in
own class.
All you need to do is wire your pieces together with DI config.

Additional benefit - you can smoothly choose between simple class implementation or make
a composite - split
your business logic into even more fine grained pieces and mix them.

Terms:
- *payment adapter* - config-based payment method implementation. Holds operation pool,
payment validator,
value handler, form block type, info block type.

[Model\Method\Adapter]([Link]
Payment/Model/Method/[Link])

- *validator pool* - check country, currency, min-max amount etc. Magento 1 equivalent
is `[Link]()`.

Fixed validator codes:


- `global` - called in `payment [Link]()`. Called by quote [Link]
right after `payment [Link]`.
- `availability` - called in `payment [Link]()`
- `country` - called in `payment [Link]()`
- `currency` - called in `payment [Link]()`

[Gateway\Validator\ValidatorInterface]([Link]
code/Magento/Payment/Gateway/Validator/[Link])

- *value handler pool* - returns configuration params like `can_void`, `can_capture`.


Holds multiple value handlers
by config param name and required handler `default`.

* [Gateway\Config\ValueHandlerPool]([Link]
code/Magento/Payment/Gateway/Config/[Link])
* [Gateway\Config\ValueHandlerInterface]([Link]
app/code/Magento/Payment/Gateway/Config/[Link])

- [*command pool*]([Link]
gateway/[Link])
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 114 of 128

- holds commands.

[Gateway\Command\CommandPool]([Link]
Magento/Payment/Gateway/Command/[Link])

- [*gateway command*]([Link]
payment-gateway/[Link])
- authorize, sale, invoice, refund, cancel, void etc. Holds request builder, transfer
factory, client,
operation validator, result processor.

Magento 1 analogs - `[Link]()`, `[Link]()`, `[Link]()` etc.

[Gateway\CommandInterface]([Link]
Magento/Payment/Gateway/[Link])

- [*request builder*]([Link]
payment-gateway/[Link])
- given subject (quote, order or invoice) as array, prepares request parameters
as array - amounts, transaction ids, cart contents etc.

[Gateway\Request\BuilderInterface]([Link]
code/Magento/Payment/Gateway/Request/[Link])

- *transfer factory* - convert request parameters into transfer object. Here you add
lowest level call params -
request method (get/post etc.), headers, API URL, encode body. Transfer object is built
with transfer factory
builder, just like you use searchCriteriaBuilder to create searchCriteria.

[Gateway\Http\TransferFactoryInterface]([Link]
app/code/Magento/Payment/Gateway/Http/[Link])

- [*gateway client*]([Link]
payment-gateway/[Link])
- simply makes a call with given parameters, e.g. sends Curl request. Don't reinvent
the wheel, you shouldn't need to implement this, just use default Zend and Soap clients,
you only need to format transfer object for them.

[Gateway\Http\ClientInterface]([Link]
Magento/Payment/Gateway/Http/[Link])

- [*response validator*]([Link]
payment-gateway/[Link])
- checks response - maybe HTTP response code, body, parse returned errors etc.

[Gateway\Validator\ResultInterface]([Link]
code/Magento/Payment/Gateway/Validator/[Link])

- [*response handler*]([Link]
payment-gateway/[Link])
- save response detauls in payment additional data, save transaction numbers, change
order status, send email etc.

[Gateway\Response\HandlerInterface]([Link]
code/Magento/Payment/Gateway/Response/[Link])

- *form block type* - payment form only in adminhtml.

- *info block type* - payment info

* [Block\Info]([Link]
Block/[Link])
- like in M1, shows key-value strings, overwrite `_prepareSpecificInformation`.
* [Block\ConfigurableInfo]([Link]
Magento/Payment/Block/[Link]) - automatically shows values from
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 115 of 128

payment.additional_information:
+ [Link] payment method declaration - show `paymentInfoKeys` and exclude
`privateInfoKeys` - comma separated.
+ override `getLabel` to show human labels instead of raw field keys

[Link] standard fields:


- active
- title
- payment_action
- can_initialize
- can_use_internal
- can_use_checkout
- can_edit
- is_gateway
- is_offline
- min_order_total
- max_order_total
- order_place_redirect_url
- ...

Gateway\ConfigInterface - reads scope config by pattern:


- getValue(field, store) - [Link](pathPattern + methodCode)
- setMethodCode - used in getValue
- setPathPattern - used in getValue

## Passing user data from frontend checkout form to payment method:

[Get payment information from frontend to backend]([Link]


v2.2/payments-integrations/base-integration/[Link])

- in JS method renderer implement `getData`. default fields `method` and `po_number`.


You can explicitly send `additional_data`, or all unknown keys will automatically be
[moved]([Link]
[Link]#L212)
into `additional_data` in backend.
-
\Magento\Checkout\Model\GuestPaymentInformationManagement::savePaymentInformationAndPlaceOrder
- \Magento\Quote\Model\PaymentMethodManagement::set
- quote payment.[importData]([Link]
Magento/Quote/Model/Quote/[Link]#L170)
- event `sales_quote_payment_import_data_before`
- `method [Link]`
- method [Link] - checks one by one:
* can use checkout - `$paymentMethod->canUseCheckout`
* use for country - `$paymentMethod->canUseForCountry`
* use for currency - `$paymentMethod->canUseForCurrency`
* order total min max - between `$paymentMethod->getConfigData('min_order_total')` and
`max_order_total`

[Model\Checks\SpecificationInterface]([Link]
code/Magento/Payment/Model/Checks/[Link])

- `method [Link]`:
- event `payment_method_assign_data_{$code}`
- event `payment_method_assign_data`

## Old Magento 1 payment flow


1. get payment methods
- isAvailable, event `payment_method_is_active`
- [Link] -- check country, currency, min-max, zero total
2. save payment
- [Link]:
* event `sales_quote_payment_import_data_before`
* [Link]
* [Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 116 of 128

- quote payment._beforeSave
* [Link]
- [Link]

### GET /V1/carts/mine/payment-information - get available methods


- \Magento\Checkout\Model\PaymentInformationManagement::getPaymentInformation
- \Magento\Quote\Model\PaymentMethodManagement::getList
- \Magento\Payment\Model\MethodList::getAvailableMethods
* `[Link]`
* `[Link]`
* method specification.`isApplicable`

### POST /V1/carts/mine/payment-information - click Place order button


-
\Magento\Checkout\Model\PaymentInformationManagement::savePaymentInformationAndPlaceOrder
* payment = method, po_number, additional_information
- \Magento\Quote\Model\PaymentMethodManagement::set
* `[Link]` - checkout, country, currency, min-max
* quote [Link] - method, po_number, additional_information
+ event `sales_quote_payment_import_data_before`
+ merge checks + additionalChecks -- can set via DI
+ \Magento\Payment\Model\Checks\SpecificationFactory::create -- use DI to register
new checks by code
+ `[Link]`
+ method [Link] - checkout, country, currency, min-max + DI
registered
+ `[Link]` -- assuming Payment Adapter class:
- event `payment_method_assign_data_{$code}`
- event `payment_method_assign_data`
+ `[Link]` -- assuming Payment Adapter class:
- validator[`global`].validate

## Facade - Payment Adapter


- isActive - getConfiguredValue('active'):
* value handler pool[`active`].handle(['field'=>'active'])
* or value handler pool[`default`].handle(['field'=>'active'])
- isAvailable
* isActive
* validatorPool[`availability`].validate
* event `payment_method_is_active` -- can override result, same as M1
- assignData
* event `payment_method_assign_data_{$code}`
* event `payment_method_assign_data`
- validate -- called after [Link] in placeOrder
* validatorPool[`global`].validate
- canUseForCountry
* validatorPool[`country`].validate
* called by method [Link]
- canUseForCurrency
* validatorPool[`currency`].validate
- canOrder, canAuthorize, canCapture, canCaptuerPartial, canCaptureOnce,
canRefundPartialPerInvoice, canVoid,
canUseInternal, canUseCheckout, canEdit, canFetchTransactionInfo, canReviewPayment,
isGateway, isOffline,
isInitializationNeeded:
* read from config `can_*`

*Commands:*

Commands are normally executed by `[Link](name).execute()`. There's a strange


opportunity to
inject *command manager* - `commandExecutor` argument - that will run all commands
instead.
There's even a default command manager implementation - it contains command pool and runs
then just the same.
But this doesn't seem to be used.
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 117 of 128

- fetchTransactionInfo() - `can_fetch_transaction_information`,
`fetch_transaction_information` gateway command
- initialize() - `can_initialize`, `initiailze` command
- order() - `can_order`, `order` gateway command. same as authorize + capture = sale?
- authorize() - `can_authorize`, `authorize` command
- capture() - `can_capture`, `capture` command
- refund() - `can_refund`, `refund` command
- cancel() - `can_cancel`, `can_cancel` command
- void() - `can_void`, `void` command
- acceptPayment() - `can_accept_payment`, `accept_payment` command
- denyPayment() - `can_deny_payment`, `deny_payment` command

Two more special commands: `vault_authorize`, `vault_sale`, `vault_capture`

braintree - BraintreeFacade = Model\Method\Adapter:


- BraintreeValueHandlerPool: default, can_void, can_cancel
- BraintreeCommandPool: authorize, sale, capture, settlement, vault_authorize, vault_sale,
vault_capture, void, refund, cancel, deny_payment
- BraintreeValidatorPool: country

braintree_cc_vault - BraintreeCreditCardVaultFacade = Magento\Vault\Model\Method\Vault:


- `vaultProvider` = BraintreeFacade. very important, all methods proxy to this
- BraintreeVaultPaymentValueHandlerPool: default

Magento\Vault\Model\Method\Vault:
- pure class, no parent, interface \Magento\Vault\Model\VaultPaymentInterface
- proxies almost all methods: is`*`, can`*`, validate
- not implemented, throws error: initialize(), order(), refund(), cancel(), void(),
acceptPayment(), denyPayment()
- assignData:
* event `payment_method_assign_data_vault`
* event `payment_method_assign_data_vault_{$code}`
* original payment `[Link]`
- authorize:
* attach token extension attribute:
+ `customer_id` from payment.additional_information
+ `publish_hash` from payment.additional_information
+ order [Link] attributes[`vault_payment_token`] =
\Magento\Vault\Api\PaymentTokenManagementInterface::getByPublicHash(`publish_hash`,
`customer_id`)
+ select from `vault_payment_token` where publish_hash = ? and customer_id = ?/NULL
* commandManagerPool[`[Link]`].executeBycode(`vault_authorize`)
- capture:
* ONLY for `order`=`sale` payment action? Authorization transaction must not exist
* attach token extension attribute
* commandManagerPool[`[Link]`].executeBycode(`vault_sale`)

### Payment Tips

Sometimes capture should do one of 3 things - sale, capture or vault_capture.


Braintree makes CaptureStrategyCommand command that checks payment and
calls needed capture type command.

Virtual types can extend and augment each other pretty heavily, for example
BraintreePayPalSaleCommand is based on BraintreePayPalAuthorizeCommand
and changes only request builder.

To make Vault work, after you receive normal payment, you save token by customer.
Braintree
does this in AuthorizationCommand handler chain in one of handlers. Token
is simply read from response and put to order
payment.extension_attributes[`vault_payment_token`].
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 118 of 128

Vault on frontend:
- in checkout config provider, finds all payment methods implementing Vault interface and
returns codes for JS
- in checkout payment methods, adds `vault` payment method group
- get component providers \Magento\Vault\Model\Ui\TokenUiComponentProviderInterface -
return params for JS component
* Braintree registers component in DI \Magento\Vault\Model\Ui\TokensConfigProvider
- tokens = select from `vault_payment_token` where customer_id = ? and is_visible = 1 and
is_active = 1 and expires_at > time
- for each token, run component providers[[Link]].getComponentForToken
- [Link] = [
`{$code}_{$i}` = componentConfig,
braintree_cc_vault_0 = {...},
braintree_cc_vault_1 = {...},
]

## What types of payment methods exist?


- offline
- online
- gateway
- vault - works over gateway, but executes different commands for authorize and capture.
Always work in terms of previously saved token for customer.

## What are the different payment flows?


- isInitializationNeeded = true

Manually do the job in `initialize` and return custom order status and state.

- authorize, capture
- order = authorize + capture
## 9.1 Demonstrate ability to customize sales operations

Describe how to modify order processing and integrate it with a third-party ERP system.

*Describe how to modify order processing flow. How would you add new states and statuses
for an order?*

*How do you change the behavior of existing states and statuses?*

Described how to customize invoices.

*How would you customize invoice generation, capturing, and management?*

Describe refund functionality in Magento.

*Which refund types are available, and how are they used?*
# Overview

Magento 2 Certified Professional Developer notes

# 1. Topics

1. 18% 1 - Magento Architecture and Customization Techniques (11 questions)


- modules, config, di, plugins, events, cron, cli, cache
2. 12% 2 - Request Flow Processing (7 questions)
- modes, front contr., url, rewrite, action contr., response, routes, 404, layout,
page layout
3. 10% 3 - Customizing the Magento UI (6 questions)
- theme, template, block, block cache, JS, UI component (briefly)
4. 7% 4 - Working with Databases in Magento (4 questions)
- repository, api data class, search criteria, table, load/save, collection,
select, migration
5. 8% 5 - Using the Entity-Attribute-Value (EAV) Model (5 questions)
- hierarchy, storage, load/save, attributes, frontend/source/backend
6. 10% 6 - Developing with Adminhtml (6 questions)
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 119 of 128

- form, grid, [Link], menu, acl


7. 12% 7 - Customizing the Catalog (7 questions)
- product types, price, price render, category, catalog rules
8. 13% 8 - Customizing the Checkout Process (8 questions)
- cart rule, add to cart, quote totals, product type render, shipping method,
payment method
* normal/wishlist/reorder/quote merge
9. 5% 9 - Sales Operations (3 questions)
- order processing, status, invoice, refund
10. 5% 10 - Customer Management (3 questions)
- my account, customer extension attributes, address, customer group, tax

### 1.2 Magento 2 Certified Professional Developer

+ 60 Multiple Choice items


+ 90 minutes to complete the exam
+ A score of 64% or higher is needed to pass the Magento 2 Certified Professional
Developer exam
+ Based on Magento Open Source (2.2) and Magento Commerce (2.2), but applicable to those
using any version of Magento 2.

# 2. Resources

## 2.1. Links
+ [Magento 2 Certified Professional Developer]([Link]
professional-developer)
+ [Magento 2.2 Developer Documentation]([Link]
+ [Markdown Cheatsheet]([Link]
+ [Markdown Cheatsheet RU]([Link]
+ [Swiftotter Study Guide]([Link]
certified-developer-study-guide)

## 2.2. Credits

Inspired by [Link]
# Customer Segments

Conditions:

- customer address attributes


- customer attributes
- cart item, total qty, totals
- products in cart, in wishlist, viewed products, ordered products, viewed date, ordered
date
- order address, totals, number of orders, average total amount, order date, order status
- only logged in

Listens all model evetns, matches conditions by


\Magento\CustomerSegment\Model\Segment\Condition\Order\Address::getMatchedEvents

## Database

- magento_customersegment_segment - name, conditions, actions


- magento_customersegment_website - many-to-many
- magento_customersegment_customer - website, added/updated date
- magento_customersegment_event - after saving segment rule, holds events that related to
selected conditions

## Admin conditions

namespace Magento\CustomerSegment;

Admin form - old widget style tabs:


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 120 of 128

- Block\Adminhtml\Customersegment\Edit\Tab\General
- Block\Adminhtml\Customersegment\Edit\Tab\Conditions

Rendering conditions form:

render conditions -> renderer \Magento\Rule\Block\Conditions -> [Link] ->


root Model\Segment\Condition\Combine\Root

Condition classes - hold matching rules:

- Model\Condition\Combine\AbstractCombine
- Model\Segment\Condition\Order\Address - order_address join sales_order join
customer_entity
- Model\Segment\Condition\Sales\Combine
- Model\Segment\Condition\Sales\Ordersnumber - COUNT(*)
- Model\Segment\Condition\Sales\Salesamount - sum/avg sales_order.base_grand_total
- Model\Segment\Condition\Sales\Purchasedquantity - sum/avg
sales_order.total_qty_ordered
- Model\Segment\Condition\Customer\Address
- Model\Segment\Condition\Customer\Address\DefaultAddress

## Refresh Segment Data

Some conditions don't work for guest visitors, despite description! Example - cart grand
total amount:

- click "Refresh" inside segment admin


- Controller\Adminhtml\Report\Customer\Customersegment\Refresh::execute
- Model\Segment::matchCustomers
- Model\ResourceModel\Segment::aggregateMatchedCustomers
- Model\ResourceModel\Segment::processConditions
- Model\Segment\Condition\Combine\Root::getSatisfiedIds
- // individual conditions
- Model\Segment\Condition\Shoppingcart\Amount::getSatisfiedIds

```SQL
SELECT `quote`.`customer_id` FROM `quote`
WHERE (quote.is_active=1) AND (quote.store_id IN('1')) AND (quote.base_grand_total >=
'100') AND (customer_id IS NOT NULL)
```

## Viewed product index

Condition by recently viewed events **SEEMS BROKEN** when full page cache is enabled.
This condition relies on `report_viewed_product_index` table, which is handled by report
module.

Configuration:

- `reports/options/enabled`
- `reports/options/product_view_enabled`

\Magento\Reports\Model\ReportStatus::isReportEnabled

\Magento\Reports\Model\Product\Index\Viewed

`catalog_controller_product_view` -> \Magento\Reports\Observer\CatalogProductViewObserver


- save into `report_viewed_product_index`
- save into `report_event`, take customer from session

This does not work when full page cache is enabled and Varnish returns HTML directly.
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 121 of 128

## Listening to events and updating matched customers

Segments needs to listen to some events related to conditions.

After segment with conditions is saved, table `magento_customersegment_event` holds


events that it listens to:

- Model\Segment::beforeSave
- Model\Segment::collectMatchedEvents
- recursively for each added conditions children:
- Model\Condition\Combine\AbstractCombine::getMatchedEvents

Segment rule table holds rendered `condition_sql`:

- Model\Segment::beforeSave
- Model\Rule\Condition\Combine\Root::getConditionsSql
- recursively for each added conditions children:
- Model\Condition\Combine\AbstractCombine::getConditionsSql
- `_prepareConditionsSql`

`condition_sql` column placeholders:


- :customer_id
- :website_id
- :quote_id
- :visitor_id

Updating customer segments on events:

- event `customer_login`
- Observer\ProcessEventObserver::execute
- Model\Customer::processEvent
- Model\Customer::getActiveSegmentsForEvent
- Model\ResourceModel\Segment\Collection::addEventFilter - join table
`magento_customersegment_event`
- Model\Customer::_processSegmentsValidation
- Model\Segment::validateCustomer
* visitor_id, quote_id passed for guest
- Model\Segment\Condition\Combine\Root::isSatisfiedBy
* all conditions[].isSatisfiedBy
* all conditions[].Model\Condition\Combine\AbstractCombine::getConditionsSql

Result:

- Matched segments are added to `magento_customersegment_customer`


- http context \Magento\CustomerSegment\Helper\Data::CONTEXT_SEGMENT = 'customer_segment'
is updated with new segments
- [Link]

Event -> interested segment rules.

## Impact on performance

1. Customer segments are added to full page cache keys

\Magento\CustomerSegment\Helper\Data::CONTEXT_SEGMENT = 'customer_segment'

\Magento\PageCache\Model\App\Response\HttpPlugin::beforeSendResponse
\Magento\Framework\App\Response\Http::sendVary
\Magento\Framework\App\Http\Context::getVaryString
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 122 of 128

Example:

```php
sha1($this->serializer->serialize([
'customer_group' => '1',
'customer_logged_in' => true,
'customer_segment' => ["1"]
]))
```

This lowers chances of hitting cached page, increasing load on the server.

1. Listens to many object save events, calculates matching conditions

## DepersonalizePlugin

Does 2 things:

1. Makes sure [Link] is not deleted by Customer module


DepersonalizePlugin
1. Sets full page cache key httpContext->setValue(Data::CONTEXT_SEGMENT)

\Magento\Framework\Pricing\Render\Layout::loadLayout:
```
$this->layout->getUpdate()->load();
// 1. \Magento\Customer\Model\Layout\DepersonalizePlugin::beforeGenerateXml:
// - remember customerGroupId and formKey from session
// 2.
\Magento\CustomerSegment\Model\Layout\DepersonalizePlugin::beforeGenerateXml:
// - remember customerSegmentIds from customer session
$this->layout->generateXml();
$this->layout->generateElements();
// 3.
\Magento\PageCache\Model\Layout\DepersonalizePlugin::afterGenerateElements:
// - event `depersonalize_clear_session`
// - session_write_close();
// - clear message session
// 4. \Magento\Customer\Model\Layout\DepersonalizePlugin::afterGenerateElements:
// - clear customer session
// - restore session._form_key
// - restore customer [Link], empty customer object with
customer group
// 5. \Magento\Catalog\Model\Layout\DepersonalizePlugin::afterGenerateElements:
// - clear catalog session
// 6.
\Magento\Persistent\Model\Layout\DepersonalizePlugin::afterGenerateElements:
// - clear persistent session
// 7.
\Magento\CustomerSegment\Model\Layout\DepersonalizePlugin::afterGenerateElements:
// - httpContext->setValue(Data::CONTEXT_SEGMENT)
// - restore customerSession->setCustomerSegmentIds
// 8. \Magento\Checkout\Model\Layout\DepersonalizePlugin::afterGenerateElements:
// - clear checkout session
```

## Create segment programmatically

Doesn't have API interfaces, repository.

```PHP
/** @var \Magento\CustomerSegment\Model\Segment $segment */
$segment = $this->segmentFactory->create();
$segment->setName($name);
$segment->setDescription(null);
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 123 of 128

$segment->setIsActive(1);
$segment->setWebsiteIds(array_keys($this->storeManager->getWebsites()));
$segment-
>setApplyTo(\Magento\CustomerSegment\Model\Segment::APPLY_TO_VISITORS_AND_REGISTERED);
// @see \Magento\Rule\Model\AbstractModel::loadPost
$segment->getConditions()->setConditions([])->loadArray([
'type' => \Magento\CustomerSegment\Model\Segment\Condition\Combine\Root::class,
'aggregator' => 'all',
'operator' => '',
'value' => '1',
'conditions' => [
[
'type' =>
\Magento\CustomerSegment\Model\Segment\Condition\Shoppingcart\Amount::class,
'attribute' => 'grand_total',
'operator' => '>=',
'value' => '100',
]
],
]);
$segment->save();
```

## Catalog frontend action

Magento has alternative implementation of recently viewed products, handled by Catalog


module.
It works with full page cache, but:

- is **not integrated** into customer segment conditions


- frontend synchronization must be enabled, adding extra calls on every page

When sync is enabled, it makes 2 AJAX calls with every page opening:
- synchronize recently viewed products from local storage
- synchronize recently compared products from local storage

Configuration:

- `catalog/recently_products/synchronize_with_backend` = 0
- `catalog/recently_products/recently_viewed_lifetime` = 1000
- `catalog/recently_products/scope` - store view, store, website

Other:

- db table `catalog_product_frontend_action`
- cron `catalog_product_frontend_actions_flush` - every minute
\Magento\Catalog\Cron\FrontendActionsFlush:
- deletes compared, viewed older than (default 1000) seconds
- frontend controller 'catalog/product_frontend_action/synchronize'

Notable classes:

- Magento\Catalog\Model\FrontendStorageConfigurationPool
- Magento\Catalog\Model\Widget\RecentlyViewedStorageConfiguration

\Magento\Catalog\Block\FrontendStorageManager:

- recently_viewed_product
- recently_compared_product
- product_data_storage
- vendor/magento/module-catalog/view/frontend/web/js/[Link]

customer data section:


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 124 of 128

- recently_viewed_product
- recently_compared_product
- product_data_storage

Triggering recently viewed update on frontend:

- catalog_product_view.xml
- Magento\Catalog\Block\Ui\ProductViewCounter
- vendor/magento/module-catalog/view/frontend/templates/product/view/[Link]
- vendor/magento/module-catalog/view/frontend/web/js/product/view/[Link]

\Magento\Catalog\Model\ProductRender - DTO

\Magento\Catalog\Ui\DataProvider\Product\ProductRenderCollectorComposite::collect:

- Url - url, addToCartButton, addToCompareButton


- AdditionalInfo - isSalable, type, name, id
- Image - images
- Price - priceInfo
- ... wishlist, review, tax, gift card, bundle price

widget `catalog_recently_compared` template `product/widget/compared/[Link]`:

- UI component `widget_recently_compared` - listing, columns


- Magento_Catalog/js/product/[Link]
- Magento_Catalog/js/product/storage/[Link]
- Magento_Catalog/js/product/storage/[Link] - get product data from customer
section. recently viewed, compared
# Magento 2 Staging

## High level staging implementation overview

- Future editions of products/categories etc. are saved as a copy with a timestamped from-
to columns.
- Current staging version is saved as a flag, used in all collections to add WHERE
condition
- You can browse (preview) frontend site seeing future versions passing version and
signature GET parameters

## Demonstrate an understanding of the events processing flow

Influence of Staging on the event processing.

What is a modification of the event processing mechanism introduced by the staging module?

\Magento\Staging\Model\Event\Manager:

- ban some events in preview mode - none by default


- ban some observers in preview mode

- `catalog_category_prepare_save`
- `catalog_category_save_after`
- `catalog_category_save_before`
- `catalog_category_move_after`

## DB collection flag `disable_staging_preview`

- connection->select()
- \Magento\Framework\DB\Adapter\Pdo\Mysql::select
- Magento\Framework\DB\SelectFactory::create
- Magento\Framework\DB\Select\SelectRenderer
- // work with select...
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 125 of 128

- \Magento\Framework\DB\Select::assemble
- \Magento\Framework\DB\Select\SelectRenderer::render
- \Magento\Framework\DB\Select\FromRenderer
- \Magento\Staging\Model\Select\FromRenderer

```
from = [
flag = [
joinType = 'from'
tableName = 'flag'
]
]
```

```
$select->where($alias . '.created_in <= ?', $versionId);
$select->where($alias . '.updated_in > ?', $versionId);
```

Only known staged tables are affected -


\Magento\Staging\Model\StagingList::getEntitiesTables - `*_entity`

## Staging modification to the Magento database operations (row_id, entity managers)

Flow modifications introduced by staging (triggers, row_id, data versions).

- deletes foreign keys to removed entity_id column


- creates foreign key to row_id
- replaces indexes

**Triggers** created differently! Attributes no longer have entity_id for `_cl` tables,
so one more
extra call to load entity_id by row_id is added to each update by schedule trigger.

\Magento\Framework\Mview\View\Subscription::buildStatement
vs
\Magento\CatalogStaging\Model\Mview\View\Attribute\Subscription::buildStatement

trg_catalog_product_entity_int_after_insert:
```SQL
-- before
INSERT IGNORE INTO `catalog_product_flat_cl` (`entity_id`) VALUES (NEW.`entity_id`);
-- after
SET @entity_id = (SELECT `entity_id` FROM `catalog_product_entity` WHERE `row_id` =
NEW.`row_id`);
INSERT IGNORE INTO `catalog_product_flat_cl` (`entity_id`) values(@entity_id);
```

trg_catalog_product_entity_int_after_update:
```SQL
-- before
IF (NEW.`value_id` <=> OLD.`value_id` OR NEW.`attribute_id` <=> OLD.`attribute_id` OR
NEW.`store_id` <=> OLD.`store_id` OR NEW.`entity_id` <=> OLD.`entity_id` OR NEW.`value`
<=> OLD.`value`) THEN INSERT IGNORE INTO `catalog_product_flat_cl` (`entity_id`) VALUES
(NEW.`entity_id`); END IF;
-- after
SET @entity_id = (SELECT `entity_id` FROM `catalog_product_entity` WHERE `row_id` =
NEW.`row_id`);
IF (NOT(NEW.`value_id` <=> OLD.`value_id`) OR NOT(NEW.`attribute_id` <=>
OLD.`attribute_id`) OR NOT(NEW.`store_id` <=> OLD.`store_id`) OR NOT(NEW.`value` <=>
OLD.`value`) OR NOT(NEW.`row_id` <=> OLD.`row_id`)) THEN INSERT IGNORE INTO
`catalog_product_flat_cl` (`entity_id`) values(@entity_id); END IF;
```
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 126 of 128

trg_catalog_product_entity_int_after_delete:
```SQL
-- before
INSERT IGNORE INTO `catalog_product_flat_cl` (`entity_id`) VALUES (OLD.`entity_id`);
-- after
SET @entity_id = (SELECT `entity_id` FROM `catalog_product_entity` WHERE `row_id` =
OLD.`row_id`);
INSERT IGNORE INTO `catalog_product_flat_cl` (`entity_id`) values(@entity_id);
```

## sequence tables

Staging migration scripts do this: `row_id` becomes auto_increment, and `entity_id`


(`page_id` etc.) column
is no longer auto incremented, but still required!

Requests like `INSERT INTO cms_page (identified) VALUES("test_page")` would result in


error because page_id
is normally auto_increment and not passed in request.

Magento fixes this by manually generating auto increments for entity_id in separate
sequence tables.

Exmple:

- \Magento\Framework\Model\ResourceModel\Db\CreateEntityRow::prepareData
- `$output[$metadata->getIdentifierField()] = $metadata->generateIdentifier();`
- \Magento\Framework\EntityManager\Sequence\Sequence::getNextValue
- insert and return next ID into `sequence_*` table

`sequence_cms_page`, `sequence_catalog_category`, `sequence_catalogrule`, ...

In community edition (no staging), `$metadata->generateIdentifier()` because


sequenceTable is not passed.

- \Magento\Framework\EntityManager\MetadataPool::getMetadata
- \Magento\Framework\EntityManager\MetadataPool::createMetadata
- new \Magento\Framework\EntityManager\EntityMetadata
- `sequence` argument:
\Magento\Framework\EntityManager\Sequence\SequenceFactory::create:
- no staging - sequence = null
- staging defines `sequenceTable` - `new
\Magento\Framework\EntityManager\Sequence\Sequence(connection, sequenceTable)`

## Common staging plugins

- FrontControllerInterface::beforeDispatch - validate `___version`, `___timestamp`,


`___signature`
- Magento\PageCache\Model\Config::afterIsEnabled - disabled in preview mode
- Magento\Store\Model\BaseUrlChecker - disabled in preview mode
- Magento\Framework\Stdlib\DateTime\Timezone::isScopeDateInInterval - interval never
validated in preview mode
- Magento\Store\Model\StoreResolver::getCurrentStoreId - use `___store` in preview mode
- Magento\Customer\Model\Session::regenerateId, destroy - never regenerate/destroy in
preview mode
- getUrl - adds `___version`, `___store`, possibly `__timestamp`, `__signature` in
preview mode
- disables block_html caching

## Staging-related modifications of the indexing process


File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 127 of 128

- plugin before Magento\Catalog\Controller\Category\View


- \Magento\CatalogStaging\Model\Plugin\Controller\View::beforeExecute
- \Magento\CatalogStaging\Model\Indexer\Category\Product\Preview::execute
- \Magento\Catalog\Model\Indexer\Category\Product\AbstractAction::reindex

Reindex using temporary tables with suffix `_catalog_staging_tmp`,


e.g. `catalog_category_product_index_store1_catalog_staging_tmp`

## Save staging update

\Magento\SalesRuleStaging\Controller\Adminhtml\Update\Save::execute
\Magento\Staging\Model\Entity\Update\Save::execute

```
staging[mode]: save
staging[update_id]:
staging[name]: 3 September
staging[description]: Everything is new
staging[start_time]: 2020-09-03T[Link].000Z
staging[end_time]:
```

\Magento\Staging\Model\Entity\Update\Action\Pool::getAction(
entityType = 'Magento\SalesRule\Api\Data\RuleInterface',
namespace = 'save',
actionType = mode = 'save'
)

- \Magento\Staging\Model\Entity\Update\Action\Save\SaveAction:
- \Magento\Staging\Model\Entity\Update\Action\Save\SaveAction::createUpdate
- \Magento\Staging\Model\EntityStaging::schedule
- \Magento\CatalogRuleStaging\Model\CatalogRuleStaging::schedule
- validates intersecting updates
- sets `created_in`, saves entity model (catalog rule)
- Magento\CatalogRuleStaging\Model\Rule\Hydrator
- \Magento\CatalogRuleStaging\Model\Rule\Retriever::getEntity - $this->ruleRepository-
>get($entityId)

## Preview

All links are automatially updated:


- \Magento\Framework\Url::getUrl
- \Magento\Framework\Url\RouteParamsPreprocessorComposite::execute
- \Magento\Staging\Model\Preview\RouteParamsPreprocessor::execute
- `[_query][___version]=`, `[_query][___store]=`
- `__timestamp=`, `__signature=`

Example:

vendor/magento/module-catalog/view/frontend/layout/[Link]:
```XML
<block class="Magento\Catalog\Block\FrontendStorageManager" name="frontend-storage-
manager" before="-"
template="Magento_Catalog::frontend_storage_manager.phtml">
<arguments>
<!-- ... -->
<item name="updateRequestConfig" xsi:type="array">
<item name="url" xsi:type="serviceUrl" path="/products-render-info"/>
</item>
```

- \Magento\Store\Model\Argument\Interpreter\ServiceUrl::evaluate
- [Link]
File: /home/hb/Downloads/magento2-exam-notes-master/[Link]
Page 128 of 128

___store=default&___version=1599422820&__signature=b5cd66d9ea383ea57356f28cf2dd0f8edeecbc516b4d7982f8aef2
render-info

URL examples:

- REST - `?___version=`
- category - `?update_id=`

Preview from admin panel:

- [Link]
preview_store=default&preview_url=https:/[Link]/&preview_version=1599116400
- header and iframe
- [Link]
___version=1599116400&___store=default&__timestamp=1599077245&__signature=a8f3a05adb92374873a3f9c4e1d36c1

\Magento\Staging\Plugin\Framework\App\FrontController::beforeDispatch - validate
signature:

```
__signature = hash(sha256, "__version,__timestamp", secret key from [Link])
```

## Set current version

\Magento\CatalogStaging\Model\Category\DataProvider::getCurrentCategory

```
$updateId = (int) $this->request->getParam('update_id');
$this->versionManager->setCurrentVersionId($update->getId());
```

## checkout-staging

- disable place order in preview


- any quote, saved in preview mode, is saved in db table `quote_preview` and deleted by
cron

You might also like