├── Common
├── Cms.php
├── EavAttribute.php
└── EavAttribute
│ └── Context.php
├── LICENSE
├── README.md
├── Setup
├── Migration.php
└── Migration
│ ├── Context.php
│ └── Facade
│ ├── AdminConfig.php
│ ├── CategoryAttribute.php
│ ├── CmsBlock.php
│ ├── CmsPage.php
│ ├── CustomerAttribute.php
│ └── ProductAttribute.php
├── composer.json
├── docs
├── header.png
├── header.svg
├── sponsors
│ └── caravelx.svg
├── tldr.png
└── tldr.xcf
├── etc
└── module.xml
└── registration.php
/Common/Cms.php:
--------------------------------------------------------------------------------
1 | collectionFactory = $collectionFactory;
34 | $this->repository = $repository;
35 | $this->factory = $factory;
36 | }
37 |
38 | /**
39 | * Load cms content data by given identifier
40 | *
41 | * @param string $identifier
42 | * @param int $storeId
43 | * @return Block|Page
44 | */
45 | public function get(string $identifier, $storeId = null)
46 | {
47 | $id = $this->findId($identifier, $storeId);
48 |
49 | return $this->repository->getById($id);
50 | }
51 |
52 | /**
53 | * Create a new content on database through corresponding cms repository
54 | *
55 | * @param string $identifier
56 | * @param array $data
57 | * @param int|null $storeId
58 | * @return Block|Page
59 | */
60 | public function create($identifier, $data, $storeId = null)
61 | {
62 | $model = $this->factory->create()
63 | ->setIdentifier($identifier)
64 | ->addData($data)
65 | ->setStoreId($storeId);
66 |
67 | return $this->repository->save($model);
68 | }
69 |
70 | /**
71 | * Create a new content only if it not exists yet
72 | *
73 | * @param string $identifier
74 | * @param array $data
75 | * @param int|null $storeId
76 | * @return Block|Page
77 | */
78 | public function safeCreate($identifier, $data, $storeId = null)
79 | {
80 | if ($this->exists($identifier, $storeId)) {
81 | return $this->get($identifier, $storeId);
82 | }
83 |
84 | return $this->create($identifier, $data, $storeId);
85 | }
86 |
87 | /**
88 | * Update cms content based on given identifier
89 | *
90 | * @param string $identifier
91 | * @param array $data
92 | * @param int|null $storeId
93 | * @return Block|Page
94 | */
95 | public function update($identifier, $data, $storeId = null)
96 | {
97 | $model = $this->get($identifier, $storeId);
98 | $model->addData($data);
99 |
100 | return $this->repository->save($model);
101 | }
102 |
103 | /**
104 | * Update cms content only if it already exists
105 | *
106 | * @param string $identifier
107 | * @param array $data
108 | * @param int|null $storeId
109 | * @return Block|Page|null
110 | */
111 | public function safeUpdate($identifier, $data, $storeId = null)
112 | {
113 | if (!$this->exists($identifier, $storeId)) {
114 | return null;
115 | }
116 |
117 | return $this->update($identifier, $data, $storeId);
118 | }
119 |
120 | /**
121 | * Delete cms content by given id
122 | * (row_id on database)
123 | *
124 | * @param string $identifier
125 | * @param int|null $storeId
126 | * @return bool
127 | */
128 | public function delete($identifier, $storeId = null)
129 | {
130 | $id = $this->findId($identifier, $storeId);
131 |
132 | return $this->repository->deleteById($id);
133 | }
134 |
135 | /**
136 | * Check if given block exists
137 | *
138 | * @param string $identifier
139 | * @param int|null $storeId
140 | * @return bool
141 | */
142 | public function exists($identifier, $storeId = null)
143 | {
144 | try {
145 | $this->findId($identifier, $storeId);
146 | } catch (\Throwable $th) {
147 | return false;
148 | }
149 |
150 | return true;
151 | }
152 |
153 | /**
154 | * Find a cms model by given identifier
155 | * (and optionally storeId)
156 | *
157 | * @param string $identifier
158 | * @param int $storeId
159 | * @return int
160 | */
161 | protected function findId($identifier, $storeId = null)
162 | {
163 | $collection = $this->collectionFactory->create();
164 | $collection->addFieldToFilter('identifier', $identifier);
165 |
166 | if ($storeId) {
167 | $collection->addStoreFilter($storeId);
168 | }
169 |
170 | if (!$collection->count()) {
171 | throw new LocalizedException(
172 | __("CMS content with identifier '$identifier' not found")
173 | );
174 | }
175 |
176 | return $collection->getFirstItem()->getId();
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/Common/EavAttribute.php:
--------------------------------------------------------------------------------
1 | config = $context->config;
35 | $this->eavSetupFactory = $context->eavSetupFactory;
36 | $this->resourceConnection = $context->resourceConnection;
37 | }
38 |
39 | /**
40 | * Create a new attribute
41 | *
42 | * @param string $code
43 | * @param array $data
44 | * @return \Magento\Eav\Setup\EavSetup
45 | */
46 | public function create($code, $data)
47 | {
48 | $this->getEavSetup()->addAttribute(
49 | static::ENTITY_TYPE,
50 | $code,
51 | $data
52 | );
53 | }
54 |
55 | /**
56 | * Update an existing attribute
57 | *
58 | * @param string $code
59 | * @param array $data
60 | * @return \Magento\Eav\Setup\EavSetup
61 | */
62 | public function update($code, $data)
63 | {
64 | $this->getEavSetup()->updateAttribute(
65 | static::ENTITY_TYPE,
66 | $code,
67 | $data
68 | );
69 | }
70 |
71 | /**
72 | * Check if given attribute exists
73 | *
74 | * @param string $code
75 | * @return bool
76 | */
77 | public function exists($code)
78 | {
79 | return (bool) $this->config->getAttribute(static::ENTITY_TYPE, $code)->getId();
80 | }
81 |
82 | /**
83 | * Retrieve a fresh instance of the EavSetup
84 | *
85 | * @return \Magento\Eav\Setup\EavSetup
86 | */
87 | protected function getEavSetup()
88 | {
89 | return $this->eavSetupFactory->create();
90 | }
91 |
92 | /**
93 | * Database facade for quick operations
94 | */
95 | protected function getConnection()
96 | {
97 | return $this->resourceConnection->getConnection();
98 | }
99 |
100 | /**
101 | * Retrieve given table name on database,
102 | * with preffix and etc if applicable
103 | *
104 | * @param string $rawTableName
105 | * @return string
106 | */
107 | protected function getTableName($rawTableName)
108 | {
109 | return $this->resourceConnection->getTableName($rawTableName);
110 | }
111 |
112 | /**
113 | * Retrieve entity type if
114 | *
115 | * @return int
116 | */
117 | protected function getEntityTypeId()
118 | {
119 | $tableName = $this->getTableName('eav_entity_type');
120 | $select = $this->getConnection()->select()
121 | ->from($tableName, 'entity_type_id')
122 | ->where('entity_type_code=?', static::ENTITY_TYPE);
123 |
124 | return (int) $this->getConnection()->fetchOne($select);
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/Common/EavAttribute/Context.php:
--------------------------------------------------------------------------------
1 | config = $config;
34 | $this->eavSetupFactory = $eavSetupFactory;
35 | $this->resourceConnection = $resourceConnection;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Discorgento
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
A dev-friendly approach to keep track of database changes in Magento 2
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Our Sponsors
12 |
13 |
14 |
15 |
16 | ## Overview 💭
17 | Just changed something on the admin panel or on the database and now you need to replicate it again in staging and production? No worries, [we](https://discorgento.com/discord) got you covered.
18 |
19 | Probably you already heard about [data patches](https://developer.adobe.com/commerce/php/development/components/declarative-schema/patches/), but what if I say that it can be really, really simplified?
20 |
21 | 
22 | From 50 lines to just 15, or simply 70% less code. SEVENTY percent fewer lines.
23 | But we're just getting started.
24 |
25 | ## Install 🔧
26 | This module is compatible with both Magento 2.3 and 2.4, from PHP 7.3 to 8.3.
27 | ```
28 | composer require discorgento/module-migrations:^2 && bin/magento setup:upgrade
29 | ```
30 |
31 | ## Usage 🥤
32 | Quick demo on how to use it:
33 |
34 | > There's also an extended version in Brazillian Portuguese including CMS content management overview available [here](https://odysee.com/@discorgento:8/Introdu%C3%A7%C3%A3o-ao-Modulo-Migrations-Magento-discorgento-module-migrations:9).
35 |
36 | Besides simplifying the basic structure like showed before, we also provide some [facades](https://refactoring.guru/design-patterns/facade) to common tasks like handling [admin config](https://github.com/discorgento/module-migrations/wiki/Admin-Config), [product attributes](https://github.com/discorgento/module-migrations/wiki/Product-Attributes), [cms content](https://github.com/discorgento/module-migrations/wiki/Cms-Content) and [more](https://github.com/discorgento/module-migrations/wiki). As an example, you can use a snippet like this to create a whole new CMS Page, including Page Builder widgets on its content:
37 |
38 | ```php
39 | cmsPage = $cmsPage;
56 | }
57 |
58 | protected function execute()
59 | {
60 | $this->cmsPage->create('my-new-page', [
61 | 'title' => 'Lorem Ipsum',
62 | 'content' => <<Hello World!
64 | HTML,
65 | ]);
66 | }
67 | }
68 | ```
69 |
70 | Run a `bin/magento setup:upgrade`, navigate to the _/my-new-page path_, and that's it. And naturally as this is part of the deployment of new releases of your store, it will be automatically replicated in your integration/staging/production/whatever environments (and even your coworkers machines).
71 |
72 | > **💡 Tip:** Don't forget to check our [official wiki](https://github.com/discorgento/module-migrations/wiki) to make the most use of this powerful m2 tool!
73 |
74 | ## Notes 🗒
75 | - roadmap: create cli command to generate migrations for existant cms content (thanks [@vpjoao98](https://github.com/vpjoao98));
76 | - issues and PRs are welcome in this repo;
77 | - we want **YOU** for [our community](https://discorgento.com/discord)!
78 |
--------------------------------------------------------------------------------
/Setup/Migration.php:
--------------------------------------------------------------------------------
1 | context = $context;
23 | }
24 |
25 | /**
26 | * Your migration logic goes here
27 | */
28 | abstract protected function execute();
29 |
30 | /**
31 | * Undo/revert the migration (optional)
32 | *
33 | * @return void
34 | */
35 | protected function rollback()
36 | {
37 | // optional, override to implement an undo feature in your migration
38 | }
39 |
40 | /**
41 | * @inheritDoc
42 | *
43 | * No reason to use the raw apply() method, use execute() instead
44 | */
45 | final public function apply()
46 | {
47 | if ($appliedAlias = $this->hasAlreadyAppliedAlias()) {
48 | $this->context->logger->info(__(
49 | 'Patch data "%1" skipped because the it was already applied under the old name "%2"',
50 | [static::class, $appliedAlias]
51 | ));
52 |
53 | return;
54 | }
55 |
56 | $this->getConnection()->startSetup();
57 |
58 | if ($areaCode = static::AREA_CODE) {
59 | try {
60 | $this->context->state->setAreaCode($areaCode);
61 | } catch (\Throwable $th) {
62 | // already set
63 | }
64 | }
65 |
66 | $this->execute();
67 |
68 | $this->getConnection()->endSetup();
69 | }
70 |
71 | /**
72 | * @inheritDoc
73 | *
74 | * No reason to use the raw revert() method, use rollback() instead
75 | */
76 | final public function revert()
77 | {
78 | $this->getConnection()->startSetup();
79 | $this->rollback();
80 | $this->getConnection()->endSetup();
81 | }
82 |
83 | /**
84 | * Workaround for Magento core bug
85 | *
86 | * Return false if there's no previous applied alias,
87 | * otherwise return the applied alias name
88 | *
89 | * @see https://github.com/magento/magento2/issues/31396
90 | *
91 | * @return string|false
92 | */
93 | private function hasAlreadyAppliedAlias()
94 | {
95 | foreach ($this->getAliases() as $alias) {
96 | if ($this->context->patchHistory->isApplied($alias)) {
97 | return $alias;
98 | }
99 | }
100 |
101 | return false;
102 | }
103 |
104 | /**
105 | * Shorthand for getting the database connection
106 | */
107 | protected function getConnection(): AdapterInterface
108 | {
109 | return $this->getModuleDataSetup()->getConnection();
110 | }
111 |
112 | /**
113 | * Get given table name in database including prefix
114 | *
115 | * @param string $rawName
116 | * @return string
117 | */
118 | protected function getTableName(string $rawName): string
119 | {
120 | return $this->getModuleDataSetup()->getTable($rawName);
121 | }
122 |
123 | /**
124 | * Module Setup Data getter
125 | *
126 | * @return ModuleDataSetupInterface
127 | */
128 | protected function getModuleDataSetup()
129 | {
130 | return $this->context->moduleDataSetup;
131 | }
132 |
133 | /**
134 | * @inheritDoc
135 | */
136 | public function getAliases()
137 | {
138 | return [];
139 | }
140 |
141 | /**
142 | * @inheritDoc
143 | */
144 | public static function getDependencies()
145 | {
146 | return [];
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Setup/Migration/Context.php:
--------------------------------------------------------------------------------
1 | moduleDataSetup = $moduleDataSetup;
33 | $this->logger = $logger;
34 | $this->patchHistory = $patchHistory;
35 | $this->state = $state;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Setup/Migration/Facade/AdminConfig.php:
--------------------------------------------------------------------------------
1 | configWriter = $configWriter;
23 | $this->scopeConfig = $scopeConfig;
24 | }
25 |
26 | /**
27 | * Retrieve config value from given path
28 | *
29 | * @param string $path
30 | * @param string $scope
31 | * @param int $scopeId
32 | */
33 | public function get($path, $scope = ScopeConfig::SCOPE_TYPE_DEFAULT, $scopeId = null)
34 | {
35 | return $this->scopeConfig->getValue($path, $scope, $scopeId);
36 | }
37 |
38 | /**
39 | * Set config value of given path
40 | *
41 | * @param string|array $path can also be a map of multiple config
42 | * @param string|int|array $value
43 | * @param string $scope
44 | * @param int $scopeId
45 | */
46 | public function set($path, $value = null, $scope = ScopeConfig::SCOPE_TYPE_DEFAULT, $scopeId = 0)
47 | {
48 | if (!is_array($path)) {
49 | return $this->configWriter->save($path, $value, $scope, $scopeId);
50 | }
51 |
52 | $paths = $path;
53 | foreach ($paths as $path => $value) {
54 | $this->set(
55 | $path,
56 | $value['value'] ?? $value,
57 | $value['scope'] ?? $scope,
58 | $value['scope_id'] ?? $scopeId,
59 | );
60 | }
61 | }
62 |
63 | /**
64 | * Exclude given config from core_config_data, thus restoting it to the default value set at its config.xml
65 | *
66 | * @param string|array $path
67 | * @param string $scope
68 | * @param int $scopeId
69 | */
70 | public function restore($path, $scope = ScopeConfig::SCOPE_TYPE_DEFAULT, $scopeId = 0)
71 | {
72 | $paths = is_string($path) ? [$path] : $path;
73 | foreach ($paths as $path) {
74 | $this->configWriter->delete($path, $scope, $scopeId);
75 | }
76 | }
77 |
78 | /**
79 | * Reset given config(s) to it's original config.xml value
80 | *
81 | * @deprecated 2.0.3 replaced with "restore" to match the admin settings naming
82 | * @see Discorgento\Migrations\Setup\Migration\Facade\AdminConfig::restore
83 | *
84 | * @param string|array $path
85 | * @param string $scope
86 | * @param int $scopeId
87 | * @return void
88 | */
89 | public function reset($path, $scope = ScopeConfig::SCOPE_TYPE_DEFAULT, $scopeId = 0)
90 | {
91 | $this->restore($path, $scope, $scopeId);
92 | }
93 |
94 | /**
95 | * Append given value to given path
96 | *
97 | * @param string $path
98 | * @param string|int|array $value
99 | * @param string $scope
100 | * @param int $scopeId
101 | */
102 | public function append($path, $value, $scope = ScopeConfig::SCOPE_TYPE_DEFAULT, $scopeId = null)
103 | {
104 | $oldValue = $this->scopeConfig->getValue($path, $scope, $scopeId);
105 | $newValue = $oldValue . $value;
106 | $this->configWriter->save($path, $newValue, $scope, $scopeId ?: 0);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/Setup/Migration/Facade/CategoryAttribute.php:
--------------------------------------------------------------------------------
1 | categoryRepository = $categoryRepository;
24 | }
25 |
26 | /**
27 | * Update given attribute value for multiple categories at once
28 | *
29 | * @todo Find a faster way of doing this, like with the products
30 | *
31 | * @param array $entityIds
32 | * @param array $data
33 | * @return void
34 | */
35 | public function massUpdate($entityIds, $data)
36 | {
37 | foreach ($entityIds as $categoryId) {
38 | /** @var \Magento\Catalog\Model\Category */
39 | $category = $this->categoryRepository->get($categoryId);
40 | $category->addData($data);
41 |
42 | $this->categoryRepository->save($category);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Setup/Migration/Facade/CmsBlock.php:
--------------------------------------------------------------------------------
1 | attributeManagement = $attributeManagement;
37 | $this->attributeSetRepository = $attributeSetRepository;
38 | $this->productAction = $productAction;
39 | }
40 |
41 | /**
42 | * Shorthand to quickly create a dropdown-type attribute
43 | *
44 | * @param string $code
45 | * @param string $label
46 | * @param array $values
47 | * @param array $config
48 | */
49 | public function createDropdown($code, $label, $values, $config = [])
50 | {
51 | $this->create(
52 | $code,
53 | array_merge([
54 | 'label' => $label,
55 | 'input' => 'select',
56 | 'type' => 'int',
57 | 'source_model' => \Magento\Eav\Model\Entity\Attribute\Source\Table::class,
58 | 'filterable' => 1,
59 | 'required' => false,
60 | 'option' => ['values' => $values],
61 | 'user_defined' => true,
62 | ], $config)
63 | );
64 | }
65 |
66 | /**
67 | * Assign an attribute to some attribute set (or default if none specified)
68 | *
69 | * @param string $attributeCode
70 | * @param int|string|AttributeSet $attributeSet
71 | * @param int|string|AttributeGroup $group
72 | * @param string $after
73 | */
74 | public function assignToAttributeSet($attributeCode, $attributeSet = null, $group = null, $after = null)
75 | {
76 | if (is_array($attributeSet)) {
77 | return $this->assignToAttributeSetLegacy($attributeCode, $attributeSet);
78 | }
79 |
80 | $attributeSetId = $this->resolveAttributeSetId($attributeSet);
81 | $attributeGroupId = $this->resolveGroupId($group, $attributeSet);
82 | $sortOrder = $after ? $this->getSortOrder($after, $attributeSet) + 1 : 999;
83 |
84 | $this->attributeManagement->assign(
85 | self::ENTITY_TYPE,
86 | $attributeSetId,
87 | $attributeGroupId,
88 | $attributeCode,
89 | $sortOrder
90 | );
91 | }
92 |
93 | /**
94 | * Old implementation of attribute set assign
95 | * for retrocompatibility purposes only
96 | *
97 | * @param string $attributeCode
98 | * @param array $options
99 | */
100 | private function assignToAttributeSetLegacy($attributeCode, $options = [])
101 | {
102 | $attributeSetId = (int) ($options['attribute_set_id'] ?? $this->getEavSetup()
103 | ->getDefaultAttributeSetId(self::ENTITY_TYPE));
104 |
105 | $attributeGroupId = $options['group_id'] ?? $this->getDefaultGroupId($attributeSetId);
106 | $sortOrder = $options['sort_order'] ?? 999;
107 |
108 | $this->attributeManagement->assign(
109 | self::ENTITY_TYPE,
110 | $attributeSetId,
111 | $attributeGroupId,
112 | $attributeCode,
113 | $sortOrder
114 | );
115 | }
116 |
117 | /**
118 | * Remove given attribute from given attribute set
119 | *
120 | * @param string $attributeCode
121 | * @param int $attributeSetId
122 | */
123 | public function unassignFromAttributeSet($attributeCode, $attributeSetId = null)
124 | {
125 | $attributeSetId = $attributeSetId ?: $this->getEavSetup()
126 | ->getDefaultAttributeSetId(self::ENTITY_TYPE);
127 |
128 | $this->attributeManagement->unassign($attributeSetId, $attributeCode);
129 | }
130 |
131 | /**
132 | * Update given attribute value for multiple products at once
133 | *
134 | * @param array $entityIds
135 | * @param array $data
136 | * @return void
137 | */
138 | public function massUpdate($entityIds, $data)
139 | {
140 | $this->productAction->updateAttributes(
141 | $entityIds,
142 | $data,
143 | self::SCOPE_STORE
144 | );
145 | }
146 |
147 | /**
148 | * Return the id of given attribute set
149 | *
150 | * @param int|string|AttributeSet $attributeSet
151 | */
152 | private function resolveAttributeSetId($attributeSet = null) : int
153 | {
154 | if ($attributeSet === null) {
155 | return $this->getDefaultAttributeSetId();
156 | }
157 |
158 | if (is_object($attributeSet)) {
159 | return (int) $attributeSet->getId();
160 | }
161 |
162 | if (!is_int($attributeSet)) {
163 | $select = $this->getConnection()->select()
164 | ->from($this->getTableName('eav_attribute_set'), 'attribute_set_id')
165 | ->where('attribute_set_name = ?', $attributeSet)
166 | ->where('entity_type_id = ?', $this->getEntityTypeId());
167 | $attributeSetId = $this->getConnection()->fetchOne($select);
168 |
169 | if (empty($attributeSetId)) {
170 | throw new NoSuchEntityException(__("Attribute Set with name $attributeSet not found"));
171 | }
172 |
173 | return (int) $attributeSetId;
174 | }
175 |
176 | return $attributeSet;
177 | }
178 |
179 | /**
180 | * Resolve given attribute set group into its id
181 | *
182 | * @param int|string|AttributeGroup $group
183 | * @param int|string|AttributeSet $attributeSet
184 | */
185 | private function resolveGroupId($group, $attributeSet = null) : int
186 | {
187 | if ($group === null) {
188 | return $this->getDefaultGroupId($attributeSet);
189 | }
190 |
191 | if (is_object($group)) {
192 | return (int) $group->getId();
193 | }
194 |
195 | if (!is_int($group)) {
196 | $select = $this->getConnection()->select()
197 | ->from($this->getTableName('eav_attribute_group'), 'attribute_group_id')
198 | ->where('attribute_group_name = ?', $group)
199 | ->where('attribute_set_id = ?', $this->resolveAttributeSetId($attributeSet));
200 | $groupId = $this->getConnection()->fetchOne($select);
201 |
202 | if (empty($groupId)) {
203 | throw new NoSuchEntityException(__("Attribute Group with name $attributeSet not found"));
204 | }
205 |
206 | return (int) $groupId;
207 | }
208 |
209 | return $group;
210 | }
211 |
212 | /**
213 | * Retrieve default product attribute set id
214 | */
215 | private function getDefaultAttributeSetId() : int
216 | {
217 | return (int) $this->getEavSetup()->getDefaultAttributeSetId(self::ENTITY_TYPE);
218 | }
219 |
220 | /**
221 | * Retrieve default group id of given attribute set
222 | *
223 | * @param int|string|AttributeSet $attributeSet
224 | */
225 | private function getDefaultGroupId($attributeSet = null) : int
226 | {
227 | $attributeSetId = $this->resolveAttributeSetId($attributeSet);
228 | $attributeSet = $this->attributeSetRepository->get($attributeSetId);
229 |
230 | return (int) $attributeSet->getDefaultGroupId();
231 | }
232 |
233 | /**
234 | * Retrieve current sort position of given attribute
235 | *
236 | * @param string $attributeCode
237 | * @param string|int|AttributeSet $attributeSet
238 | * @return int
239 | */
240 | private function getSortOrder($attributeCode, $attributeSet = null)
241 | {
242 | $entityTypeId = $this->getEntityTypeId();
243 | $attributeSetId = $this->resolveAttributeSetId($attributeSet);
244 |
245 | $attributeIdSelect = $this->getConnection()->select()
246 | ->from($this->getTableName('eav_attribute'), 'attribute_id')
247 | ->where('attribute_code = ?', $attributeCode);
248 |
249 | $select = $this->getConnection()->select()
250 | ->from($this->getTableName('eav_entity_attribute'), 'sort_order')
251 | ->where('entity_type_id = ?', $entityTypeId)
252 | ->where('attribute_set_id = ?', $attributeSetId)
253 | ->where('attribute_id = ?', $attributeIdSelect);
254 |
255 | return (int) $this->getConnection()->fetchOne($select) ?: 999;
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "discorgento/module-migrations",
3 | "description": "A dev-friendly approach to quickly create migrations (patch data) on Magento 2",
4 | "type": "magento2-module",
5 | "license": "MIT",
6 | "require": {
7 | "php": ">=7.3.0 <8.4"
8 | },
9 | "autoload": {
10 | "files": [
11 | "registration.php"
12 | ],
13 | "psr-4": {
14 | "Discorgento\\Migrations\\": ""
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/docs/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discorgento/module-migrations/aa5db59b1114206bf6da49343e38980ae1027c48/docs/header.png
--------------------------------------------------------------------------------
/docs/header.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/sponsors/caravelx.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/tldr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discorgento/module-migrations/aa5db59b1114206bf6da49343e38980ae1027c48/docs/tldr.png
--------------------------------------------------------------------------------
/docs/tldr.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/discorgento/module-migrations/aa5db59b1114206bf6da49343e38980ae1027c48/docs/tldr.xcf
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 |