├── .eslintignore
├── .eslintrc
├── Api
├── AttributeRepositoryInterface.php
└── Data
│ ├── SellerAttributeInterface.php
│ └── SellerInterface.php
├── ISSUE_TEMPLATE.md
├── Model
├── Locator
│ ├── LocatorInterface.php
│ └── RegistryLocator.php
├── ResourceModel
│ ├── AttributePersistor.php
│ ├── Seller.php
│ └── Seller
│ │ ├── Attribute.php
│ │ ├── Attribute
│ │ └── Collection.php
│ │ └── Collection.php
├── Seller.php
├── Seller
│ ├── Attribute
│ │ ├── Repository.php
│ │ └── ScopeOverriddenValue.php
│ └── DataProvider.php
├── SellerMediaUpload.php
└── SellerRepository.php
├── Setup
└── Patch
│ ├── Data
│ └── DefaultSellerAttributes.php
│ └── SellerSetup.php
├── Ui
└── Component
│ └── Seller
│ ├── Form
│ ├── DataProvider.php
│ ├── FieldMapper.php
│ └── Modifier
│ │ └── Eav.php
│ └── Listing
│ ├── AddStoreFieldToCollection.php
│ └── DataProvider.php
├── composer.json
├── etc
├── acl.xml
├── adminhtml
│ ├── di.xml
│ └── menu.xml
├── db_schema.xml
├── db_schema_whitelist.json
├── di.xml
├── events.xml
└── module.xml
├── lib
└── web
│ └── fonts
│ └── retailersuite
│ ├── retailersuite.eot
│ ├── retailersuite.svg
│ ├── retailersuite.ttf
│ ├── retailersuite.woff
│ └── retailersuite.woff2
├── registration.php
└── view
└── adminhtml
└── web
└── css
└── source
└── _module.less
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*{.,-}min.js
2 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | ecmaFeatures:
2 | modules: true
3 | jsx: true
4 |
5 | env:
6 | amd: true
7 | browser: true
8 | es6: true
9 | jquery: true
10 | node: true
11 |
12 | # http://eslint.org/docs/rules/
13 | rules:
14 | # Possible Errors
15 | comma-dangle: [2, never]
16 | no-cond-assign: 2
17 | no-console: 0
18 | no-constant-condition: 2
19 | no-control-regex: 2
20 | no-debugger: 2
21 | no-dupe-args: 2
22 | no-dupe-keys: 2
23 | no-duplicate-case: 2
24 | no-empty: 2
25 | no-empty-character-class: 2
26 | no-ex-assign: 2
27 | no-extra-boolean-cast: 2
28 | no-extra-parens: 0
29 | no-extra-semi: 2
30 | no-func-assign: 2
31 | no-inner-declarations: [2, functions]
32 | no-invalid-regexp: 2
33 | no-irregular-whitespace: 2
34 | no-negated-in-lhs: 2
35 | no-obj-calls: 2
36 | no-regex-spaces: 2
37 | no-sparse-arrays: 2
38 | no-unexpected-multiline: 2
39 | no-unreachable: 2
40 | use-isnan: 2
41 | valid-jsdoc: 0
42 | valid-typeof: 2
43 |
44 | # Best Practices
45 | accessor-pairs: 2
46 | block-scoped-var: 0
47 | complexity: [2, 6]
48 | consistent-return: 0
49 | curly: 0
50 | default-case: 0
51 | dot-location: 0
52 | dot-notation: 0
53 | eqeqeq: 2
54 | guard-for-in: 2
55 | no-alert: 2
56 | no-caller: 2
57 | no-case-declarations: 2
58 | no-div-regex: 2
59 | no-else-return: 0
60 | no-empty-label: 2
61 | no-empty-pattern: 2
62 | no-eq-null: 2
63 | no-eval: 2
64 | no-extend-native: 2
65 | no-extra-bind: 2
66 | no-fallthrough: 2
67 | no-floating-decimal: 0
68 | no-implicit-coercion: 0
69 | no-implied-eval: 2
70 | no-invalid-this: 0
71 | no-iterator: 2
72 | no-labels: 0
73 | no-lone-blocks: 2
74 | no-loop-func: 2
75 | no-magic-number: 0
76 | no-multi-spaces: 0
77 | no-multi-str: 0
78 | no-native-reassign: 2
79 | no-new-func: 2
80 | no-new-wrappers: 2
81 | no-new: 2
82 | no-octal-escape: 2
83 | no-octal: 2
84 | no-proto: 2
85 | no-redeclare: 2
86 | no-return-assign: 2
87 | no-script-url: 2
88 | no-self-compare: 2
89 | no-sequences: 0
90 | no-throw-literal: 0
91 | no-unused-expressions: 2
92 | no-useless-call: 2
93 | no-useless-concat: 2
94 | no-void: 2
95 | no-warning-comments: 0
96 | no-with: 2
97 | radix: 2
98 | vars-on-top: 0
99 | wrap-iife: 2
100 | yoda: 0
101 |
102 | # Strict
103 | strict: 0
104 |
105 | # Variables
106 | init-declarations: 0
107 | no-catch-shadow: 2
108 | no-delete-var: 2
109 | no-label-var: 2
110 | no-shadow-restricted-names: 2
111 | no-shadow: 0
112 | no-undef-init: 2
113 | no-undef: 0
114 | no-undefined: 0
115 | no-unused-vars: 0
116 | no-use-before-define: 0
117 |
118 | # Node.js and CommonJS
119 | callback-return: 2
120 | global-require: 2
121 | handle-callback-err: 2
122 | no-mixed-requires: 0
123 | no-new-require: 0
124 | no-path-concat: 2
125 | no-process-exit: 2
126 | no-restricted-modules: 0
127 | no-sync: 0
128 |
129 | # Stylistic Issues
130 | array-bracket-spacing: 0
131 | block-spacing: 0
132 | brace-style: 0
133 | camelcase: 0
134 | comma-spacing: 0
135 | comma-style: 0
136 | computed-property-spacing: 0
137 | consistent-this: 0
138 | eol-last: 0
139 | func-names: 0
140 | func-style: 0
141 | id-length: 0
142 | id-match: 0
143 | indent: 0
144 | jsx-quotes: 0
145 | key-spacing: 0
146 | linebreak-style: 0
147 | lines-around-comment: 0
148 | max-depth: 0
149 | max-len: 0
150 | max-nested-callbacks: 0
151 | max-params: 0
152 | max-statements: [2, 30]
153 | new-cap: 0
154 | new-parens: 0
155 | newline-after-var: 0
156 | no-array-constructor: 0
157 | no-bitwise: 0
158 | no-continue: 0
159 | no-inline-comments: 0
160 | no-lonely-if: 0
161 | no-mixed-spaces-and-tabs: 0
162 | no-multiple-empty-lines: 0
163 | no-negated-condition: 0
164 | no-nested-ternary: 0
165 | no-new-object: 0
166 | no-plusplus: 0
167 | no-restricted-syntax: 0
168 | no-spaced-func: 0
169 | no-ternary: 0
170 | no-trailing-spaces: 0
171 | no-underscore-dangle: 0
172 | no-unneeded-ternary: 0
173 | object-curly-spacing: 0
174 | one-var: 0
175 | operator-assignment: 0
176 | operator-linebreak: 0
177 | padded-blocks: 0
178 | quote-props: 0
179 | quotes: 0
180 | require-jsdoc: 0
181 | semi-spacing: 0
182 | semi: 0
183 | sort-vars: 0
184 | space-after-keywords: 0
185 | space-before-blocks: 0
186 | space-before-function-paren: 0
187 | space-before-keywords: 0
188 | space-in-parens: 0
189 | space-infix-ops: 0
190 | space-return-throw-case: 0
191 | space-unary-ops: 0
192 | spaced-comment: 0
193 | wrap-regex: 0
194 |
195 | # ECMAScript 6
196 | arrow-body-style: 0
197 | arrow-parens: 0
198 | arrow-spacing: 0
199 | constructor-super: 0
200 | generator-star-spacing: 0
201 | no-arrow-condition: 0
202 | no-class-assign: 0
203 | no-const-assign: 0
204 | no-dupe-class-members: 0
205 | no-this-before-super: 0
206 | no-var: 0
207 | object-shorthand: 0
208 | prefer-arrow-callback: 0
209 | prefer-const: 0
210 | prefer-reflect: 0
211 | prefer-spread: 0
212 | prefer-template: 0
213 | require-yield: 0
214 |
--------------------------------------------------------------------------------
/Api/AttributeRepositoryInterface.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Preconditions
4 |
5 |
6 |
7 | Magento Version :
8 |
9 |
10 | Module Seller Version :
11 |
12 |
13 | Environment :
14 |
15 |
16 | Third party modules :
17 |
18 | ### Steps to reproduce
19 |
20 | 1.
21 | 2.
22 | 3.
23 |
24 | ### Expected result
25 |
26 | 1.
27 |
28 | ### Actual result
29 |
30 | 1. [Screenshot, logs]
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Model/Locator/LocatorInterface.php:
--------------------------------------------------------------------------------
1 | seller) {
33 | return $this->seller;
34 | }
35 |
36 | if ($this->registry->registry('current_seller')) {
37 | return $this->seller = $this->registry->registry('current_seller');
38 | }
39 |
40 | return null;
41 | }
42 | /**
43 | * @inheritdoc
44 | */
45 | public function getStore(): ?StoreInterface
46 | {
47 | if (null !== $this->store) {
48 | return $this->store;
49 | }
50 |
51 | if ($this->getSeller() && $this->getSeller()->getData('store_id')) {
52 | $this->store = $this->storeManager->getStore($this->getSeller()->getData('store_id'));
53 |
54 | return $this->store;
55 | }
56 |
57 | if ($this->registry->registry('current_store') !== null) {
58 | $this->store = $this->registry->registry('current_store');
59 |
60 | return $this->store;
61 | }
62 |
63 | return $this->storeManager->getStore(Store::DEFAULT_STORE_ID);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Model/ResourceModel/AttributePersistor.php:
--------------------------------------------------------------------------------
1 | isScopeGlobal();
23 | }
24 |
25 | return parent::getScopeValue($scope, $attribute, $useDefault);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Model/ResourceModel/Seller.php:
--------------------------------------------------------------------------------
1 | _type)) {
46 | $this->setType(SellerInterface::ENTITY);
47 | }
48 |
49 | return parent::getEntityType();
50 | }
51 |
52 | /**
53 | * Set store Id.
54 | */
55 | public function setStoreId(int $storeId): self
56 | {
57 | $this->storeId = $storeId;
58 |
59 | return $this;
60 | }
61 |
62 | /**
63 | * Return store id.
64 | */
65 | public function getStoreId(): int
66 | {
67 | if ($this->storeId === null) {
68 | return $this->storeManager->getStore()->getId();
69 | }
70 |
71 | return $this->storeId;
72 | }
73 |
74 | /**
75 | * Check if seller id exist.
76 | */
77 | public function checkId(int $entityId): string
78 | {
79 | $select = $this->getConnection()->select()->from(
80 | $this->getEntityTable(),
81 | 'entity_id'
82 | )->where(
83 | 'entity_id = :entity_id'
84 | );
85 | $bind = ['entity_id' => $entityId];
86 |
87 | return $this->getConnection()->fetchOne($select, $bind);
88 | }
89 |
90 | /**
91 | * Check array of seller identifiers.
92 | */
93 | public function verifyIds(array $ids): array
94 | {
95 | if (empty($ids)) {
96 | return [];
97 | }
98 |
99 | $select = $this->getConnection()->select()->from(
100 | $this->getEntityTable(),
101 | 'entity_id'
102 | )->where(
103 | 'entity_id IN(?)',
104 | $ids
105 | );
106 |
107 | return $this->getConnection()->fetchCol($select);
108 | }
109 |
110 | /**
111 | * Get "is_active" attribute identifier.
112 | */
113 | public function getIsActiveAttributeId(): int
114 | {
115 | if ($this->isActiveAttributeId === null) {
116 | $this->isActiveAttributeId = (int) $this->_eavConfig
117 | ->getAttribute($this->getEntityType(), 'is_active')
118 | ->getAttributeId();
119 | }
120 |
121 | return $this->isActiveAttributeId;
122 | }
123 |
124 | /**
125 | * @inheritdoc
126 | */
127 | public function load($object, $entityId, $attributes = [])
128 | {
129 | // Reset firstly loaded attributes
130 | $this->_attributes = [];
131 | $this->loadAttributesMetadata($attributes);
132 |
133 | $object = $this->entityManager->load($object, (string) $entityId);
134 |
135 | if (!$this->entityManager->has($object)) {
136 | $object->isObjectNew(true);
137 | }
138 |
139 | $this->afterLoad($object);
140 |
141 | return $this;
142 | }
143 |
144 | /**
145 | * @inheritdoc
146 | */
147 | public function delete($object)
148 | {
149 | $this->beforeDelete($object);
150 | $this->entityManager->delete($object);
151 | $this->afterDelete($object);
152 |
153 | return $this;
154 | }
155 |
156 | /**
157 | * @inheritdoc
158 | */
159 | public function save(AbstractModel $object)
160 | {
161 | $this->beforeSave($object);
162 | $this->entityManager->save($object);
163 | $this->afterSave($object);
164 |
165 | return $this;
166 | }
167 |
168 | /**
169 | * Retrieve Attribute set data by id or name.
170 | */
171 | public function getAttributeSetIdByName(?string $attributeSetId): int
172 | {
173 | $select = $this->_resource->getConnection()->select();
174 | $field = 'attribute_set_name';
175 | $table = $this->_resource->getTableName("eav_attribute_set");
176 |
177 | $select->from($table, "attribute_set_id")
178 | ->where($this->getConnection()->prepareSqlCondition("entity_type_id", ['eq' => $this->getTypeId()]))
179 | ->where($this->getConnection()->prepareSqlCondition($field, ['eq' => $attributeSetId]));
180 |
181 | return (int) $this->_resource->getConnection()->fetchOne($select);
182 | }
183 |
184 | /**
185 | * @inheritdoc
186 | */
187 | public function beforeSave(DataObject $object): void
188 | {
189 | // Enforce loading of all attributes to ensure their beforeSave is correctly processed.
190 | $this->loadAllAttributes($object);
191 | parent::beforeSave($object);
192 | }
193 |
194 | /**
195 | * Get Seller identifier by code.
196 | */
197 | public function getIdByCode(string $code): int
198 | {
199 | $connection = $this->getConnection();
200 | $select = $connection->select()
201 | ->from($this->getEntityTable(), 'entity_id')
202 | ->where('seller_code = :seller_code');
203 |
204 | return (int) $connection->fetchOne($select, [':seller_code' => $code]);
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/Model/ResourceModel/Seller/Attribute.php:
--------------------------------------------------------------------------------
1 | getAttributeCode(), $this->globalAttributes);
32 | }
33 |
34 | /**
35 | * Retrieve attribute is website scope website
36 | */
37 | public function isScopeWebsite(): bool
38 | {
39 | return $this->getIsGlobal() == self::SCOPE_WEBSITE;
40 | }
41 |
42 | /**
43 | * Retrieve attribute is store scope flag
44 | */
45 | public function isScopeStore(): bool
46 | {
47 | return !$this->isScopeGlobal() && !$this->isScopeWebsite();
48 | }
49 |
50 | /**
51 | * @inheritdoc
52 | */
53 | public function __sleep()
54 | {
55 | $this->unsetData('entity_type');
56 |
57 | return parent::__sleep();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Model/ResourceModel/Seller/Attribute/Collection.php:
--------------------------------------------------------------------------------
1 | _init(
22 | SellerAttribute::class,
23 | Attribute::class
24 | );
25 | }
26 |
27 | /**
28 | * @inheritdoc
29 | */
30 | protected function _initSelect(): self
31 | {
32 | $this->getSelect()->from(['main_table' => $this->getResource()->getMainTable()])
33 | ->where(
34 | 'main_table.entity_type_id=?',
35 | $this->eavConfig->getEntityType(SellerInterface::ENTITY)->getId()
36 | );
37 |
38 | return $this;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Model/ResourceModel/Seller/Collection.php:
--------------------------------------------------------------------------------
1 | sellerAttributeSetName = $attributeSetName;
57 | parent::__construct(
58 | $entityFactory,
59 | $logger,
60 | $fetchStrategy,
61 | $eventManager,
62 | $eavConfig,
63 | $resource,
64 | $eavEntityFactory,
65 | $resourceHelper,
66 | $universalFactory,
67 | $connection
68 | );
69 | }
70 |
71 | /**
72 | * @inheritdoc
73 | */
74 | protected function _construct()
75 | {
76 | $this->_init(Seller::class, SellerResource::class);
77 |
78 | if ($this->sellerAttributeSetId == null) {
79 | if ($this->sellerAttributeSetName !== null) {
80 | /** @var SellerResource $resource */
81 | $resource = $this->getResource();
82 | $this->sellerAttributeSetId = $resource
83 | ->getAttributeSetIdByName($this->sellerAttributeSetName);
84 | }
85 | }
86 | }
87 |
88 | /**
89 | * Set store scope.
90 | */
91 | public function setStore(Store $store): self
92 | {
93 | $this->setStoreId($this->storeManager->getStore($store)->getId());
94 |
95 | return $this;
96 | }
97 |
98 | /**
99 | * Set store scope.
100 | */
101 | public function setStoreId(mixed $storeId): self
102 | {
103 | if ($storeId instanceof Store) {
104 | $storeId = $storeId->getId();
105 | }
106 | $this->storeId = (int) $storeId;
107 |
108 | return $this;
109 | }
110 |
111 | /**
112 | * Return current store id.
113 | */
114 | public function getStoreId(): int
115 | {
116 | if ($this->storeId === null) {
117 | $this->setStoreId($this->storeManager->getStore()->getId());
118 | }
119 |
120 | return $this->storeId;
121 | }
122 |
123 | /**
124 | * Retrieve default store id.
125 | */
126 | public function getDefaultStoreId(): int
127 | {
128 | return Store::DEFAULT_STORE_ID;
129 | }
130 |
131 | /**
132 | * @inheritdoc
133 | */
134 | protected function _initSelect()
135 | {
136 | parent::_initSelect();
137 |
138 | // Retrieve only sellers of current attribute set if specified.
139 | if ($this->sellerAttributeSetId !== null) {
140 | $this->addFieldToFilter('attribute_set_id', (int) $this->sellerAttributeSetId);
141 | }
142 |
143 | return $this;
144 | }
145 |
146 | /**
147 | * @inheritdoc
148 | */
149 | protected function _getLoadAttributesSelect($table, $attributeIds = [])
150 | {
151 | if (empty($attributeIds)) {
152 | $attributeIds = $this->_selectAttributes;
153 | }
154 |
155 | $storeId = $this->getStoreId();
156 | $connection = $this->getConnection();
157 |
158 | $entityIdField = $this->getEntityPkName($this->getEntity());
159 |
160 | $select = $this->getBaseAttributesSelect($table, $attributeIds);
161 |
162 | $storeCondition = $this->getDefaultStoreId();
163 |
164 | if ($storeId) {
165 | $joinCondition = [
166 | 't_s.attribute_id = t_d.attribute_id',
167 | "t_s.{$entityIdField} = t_d.{$entityIdField}",
168 | $connection->quoteInto('t_s.store_id = ?', $storeId),
169 | ];
170 |
171 | $select->joinLeft(['t_s' => $table], implode(' AND ', $joinCondition), []);
172 |
173 | $storeCondition = $connection->getIfNullSql('t_s.store_id', Store::DEFAULT_STORE_ID);
174 | }
175 |
176 | $select->where('t_d.store_id = ?', $storeCondition);
177 |
178 | return $select;
179 | }
180 |
181 | /**
182 | * @inheritdoc
183 | * @SuppressWarnings(PHPMD.ExcessiveParameterList)
184 | * @SuppressWarnings(PHPMD.ElseExpression) cf. \Magento\Catalog\Model\ResourceModel\Collection\AbstractCollection
185 | */
186 | protected function _joinAttributeToSelect($method, $attribute, $tableAlias, $condition, $fieldCode, $fieldAlias)
187 | {
188 | $storeId = $this->getStoreId();
189 | if (isset($this->_joinAttributes[$fieldCode]['store_id'])) {
190 | $storeId = $this->_joinAttributes[$fieldCode]['store_id'];
191 | }
192 |
193 | $connection = $this->getConnection();
194 |
195 | if ($storeId != $this->getDefaultStoreId() && !$attribute->isScopeGlobal()) {
196 | /**
197 | * Add joining default value for not default store
198 | * if value for store is null - we use default value
199 | */
200 | $defCondition = '(' . implode(') AND (', $condition) . ')';
201 | $defAlias = $tableAlias . '_default';
202 | $defAlias = $this->getConnection()->getTableName($defAlias);
203 | $defFieldAlias = str_replace($tableAlias, $defAlias, $fieldAlias);
204 | $tableAlias = $this->getConnection()->getTableName($tableAlias);
205 |
206 | $defCondition = str_replace($tableAlias, $defAlias, $defCondition);
207 | $defCondition .= $connection->quoteInto(
208 | // @phpstan-ignore-next-line as $alias null value is default
209 | ' AND ' . $connection->quoteColumnAs("{$defAlias}.store_id", null) . " = ?",
210 | $this->getDefaultStoreId()
211 | );
212 |
213 | $this->getSelect()->{$method}(
214 | [$defAlias => $attribute->getBackend()->getTable()],
215 | $defCondition,
216 | []
217 | );
218 |
219 | $method = 'joinLeft';
220 | $fieldAlias = $this->getConnection()->getCheckSql(
221 | "{$tableAlias}.value_id > 0",
222 | $fieldAlias,
223 | $defFieldAlias
224 | );
225 | $this->_joinAttributes[$fieldCode]['condition_alias'] = $fieldAlias;
226 | $this->_joinAttributes[$fieldCode]['attribute'] = $attribute;
227 | } else {
228 | $storeId = $this->getDefaultStoreId();
229 | }
230 |
231 | $condition[] = $connection->quoteInto(
232 | // @phpstan-ignore-next-line as $alias null value is default
233 | $connection->quoteColumnAs("{$tableAlias}.store_id", null) . ' = ?',
234 | $storeId
235 | );
236 |
237 | return parent::_joinAttributeToSelect($method, $attribute, $tableAlias, $condition, $fieldCode, $fieldAlias);
238 | }
239 |
240 | /**
241 | * Retrieve Base select for attributes of this collection.
242 | *
243 | * @throws LocalizedException
244 | */
245 | private function getBaseAttributesSelect(string $table, array $attributeIds = []): Select
246 | {
247 | $connection = $this->getConnection();
248 | $entityTable = $this->getEntity()->getEntityTable();
249 |
250 | $entityIdField = $this->getEntityPkName($this->getEntity());
251 |
252 | return $connection->select()->from(
253 | ['t_d' => $table],
254 | ['attribute_id']
255 | )->join(
256 | ['e' => $entityTable],
257 | "e.{$entityIdField} = t_d.{$entityIdField}",
258 | ['e.entity_id']
259 | )->where(
260 | "e.entity_id IN (?)",
261 | array_keys($this->_itemsById)
262 | )->where(
263 | 't_d.attribute_id IN (?)',
264 | $attributeIds
265 | );
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/Model/Seller.php:
--------------------------------------------------------------------------------
1 | _init(SellerResource::class);
79 | }
80 |
81 | /**
82 | * @inheritdoc
83 | */
84 | public function getName(): string
85 | {
86 | return $this->_getData(self::KEY_NAME);
87 | }
88 |
89 | /**
90 | * @inheritdoc
91 | */
92 | public function getSellerCode(): string
93 | {
94 | return $this->_getData(self::KEY_SELLER_CODE);
95 | }
96 |
97 | /**
98 | * @inheritdoc
99 | */
100 | public function getCreatedAt(): ?string
101 | {
102 | return $this->getData('created_at');
103 | }
104 |
105 | /**
106 | * @inheritdoc
107 | */
108 | public function getUpdatedAt(): ?string
109 | {
110 | return $this->getData(self::KEY_UPDATED_AT);
111 | }
112 |
113 | /**
114 | * @inheritdoc
115 | */
116 | public function getIsActive(): bool
117 | {
118 | return (bool) $this->getData(self::KEY_IS_ACTIVE);
119 | }
120 |
121 | /**
122 | * @inheritdoc
123 | */
124 | public function setName(string $name): self
125 | {
126 | return $this->setData(self::KEY_NAME, $name);
127 | }
128 |
129 | /**
130 | * @inheritdoc
131 | */
132 | public function setSellerCode(string $sellerCode): self
133 | {
134 | return $this->setData(self::KEY_SELLER_CODE, $sellerCode);
135 | }
136 |
137 | /**
138 | * @inheritdoc
139 | */
140 | public function setIsActive(bool $isActive): self
141 | {
142 | return $this->setData(self::KEY_IS_ACTIVE, (bool) $isActive);
143 | }
144 |
145 | /**
146 | * @inheritdoc
147 | */
148 | public function setCreatedAt(string $createdAt): self
149 | {
150 | return $this->setData(self::KEY_CREATED_AT, $createdAt);
151 | }
152 |
153 | /**
154 | * @inheritdoc
155 | */
156 | public function setUpdatedAt(string $updatedAt): self
157 | {
158 | return $this->setData(self::KEY_UPDATED_AT, $updatedAt);
159 | }
160 |
161 | /**
162 | * Get default attribute source model
163 | */
164 | public function getDefaultAttributeSourceModel(): string
165 | {
166 | return Table::class;
167 | }
168 |
169 | /**
170 | * @inheritdoc
171 | */
172 | public function getAttributeSetName(): string
173 | {
174 | return 'Default';
175 | }
176 |
177 | /**
178 | * @inheritdoc
179 | */
180 | public function getIdentities(): array
181 | {
182 | $identities = [self::CACHE_TAG . '_' . $this->getId()];
183 | if ($this->_appState->getAreaCode() == Area::AREA_FRONTEND) {
184 | $identities[] = self::CACHE_TAG;
185 | }
186 |
187 | return array_unique($identities);
188 | }
189 |
190 | /**
191 | * @inheritdoc
192 | */
193 | protected function getCustomAttributesCodes(): array
194 | {
195 | if ($this->customAttributesCodes === null) {
196 | $this->customAttributesCodes = $this->getEavAttributesCodes($this->metadataService);
197 | $this->customAttributesCodes = array_diff($this->customAttributesCodes, $this->interfaceAttributes);
198 | }
199 |
200 | return $this->customAttributesCodes;
201 | }
202 |
203 | /**
204 | * @inheritdoc
205 | */
206 | public function getMediaPath(): ?string
207 | {
208 | return $this->getData(self::MEDIA_PATH);
209 | }
210 |
211 | /**
212 | * @inheritdoc
213 | */
214 | public function setMediaPath(string $path): self
215 | {
216 | return $this->setData(self::MEDIA_PATH, $path);
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/Model/Seller/Attribute/Repository.php:
--------------------------------------------------------------------------------
1 | getList($this->searchCriteriaBuilder->create())->getItems();
33 | }
34 |
35 | /**
36 | * @inheritdoc
37 | */
38 | public function getList(SearchCriteriaInterface $searchCriteria): AttributeSearchResultsInterface
39 | {
40 | return $this->eavAttributeRepository->getList(
41 | SellerInterface::ENTITY,
42 | $searchCriteria
43 | );
44 | }
45 |
46 | /**
47 | * @inheritdoc
48 | */
49 | public function get(string $attributeCode): AttributeInterface|SellerAttributeInterface
50 | {
51 | return $this->eavAttributeRepository->get(
52 | SellerInterface::ENTITY,
53 | $attributeCode
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Model/Seller/Attribute/ScopeOverriddenValue.php:
--------------------------------------------------------------------------------
1 | resourceConnection = $resourceConnection->getConnection();
38 | }
39 |
40 | /**
41 | * Whether attribute value is overridden in specific store.
42 | *
43 | * @throws LocalizedException
44 | */
45 | public function containsValue(SellerInterface $entity, string $attributeCode, int $storeId): bool
46 | {
47 | if ((int) $storeId === Store::DEFAULT_STORE_ID) {
48 | return false;
49 | }
50 | if ($this->attributesValues === null) {
51 | $this->initAttributeValues($entity, (int) $storeId);
52 | }
53 |
54 | return isset($this->attributesValues[$storeId])
55 | && array_key_exists($attributeCode, $this->attributesValues[$storeId]);
56 | }
57 |
58 | /**
59 | * Init Attributes Values.
60 | *
61 | * @throws LocalizedException
62 | */
63 | private function initAttributeValues(SellerInterface $entity, int $storeId): void
64 | {
65 | $attributeTables = [];
66 |
67 | /** @var AbstractAttribute $attribute */
68 | foreach ($this->getScopedAttributes() as $attribute) {
69 | if (!$attribute->isStatic()) {
70 | $attributeTables[$attribute->getBackend()->getTable()][] = $attribute->getAttributeId();
71 | }
72 | }
73 |
74 | $storeIds = [Store::DEFAULT_STORE_ID];
75 | if ($storeId !== Store::DEFAULT_STORE_ID) {
76 | $storeIds[] = $storeId;
77 | }
78 |
79 | $selects = [];
80 | foreach ($attributeTables as $attributeTable => $attributeCodes) {
81 | $select = $this->resourceConnection->select()
82 | ->from(['t' => $attributeTable], ['value' => 't.value', 'store_id' => 't.store_id'])
83 | ->join(
84 | ['a' => $this->resourceConnection->getTableName('eav_attribute')],
85 | 'a.attribute_id = t.attribute_id',
86 | ['attribute_code' => 'a.attribute_code']
87 | )
88 | ->where('entity_id = ?', $entity->getId())
89 | ->where('t.attribute_id IN (?)', $attributeCodes)
90 | ->where('t.store_id IN (?)', $storeIds);
91 | $selects[] = $select;
92 | }
93 |
94 | $unionSelect = new UnionExpression($selects, Select::SQL_UNION_ALL);
95 |
96 | $attributes = $this->resourceConnection->fetchAll((string) $unionSelect);
97 | foreach ($attributes as $attribute) {
98 | $this->attributesValues[$attribute['store_id']][$attribute['attribute_code']] = $attribute['value'];
99 | }
100 | }
101 |
102 | /**
103 | * Retrieve a list of attributes that can be scoped by store.
104 | *
105 | * @return AttributeInterface[]
106 | */
107 | private function getScopedAttributes(): array
108 | {
109 | $searchResult = $this->attributeRepository->getList(
110 | $this->searchCriteriaBuilder->addFilters([])->create()
111 | );
112 |
113 | return array_filter($searchResult->getItems(), function ($item) {
114 | /** @var SellerAttributeInterface $item */
115 | return !$item->isScopeGlobal();
116 | });
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Model/Seller/DataProvider.php:
--------------------------------------------------------------------------------
1 | mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
33 | }
34 |
35 | /**
36 | * Remove media From Tmp Directory.
37 | *
38 | * @throws LocalizedException
39 | */
40 | public function removeMediaFromTmp(SellerInterface $seller): void
41 | {
42 | $media = str_replace($seller->getData('retailer_id') . '_', '', $seller->getMediaPath());
43 | $baseTmpPath = $this->imageUploader->getBaseTmpPath();
44 | if (!empty($media) && $this->pathExist($baseTmpPath, $media)) {
45 | $this->moveFileFromTmp($media, $seller);
46 | }
47 | }
48 |
49 | /**
50 | * Move media From Tmp Directory.
51 | *
52 | * @throws LocalizedException
53 | */
54 | public function moveFileFromTmp(string $imageName, SellerInterface $seller): void
55 | {
56 | $baseTmpPath = $this->imageUploader->getBaseTmpPath();
57 | $basePath = $this->imageUploader->getBasePath();
58 |
59 | $baseImagePath = $this->imageUploader->getFilePath(
60 | $basePath,
61 | $seller->getData('retailer_id') . '_' . $imageName
62 | );
63 | $baseTmpImagePath = $this->imageUploader->getFilePath($baseTmpPath, $imageName);
64 |
65 | try {
66 | $this->coreFileStorageDatabase->copyFile(
67 | $baseTmpImagePath,
68 | $baseImagePath
69 | );
70 | $this->mediaDirectory->renameFile(
71 | $baseTmpImagePath,
72 | $baseImagePath
73 | );
74 | } catch (Exception $e) {
75 | throw new LocalizedException(
76 | __('Something went wrong while saving the file(s).')
77 | );
78 | }
79 | }
80 |
81 | /**
82 | * Remove File.
83 | *
84 | * @throws FileSystemException
85 | */
86 | public function removeMedia(SellerInterface $seller): void
87 | {
88 | $media = $seller->getData('retailer_id') . '_' . $seller->getMediaPath();
89 | $basePath = $this->imageUploader->getBasePath();
90 | if ($seller->getData('retailer_id') && $seller->getMediaPath() && $this->pathExist($basePath, $media)) {
91 | $this->removeFile($media);
92 | }
93 | }
94 |
95 | /**
96 | * Check if media exist.
97 | *
98 | * @throws FileSystemException
99 | */
100 | public function pathExist(string $basePath, string $fileName): bool
101 | {
102 | return file_exists($this->getPath($basePath, $fileName));
103 | }
104 |
105 | /**
106 | * Get media Path.
107 | *
108 | * @throws FileSystemException
109 | */
110 | public function getPath(string $basePath, string $fileName): string
111 | {
112 | return $this->directoryList->getPath(DirectoryList::MEDIA) .
113 | '/'
114 | . $this->imageUploader->getFilePath($basePath, $fileName);
115 | }
116 |
117 | /**
118 | * Remove File.
119 | *
120 | * @throws FileSystemException
121 | */
122 | public function removeFile(string $fileName): void
123 | {
124 | unlink($this->getPath($this->imageUploader->getBasePath(), $fileName));
125 | }
126 |
127 | /**
128 | * Retrieve MIME type of requested file.
129 | */
130 | public function getMimeType(string $fileName): string
131 | {
132 | $filePath = $this->getPath($this->imageUploader->getBasePath(), $fileName);
133 | $absoluteFilePath = $this->mediaDirectory->getAbsolutePath($filePath);
134 |
135 | $result = $this->mime->getMimeType($absoluteFilePath);
136 |
137 | return $result;
138 | }
139 |
140 | /**
141 | * Get file statistics data.
142 | */
143 | public function getStat(string $fileName): array
144 | {
145 | $filePath = $this->getPath($this->imageUploader->getBasePath(), $fileName);
146 |
147 | return $this->mediaDirectory->stat($filePath);
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/Model/SellerRepository.php:
--------------------------------------------------------------------------------
1 | applyAttributeSet($seller);
42 |
43 | $this->resourceModel->beforeSave($seller);
44 | $seller = $this->entityManager->save($seller);
45 | $this->resourceModel->afterSave($seller);
46 |
47 | unset($this->sellerRepositoryById[$seller->getId()]);
48 |
49 | return $seller;
50 | }
51 |
52 | /**
53 | * Get info about seller by seller id.
54 | *
55 | * @throws NoSuchEntityException
56 | */
57 | public function get(int $sellerId, ?int $storeId = null): SellerInterface
58 | {
59 | $cacheKey = $storeId ?? 'all';
60 |
61 | if (!isset($this->sellerRepositoryById[$sellerId][$cacheKey])) {
62 | /** @var SellerModel $sellerModel */
63 | $sellerModel = $this->sellerFactory->create();
64 |
65 | if (null !== $storeId) {
66 | $sellerModel->setData('store_id', $storeId);
67 | }
68 |
69 | $seller = $this->entityManager->load($sellerModel, (string) $sellerId);
70 | $this->resourceModel->afterLoad($sellerModel);
71 |
72 | if (!$seller->getId()) {
73 | $exception = new NoSuchEntityException();
74 | throw $exception->singleField($seller->getIdFieldName(), $sellerId);
75 | }
76 |
77 | $this->sellerRepositoryById[$sellerId][$cacheKey] = $seller;
78 | }
79 |
80 | return $this->sellerRepositoryById[$sellerId][$cacheKey];
81 | }
82 |
83 | /**
84 | * Retrieve seller by seller code.
85 | *
86 | * @throws NoSuchEntityException
87 | */
88 | public function getByCode(string $sellerCode, ?int $storeId = null): SellerInterface
89 | {
90 | $sellerId = $this->resourceModel->getIdByCode($sellerCode);
91 | if (!$sellerId) {
92 | throw new NoSuchEntityException(__('Requested seller doesn\'t exist'));
93 | }
94 |
95 | return $this->get($sellerId, $storeId);
96 | }
97 |
98 | /**
99 | * Delete seller.
100 | *
101 | * @throws NoSuchEntityException
102 | * @throws InputException
103 | * @throws StateException
104 | */
105 | public function delete(SellerInterface $seller): bool
106 | {
107 | /** @var SellerModel $seller */
108 | $sellerId = $seller->getId();
109 |
110 | $this->resourceModel->beforeDelete($seller);
111 | $deleteResult = $this->entityManager->delete($seller);
112 | $this->resourceModel->afterDelete($seller);
113 |
114 | if ($deleteResult && isset($this->sellerRepositoryById[$sellerId])) {
115 | unset($this->sellerRepositoryById[$sellerId]);
116 | }
117 |
118 | return $deleteResult;
119 | }
120 |
121 | /**
122 | * Delete seller by identifier.
123 | *
124 | * @throws NoSuchEntityException
125 | * @throws InputException
126 | * @throws StateException
127 | */
128 | public function deleteByIdentifier(int $sellerId): bool
129 | {
130 | $seller = $this->get($sellerId);
131 |
132 | return $this->delete($seller);
133 | }
134 |
135 | /**
136 | * Retrieve Attribute Set Id to use for this entity, if any.
137 | */
138 | public function getEntityAttributeSetId(): ?int
139 | {
140 | $attributeSetId = null;
141 |
142 | if (null !== $this->sellerAttributeSetName) {
143 | /** @var SellerModel $sellerModel */
144 | $sellerModel = $this->sellerFactory->create();
145 | /** @var ResourceModel $resourceModel */
146 | $resourceModel = $sellerModel->getResource();
147 | $attributeSetId = $resourceModel->getAttributeSetIdByName($this->sellerAttributeSetName);
148 | }
149 |
150 | return $attributeSetId;
151 | }
152 |
153 | /**
154 | * Apply correct attribute set to the current seller item.
155 | */
156 | private function applyAttributeSet(SellerInterface $seller): SellerInterface
157 | {
158 | // add a fallback in case retailer attribute_set_id is not correctly returned from Retailer entity
159 | if (null === $this->sellerAttributeSetName && $seller->getAttributeSetName()) {
160 | $this->sellerAttributeSetName = $seller->getAttributeSetName();
161 | }
162 |
163 | $attributeSetId = $this->getEntityAttributeSetId();
164 | if (null !== $attributeSetId) {
165 | $seller->setData('attribute_set_id', $attributeSetId);
166 | }
167 |
168 | return $seller;
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/Setup/Patch/Data/DefaultSellerAttributes.php:
--------------------------------------------------------------------------------
1 | sellerSetupFactory->create(['setup' => $this->moduleDataSetup]);
32 | $sellerSetup->installEntities();
33 |
34 | return $this;
35 | }
36 |
37 | /**
38 | * @inheritdoc
39 | */
40 | public static function getDependencies(): array
41 | {
42 | return [];
43 | }
44 |
45 | /**
46 | * @inheritdoc
47 | */
48 | public static function getVersion(): string
49 | {
50 | return '2.0.1';
51 | }
52 |
53 | /**
54 | * @inheritdoc
55 | */
56 | public function getAliases(): array
57 | {
58 | return [];
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Setup/Patch/SellerSetup.php:
--------------------------------------------------------------------------------
1 | [
26 | 'entity_model' => Seller::class,
27 | 'attribute_model' => Attribute::class,
28 | 'table' => 'smile_seller_entity',
29 | 'entity_attribute_collection' => AttributeCollection::class,
30 | 'attributes' => [
31 | 'name' => [
32 | 'type' => 'varchar',
33 | 'label' => 'Name',
34 | 'input' => 'text',
35 | 'sort_order' => 1,
36 | ],
37 | 'created_at' => [
38 | 'type' => 'static',
39 | 'input' => 'date',
40 | 'sort_order' => 19,
41 | 'visible' => false,
42 | ],
43 | 'updated_at' => [
44 | 'type' => 'static',
45 | 'input' => 'date',
46 | 'sort_order' => 20,
47 | ],
48 | 'seller_code' => [
49 | 'type' => 'static',
50 | 'label' => 'Seller Code',
51 | 'input' => 'text',
52 | 'frontend_class' => 'validate-length maximum-length-64',
53 | 'unique' => true,
54 | ],
55 | 'is_active' => [
56 | 'type' => 'int',
57 | 'label' => 'Is Active',
58 | 'input' => 'select',
59 | 'source' => Boolean::class,
60 | 'sort_order' => 2,
61 | ],
62 | 'description' => [
63 | 'type' => 'text',
64 | 'label' => 'Description',
65 | 'input' => 'textarea',
66 | 'required' => false,
67 | 'sort_order' => 4,
68 | ],
69 | 'meta_title' => [
70 | 'type' => 'varchar',
71 | 'label' => 'Page Title',
72 | 'input' => 'text',
73 | 'required' => false,
74 | 'sort_order' => 6,
75 | ],
76 | 'meta_keywords' => [
77 | 'type' => 'text',
78 | 'label' => 'Meta Keywords',
79 | 'input' => 'textarea',
80 | 'required' => false,
81 | 'sort_order' => 7,
82 | ],
83 | 'meta_description' => [
84 | 'type' => 'text',
85 | 'label' => 'Meta Description',
86 | 'input' => 'textarea',
87 | 'required' => false,
88 | 'sort_order' => 8,
89 | ],
90 | ],
91 | ],
92 | ];
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/Ui/Component/Seller/Form/DataProvider.php:
--------------------------------------------------------------------------------
1 | meta = $this->prepareMeta($meta);
28 | }
29 |
30 | /**
31 | * @inheritdoc
32 | */
33 | public function getData()
34 | {
35 | $data = parent::getData();
36 |
37 | foreach ($this->pool->getModifiersInstances() as $modifier) {
38 | $data = $modifier->modifyData($data);
39 | }
40 |
41 | return $data;
42 | }
43 |
44 | /**
45 | * @inheritdoc
46 | */
47 | public function getCollection()
48 | {
49 | if ($this->collection === null) {
50 | $this->collection = $this->collectionFactory->create();
51 | $this->collection->addAttributeToSelect('*');
52 | if ($this->locator->getStore()) {
53 | $this->collection->setStoreId($this->locator->getStore()->getId());
54 | }
55 | }
56 |
57 | return $this->collection;
58 | }
59 |
60 | /**
61 | * Prepare meta data.
62 | */
63 | private function prepareMeta(array $meta): array
64 | {
65 | foreach ($this->pool->getModifiersInstances() as $modifier) {
66 | $meta = $modifier->modifyMeta($meta);
67 | }
68 |
69 | return $meta;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Ui/Component/Seller/Form/FieldMapper.php:
--------------------------------------------------------------------------------
1 | initFieldsMap($attributeCollectionFactory, $attributeSetId);
28 | }
29 |
30 | /**
31 | * Attribute collection for the current mapper.
32 | */
33 | public function getAttributesCollection(): Collection
34 | {
35 | return $this->attributesCollection;
36 | }
37 |
38 | /**
39 | * Mapping of the attribute by fieldsets.
40 | */
41 | public function getFieldsMap(): array
42 | {
43 | return $this->fieldsMap;
44 | }
45 |
46 | /**
47 | * Fieldset properties.
48 | */
49 | public function getFieldsets(): array
50 | {
51 | return $this->fieldsets;
52 | }
53 |
54 | /**
55 | * Init attribute collection, fieldsets and mapping.
56 | */
57 | private function initFieldsMap(
58 | AttributeCollectionFactory $attributeCollectionFactory,
59 | string $attributeSetId
60 | ): FieldMapper {
61 |
62 | $this->fieldsMap = [];
63 | $this->attributesCollection = $attributeCollectionFactory->create();
64 | $this->attributesCollection->setAttributeSetFilterBySetName($attributeSetId, SellerInterface::ENTITY);
65 | $this->attributesCollection->addSetInfo();
66 |
67 | foreach ($this->attributesCollection as $attribute) {
68 | $attributeGroupId = $attribute->getAttributeGroupId();
69 | /** @var Group $attributeGroup */
70 | $attributeGroup = $this->attributeGroupRepository->get($attributeGroupId);
71 | $fieldsetCode = str_replace('-', '_', $attributeGroup->getAttributeGroupCode());
72 | $this->fieldsets[$fieldsetCode] = [
73 | 'name' => $attributeGroup->getAttributeGroupName(),
74 | 'sortOrder' => $attributeGroup->getSortOrder(),
75 | ];
76 | $this->fieldsMap[$fieldsetCode][] = $attribute->getAttributeCode();
77 | }
78 |
79 | return $this;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Ui/Component/Seller/Form/Modifier/Eav.php:
--------------------------------------------------------------------------------
1 | 'frontend_input',
46 | 'required' => 'is_required',
47 | 'label' => 'frontend_label',
48 | 'sortOrder' => 'sort_order',
49 | 'notice' => 'note',
50 | 'default' => 'default_value',
51 | 'size' => 'multiline_count',
52 | ];
53 |
54 | /**
55 | * Form element mapping.
56 | */
57 | private array $formElement = [
58 | 'text' => 'input',
59 | 'boolean' => 'checkbox',
60 | ];
61 |
62 | private array $validationRules = [
63 | 'email' => ['validate-email' => true],
64 | 'date' => ['validate-date' => true],
65 | ];
66 |
67 | public function __construct(
68 | private LocatorInterface $locator,
69 | private ScopeOverriddenValue $scopeOverriddenValue,
70 | StoreManagerInterface $storeManagerInterface,
71 | AttributeRepositoryInterface $attributeRepositoryInterface,
72 | private EavValidationRules $eavValidationRules,
73 | private FieldMapper $fieldMapper,
74 | private SellerMediaUpload $media
75 | ) {
76 | $this->storeManager = $storeManagerInterface;
77 | $this->attributeRepository = $attributeRepositoryInterface;
78 | }
79 |
80 | /**
81 | * @inheritdoc
82 | */
83 | public function modifyData(array $data)
84 | {
85 | if ($this->locator->getSeller()) {
86 | if (isset($data[$this->locator->getSeller()->getId()])) {
87 | $data[$this->locator->getSeller()->getId()]['store_id'] = $this->locator->getStore()->getId();
88 | $data[$this->locator->getSeller()->getId()] = $this->convertValues(
89 | $this->locator->getSeller(),
90 | $data[$this->locator->getSeller()->getId()]
91 | );
92 | }
93 | }
94 |
95 | return $data;
96 | }
97 |
98 | /**
99 | * @inheritdoc
100 | */
101 | public function modifyMeta(array $meta)
102 | {
103 | return array_replace_recursive(
104 | $meta,
105 | $this->prepareFieldsMeta($this->getFieldsMap(), $this->getAttributesMeta())
106 | );
107 | }
108 |
109 | /**
110 | * Get attributes meta.
111 | *
112 | * @throws LocalizedException
113 | */
114 | private function getAttributesMeta(): array
115 | {
116 | $meta = [];
117 |
118 | /** @var SellerAttributeInterface|AbstractAttribute $attribute */
119 | foreach ($this->getAttributes()->getItems() as $attribute) {
120 | $code = $attribute->getAttributeCode();
121 |
122 | foreach ($this->metaProperties as $metaName => $origName) {
123 | $value = $attribute->getDataUsingMethod($origName);
124 |
125 | $meta[$code][$metaName] = $value;
126 | if ('frontend_input' === $origName) {
127 | $meta[$code]['formElement'] = $this->formElement[$value] ?? $value;
128 | }
129 | if ($attribute->usesSource()) {
130 | $meta[$code]['options'] = $attribute->getSource()->getAllOptions();
131 | }
132 | }
133 |
134 | $rules = $this->eavValidationRules->build($attribute, $meta[$code]);
135 | if ($attribute->getFrontendInput() && isset($this->validationRules[$attribute->getFrontendInput()])) {
136 | // phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge
137 | $rules = array_merge($rules, $this->validationRules[$attribute->getFrontendInput()]);
138 | }
139 |
140 | if (!empty($rules)) {
141 | $meta[$code]['validation'] = $rules;
142 | }
143 |
144 | $meta[$code]['label'] = __($meta[$code]['label']);
145 | $meta[$code] += $this->customizeCheckbox($attribute);
146 | $meta[$code]['componentType'] = Field::NAME;
147 | $meta[$code] += $this->addUseDefaultValueCheckbox($attribute);
148 | $meta[$code]['scopeLabel'] = $this->getScopeLabel($attribute);
149 | }
150 |
151 | return $meta;
152 | }
153 |
154 | /**
155 | * List of EAV attributes of the current model.
156 | */
157 | private function getAttributes(): Collection
158 | {
159 | return $this->fieldMapper->getAttributesCollection();
160 | }
161 |
162 | /**
163 | * Field map by fieldset code.
164 | */
165 | private function getFieldsMap(): array
166 | {
167 | return $this->fieldMapper->getFieldsMap();
168 | }
169 |
170 | /**
171 | * Prepare fields meta based on xml declaration of form and fields metadata.
172 | */
173 | private function prepareFieldsMeta(array $fieldsMap, array $fieldsMeta): array
174 | {
175 | $result = [];
176 | $fieldsets = $this->fieldMapper->getFieldsets();
177 |
178 | foreach ($fieldsMap as $fieldSet => $fields) {
179 | foreach ($fields as $field) {
180 | if (!isset($result[$fieldSet])) {
181 | $result[$fieldSet]['arguments']['data']['config'] = [
182 | 'componentType' => Fieldset::NAME,
183 | 'label' => __($fieldsets[$fieldSet]['name']),
184 | 'sortOrder' => $fieldsets[$fieldSet]['sortOrder'],
185 | 'collapsible' => true,
186 | ];
187 | }
188 |
189 | if (isset($fieldsMeta[$field])) {
190 | $result[$fieldSet]['children'][$field]['arguments']['data']['config'] = $fieldsMeta[$field];
191 | }
192 | }
193 | }
194 |
195 | return $result;
196 | }
197 |
198 | /**
199 | * Retrieve label of attribute scope (global, website, store).
200 | */
201 | private function getScopeLabel(mixed $attribute): string
202 | {
203 | $html = '';
204 | if (
205 | !$attribute || $this->storeManager->isSingleStoreMode()
206 | || $attribute->getFrontendInput() === AttributeInterface::FRONTEND_INPUT
207 | ) {
208 | return $html;
209 | }
210 |
211 | if ($attribute->isScopeGlobal()) {
212 | $html .= __('[GLOBAL]');
213 | } elseif ($attribute->isScopeWebsite()) {
214 | $html .= __('[WEBSITE]');
215 | } elseif ($attribute->isScopeStore()) {
216 | $html .= __('[STORE VIEW]');
217 | }
218 |
219 | return $html;
220 | }
221 |
222 | /**
223 | * Add the "Use Default Value" checkbox if needed.
224 | */
225 | private function addUseDefaultValueCheckbox(SellerAttributeInterface $attribute): array
226 | {
227 | $canDisplayService = $this->canDisplayUseDefault($attribute);
228 | $meta = [];
229 |
230 | if ($canDisplayService) {
231 | $meta['service'] = ['template' => 'ui/form/element/helper/service'];
232 | $meta['disabled'] = !$this->scopeOverriddenValue->containsValue(
233 | $this->locator->getSeller(),
234 | $attribute->getAttributeCode(),
235 | (int) $this->locator->getStore()->getId()
236 | );
237 | }
238 |
239 | return $meta;
240 | }
241 |
242 | /**
243 | * Whether attribute can have default value.
244 | */
245 | private function canDisplayUseDefault(SellerAttributeInterface $attribute): bool
246 | {
247 | $attributeCode = $attribute->getAttributeCode();
248 |
249 | /** @var ResourceModelSeller|SellerInterface|null $seller */
250 | $seller = $this->locator->getSeller();
251 |
252 | if (isset($this->canDisplayUseDefault[$attributeCode])) {
253 | return $this->canDisplayUseDefault[$attributeCode];
254 | }
255 |
256 | return $this->canDisplayUseDefault[$attributeCode] = (
257 | !$attribute->isScopeGlobal()
258 | && $seller
259 | && $seller->getId()
260 | && $seller->getStoreId()
261 | );
262 | }
263 |
264 | /**
265 | * Customize checkboxes.
266 | */
267 | private function customizeCheckbox(SellerAttributeInterface $attribute): array
268 | {
269 | $meta = [];
270 |
271 | if ($attribute->getFrontendInput() === 'boolean') {
272 | $meta['prefer'] = 'toggle';
273 | $meta['valueMap'] = [
274 | 'true' => '1',
275 | 'false' => '0',
276 | ];
277 | }
278 |
279 | return $meta;
280 | }
281 |
282 | /**
283 | * Converts category image data to acceptable for rendering format.
284 | */
285 | private function convertValues(SellerInterface $seller, array $data): array
286 | {
287 | foreach ($this->getAttributes() as $attribute) {
288 | $attributeCode = $attribute->getAttributeCode();
289 | if (!isset($data[$attributeCode])) {
290 | continue;
291 | }
292 |
293 | if ($attribute->getBackend() instanceof ImageBackendModel || $attribute->getFrontendInput() === 'image') {
294 | unset($data[$attributeCode]);
295 | $fileName = $seller->getData($attributeCode);
296 | $stat = $this->media->getStat($fileName);
297 | $mime = $this->media->getMimeType($fileName);
298 |
299 | $data[$attributeCode][0]['name'] = $fileName;
300 | $data[$attributeCode][0]['url'] = $this->getBaseImageUrl() . $fileName;
301 | $data[$attributeCode][0]['size'] = isset($stat['size']) ?: 0;
302 | $data[$attributeCode][0]['type'] = $mime;
303 | }
304 | }
305 |
306 | return $data;
307 | }
308 |
309 | /**
310 | * Get base image url.
311 | */
312 | public function getBaseImageUrl(): string
313 | {
314 | /** @var Store $currentStore */
315 | $currentStore = $this->storeManager->getStore();
316 | $mediaUrl = $currentStore->getBaseUrl(UrlInterface::URL_TYPE_MEDIA);
317 |
318 | return $mediaUrl . 'seller/';
319 | }
320 | }
321 |
--------------------------------------------------------------------------------
/Ui/Component/Seller/Listing/AddStoreFieldToCollection.php:
--------------------------------------------------------------------------------
1 | storeManager->getStore($condition['eq']);
31 | /** @var \Smile\Seller\Model\ResourceModel\Seller\Collection $collection */
32 | // @phpstan-ignore-next-line as generated object
33 | $collection->setStore($store);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Ui/Component/Seller/Listing/DataProvider.php:
--------------------------------------------------------------------------------
1 | collection = $collectionFactory->create();
33 | }
34 |
35 | /**
36 | * Get data.
37 | */
38 | public function getData(): array
39 | {
40 | if (!$this->getCollection()->isLoaded()) {
41 | $this->getCollection()->load();
42 | }
43 | $items = $this->getCollection()->toArray();
44 |
45 | return [
46 | 'totalRecords' => $this->getCollection()->getSize(),
47 | 'items' => array_values($items),
48 | ];
49 | }
50 |
51 | /**
52 | * @inheritdoc
53 | */
54 | public function addField($field, $alias = null)
55 | {
56 | if (isset($this->addFieldStrategies[$field])) {
57 | $this->addFieldStrategies[$field]->addField($this->getCollection(), $field, $alias);
58 |
59 | return ;
60 | }
61 | parent::addField($field, $alias);
62 | }
63 |
64 | /**
65 | * @inheritdoc
66 | */
67 | public function addFilter(Filter $filter): void
68 | {
69 | if (isset($this->addFilterStrategies[$filter->getField()])) {
70 | $this->addFilterStrategies[$filter->getField()]
71 | ->addFilter(
72 | $this->getCollection(),
73 | $filter->getField(),
74 | [$filter->getConditionType() => $filter->getValue()]
75 | );
76 | }
77 | if (!isset($this->addFilterStrategies[$filter->getField()])) {
78 | parent::addFilter($filter);
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smile/module-seller",
3 | "type": "magento2-component",
4 | "description": "Smile Retailer Suite - Seller Module",
5 | "keywords": ["magento2", "retailer suite", "retailer", "shop", "seller"],
6 | "license": "OSL-3.0",
7 | "authors": [
8 | {
9 | "name": "Aurélien Foucret",
10 | "email": "aurelien.foucret@smile.fr"
11 | },
12 | {
13 | "name": "Romain Ruaud",
14 | "email": "romain.ruaud@smile.fr"
15 | }
16 | ],
17 | "require": {
18 | "php": "^8.1",
19 | "magento/framework": ">=103.0.4",
20 | "magento/module-eav": ">=102.1.4",
21 | "magento/module-store": ">=101.1.4"
22 | },
23 | "require-dev": {
24 | "smile/magento2-smilelab-quality-suite": "^3.0"
25 | },
26 | "repositories": [
27 | {
28 | "type": "composer",
29 | "url": "https://repo.magento.com/"
30 | }
31 | ],
32 | "minimum-stability": "dev",
33 | "prefer-stable": true,
34 | "autoload": {
35 | "files": [
36 | "registration.php"
37 | ],
38 | "psr-4": {
39 | "Smile\\Seller\\": ""
40 | }
41 | },
42 | "config": {
43 | "allow-plugins": {
44 | "magento/composer-dependency-version-audit-plugin": true,
45 | "dealerdirect/phpcodesniffer-composer-installer": true
46 | },
47 | "sort-packages": true
48 | },
49 | "extra": {
50 | "map": [
51 | [
52 | "lib/web/fonts/retailersuite",
53 | "lib/web/fonts/retailersuite"
54 | ]
55 | ]
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/etc/acl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/etc/adminhtml/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | - Smile\Seller\Ui\Component\Seller\Listing\AddStoreFieldToCollection
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/etc/adminhtml/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
--------------------------------------------------------------------------------
/etc/db_schema.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
8 |
10 |
12 |
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
31 |
33 |
35 |
37 |
39 |
40 |
41 |
42 |
43 |
47 |
51 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
74 |
76 |
78 |
80 |
82 |
84 |
85 |
86 |
87 |
88 |
92 |
96 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
119 |
121 |
123 |
125 |
127 |
129 |
130 |
131 |
132 |
133 |
137 |
141 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
164 |
166 |
168 |
170 |
172 |
174 |
175 |
176 |
177 |
178 |
182 |
186 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
209 |
211 |
213 |
215 |
217 |
219 |
220 |
221 |
222 |
223 |
227 |
231 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
264 |
274 |
275 |
--------------------------------------------------------------------------------
/etc/db_schema_whitelist.json:
--------------------------------------------------------------------------------
1 | {
2 | "smile_seller_entity": {
3 | "column": {
4 | "entity_id": true,
5 | "attribute_set_id": true,
6 | "seller_code": true,
7 | "created_at": true,
8 | "updated_at": true
9 | },
10 | "index": {
11 | "SMILE_SELLER_ENTITY_SELLER_CODE": true
12 | },
13 | "constraint": {
14 | "PRIMARY": true
15 | }
16 | },
17 | "smile_seller_entity_datetime": {
18 | "column": {
19 | "value_id": true,
20 | "attribute_id": true,
21 | "store_id": true,
22 | "entity_id": true,
23 | "value": true
24 | },
25 | "index": {
26 | "SMILE_SELLER_ENTITY_DATETIME_ENTITY_ID": true,
27 | "SMILE_SELLER_ENTITY_DATETIME_ATTRIBUTE_ID": true,
28 | "SMILE_SELLER_ENTITY_DATETIME_STORE_ID": true
29 | },
30 | "constraint": {
31 | "PRIMARY": true,
32 | "SMILE_SELLER_ENTT_DTIME_ATTR_ID_EAV_ATTR_ATTR_ID": true,
33 | "SMILE_SELLER_ENTT_DTIME_ENTT_ID_SMILE_SELLER_ENTT_ENTT_ID": true,
34 | "SMILE_SELLER_ENTITY_DATETIME_STORE_ID_STORE_STORE_ID": true,
35 | "SMILE_SELLER_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true
36 | }
37 | },
38 | "smile_seller_entity_decimal": {
39 | "column": {
40 | "value_id": true,
41 | "attribute_id": true,
42 | "store_id": true,
43 | "entity_id": true,
44 | "value": true
45 | },
46 | "index": {
47 | "SMILE_SELLER_ENTITY_DECIMAL_ENTITY_ID": true,
48 | "SMILE_SELLER_ENTITY_DECIMAL_ATTRIBUTE_ID": true,
49 | "SMILE_SELLER_ENTITY_DECIMAL_STORE_ID": true
50 | },
51 | "constraint": {
52 | "PRIMARY": true,
53 | "SMILE_SELLER_ENTT_DEC_ATTR_ID_EAV_ATTR_ATTR_ID": true,
54 | "SMILE_SELLER_ENTT_DEC_ENTT_ID_SMILE_SELLER_ENTT_ENTT_ID": true,
55 | "SMILE_SELLER_ENTITY_DECIMAL_STORE_ID_STORE_STORE_ID": true,
56 | "SMILE_SELLER_ENTITY_DECIMAL_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true
57 | }
58 | },
59 | "smile_seller_entity_int": {
60 | "column": {
61 | "value_id": true,
62 | "attribute_id": true,
63 | "store_id": true,
64 | "entity_id": true,
65 | "value": true
66 | },
67 | "index": {
68 | "SMILE_SELLER_ENTITY_INT_ENTITY_ID": true,
69 | "SMILE_SELLER_ENTITY_INT_ATTRIBUTE_ID": true,
70 | "SMILE_SELLER_ENTITY_INT_STORE_ID": true
71 | },
72 | "constraint": {
73 | "PRIMARY": true,
74 | "SMILE_SELLER_ENTITY_INT_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true,
75 | "SMILE_SELLER_ENTITY_INT_ENTITY_ID_SMILE_SELLER_ENTITY_ENTITY_ID": true,
76 | "SMILE_SELLER_ENTITY_INT_STORE_ID_STORE_STORE_ID": true,
77 | "SMILE_SELLER_ENTITY_INT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true
78 | }
79 | },
80 | "smile_seller_entity_text": {
81 | "column": {
82 | "value_id": true,
83 | "attribute_id": true,
84 | "store_id": true,
85 | "entity_id": true,
86 | "value": true
87 | },
88 | "index": {
89 | "SMILE_SELLER_ENTITY_TEXT_ENTITY_ID": true,
90 | "SMILE_SELLER_ENTITY_TEXT_ATTRIBUTE_ID": true,
91 | "SMILE_SELLER_ENTITY_TEXT_STORE_ID": true
92 | },
93 | "constraint": {
94 | "PRIMARY": true,
95 | "SMILE_SELLER_ENTITY_TEXT_ATTRIBUTE_ID_EAV_ATTRIBUTE_ATTRIBUTE_ID": true,
96 | "SMILE_SELLER_ENTITY_TEXT_ENTITY_ID_SMILE_SELLER_ENTITY_ENTITY_ID": true,
97 | "SMILE_SELLER_ENTITY_TEXT_STORE_ID_STORE_STORE_ID": true,
98 | "SMILE_SELLER_ENTITY_TEXT_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true
99 | }
100 | },
101 | "smile_seller_entity_varchar": {
102 | "column": {
103 | "value_id": true,
104 | "attribute_id": true,
105 | "store_id": true,
106 | "entity_id": true,
107 | "value": true
108 | },
109 | "index": {
110 | "SMILE_SELLER_ENTITY_VARCHAR_ENTITY_ID": true,
111 | "SMILE_SELLER_ENTITY_VARCHAR_ATTRIBUTE_ID": true,
112 | "SMILE_SELLER_ENTITY_VARCHAR_STORE_ID": true
113 | },
114 | "constraint": {
115 | "PRIMARY": true,
116 | "SMILE_SELLER_ENTT_VCHR_ATTR_ID_EAV_ATTR_ATTR_ID": true,
117 | "SMILE_SELLER_ENTT_VCHR_ENTT_ID_SMILE_SELLER_ENTT_ENTT_ID": true,
118 | "SMILE_SELLER_ENTITY_VARCHAR_STORE_ID_STORE_STORE_ID": true,
119 | "SMILE_SELLER_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID_STORE_ID": true
120 | }
121 | },
122 | "sales_order": {
123 | "column": {
124 | "seller_id": true,
125 | "pickup_date": true
126 | },
127 | "constraint": {
128 | "SALES_ORDER_SELLER_ID_SMILE_SELLER_ENTITY_ENTITY_ID": true
129 | }
130 | },
131 | "quote": {
132 | "column": {
133 | "seller_id": true,
134 | "pickup_date": true
135 | },
136 | "constraint": {
137 | "QUOTE_SELLER_ID_SMILE_SELLER_ENTITY_ENTITY_ID": true
138 | }
139 | }
140 | }
--------------------------------------------------------------------------------
/etc/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Smile\Seller\EntityCreator\MetadataPool
11 |
12 |
13 |
14 |
15 |
16 | Smile\Seller\EntityCreator\MetadataPool
17 | Smile\Seller\Model\Entity\CreationScopeResolver
18 |
19 |
20 |
21 |
22 |
23 |
24 | Smile\Seller\Model\ResourceModel\AttributePersistor
25 |
26 |
27 |
28 |
29 |
30 |
31 | seller
32 | seller
33 |
34 | - jpg
35 | - jpeg
36 | - gif
37 | - png
38 | - pdf
39 |
40 |
41 |
42 |
43 |
44 |
45 | Smile\Seller\SellerImageUpload
46 |
47 |
48 |
49 |
50 |
51 | Smile\Seller\SellerImageUpload
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/etc/events.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/web/fonts/retailersuite/retailersuite.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Smile-SA/magento2-module-seller/39257a76e0514becf5cf64cec18d455a803c66f6/lib/web/fonts/retailersuite/retailersuite.eot
--------------------------------------------------------------------------------
/lib/web/fonts/retailersuite/retailersuite.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/lib/web/fonts/retailersuite/retailersuite.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Smile-SA/magento2-module-seller/39257a76e0514becf5cf64cec18d455a803c66f6/lib/web/fonts/retailersuite/retailersuite.ttf
--------------------------------------------------------------------------------
/lib/web/fonts/retailersuite/retailersuite.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Smile-SA/magento2-module-seller/39257a76e0514becf5cf64cec18d455a803c66f6/lib/web/fonts/retailersuite/retailersuite.woff
--------------------------------------------------------------------------------
/lib/web/fonts/retailersuite/retailersuite.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Smile-SA/magento2-module-seller/39257a76e0514becf5cf64cec18d455a803c66f6/lib/web/fonts/retailersuite/retailersuite.woff2
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 | a:before {
12 | font-family: @retailersuite-icons-admin__font-name;
13 | content: "\e900";
14 | }
15 |
--------------------------------------------------------------------------------