Skip to content

Commit deae4f4

Browse files
authored
Merge branch 'develop' into PB-378
2 parents 25ba445 + 88a8eb0 commit deae4f4

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. Such changes cause existing content to appear incorrectly, or not at all.
4+
5+
To fix this limitation for versions 1.3+, Page Builder uses Magento's native upgrade mechanism, coupled with our content upgrade helpers. These helpers convert existing content so that 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 helpers, 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+
To fix this issue, we used the Page Builder DOM helper classes (`Magento\PageBuilder\Model\Dom\*`) to create a converter and a data patch for the native Row content type:
12+
13+
1. **Converter** (See `FixFullWidthRowPadding.php`)
14+
2. **Data Patch** (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 uses Page Builder's DOM helper classes to change the DOM of the Row content type within each master format it receives.
21+
22+
Page Builder's `FixFullWidthRowPadding` converter 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+
### Data patch class example
86+
87+
The data patch class implements the `DataPatchInterface`. Specifically, it uses the Page Builder `UpgradeContentHelper` 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 exists. 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 Page Builder's content upgrade helpers for your own 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` to create a converter for your content type, using Page Builder's `FixFullWidthRowPadding` class as an example.
227+
228+
1. Implement the `DataPatchInterface` to create a data patch 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, restore your database content to the original values, tweak your converter, then run `bin/magento setup:upgrade` again. Repeat as necessary.
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. If necessary, you will need to create a converter and data patch to customize how the native content types are updated for your changes.
243+
244+
For example, in version 1.3, we updated the configuration of the native Row content type. As mentioned, we moved the padding attribute of the `full-width` appearance from the `<main>` element to the `<inner>` element. So if your Row configuration is different in your custom content type (for example, you removed the `<inner>` element), then you will need to upgrade your overloaded Row as described in the previous steps.
Loading
Loading

0 commit comments

Comments
 (0)