Skip to content

Commit 34dcee8

Browse files
committed
PB-371: Documentation for Page Builder content upgrade process
Initial draft for review
1 parent 06cfd30 commit 34dcee8

File tree

3 files changed

+244
-0
lines changed

3 files changed

+244
-0
lines changed
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# How to make configuration changes backwards compatible
2+
3+
Before version 1.3, changes to a content-type configuration could (and usually would) break the existing content that was saved with the previous configuration. Why? Because a content type's configuration maps data from its source (the master format) to its display templates. So when the configuration mapping changes, the display of existing content might also change. With significant configuration changes, data (such as styles, attributes, and html) is lost. This change causes existing content to appear incorrectly, or not at all.
4+
5+
The Page Builder content upgrade framework provides an API to convert existing content so it maps to new configurations and displays correctly.
6+
7+
## Example usage for Row
8+
9+
The Page Builder team recently had to change the configuration of the Row's full-width appearance to fix a layout issue. The fix was simple. We moved a style attribute from one element in the Row's full-width appearance to another element. But without the upgrade framework, our change to the Row's configuration would have broken all previously saved Page Builder content with Rows. And because all Page Builder content starts with a Row, all Page Builder content would be broken!
10+
11+
Our fix to this issue was to build a framework that requires two classes:
12+
13+
1. **Converter** (See `FixFullWidthRowPadding.php`)
14+
2. **Patcher** (See `UpgradeFullWidthPadding.php`)
15+
16+
![Example converter and upgrader classes](../images/upgrade-framework-example-pb.png)
17+
18+
### Converter class example
19+
20+
The converter class implements the `DataConverterInterface`. Specifically, it implements the `convert` function where it makes the actual DOM changes to the content types within each master format it receives.
21+
22+
Page Builder's `FixFullWidthRowPadding` class is provided here as an example implementation:
23+
24+
```php
25+
<?php
26+
/**
27+
* Copyright © Magento, Inc. All rights reserved.
28+
* See COPYING.txt for license details.
29+
*/
30+
31+
declare(strict_types=1);
32+
33+
namespace Magento\PageBuilder\Setup\Converters;
34+
35+
use Magento\Framework\DB\DataConverter\DataConverterInterface;
36+
use Magento\PageBuilder\Model\Dom\Adapter\ElementInterface;
37+
use Magento\PageBuilder\Model\Dom\HtmlDocument;
38+
use Magento\PageBuilder\Model\Dom\HtmlDocumentFactory;
39+
40+
/**
41+
* Converter to move padding in full width columns from the main row element to the inner element
42+
*/
43+
class FixFullWidthRowPadding implements DataConverterInterface
44+
{
45+
/**
46+
* @var HtmlDocumentFactory
47+
*/
48+
private $htmlDocumentFactory;
49+
50+
/**
51+
* @param HtmlDocumentFactory $htmlDocumentFactory
52+
*/
53+
public function __construct(HtmlDocumentFactory $htmlDocumentFactory)
54+
{
55+
$this->htmlDocumentFactory = $htmlDocumentFactory;
56+
}
57+
58+
/**
59+
* @inheritDoc
60+
*/
61+
public function convert($value)
62+
{
63+
/** @var HtmlDocument $document */
64+
$document = $this->htmlDocumentFactory->create([ 'document' => $value ]);
65+
$fullWidthRows = $document->querySelectorAll("div[data-content-type='row'][data-appearance='full-width']");
66+
/** @var ElementInterface $row */
67+
foreach ($fullWidthRows as $row) {
68+
$style = $row->getAttribute("style");
69+
preg_match("/padding:(.*?);/", $style, $results);
70+
$padding = isset($results[1]) ? trim($results[1]) : '';
71+
if (!$padding) {
72+
continue;
73+
}
74+
// remove padding from main row element
75+
$row->removeStyle("padding");
76+
// add padding to inner row element
77+
$innerDiv = $row->querySelector(".row-full-width-inner");
78+
$innerDiv->addStyle("padding", $padding);
79+
}
80+
return $fullWidthRows->count() > 0 ? $document->stripHtmlWrapperTags() : $value;
81+
}
82+
}
83+
```
84+
85+
### Patcher class example
86+
87+
The patcher class implements the `DataPatchInterface`. Specifically, it uses the framework's `UpgradeContentHelper.php` class to apply the converter class to all the database entities where Page Builder content exists. These locations are provided by the `UpgradableEntitiesPool`, described later in this topic
88+
89+
Page Builder's `UpgradeFullWidthPadding` class is provided here as an example implementation:
90+
91+
```php
92+
<?php
93+
/**
94+
* Copyright © Magento, Inc. All rights reserved.
95+
* See COPYING.txt for license details.
96+
*/
97+
namespace Magento\PageBuilder\Setup\Patch\Data;
98+
99+
use Magento\Framework\DB\FieldDataConversionException;
100+
use Magento\Framework\Setup\Patch\DataPatchInterface;
101+
use Magento\PageBuilder\Setup\Converters\FixFullWidthRowPadding;
102+
use Magento\PageBuilder\Setup\UpgradeContentHelper;
103+
104+
/**
105+
* Patch upgrade mechanism allows us to do atomic data changes
106+
*/
107+
class UpgradeFullWidthPadding implements DataPatchInterface
108+
{
109+
/**
110+
* @var UpgradeContentHelper
111+
*/
112+
private $helper;
113+
114+
/**
115+
* @param UpgradeContentHelper $helper
116+
*/
117+
public function __construct(
118+
UpgradeContentHelper $helper
119+
) {
120+
$this->helper = $helper;
121+
}
122+
123+
/**
124+
* Do upgrade
125+
*
126+
* @return void
127+
* @throws FieldDataConversionException
128+
*/
129+
public function apply()
130+
{
131+
$this->helper->upgrade([
132+
FixFullWidthRowPadding::class
133+
]);
134+
}
135+
136+
/**
137+
* @inheritdoc
138+
*/
139+
public function getAliases()
140+
{
141+
return [];
142+
}
143+
144+
/**
145+
* @inheritdoc
146+
*/
147+
public static function getDependencies()
148+
{
149+
return [];
150+
}
151+
}
152+
```
153+
154+
## UpgradableEntitiesPool
155+
156+
The `UpgradableEntitiesPool` provides the locations in the database where Page Builder content can exist. By default, these entities include: `cms_block`, `cms_page`, `catalog_category_entity_text`, `catalog_product_entity_text`, and `pagebuilder_template`. Page Builder defines these entities in `Magento/PageBuilder/etc/di.xml`, as shown here:
157+
158+
```xml
159+
<type name="Magento\PageBuilder\Model\UpgradableEntitiesPool">
160+
<arguments>
161+
<argument name="entities" xsi:type="array">
162+
<item name="cms_block" xsi:type="array">
163+
<item name="identifier" xsi:type="string">block_id</item>
164+
<item name="fields" xsi:type="array">
165+
<item name="content" xsi:type="boolean">true</item>
166+
</item>
167+
</item>
168+
<item name="cms_page" xsi:type="array">
169+
<item name="identifier" xsi:type="string">page_id</item>
170+
<item name="fields" xsi:type="array">
171+
<item name="content" xsi:type="boolean">true</item>
172+
</item>
173+
</item>
174+
<item name="catalog_category_entity_text" xsi:type="array">
175+
<item name="identifier" xsi:type="string">value_id</item>
176+
<item name="fields" xsi:type="array">
177+
<item name="value" xsi:type="boolean">true</item>
178+
</item>
179+
</item>
180+
<item name="catalog_product_entity_text" xsi:type="array">
181+
<item name="identifier" xsi:type="string">value_id</item>
182+
<item name="fields" xsi:type="array">
183+
<item name="value" xsi:type="boolean">true</item>
184+
</item>
185+
</item>
186+
<item name="pagebuilder_template" xsi:type="array">
187+
<item name="identifier" xsi:type="string">template_id</item>
188+
<item name="fields" xsi:type="array">
189+
<item name="template" xsi:type="boolean">true</item>
190+
</item>
191+
</item>
192+
</argument>
193+
</arguments>
194+
</type>
195+
```
196+
197+
If you have created additional database entities for storing Page Builder content, you need to add your custom entity to your `etc/di.xml` as shown in the following example:
198+
199+
```xml
200+
<type name="Magento\PageBuilder\Model\UpgradableEntitiesPool">
201+
<arguments>
202+
<argument name="entities" xsi:type="array">
203+
<item name="my_custom_block" xsi:type="array">
204+
<item name="identifier" xsi:type="string">custom_block_id</item>
205+
<item name="fields" xsi:type="array">
206+
<item name="content" xsi:type="boolean">true</item>
207+
</item>
208+
</item>
209+
</argument>
210+
</arguments>
211+
</type>
212+
```
213+
214+
## How to upgrade your custom content types
215+
216+
To use this framework for your custom content-type configuration changes, follow these steps:
217+
218+
1. Set up a new environment that uses a _copy_ of your production data, far away from any real data.
219+
220+
1. Create a test branch off your local environment where you can make and test your data patch converters. You will switch back and forth off this branch to reset the data in your Magento instance as needed.
221+
222+
1. Within the test branch for your content type module changes, set up a directory structure similar to this:
223+
224+
![Custom converter and upgrader classes](../images/upgrade-framework-example-custom.png)
225+
226+
1. Implement the `DataConverterInterface` for your content type, using Page Builder's `FixFullWidthRowPadding` class as an example.
227+
228+
1. Implement the `DataPatchInterface` for your content type, using Page Builder's `UpgradeFullWidthPadding` class as an example.
229+
230+
1. Make a copy of the data in the `content` fields of existing entities (`cms_page`, `cms_block`, and so on). You will compare this content with the content changes made after running your upgrade patch.
231+
232+
1. Run `bin/magento setup:upgrade` to test your custom conversion.
233+
234+
After running `setup:upgrade`, you should see an entry at the bottom of the `patch_list` table of your Magento database. The entry uses your module's namespace with the name of your DataPatch class. So use a descriptive name to ensure the `patch_list` entry makes sense to others.
235+
236+
1. Compare your post-upgraded content to the previous content.
237+
238+
If the conversion didn't convert your content as planned, remove your entry from the `patch_list` table, switch off away from your test branch, and run `bin/magento setup:upgrade` to reset the database content to try again.
239+
240+
## How to upgrade overloaded Page Builder content types
241+
242+
If you have overloaded the configurations of native Page Builder content types, you need to review Page Builder's native configuration changes for each release and create converters to customize how the native content types are updated for your changes, as necessary.
243+
244+
For example, In version 1.3, we updated the configuration of the native Row content type. Specifically, we moved the padding attribute of the `full-width` appearance from the `<main>` element to the `<inner>` element. So if the Row configuration is different in your custom content type, maybe you removed the `<inner>` element, then you will need to upgrade your overloaded Row as described in the previous steps.
34.2 KB
Loading
32.1 KB
Loading

0 commit comments

Comments
 (0)