├── .gitignore
├── Api
├── ConfigInterface.php
├── Data
│ ├── RuleInterface.php
│ └── RuleSearchResultsInterface.php
├── PositionsInterface.php
├── RelatedCollectionInterface.php
├── RelatedItemsProcessorInterface.php
├── RelatedResourceModelInterface.php
└── RuleRepositoryInterface.php
├── Block
├── Adminhtml
│ ├── CheckEnableInfo.php
│ ├── Rule
│ │ ├── DuplicateButton.php
│ │ └── Edit
│ │ │ ├── WhatConditions.php
│ │ │ ├── WhereConditions.php
│ │ │ └── WhereConditions
│ │ │ └── Where.php
│ ├── System
│ │ └── Config
│ │ │ └── Form
│ │ │ └── Info.php
│ └── TmpInfo.php
└── RelatedProductList.php
├── Console
└── Command
│ └── ApplyRules.php
├── Controller
└── Adminhtml
│ ├── Actions.php
│ ├── Promo
│ └── NewConditionHtml.php
│ ├── Rule.php
│ └── Rule
│ ├── Apply.php
│ ├── Delete.php
│ ├── Edit.php
│ ├── Index.php
│ ├── InlineEdit.php
│ ├── MassDelete.php
│ ├── MassStatus.php
│ ├── NewAction.php
│ └── Save.php
├── Cron
└── UpdateRelatedProducts.php
├── LICENSE.txt
├── Model
├── ActionValidator.php
├── AutoRelatedProductAction.php
├── Condition
│ └── Product.php
├── Config.php
├── Config
│ └── Source
│ │ ├── MergeType.php
│ │ ├── Positions.php
│ │ ├── RelatedTemplate.php
│ │ └── SortBy.php
├── RelatedItemsProcessor.php
├── ResourceModel
│ ├── Rule.php
│ └── Rule
│ │ └── Collection.php
├── Rule.php
├── RuleManager.php
└── RuleRepository.php
├── Observer
└── Frontend
│ └── Layout
│ └── GenerateBlocksAfter.php
├── Plugin
├── Frontend
│ └── Magento
│ │ ├── Catalog
│ │ ├── Block
│ │ │ └── Product
│ │ │ │ ├── ProductList
│ │ │ │ ├── Related.php
│ │ │ │ └── Upsell.php
│ │ │ │ └── View
│ │ │ │ └── Details.php
│ │ └── Model
│ │ │ └── ResourceModel
│ │ │ └── Product
│ │ │ └── Link
│ │ │ └── Product
│ │ │ └── Collection.php
│ │ ├── Framework
│ │ └── View
│ │ │ └── Result
│ │ │ └── Layout.php
│ │ └── TargetRule
│ │ └── Block
│ │ └── Catalog
│ │ └── Product
│ │ └── ProductList
│ │ └── Related.php
└── Magento
│ └── SalesRule
│ └── Model
│ └── Rule
│ └── Condition
│ ├── Combine.php
│ └── Product
│ └── Combine.php
├── README.md
├── Ui
├── Component
│ └── Listing
│ │ └── Column
│ │ └── Actions.php
└── DataProvider
│ └── Rule
│ ├── Form
│ └── RuleDataProvider.php
│ └── Rule.php
├── composer.json
├── etc
├── acl.xml
├── adminhtml
│ ├── menu.xml
│ ├── routes.xml
│ └── system.xml
├── config.xml
├── crontab.xml
├── db_schema.xml
├── di.xml
├── frontend
│ ├── di.xml
│ └── events.xml
└── module.xml
├── registration.php
└── view
├── adminhtml
├── layout
│ ├── autorp_info.xml
│ ├── autorp_rule_edit.xml
│ ├── autorp_rule_index.xml
│ ├── default.xml
│ └── related_product_list.xml
├── templates
│ ├── form
│ │ └── versionsManager.phtml
│ ├── info.phtml
│ └── tmpInfo.phtml
├── ui_component
│ ├── autorp_listing.xml
│ └── autorp_rule_form.xml
└── web
│ └── js
│ ├── form
│ └── element
│ │ └── is-product-conditions.js
│ └── lib
│ └── core
│ └── collection.js
└── frontend
└── templates
└── product
└── list
└── related_product_items.phtml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
--------------------------------------------------------------------------------
/Api/ConfigInterface.php:
--------------------------------------------------------------------------------
1 | config = $config;
34 | parent::__construct($context, $data);
35 | }
36 |
37 | /**
38 | * @return bool
39 | */
40 | public function isEnabled(): bool
41 | {
42 | return $this->config->isEnabled();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Rule/DuplicateButton.php:
--------------------------------------------------------------------------------
1 | authorization->isAllowed("Magefan_AutoRelatedProduct::rule")) {
24 | return $data;
25 | }
26 |
27 | if ($this->getObjectId()) {
28 | $data = [
29 | 'label' => __('Duplicate (Plus)'),
30 | 'class' => 'duplicate',
31 | 'on_click' => '(typeof versionsManager !== "undefined" && versionsManager._currentPlan == "Basic") ? versionsManager.showAlert("Plus or Extra") : window.location=\'' . $this->getDuplicateUrl() . '\'',
32 | 'sort_order' => 40,
33 | ];
34 | }
35 | return $data;
36 | }
37 |
38 | /**
39 | * @return string
40 | */
41 | public function getDuplicateUrl()
42 | {
43 | return $this->getUrl('mfautorp/*/duplicate', ['id' => $this->getObjectId()]);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Rule/Edit/WhatConditions.php:
--------------------------------------------------------------------------------
1 | rendererFieldset = $rendererFieldset;
61 | $this->actions = $actions;
62 | $this->ruleFactory = $ruleFactory;
63 | parent::__construct($context, $registry, $formFactory, $data);
64 | }
65 |
66 | /**
67 | * {@inheritdoc}
68 | * @codeCoverageIgnore
69 | */
70 | public function getTabClass()
71 | {
72 | return null;
73 | }
74 |
75 | /**
76 | * {@inheritdoc}
77 | */
78 | public function getTabUrl()
79 | {
80 | return null;
81 | }
82 |
83 | /**
84 | * {@inheritdoc}
85 | */
86 | public function isAjaxLoaded()
87 | {
88 | return false;
89 | }
90 |
91 | /**
92 | * {@inheritdoc}
93 | */
94 | public function getTabLabel()
95 | {
96 | return __('What Conditions');
97 | }
98 |
99 | /**
100 | * {@inheritdoc}
101 | */
102 | public function getTabTitle()
103 | {
104 | return __('What Conditions');
105 | }
106 |
107 | /**
108 | * {@inheritdoc}
109 | */
110 | public function canShowTab()
111 | {
112 | return true;
113 | }
114 |
115 | /**
116 | * {@inheritdoc}
117 | */
118 | public function isHidden()
119 | {
120 | return false;
121 | }
122 |
123 | /**
124 | * @return \Magento\Backend\Block\Widget\Form\Generic
125 | * @throws \Magento\Framework\Exception\LocalizedException
126 | */
127 | protected function _prepareForm()
128 | {
129 | $model = $this->_coreRegistry->registry('current_model');
130 | $form = $this->addTabToForm($model);
131 | $this->setForm($form);
132 | return parent::_prepareForm();
133 | }
134 |
135 | /**
136 | * @param $model
137 | * @param $fieldsetId
138 | * @param $formName
139 | * @return \Magento\Framework\Data\Form
140 | * @throws \Magento\Framework\Exception\LocalizedException
141 | */
142 | protected function addTabToForm($model, $fieldsetId = 'conditions_fieldset', $formName = 'autorp_rule_form')
143 | {
144 | if (!$this->getRequest()->getParam('js_form_object')) {
145 | $this->getRequest()->setParam('js_form_object', $formName);
146 | }
147 |
148 | $rule = $this->ruleFactory->create();
149 | $rule->setData('conditions_serialized', $model->getData('conditions_serialized'));
150 | $model = $rule;
151 | $fieldSetId = $model->getConditionsFieldSetId($formName);
152 | $newChildUrl = $this->getUrl(
153 | 'sales_rule/promo_quote/newConditionHtml/form/' . $fieldSetId,
154 | [
155 | 'form_namespace' => $formName,
156 | ]
157 | );
158 | /** @var \Magento\Framework\Data\Form $form */
159 | $form = $this->_formFactory->create();
160 | $form->setHtmlIdPrefix('rule_');
161 | $renderer = $this->getLayout()->createBlock(Fieldset::class);
162 | $renderer = $renderer->setNameInLayout(
163 | 'mfautorp_what_conditions_serialized'
164 | )->setTemplate(
165 | 'Magento_CatalogRule::promo/fieldset.phtml'
166 | )->setNewChildUrl(
167 | $newChildUrl
168 | )->setFieldSetId(
169 | $fieldSetId
170 | );
171 | $fieldset = $form->addFieldset(
172 | $fieldsetId,
173 | [
174 | 'legend' => __(
175 | 'Apply the rule only to items matching the following conditions (leave blank for all items).'
176 | )
177 | ]
178 | )->setRenderer(
179 | $renderer
180 | );
181 |
182 | $fieldset->addField(
183 | 'conditions',
184 | 'text',
185 | [
186 | 'name' => 'conditions',
187 | 'label' => __('conditions'),
188 | 'title' => __('conditions'),
189 | 'required' => true,
190 | 'data-form-part' => $formName
191 | ]
192 | )->setRule(
193 | $model
194 | )->setRenderer(
195 | $this->actions
196 | );
197 | $form->setValues($model->getData());
198 | $this->setConditionFormName($model->getConditions(), $formName, $fieldSetId);
199 |
200 | return $form;
201 | }
202 |
203 | /**
204 | * @param \Magento\Rule\Model\Condition\AbstractCondition $conditions
205 | * @param $formName
206 | * @param $jsFormName
207 | * @return void
208 | */
209 | private function setConditionFormName(\Magento\Rule\Model\Condition\AbstractCondition $conditions, $formName, $jsFormName)
210 | {
211 | $conditions->setFormName($formName);
212 | $conditions->setJsFormObject($jsFormName);
213 | if ($conditions->getConditions() && is_array($conditions->getConditions())) {
214 | foreach ($conditions->getConditions() as $condition) {
215 | $this->setConditionFormName($condition, $formName, $jsFormName);
216 | }
217 | }
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Rule/Edit/WhereConditions.php:
--------------------------------------------------------------------------------
1 | rendererFieldset = $rendererFieldset;
56 | $this->conditions = $conditions;
57 | $this->ruleFactory = $ruleFactory;
58 | parent::__construct($context, $registry, $formFactory, $data);
59 | }
60 |
61 | /**
62 | * {@inheritdoc}
63 | * @codeCoverageIgnore
64 | */
65 | public function getTabClass()
66 | {
67 | return null;
68 | }
69 |
70 | /**
71 | * {@inheritdoc}
72 | */
73 | public function getTabUrl()
74 | {
75 | return null;
76 | }
77 |
78 | /**
79 | * {@inheritdoc}
80 | */
81 | public function isAjaxLoaded()
82 | {
83 | return false;
84 | }
85 |
86 | /**
87 | * {@inheritdoc}
88 | */
89 | public function getTabLabel()
90 | {
91 | return __('Where Conditions');
92 | }
93 |
94 | /**
95 | * {@inheritdoc}
96 | */
97 | public function getTabTitle()
98 | {
99 | return __('Where Conditions');
100 | }
101 |
102 | /**
103 | * {@inheritdoc}
104 | */
105 | public function canShowTab()
106 | {
107 | return true;
108 | }
109 |
110 | /**
111 | * {@inheritdoc}
112 | */
113 | public function isHidden()
114 | {
115 | return false;
116 | }
117 |
118 | /**
119 | * @return \Magento\Backend\Block\Widget\Form\Generic
120 | * @throws \Magento\Framework\Exception\LocalizedException
121 | */
122 | protected function _prepareForm()
123 | {
124 | $model = $this->_coreRegistry->registry('current_model');
125 | $form = $this->addTabToForm($model);
126 | $this->setForm($form);
127 | return parent::_prepareForm();
128 | }
129 |
130 | /**
131 | * @param $model
132 | * @param $fieldsetId
133 | * @param $formName
134 | * @return \Magento\Framework\Data\Form
135 | * @throws \Magento\Framework\Exception\LocalizedException
136 | */
137 | protected function addTabToForm($model, $fieldsetId = 'actions_fieldset', $formName = 'autorp_rule_form')
138 | {
139 | if (!$this->getRequest()->getParam('js_form_object')) {
140 | $this->getRequest()->setParam('js_form_object', $formName);
141 | }
142 |
143 | $model = $this->_coreRegistry->registry('current_model');
144 | $rule = $this->ruleFactory->create();
145 | $rule->setData('conditions_serialized', $model->getData('actions_serialized'));
146 | $model = $rule;
147 | $fieldSetId = 'autorp_rule_formrule_actions_fieldset_' . $model->getId();
148 | $newChildUrl = $this->getUrl(
149 | 'sales_rule/promo_quote/newActionHtml/form/' . $fieldSetId,
150 | ['form_namespace' => $formName]
151 | );
152 | /** @var \Magento\Framework\Data\Form $form */
153 | $form = $this->_formFactory->create();
154 | $form->setHtmlIdPrefix('rule_where');
155 | $renderer = $this->getLayout()->createBlock(Fieldset::class);
156 | $renderer = $renderer->setNameInLayout(
157 | 'mfautorp_where_actions_serialized'
158 | )->setTemplate(
159 | 'Magento_CatalogRule::promo/fieldset.phtml'
160 | )->setNewChildUrl(
161 | $newChildUrl
162 | )->setFieldSetId(
163 | $fieldSetId
164 | );
165 | $fieldset = $form->addFieldset(
166 | $fieldsetId,
167 | [
168 | 'legend' => __(
169 | 'Apply the rule only if the following conditions are met (leave blank for all products).'
170 | )
171 | ]
172 | )->setRenderer(
173 | $renderer
174 | );
175 | $fieldset->addField(
176 | 'actions',
177 | 'text',
178 | [
179 | 'name' => 'actions',
180 | 'label' => __('actions'),
181 | 'title' => __('actions'),
182 | 'required' => true,
183 | 'data-form-part' => $formName
184 | ]
185 | )->setRule(
186 | $model
187 | )->setRenderer(
188 | $this->conditions
189 | );
190 | $form->setValues($model->getData());
191 | $this->setConditionFormName($model->getConditions(), $formName);
192 | return $form;
193 | }
194 |
195 | /**
196 | * Handles addition of form name to condition and its conditions.
197 | *
198 | * @param \Magento\Rule\Model\Condition\AbstractCondition $conditions
199 | * @param string $formName
200 | * @return void
201 | */
202 | private function setConditionFormName(\Magento\Rule\Model\Condition\AbstractCondition $conditions, $formName)
203 | {
204 | $conditions->setFormName($formName);
205 | if ($conditions->getConditions() && is_array($conditions->getConditions())) {
206 | foreach ($conditions->getConditions() as $condition) {
207 | $this->setConditionFormName($condition, $formName);
208 | }
209 | }
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Rule/Edit/WhereConditions/Where.php:
--------------------------------------------------------------------------------
1 | getRule() && $element->getRule()->getConditions()) {
21 | $html = str_replace('conditions', 'actions', $element->getRule()->getConditions()->asHtmlRecursive());
22 | }
23 |
24 | return $html;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Block/Adminhtml/System/Config/Form/Info.php:
--------------------------------------------------------------------------------
1 | config = $config;
50 | $this->getModuleVersion = $getModuleVersion;
51 | $this->ruleCollection = $ruleCollection;
52 | $this->session = $session;
53 | parent::__construct($context, $data);
54 | }
55 |
56 | /**
57 | * @return bool
58 | */
59 | public function isSomeFeaturesRestricted(): bool
60 | {
61 | if ($this->getModuleVersion->execute('Magefan_AutoRelatedProductExtra') || $this->getModuleVersion->execute('Magefan_AutoRelatedProductPlus')) {
62 | return true;
63 | }
64 |
65 | return false;
66 | }
67 |
68 | /**
69 | * @return string
70 | */
71 | public function getAffectedRulesByDisplayModes(): string
72 | {
73 | $rules = $this->ruleCollection->create()
74 | ->addFieldToFilter('status', 1);
75 |
76 | $connection = $rules->getConnection();
77 | $tableName = $rules->getMainTable();
78 |
79 | $conditions = [];
80 |
81 | if ($connection->tableColumnExists($tableName, 'from_one_category_only')) {
82 | $conditions[] = 'from_one_category_only = 1';
83 | }
84 |
85 | if ($connection->tableColumnExists($tableName, 'only_with_higher_price')) {
86 | $conditions[] = 'only_with_higher_price = 1';
87 | }
88 |
89 | if ($connection->tableColumnExists($tableName, 'only_with_lower_price')) {
90 | $conditions[] = 'only_with_lower_price = 1';
91 | }
92 |
93 | if (!empty($conditions)) {
94 | $rules->getSelect()->where(implode(' OR ', $conditions));
95 | }
96 |
97 | return implode(',', $rules->getAllIds());
98 | }
99 |
100 | /**
101 | * @return string
102 | */
103 | public function getAffectedRulesBySortBy(): string
104 | {
105 | $restrictedSortByOptionsIds = [
106 | SortBy::NAME,
107 | SortBy::NEWEST,
108 | SortBy::PRICE_DESC,
109 | SortBy::PRICE_ASC
110 | ];
111 |
112 | $rules = $this->ruleCollection->create()
113 | ->addFieldToFilter('status', 1)
114 | ->addFieldToFilter('sort_by', ['in' => $restrictedSortByOptionsIds]);
115 |
116 | return implode(',', $rules->getAllIds());
117 | }
118 |
119 | /**
120 | * @return string
121 | */
122 | public function toHtml()
123 | {
124 | if (!$this->config->isEnabled() || $this->session->getIsNeedToShowAlert() === false) {
125 | return '';
126 | }
127 |
128 | $this->session->setIsNeedToShowAlert(
129 | !$this->isSomeFeaturesRestricted() && ($this->getAffectedRulesByDisplayModes() || $this->getAffectedRulesBySortBy())
130 | );
131 |
132 | return parent::_toHtml();
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/Block/RelatedProductList.php:
--------------------------------------------------------------------------------
1 | config = $config;
50 | $this->ruleManager = $ruleManager;
51 | parent::__construct($context, $data);
52 | }
53 |
54 | /**
55 | * {@inheritdoc}
56 | */
57 | public function getTemplate()
58 | {
59 | $theme = $this->_design->getDesignTheme();
60 | while ($theme) {
61 | if ('Hyva/default' == $theme->getCode()) {
62 | return $this->_hTemplate;
63 | }
64 | $theme = $theme->getParentTheme();
65 | }
66 |
67 | return $this->getPassedTemplate() ?: $this->getTemplateFromRule() ?: parent::getTemplate();
68 | }
69 |
70 | /**
71 | * @return string
72 | */
73 | private function getPassedTemplate(): string
74 | {
75 | return $this->_data['template'] ?? '';
76 | }
77 |
78 | /**
79 | * @return string
80 | * @throws \Magento\Framework\Exception\LocalizedException
81 | */
82 | private function getTemplateFromRule(): string
83 | {
84 | $rule = $this->getRule();
85 | return $rule ? (string)$rule->getTemplate() : '';
86 | }
87 |
88 | /**
89 | * @return string
90 | */
91 | public function getTitle(): string
92 | {
93 | return (string)$this->getBlockModelData('title', 'getBlockTitle');
94 | }
95 |
96 | /**
97 | * @return int
98 | */
99 | public function getNumberOfProducts(): int
100 | {
101 | return (int)$this->getBlockModelData('number_of_products', 'getNumberOfProducts');
102 | }
103 |
104 | /**
105 | * @return bool
106 | */
107 | public function isDisplayAddToCart(): bool
108 | {
109 | return (bool)$this->getBlockModelData('display_add_to_cart', 'getDisplayAddToCart');
110 | }
111 |
112 | /**
113 | * @param $key
114 | * @param $method
115 | * @return array|mixed|null
116 | */
117 | protected function getBlockModelData($key, $method)
118 | {
119 | if (null === $this->getData($key)) {
120 | if ($rule = $this->getRule()) {
121 | $this->setData($key, $rule->$method() ?: '');
122 | } else {
123 | $this->setData($key, '');
124 | }
125 | }
126 | return $this->getData($key);
127 | }
128 |
129 | /**
130 | * @return string
131 | * @throws \Magento\Framework\Exception\LocalizedException
132 | */
133 | public function toHtml(): string
134 | {
135 | if (!$this->config->isEnabled() || !$this->getRule()) {
136 | return '';
137 | }
138 |
139 | // since m248 to avoid Warning: Undefined array key 4 in vendor/magento/module-catalog/Pricing/Render/FinalPriceBox.php on line 161
140 | $priceRender = $this->getLayout()->getBlock('product.price.render.default');
141 | $isProductListValOriginal = $priceRender ? $priceRender->getData('is_product_list') : false;
142 |
143 | if ($isProductListValOriginal) {
144 | $priceRender->setData('is_product_list', false);
145 | }
146 |
147 | $html = parent::_toHtml();
148 |
149 | if ($isProductListValOriginal) {
150 | $priceRender->setData('is_product_list', true);
151 | }
152 |
153 | if (!$html) {
154 | return '';
155 | }
156 |
157 | $html = str_replace((string)__('Related Products'), (string)__($this->getTitle()), $html);
158 |
159 | $replaceFrom = $replaceTo = [];
160 | $ruleId = $this->getRule()->getId();
161 |
162 | if (!$this->canItemsAddToCart()
163 | || 'catalog_product_view' != $this->getRequest()->getFullActionName()
164 | ) {
165 | $replaceFrom = array_merge(
166 | $replaceFrom,
167 | ['block-actions', 'field choice related']
168 | );
169 | $replaceTo = array_merge(
170 | $replaceTo,
171 | ['block-actions hide-by-rule-' . $ruleId, 'field choice related hide-by-rule-' . $ruleId]
172 | );
173 | }
174 |
175 | if (!$this->canItemsAddToCart()) {
176 |
177 | $replaceFrom = array_merge(
178 | $replaceFrom,
179 | [' tocart ']
180 | );
181 | $replaceTo = array_merge(
182 | $replaceTo,
183 | [' tocart hide-by-rule-' . $ruleId]
184 | );
185 | }
186 |
187 | if ($replaceFrom) {
188 |
189 | $html = str_replace($replaceFrom, $replaceTo, $html);
190 | $html .= '';
191 | }
192 |
193 | return $html;
194 |
195 | }
196 |
197 | /**
198 | * Get collection items
199 | *
200 | * @return Collection
201 | */
202 | public function getItems()
203 | {
204 | /**
205 | * getIdentities() depends on _itemCollection populated, but it can be empty if the block is hidden
206 | * @see https://github.com/magento/magento2/issues/5897
207 | */
208 | if ($this->_itemCollection === null) {
209 | $this->_itemCollection =
210 | $this->ruleManager->getReletedProductsColletion(
211 | $this->getRule(),
212 | [
213 | 'current_category' => $this->getCategory(),
214 | 'current_product' => $this->getProduct(),
215 | 'skip_ids' => $this->getSkipItems()
216 | ]
217 | );
218 | }
219 |
220 | return $this->_itemCollection;
221 | }
222 |
223 | /**
224 | * Get skip product ids
225 | *
226 | * @return array
227 | */
228 | protected function getSkipItems()
229 | {
230 | if ('catalog_category_view' !== $this->getRequest()->getFullActionName()) {
231 | return [];
232 | }
233 |
234 | $productListBlock = $this->getLayout()->getBlock('category.products.list');
235 | if (!$productListBlock) {
236 | return [];
237 | }
238 |
239 | $toolbar = $productListBlock->getToolbarBlock();
240 | if (!$toolbar) {
241 | return [];
242 | }
243 |
244 | $pagerBlock = $toolbar->getChildBlock('product_list_toolbar_pager');
245 | if (!($pagerBlock instanceof \Magento\Theme\Block\Html\Pager)) {
246 | return [];
247 | }
248 |
249 | $originCollection = $productListBlock->getLayer()->getProductCollection();
250 | $collection = clone $originCollection;
251 |
252 |
253 | $pagerBlock->setLimit((int)$toolbar->getLimit())
254 | ->setCollection($collection);
255 |
256 | return $collection->load()->getAllIds();
257 | }
258 |
259 | /**
260 | * Synonim to getItems
261 | *
262 | * @return Collection
263 | */
264 | public function getItemCollection()
265 | {
266 | return $this->getItems();
267 | }
268 |
269 | /**
270 | * Check if there is any items
271 | *
272 | * @return bool
273 | */
274 | public function hasItems(): bool
275 | {
276 | return count($this->getItems()) ? true : false;
277 | }
278 |
279 | public function getIdentities()
280 | {
281 | $identities = [];
282 |
283 | if ($this->getProduct()) {
284 | $identities = [Product::CACHE_TAG . '_' . $this->getProduct()->getId()];
285 | }
286 |
287 | if (count($this->getItems())) {
288 | foreach ($this->getItems() as $item) {
289 | foreach ($item->getIdentities() as $identity) {
290 | $identities[] = $identity;
291 | }
292 | }
293 | } else {
294 | $identities[] = Product::CACHE_TAG;
295 | }
296 |
297 | return $identities;
298 | }
299 |
300 | /**
301 | * @return string
302 | */
303 | public function getType()
304 | {
305 | return $this->getBlockModelData('items_type', 'getItemsType') ?: 'related';
306 | }
307 |
308 | /**
309 | * @return bool
310 | */
311 | public function canItemsAddToCart(): bool
312 | {
313 | return $this->getBlockModelData('display_add_to_cart', 'getDisplayAddToCart') ? true : false;
314 | }
315 |
316 | /**
317 | * Retrieve currently viewed category object
318 | *
319 | * @return \Magento\Catalog\Model\Category
320 | */
321 | public function getCategory()
322 | {
323 | if (!$this->hasData('category')) {
324 | $this->setData('category', $this->_coreRegistry->registry('current_category'));
325 | }
326 | return $this->getData('category');
327 | }
328 |
329 | /**
330 | * @return array|mixed|null
331 | * @throws \Magento\Framework\Exception\LocalizedException
332 | */
333 | public function getRule()
334 | {
335 | $rule = $this->getData('rule');
336 |
337 | $ruleId = ($rule && $rule->getId()) ? $rule->getId() : $this->getData('rule_id');
338 |
339 | if ($ruleId) {
340 | $rule = $this->ruleManager->getRuleById((int)$ruleId);
341 |
342 | $this->setData('rule', $rule);
343 | }
344 |
345 | return $this->getData('rule');
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/Console/Command/ApplyRules.php:
--------------------------------------------------------------------------------
1 | config = $config;
61 | $this->autoRelatedProductAction = $autoRelatedProductAction;
62 | $this->escaper = $escaper;
63 | $this->state = $state;
64 | parent::__construct($name);
65 | }
66 |
67 | /**
68 | * @param InputInterface $input
69 | * @param OutputInterface $output
70 | * @return int
71 | * @throws \Magento\Framework\Exception\LocalizedException
72 | */
73 | protected function execute(InputInterface $input, OutputInterface $output): int
74 | {
75 | if ($this->config->isEnabled()) {
76 | try {
77 | $this->state->setAreaCode(Area::AREA_GLOBAL);
78 | } catch (LocalizedException $e) {
79 | $output->writeln((string)__('Something went wrong. %1', $this->escaper->escapeHtml($e->getMessage())));
80 | }
81 |
82 | $ruleIDs = (string)$input->getOption(self::RULE_IDS_PARAM);
83 |
84 | $ruleIDs = $ruleIDs
85 | ? array_map('intval', explode(',', $ruleIDs))
86 | : [];
87 |
88 | if ($ruleIDs) {
89 | $output->writeln('' . __('The provided rule IDs: %1', '`' . implode(',', $ruleIDs) . '`') . ' ');
90 | $this->autoRelatedProductAction->execute(['rule_ids' => $ruleIDs]);
91 | } else {
92 | $this->autoRelatedProductAction->execute();
93 | }
94 |
95 | $output->writeln("Rules have been applied.");
96 | } else {
97 | $output->writeln("Extension is disabled. Please turn on it.");
98 | }
99 | return 0;
100 | }
101 |
102 | /**
103 | * {@inheritdoc}
104 | */
105 | protected function configure()
106 | {
107 | $options = [
108 | new InputOption(
109 | self::RULE_IDS_PARAM,
110 | null,
111 | InputOption::VALUE_OPTIONAL,
112 | 'Rule Ids'
113 | )
114 | ];
115 |
116 | $this->setDefinition($options);
117 |
118 | $this->setName("magefan:arp:apply");
119 | $this->setDescription("Apply by Rule IDs (comma separated)");
120 |
121 | parent::configure();
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Actions.php:
--------------------------------------------------------------------------------
1 | getRequest()->getParam('id');
26 | $formName = $this->getRequest()->getParam('form_namespace');
27 | $typeArr = explode('|', str_replace('-', '/', $this->getRequest()->getParam('type')));
28 | $type = $typeArr[0];
29 | $model = $this->_objectManager->create($type)
30 | ->setId($id)
31 | ->setType($type)
32 | ->setRule($this->_objectManager->create(\Magento\CatalogRule\Model\Rule::class))
33 | ->setPrefix('actions');
34 |
35 | if (!empty($typeArr[1])) {
36 | $model->setAttribute($typeArr[1]);
37 | }
38 |
39 | if ($model instanceof AbstractCondition) {
40 | $model->setJsFormObject($this->getRequest()->getParam('form'));
41 | $model->setFormName($formName);
42 | $html = $model->asHtmlRecursive();
43 | } else {
44 | $html = '';
45 | }
46 | $this->getResponse()->setBody($html);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Rule.php:
--------------------------------------------------------------------------------
1 | config = $config;
53 | $this->ruleCollection = $ruleCollectionFactory;
54 | $this->autoRelatedProductAction = $autoRelatedProductAction;
55 | parent::__construct($context);
56 | }
57 |
58 | /**
59 | * Action execute
60 | * @return \Magento\Framework\Controller\ResultInterface
61 | */
62 | public function execute()
63 | {
64 | if (!$this->config->isEnabled()) {
65 | $this->messageManager->addError(
66 | __(
67 | strrev(
68 | 'noitalsnarT> snoisnetxE nafegaM > noitarugifnoC >
69 | serotS ot etagivan esaelp noisnetxe eht elbane ot ,delbasid si noitalsnarT nafegaM'
70 | )
71 | )
72 | );
73 | $redirect = $this->resultRedirectFactory->create();
74 | return $redirect->setPath('admin/index/index');
75 | }
76 | $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
77 | $resultRedirect->setUrl($this->_redirect->getRefererUrl());
78 |
79 | try {
80 | $countRules = $this->ruleCollection->create()->getSize();
81 |
82 | if (!$countRules) {
83 | $this->messageManager->addError(__('Cannot find any rule.'));
84 | }
85 | if ($this->config->isEnabled()) {
86 | $this->autoRelatedProductAction->execute();
87 | $this->messageManager->addSuccess(__('Rules has been applied.'));
88 | } else {
89 | $this->messageManager->addNotice(__('Please enable the extension to apply rules.'));
90 | }
91 | } catch (\Exception $e) {
92 | $this->messageManager->addError(__('Something went wrong. %1', $e->getMessage()));
93 | }
94 |
95 | return $resultRedirect;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Rule/Delete.php:
--------------------------------------------------------------------------------
1 | jsonFactory = $jsonFactory;
38 | }
39 |
40 | /**
41 | * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
42 | */
43 | public function execute()
44 | {
45 | /** @var \Magento\Framework\Controller\Result\Json $resultJson */
46 | $resultJson = $this->jsonFactory->create();
47 | $error = false;
48 | $messages = [];
49 |
50 | if ($this->getRequest()->getParam('isAjax')) {
51 | $ruleItems = $this->getRequest()->getParam('items', []);
52 | if (!count($ruleItems)) {
53 | $messages[] = __('Please correct the data sent.');
54 | $error = true;
55 | } else {
56 | foreach (array_keys($ruleItems) as $modelid) {
57 | /** @var \Magento\Cms\Model\Block $block */
58 | $model = $this->_objectManager->create('Magefan\AutoRelatedProduct\Model\Rule')->load($modelid);
59 | try {
60 | $model->setData(array_merge($model->getData(), $ruleItems[$modelid]));
61 | $model->save();
62 | } catch (\Exception $e) {
63 | $messages[] = "[Rule ID: {$modelid}] {$e->getMessage()}";
64 | $error = true;
65 | }
66 | }
67 | }
68 | }
69 |
70 | return $resultJson->setData([
71 | 'messages' => $messages,
72 | 'error' => $error
73 | ]);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Rule/MassDelete.php:
--------------------------------------------------------------------------------
1 | filter = $filter;
44 | $this->collectionFactory = $collectionFactory;
45 | }
46 | /**
47 | * Execute action
48 | *
49 | * @return \Magento\Backend\Model\View\Result\Redirect
50 | * @throws \Magento\Framework\Exception\LocalizedException|\Exception
51 | */
52 | public function execute()
53 | {
54 | $collection = $this->filter->getCollection($this->collectionFactory->create());
55 | $collectionSize = $collection->getSize();
56 | foreach ($collection as $item) {
57 | $item->delete();
58 | }
59 |
60 | $this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize));
61 |
62 | /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
63 | $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
64 | return $resultRedirect->setPath('*/*/');
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Rule/MassStatus.php:
--------------------------------------------------------------------------------
1 | collectionFactory = $collectionFactory;
51 | $this->filter = $filter;
52 | parent::__construct($context, $dataPersistor);
53 | }
54 |
55 | /**
56 | * Action execute
57 | * @return \Magento\Framework\Controller\ResultInterface
58 | */
59 | public function execute()
60 | {
61 | if (!$this->getRequest()->isPost()) {
62 | throw new NotFoundException(__('Page not found'));
63 | }
64 |
65 | $collection = $this->filter->getCollection($this->collectionFactory->create());
66 | $ruleIds = $collection->getAllIds();
67 | $status = (int) $this->getRequest()->getParam('status');
68 |
69 | try {
70 | foreach ($ruleIds as $id) {
71 | $model = $this->_objectManager->create($this->_modelClass)
72 | ->load($id);
73 | if ($model->getId()) {
74 | $model->setData('status', $status)
75 | ->save();
76 | }
77 | }
78 | $this->messageManager->addSuccessMessage(
79 | __('A total of %1 record(s) have been updated.', count($ruleIds))
80 | );
81 | } catch (\Magento\Framework\Exception\LocalizedException $e) {
82 | $this->messageManager->addErrorMessage($e->getMessage());
83 | } catch (\Exception $e) {
84 | $this->_getSession()->addException($e, __('Something went wrong while updating the rule(s) status.'));
85 | }
86 |
87 | $this->_redirect('*/*');
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Rule/NewAction.php:
--------------------------------------------------------------------------------
1 | config = $config;
32 | $this->autoRelatedProductAction = $autoRelatedProductAction;
33 | }
34 |
35 | /**
36 | *
37 | */
38 | public function execute()
39 | {
40 | if ($this->config->isEnabled()) {
41 | $this->autoRelatedProductAction->execute();
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Please visit Magefan.com for license details (https://magefan.com/end-user-license-agreement)
2 |
--------------------------------------------------------------------------------
/Model/ActionValidator.php:
--------------------------------------------------------------------------------
1 | date = $date;
78 | $this->checkoutSession = $checkoutSession;
79 | $this->ruleFactory = $ruleFactory;
80 | $this->productRepository =$productRepository;
81 | $this->request = $request;
82 | $this->registry = $registry;
83 | $this->urlInterface = $urlInterface ?: \Magento\Framework\App\ObjectManager::getInstance()->get(UrlInterface::class);
84 | }
85 |
86 | /**
87 | * @param \Magento\Framework\Model\AbstractModel $rule
88 | * @return bool
89 | * @throws \Magento\Framework\Exception\LocalizedException
90 | * @throws \Magento\Framework\Exception\NoSuchEntityException
91 | */
92 | public function isRestricted(\Magento\Framework\Model\AbstractModel $rule): bool
93 | {
94 | if (null !== $rule->getData('is_actions_restricted')) {
95 | return $rule->getData('is_actions_restricted');
96 | }
97 |
98 | if (!$this->isConditionsTrue($rule)) {
99 | $rule->setData('is_actions_restricted', true);
100 | return true;
101 | }
102 | $rule->setData('is_actions_restricted', false);
103 | return false;
104 | }
105 |
106 | /**
107 | * @param $actionRule
108 | * @return bool
109 | * @throws \Magento\Framework\Exception\LocalizedException
110 | * @throws \Magento\Framework\Exception\NoSuchEntityException
111 | */
112 | public function isConditionsTrue($actionRule): bool
113 | {
114 | if (!$actionRule->getData('actions_serialized')) {
115 | return true;
116 | }
117 |
118 | $rule = $this->ruleFactory->create();
119 | $rule->setData('conditions_serialized', $actionRule->getData('actions_serialized'));
120 |
121 | $conditions = $rule->getConditions();
122 |
123 | if (empty($conditions['conditions'])) {
124 | return true;
125 | }
126 |
127 | $quote = $this->checkoutSession->getQuote();
128 |
129 | if ($quote->getItemsQty() == $quote->getItemVirtualQty()) {
130 | $address = $quote->getBillingAddress();
131 | } else {
132 | $address = $quote->getShippingAddress();
133 | }
134 |
135 | $address->setTotalQty($quote->getItemsQty());
136 |
137 | $product = $actionRule->getProduct();
138 |
139 | if (!$product) {
140 | $product = $this->registry->registry('product');
141 | }
142 |
143 | $isMfcmsdrGetController = ($this->request->getFullActionName() == 'autorp_block_get');
144 |
145 | if (!$product) {
146 | try {
147 | $productId = $this->request->getParam('product_id');
148 | if ($productId) {
149 | $product = $this->productRepository->getById($productId);
150 | if ($isMfcmsdrGetController) {
151 | $this->registry->register('product', $product);
152 | }
153 | } else {
154 | $product = false;
155 | }
156 | } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
157 | $product = false;
158 | }
159 | }
160 |
161 | $address = clone $address;
162 |
163 | if ($product && $product->getId()) {
164 | foreach ($product->getData() as $k => $v) {
165 | if (!$address->getData($k)) {
166 | $address->setData($k, $v);
167 | }
168 | if ($k == 'quantity_and_stock_status') {
169 | if ($v['is_in_stock'] == false) {
170 | $address->setData('quantity_and_stock_status', 0);
171 | } else {
172 | $address->setData('quantity_and_stock_status', 1);
173 | }
174 | }
175 | }
176 | }
177 |
178 | if ($isMfcmsdrGetController) {
179 | $address->setData('page_action_name', $this->request->getParam('fan'));
180 | $address->setData('page_uri', $this->request->getParam('p'));
181 |
182 | if ($categoryId = $this->request->getParam('category_id')) {
183 | $address->setData('catalog_category_ids', $categoryId);
184 | }
185 | } else {
186 | $address->setData('page_action_name', $this->request->getFullActionName());
187 | $address->setData('page_uri', $this->urlInterface->getCurrentUrl());
188 |
189 | if ($this->request->getFullActionName() == 'catalog_category_view') {
190 | $address->setData('catalog_category_ids', $this->request->getParam('id'));
191 | }
192 | }
193 |
194 | return $rule->validate($address);
195 | }
196 |
197 | /**
198 | * @param $current
199 | * @param $start
200 | * @param $finish
201 | * @return bool
202 | */
203 | public function isInTimeFrame($current, $start, $finish): bool
204 | {
205 | if ($start != $finish) {
206 | if ($start && $finish) {
207 | return ($current >= $start && $current <= $finish);
208 | } elseif ($start) {
209 | return ($start <= $current);
210 | } elseif ($finish) {
211 | return ($finish >= $current);
212 | }
213 | }
214 | return true;
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/Model/AutoRelatedProductAction.php:
--------------------------------------------------------------------------------
1 | resourceConnection = $resourceConnection;
116 | $this->connection = $resourceConnection->getConnection();
117 | $this->ruleCollectionFactory = $ruleCollectionFactory;
118 | $this->ruleRepository = $ruleRepository;
119 | $this->productCollectionFactory = $productCollectionFactory;
120 | $this->validationFilter = $validationFilter;
121 | $this->sqlBuilder = $sqlBuilder ?: \Magento\Framework\App\ObjectManager::getInstance()
122 | ->get(Builder::class);
123 | $this->catalogRuleFactory = $catalogRuleFactory;
124 |
125 | $this->getParentProductIds = $getParentProductIds;
126 | $this->getWebsitesMap = $getWebsitesMap;
127 | $this->moduleManager = $moduleManager;
128 | $this->eventManager = $eventManager;
129 |
130 | if ($this->moduleManager->isEnabled('Magefan_DynamicProductAttributes')) {
131 | $this->validationFilter =
132 | \Magento\Framework\App\ObjectManager::getInstance()->get('Magefan\DynamicProductAttributes\Api\AddCustomValidationFiltersInterface');
133 | }
134 | }
135 |
136 | /**
137 | * @param array $params
138 | * @return void
139 | */
140 | public function execute(array $params = []): void
141 | {
142 | $productIdsToCleanCache = [];
143 | $oldProductToRuleData = [];
144 |
145 | $connection = $this->resourceConnection->getConnection();
146 | $tableNameArpIndex = $this->resourceConnection->getTableName('magefan_autorp_index');
147 |
148 | $ruleCollection = $this->ruleCollectionFactory->create()
149 | ->addFieldToFilter('status', 1);
150 |
151 | if (isset($params['rule_ids'])) {
152 | $ruleIds = (array)$params['rule_ids'];
153 | $ruleCollection->addFieldToFilter('id', ['in' => $ruleIds]);
154 | }
155 |
156 | if ($ruleCollection) {
157 | $oldProductToRuleCollection = $this->connection->fetchAll($this->connection->select()->from($tableNameArpIndex));
158 |
159 | foreach ($oldProductToRuleCollection as $value) {
160 | $relatedIds = explode(',', $value['related_ids']);
161 |
162 | foreach ($relatedIds as $productId) {
163 | $oldProductToRuleData[$value['rule_id'] . '_' . $productId] = $productId;
164 | }
165 | }
166 | }
167 |
168 | foreach ($ruleCollection as $item) {
169 | if ($conditionsSerialized = $item->getData('conditions_serialized')) {
170 | $ruleId = $item->getId();
171 |
172 | $rule = $this->catalogRuleFactory->create();
173 | $rule->setData('conditions_serialized', $conditionsSerialized);
174 | $rule->setData('store_ids', $item->getStoreIds());
175 |
176 | $relatedIds = $this->getListProductIds($rule);
177 |
178 | foreach ($relatedIds as $productId) {
179 | if (!isset($oldProductToRuleData[$ruleId . '_' . $productId])) {
180 | $productIdsToCleanCache[$productId] = $productId;
181 | } else {
182 | unset($oldProductToRuleData[$ruleId . '_' . $productId]);
183 | }
184 | }
185 |
186 | $connection->insertOnDuplicate(
187 | $tableNameArpIndex,
188 | [
189 | 'rule_id' => $ruleId,
190 | 'identifier' => $item->getRuleBlockIdentifier(),
191 | 'related_ids' => implode(',', $relatedIds),
192 | ],
193 | [
194 | 'rule_id',
195 | 'identifier',
196 | 'related_ids'
197 | ]
198 | );
199 | }
200 | }
201 |
202 | foreach ($oldProductToRuleData as $productId) {
203 | $productIdsToCleanCache[$productId] = $productId;
204 | }
205 |
206 | if ($productIdsToCleanCache) {
207 | $this->cleanCacheByProductIds($productIdsToCleanCache);
208 | }
209 |
210 | }
211 |
212 | /**
213 | * @param $rule
214 | * @param null $params
215 | * @return array
216 | * @throws LocalizedException
217 | */
218 | /**
219 | * @param $rule
220 | * @param null $params
221 | * @return array
222 | */
223 | public function getListProductIds($rule)
224 | {
225 | $this->productIds = [];
226 | $conditions = $rule->getConditions();
227 |
228 | if (!empty($conditions['conditions'])) {
229 | if ($rule->getWebsiteIds()) {
230 | $storeIds = [];
231 | $websites = $this->getWebsitesMap->execute();
232 |
233 | foreach ($websites as $websiteId => $defaultStoreId) {
234 | if (in_array($websiteId, $rule->getWebsiteIds())) {
235 | $storeIds[] = $defaultStoreId;
236 | }
237 | }
238 | } else {
239 | $storeIds = [0];
240 | }
241 |
242 | $conditions = $rule->getConditions()->asArray();
243 |
244 | if ($this->validationFilter !== null) {
245 | $conditions = $this->validationFilter->processCustomValidator($conditions);
246 | }
247 |
248 | $rule->getConditions()->setConditions([])->loadArray($conditions);
249 |
250 | foreach ($storeIds as $storeId) {
251 |
252 | $productCollection = $this->productCollectionFactory->create();
253 |
254 | if ($storeId) {
255 | $productCollection->setStoreId($storeId);
256 | }
257 |
258 | $conditions = $rule->getConditions();
259 |
260 | $conditions->collectValidatedAttributes($productCollection);
261 | $this->sqlBuilder->attachConditionToCollection($productCollection, $conditions);
262 |
263 | if ($this->validationFilter !== null) {
264 | $this->validationFilter->addCustomValidationFilters($productCollection);
265 | }
266 |
267 | $productCollection->getSelect()->group('e.entity_id');
268 |
269 | foreach ($productCollection as $item) {
270 | $this->productIds[] = (int) $item->getId();
271 | }
272 | }
273 | }
274 |
275 | $this->productIds = array_merge(
276 | $this->productIds,
277 | $this->getParentProductIds->execute($this->productIds)
278 | );
279 |
280 | return array_unique($this->productIds);
281 | }
282 |
283 | /**
284 | * @param array $productIds
285 | * @return void
286 | */
287 | private function cleanCacheByProductIds(array $productIds): void
288 | {
289 | $productCollection = $this->productCollectionFactory->create()
290 | ->addAttributeToFilter('entity_id', ['in' => $productIds]);
291 |
292 | foreach ($productCollection as $product) {
293 | $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $product]);
294 | }
295 | }
296 | }
297 |
--------------------------------------------------------------------------------
/Model/Condition/Product.php:
--------------------------------------------------------------------------------
1 | _addSpecialAttributes($attributes);
47 | asort($attributes);
48 | $this->setAttributeOption($attributes);
49 | return $this;
50 | }
51 |
52 | /**
53 | * @param array $attributes
54 | */
55 | protected function _addSpecialAttributes(array &$attributes)
56 | {
57 | $attributes['page_action_name'] = __('Action Name');
58 | $attributes['page_uri'] = __('URI');
59 | $attributes['catalog_category_ids'] = __('Category');
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Model/Config.php:
--------------------------------------------------------------------------------
1 | scopeConfig = $scopeConfig;
37 | }
38 |
39 | /**
40 | * Retrieve true if module is enabled
41 | *
42 | * @return bool
43 | */
44 | public function isEnabled($storeId = null): bool
45 | {
46 | return (bool)$this->getConfig(self::XML_PATH_EXTENSION_ENABLED, $storeId);
47 | }
48 |
49 | /**
50 | * Retrieve store config value
51 | * @param string $path
52 | * @param null $storeId
53 | * @return mixed
54 | */
55 | public function getConfig($path, $storeId = null)
56 | {
57 | return $this->scopeConfig->getValue(
58 | $path,
59 | ScopeInterface::SCOPE_STORE,
60 | $storeId
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Model/Config/Source/MergeType.php:
--------------------------------------------------------------------------------
1 | __('Add to Current(Native) Related Products'), 'value' => self::MERGE],
27 | ['label' => __('Add Instead Current Related Products'), 'value' => self::INSTEAD]
28 | ]);
29 |
30 | return $options;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Model/Config/Source/Positions.php:
--------------------------------------------------------------------------------
1 | options = $options;
31 | }
32 |
33 | /**
34 | * @return array
35 | */
36 | public function toOptionArray(): array
37 | {
38 | $options = [];
39 | foreach ($this->options as $option) {
40 | $option['label'] = __($option['label']);
41 | if (isset($option['value']) && is_array($option['value'])) {
42 | $option['value'] = array_values($option['value']);
43 | foreach ($option['value'] as $key => $item) {
44 | $option['value'][$key]['label'] = __($option['value'][$key]['label']);
45 | }
46 | }
47 | $options[] = $option;
48 | }
49 | return $options;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Model/Config/Source/RelatedTemplate.php:
--------------------------------------------------------------------------------
1 | self::DEFAULT, 'label' => __('Default Related Template')],
45 | ['value' => self::COMPARE, 'label' => __('Compare Template (Extra)')],
46 | ['value' => self::FBT, 'label' => __('Frequently Bought Together Template (Extra)')],
47 | ['value' => self::CUSTOM, 'label' => __(' - Set Custom Template (Plus) - ')],
48 | ];
49 | }
50 |
51 | /**
52 | * Get options in "key-value" format
53 | *
54 | * @return array
55 | */
56 | public function toArray()
57 | {
58 | $array = [];
59 | foreach ($this->toOptionArray() as $item) {
60 | $array[$item['value']] = $item['label'];
61 | }
62 | return $array;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Model/Config/Source/SortBy.php:
--------------------------------------------------------------------------------
1 | self::NONE, 'label' => __('None')],
35 | ['value' => self::RANDOM, 'label' => __('Random')],
36 | ['value' => self::NAME, 'label' => __('Name (Plus)')],
37 | ['value' => self::NEWEST, 'label' => __('Newest (Plus)')],
38 | ['value' => self::PRICE_DESC, 'label' => __('Price (high to low) (Plus)')],
39 | ['value' => self::PRICE_ASC, 'label' => __('Price (low to high) (Plus)')],
40 | ['value' => self::BEST_PR_WEEK, 'label' => __('Best Sellers (QTY) Per Week (Plus)')],
41 | ['value' => self::BEST_PR_MONTH, 'label' => __('Best Sellers (QTY) Per Month (Plus)')],
42 | ['value' => self::BEST_PR_QUARTER, 'label' => __('Best Sellers (QTY) Per Three Months (Plus)')],
43 | ['value' => self::BEST_PR_YEAR, 'label' => __(' Best Sellers (QTY) Per Year (Plus)')],
44 | ];
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/Model/RelatedItemsProcessor.php:
--------------------------------------------------------------------------------
1 | config = $config;
36 | $this->ruleManager = $ruleManager;
37 | }
38 |
39 | /**
40 | * @param AbstractBlock $subject
41 | * @param $result
42 | * @param $blockPosition
43 | * @return \Magefan\AutoRelatedProduct\Block\Collection|mixed
44 | * @throws \Magento\Framework\Exception\LocalizedException
45 | * @throws \Magento\Framework\Exception\NoSuchEntityException
46 | */
47 | public function execute(AbstractBlock $subject, $result, $blockPosition)
48 | {
49 | if (!$this->config->isEnabled() || !$rule = $this->ruleManager->getRuleForPosition($blockPosition)) {
50 | return $result;
51 | }
52 |
53 | if ($rule->getMergeType() == MergeType::MERGE) {
54 | $resultCount = count($result);
55 | $limit = $rule->getNumberOfProducts();
56 |
57 | if ($resultCount >= $limit) {
58 | return $result;
59 | }
60 |
61 | $products = $subject->getLayout()->createBlock(
62 | \Magefan\AutoRelatedProduct\Block\RelatedProductList::class
63 | )->setData('rule', $rule)->getItems();
64 |
65 | $resultIds = [];
66 |
67 | foreach ($result as $r) {
68 | $resultIds[] = $r->getId();
69 | }
70 |
71 | foreach ($products as $product) {
72 | if ($resultCount >= $limit) {
73 | break;
74 | }
75 |
76 | if (in_array($product->getId(), $resultIds)) {
77 | continue;
78 | }
79 |
80 | if (is_object($result)) {
81 | $result->addItem($product);
82 | } else {
83 | $result[] = $product;
84 | }
85 |
86 | $resultCount++;
87 | }
88 | } elseif ($rule->getMergeType() == MergeType::INSTEAD) {
89 | $result = $subject->getLayout()->createBlock(
90 | \Magefan\AutoRelatedProduct\Block\RelatedProductList::class
91 | )->setData('rule', $rule)->getItems();
92 | }
93 |
94 | return $result;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Model/ResourceModel/Rule.php:
--------------------------------------------------------------------------------
1 | salesRuleFactory = $salesRuleFactory;
67 | $this->catalogRuleFactory = $catalogRuleFactory;
68 | $this->request = $request;
69 | $this->messageManager = $messageManager;
70 | $this->url = $url;
71 | parent::__construct($context);
72 | }
73 |
74 | protected function _construct()
75 | {
76 | $this->_init('magefan_autorp_rule', 'id');
77 | }
78 |
79 | /**
80 | * Process post data before deleting
81 | * @param AbstractModel $object
82 | * @return Rule
83 | */
84 | protected function _beforeDelete(AbstractModel $object)
85 | {
86 | $condition = ['rule_id = ?' => (int)$object->getId()];
87 | $tables = [
88 | 'magefan_autorp_index',
89 | 'magefan_autorp_rule_store'
90 | ];
91 |
92 | foreach ($tables as $table) {
93 | $this->getConnection()->delete(
94 | $this->getTable($table),
95 | $condition
96 | );
97 | }
98 |
99 | return parent::_beforeDelete($object);
100 | }
101 |
102 | /**
103 | * Get ids to which specified item is assigned
104 | * @param int $postId
105 | * @param string $tableName
106 | * @param string $field
107 | * @return array
108 | */
109 | protected function _lookupIds($ruleId, $tableName, $field)
110 | {
111 | $adapter = $this->getConnection();
112 | $select = $adapter->select()->from(
113 | $this->getTable($tableName),
114 | $field
115 | )->where(
116 | 'rule_id = ?',
117 | (int)$ruleId
118 | );
119 |
120 | return $adapter->fetchCol($select);
121 | }
122 |
123 | public function lookupIds($ruleId, $tableName, $field)
124 | {
125 | return $this->_lookupIds($ruleId, $tableName, $field);
126 | }
127 |
128 | /**
129 | * Get store ids to which specified item is assigned
130 | *
131 | * @param int $postId
132 | * @return array
133 | */
134 | public function lookupStoreIds($ruleId)
135 | {
136 | return $this->_lookupIds($ruleId, 'magefan_autorp_rule_store', 'store_id');
137 | }
138 |
139 | /**
140 | * Update post connections
141 | * @param AbstractModel $object
142 | * @param Array $newRelatedIds
143 | * @param Array $oldRelatedIds
144 | * @param String $tableName
145 | * @param String $field
146 | * @param Array $rowData
147 | * @return void
148 | */
149 | protected function _updateLinks(AbstractModel $object, array $newRelatedIds, array $oldRelatedIds, $tableName, $field, $rowData = [])
150 | {
151 | $table = $this->getTable($tableName);
152 |
153 | if ($object->getId() && empty($rowData)) {
154 | $currentData = $this->_lookupAll($object->getId(), $tableName, '*');
155 | foreach ($currentData as $item) {
156 | $rowData[$item[$field]] = $item;
157 | }
158 | }
159 |
160 | $insert = $newRelatedIds;
161 | $delete = $oldRelatedIds;
162 |
163 | if ($delete) {
164 | $where = ['rule_id = ?' => (int)$object->getId(), $field.' IN (?)' => $delete];
165 |
166 | $this->getConnection()->delete($table, $where);
167 | }
168 |
169 | if ($insert) {
170 | $data = [];
171 | foreach ($insert as $id) {
172 | $id = (int)$id;
173 | $data[] = array_merge(
174 | ['rule_id' => (int)$object->getId(), $field => $id],
175 | (isset($rowData[$id]) && is_array($rowData[$id])) ? $rowData[$id] : []
176 | );
177 | }
178 | /* Fix if some rows have extra data */
179 | $allFields = [];
180 |
181 | foreach ($data as $i => $row) {
182 | foreach ($row as $key => $value) {
183 | $allFields[$key] = $key;
184 | }
185 | }
186 |
187 | foreach ($data as $i => $row) {
188 | foreach ($allFields as $key) {
189 | if (!array_key_exists($key, $row)) {
190 | $data[$i][$key] = null;
191 | }
192 | }
193 | }
194 | /* End fix */
195 | $this->getConnection()->insertMultiple($table, $data);
196 | }
197 | }
198 |
199 | /**
200 | * Update post connections
201 | * @param AbstractModel $object
202 | * @param Array $newRelatedIds
203 | * @param Array $oldRelatedIds
204 | * @param String $tableName
205 | * @param String $field
206 | * @param Array $rowData
207 | * @return void
208 | */
209 | public function updateLinks(AbstractModel $object, array $newRelatedIds, array $oldRelatedIds, $tableName, $field, $rowData = [])
210 | {
211 | $this->_updateLinks($object, $newRelatedIds, $oldRelatedIds, $tableName, $field, $rowData);
212 | }
213 |
214 | protected function _beforeSave(AbstractModel $object)
215 | {
216 | if (is_array($object->getData('customer_group_ids'))) {
217 | $object->setData('customer_group_ids', implode(',', $object->getData('customer_group_ids')));
218 | }
219 |
220 | if (is_array($object->getData('category_ids'))) {
221 | $arr = $object->getData('category_ids');
222 |
223 | if ($arr[0] == '') {
224 | unset($arr[0]);
225 | }
226 |
227 | $object->setData('category_ids', implode(',', $arr));
228 | }
229 |
230 | /* Conditions */
231 | if ($object->getRule('conditions')) {
232 | $catalogRule = $this->catalogRuleFactory->create();
233 | $catalogRule->loadPost(['conditions' => $object->getRule('conditions')]);
234 | $catalogRule->beforeSave();
235 |
236 | if ($catalogRule->getConditionsSerialized() != $object->getConditionsSerialized()) {
237 | $appyRulesLink = $this->url->getUrl('*/*/apply');
238 |
239 | $this->messageManager->addNotice(
240 | __('You have modified conditions for "Products To Display", to apply new conditions click here ', $appyRulesLink)
241 | );
242 | }
243 |
244 | $object->setData(
245 | 'conditions_serialized',
246 | $catalogRule->getConditionsSerialized()
247 | );
248 | }
249 |
250 | /* Actions */
251 | if ($object->getRule('actions') && $object->getData('block_position') != 'custom') {
252 | $salesRule = $this->salesRuleFactory->create();
253 | $salesRule->loadPost(['conditions' => $object->getRule('actions')]);
254 | $salesRule->beforeSave();
255 | $object->setData(
256 | 'actions_serialized',
257 | $salesRule->getConditionsSerialized()
258 | );
259 | } else {
260 | $object->setData('actions_serialized', null);
261 | }
262 |
263 | /* Store View IDs */
264 | if (is_array($object->getStoreIds())) {
265 | $object->setStoreIds(
266 | implode(',', $object->getStoreIds())
267 | );
268 | }
269 | return parent::_beforeSave($object);
270 | }
271 |
272 | /**
273 | * Assign post to store views, categories, related posts, etc.
274 | *
275 | * @param AbstractModel $object
276 | * @return $this
277 | */
278 | protected function _afterSave(AbstractModel $object)
279 | {
280 | $oldIds = (array)$this->lookupStoreIds($object->getId());
281 | $newIds =explode(',', $object->getStoreIds());
282 |
283 | if (!$newIds || in_array(0, $newIds)) {
284 | $newIds = [0];
285 | }
286 |
287 | $this->_updateLinks($object, $newIds, $oldIds, 'magefan_autorp_rule_store', 'store_id');
288 |
289 | return parent::_afterSave($object);
290 | }
291 |
292 | /**
293 | * @param AbstractModel $object
294 | * @return $this
295 | */
296 | protected function _afterLoad(AbstractModel $object)
297 | {
298 | if ($object->getId()) {
299 | $storeIds = $this->lookupStoreIds($object->getId());
300 | $object->setData('store_ids', $storeIds);
301 | }
302 |
303 | return parent::_afterLoad($object);
304 | }
305 |
306 |
307 | /**
308 | * Get rows to which specified item is assigned
309 | * @param int $postId
310 | * @param string $tableName
311 | * @param string $field
312 | * @return array
313 | */
314 | protected function _lookupAll($postId, $tableName, $field)
315 | {
316 | $adapter = $this->getConnection();
317 |
318 | $select = $adapter->select()->from(
319 | $this->getTable($tableName),
320 | $field
321 | )->where(
322 | 'rule_id = ?',
323 | (int)$postId
324 | );
325 |
326 | return $adapter->fetchAll($select);
327 | }
328 |
329 | /**
330 | * @param $object
331 | * @return array
332 | */
333 | public function getRelatedIds($object): array
334 | {
335 | $result =$this->_lookupIds($object->getId(), 'magefan_autorp_index', 'related_ids');
336 | if (empty($result) || empty($result[0])) {
337 | return [];
338 | }
339 |
340 | $result = explode(',', $result[0]);
341 | return $result;
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/Model/ResourceModel/Rule/Collection.php:
--------------------------------------------------------------------------------
1 | getEvent()->getObject() in this case
34 | *
35 | * @var string
36 | */
37 | protected $_eventObject = 'collection';
38 |
39 | /**
40 | * Define resource model
41 | *
42 | * @return void
43 | */
44 |
45 | /**
46 | * @var int
47 | */
48 | protected $_storeId;
49 |
50 | protected function _construct()
51 | {
52 | $this->_init(\Magefan\AutoRelatedProduct\Model\Rule::class, \Magefan\AutoRelatedProduct\Api\RelatedResourceModelInterface::class);
53 | $this->_map['fields']['id'] = 'main_table.id';
54 | $this->_map['fields']['store'] = 'store_table.store_id';
55 | $this->_map['fields']['group'] = 'group_table.group_id';
56 | }
57 |
58 | /**
59 | * Join store relation table if there is store filter
60 | *
61 | * @return void
62 | */
63 | protected function _renderFiltersBefore()
64 | {
65 | foreach (['store', 'group'] as $key) {
66 | if ($this->getFilter($key)) {
67 | $joinOptions = new \Magento\Framework\DataObject();
68 | $joinOptions->setData([
69 | 'key' => $key,
70 | 'fields' => [],
71 | 'fields' => [],
72 | ]);
73 | $this->_eventManager->dispatch(
74 | 'autorp_post_collection_render_filter_join',
75 | ['join_options' => $joinOptions]
76 | );
77 | $this->getSelect()->join(
78 | [$key . '_table' => $this->getTable('magefan_autorp_rule_' . $key)],
79 | 'main_table.id = ' . $key . '_table.rule_id',
80 | $joinOptions->getData('fields')
81 | )->group(
82 | 'main_table.id'
83 | );
84 | }
85 | }
86 | parent::_renderFiltersBefore();
87 | }
88 |
89 | /**
90 | * Perform operations after collection load
91 | *
92 | * @return $this
93 | */
94 | protected function _afterLoad()
95 | {
96 | /* $items = $this->getColumnValues('id');
97 | if (count($items)) {
98 | $connection = $this->getConnection();
99 | $tableName = $this->getTable('magefan_autorp_rule_store');
100 | $select = $connection->select()
101 | ->from(['cps' => $tableName])
102 | ->where('cps.rule_id IN (?)', $items);
103 | $result = [];
104 | foreach ($connection->fetchAll($select) as $item) {
105 | if (!isset($result[$item['rule_id']])) {
106 | $result[$item['rule_id']] = [];
107 | }
108 | $result[$item['rule_id']][] = $item['store_id'];
109 | }
110 | if ($result) {
111 | foreach ($this as $item) {
112 | $ruleId = $item->getData('id');
113 | if (!isset($result[$ruleId])) {
114 | continue;
115 | }
116 | if ($result[$ruleId] == 0) {
117 | $stores = $this->_storeManager->getStores(false, true);
118 | $storeId = current($stores)->getId();
119 | } else {
120 | $storeId = $result[$item->getData('id')];
121 | }
122 | $item->setData('_first_store_id', $storeId);
123 | $item->setData('store_ids', $result[$ruleId]);
124 | }
125 | }
126 | }
127 |
128 | $this->_previewFlag = false; */
129 | return parent::_afterLoad();
130 | }
131 |
132 | /**
133 | * Add store filter to collection
134 | * @param array|int|\Magento\Store\Model\Store $store
135 | * @param boolean $withAdmin
136 | * @return $this
137 | */
138 | public function addStoreFilter($store, $withAdmin = true)
139 | {
140 | if ($store === null) {
141 | return $this;
142 | }
143 |
144 | if (!$this->getFlag('store_filter_added')) {
145 | $this->setFlag('store_filter_added', 1);
146 |
147 | if (is_array($store)) {
148 | foreach ($store as $k => $v) {
149 | if ($k == 'like') {
150 | if (is_object($v) && $v instanceof \Zend_Db_Expr && (string)$v == "'%0%'") {
151 | return $this;
152 | } else {
153 | $this->addFilter('store', $store, 'public');
154 | return $this;
155 | }
156 | }
157 | }
158 | }
159 |
160 | if ($store instanceof \Magento\Store\Model\Store) {
161 | $this->_storeId = $store->getId();
162 | $store = [$store->getId()];
163 | }
164 |
165 | if (!is_array($store)) {
166 | $this->_storeId = $store;
167 | $store = [$store];
168 | }
169 |
170 | if (in_array(\Magento\Store\Model\Store::DEFAULT_STORE_ID, $store)) {
171 | return $this;
172 | }
173 |
174 | if ($withAdmin) {
175 | $store[] = \Magento\Store\Model\Store::DEFAULT_STORE_ID;
176 | }
177 |
178 | $this->addFilter('store', ['in' => $store], 'public');
179 | }
180 |
181 | return $this;
182 | }
183 |
184 | /**
185 | * Add customer group filter to collection
186 | * @param array|int|null $groupId
187 | * @return $this
188 | */
189 | public function addGroupFilter($groupId = null)
190 | {
191 | if (!$this->getFlag('group_filter_added') && $groupId !== null) {
192 | $this->addFilter('group', ['in' => $groupId], 'public');
193 | $this->setFlag('group_filter_added', true);
194 | }
195 |
196 | return $this;
197 | }
198 |
199 | /**
200 | * Add field filter to collection
201 | *
202 | * @param string|array $field
203 | * @param null|string|array $condition
204 | * @return $this
205 | */
206 | public function addFieldToFilter($field, $condition = null)
207 | {
208 | if (is_array($field)) {
209 | if (count($field) > 1) {
210 | return parent::addFieldToFilter($field, $condition);
211 | } elseif (count($field) === 1) {
212 | $field = $field[0];
213 | $condition = isset($condition[0]) ? $condition[0] : $condition;
214 | }
215 | }
216 |
217 | if ($field === 'store_id' || $field === 'store_ids') {
218 | return $this->addStoreFilter($condition);
219 | }
220 | return parent::addFieldToFilter($field, $condition);
221 | }
222 |
223 | /**
224 | * Add status filter to collection
225 | * @return $this
226 | */
227 | public function addActiveFilter()
228 | {
229 | return $this->addFieldToFilter('status', 1);
230 | }
231 |
232 | /**
233 | * @param $blockPosition
234 | * @return $this
235 | */
236 | public function addPositionFilter($blockPosition)
237 | {
238 | return $this->addFieldToFilter('block_position', $blockPosition);
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/Model/Rule.php:
--------------------------------------------------------------------------------
1 | getEvent()->getObject() in this case
30 | *
31 | * @var string
32 | */
33 | protected $_eventObject = 'rule';
34 |
35 | /**
36 | * @param \Magento\Framework\Model\Context $context
37 | * @param \Magento\Framework\Registry $registry
38 | * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
39 | * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
40 | * @param array $data
41 | */
42 | public function __construct(
43 | \Magento\Framework\Model\Context $context,
44 | \Magento\Framework\Registry $registry,
45 | ?\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
46 | ?\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
47 | array $data = []
48 | ) {
49 | parent::__construct($context, $registry, $resource, $resourceCollection, $data);
50 | }
51 |
52 | /**
53 | * @return void
54 | */
55 | protected function _construct()
56 | {
57 | $this->_init(\Magefan\AutoRelatedProduct\Api\RelatedResourceModelInterface::class);
58 | }
59 |
60 | /**
61 | * Retrieve model title
62 | * @param boolean $plural
63 | * @return string
64 | */
65 | public function getOwnTitle($plural = false)
66 | {
67 | return $plural ? __('Auto Related Product Rules') : __('Auto Related Product Rule');
68 | }
69 |
70 | /**
71 | * @return array|mixed|null
72 | */
73 | public function getId()
74 | {
75 | return $this->getData('id');
76 | }
77 |
78 | /**
79 | * @param string $id
80 | * @return RuleInterface
81 | */
82 | public function setId($id)
83 | {
84 | return $this->setData('id', $id);
85 | }
86 |
87 | /**
88 | * @param $ruleId
89 | * @return RuleInterface|Rule
90 | */
91 | public function setRuleId($ruleId)
92 | {
93 | return $this->setData(self::RULE_ID, $ruleId);
94 | }
95 |
96 | /**
97 | * @return array|mixed|string|null
98 | */
99 | public function getName()
100 | {
101 | return $this->getData('name');
102 | }
103 |
104 | /**
105 | * @param $name
106 | * @return Rule|mixed
107 | */
108 | public function setName($name)
109 | {
110 | return $this->setData('name', $name);
111 | }
112 |
113 | /**
114 | * @return array|mixed|null
115 | */
116 | public function getDescription()
117 | {
118 | return $this->getData('description');
119 | }
120 |
121 | /**
122 | * @return array|mixed|null
123 | */
124 | public function getStatus()
125 | {
126 | return $this->getData('status');
127 | }
128 |
129 |
130 |
131 | /**
132 | * @return false|string|string[]|null
133 | */
134 | public function getStoreIds()
135 | {
136 | return $this->getData('store_ids');
137 | }
138 |
139 | /**
140 | * @param $storeIds
141 | * @return Rule|mixed
142 | */
143 | public function setStoreIds($storeIds)
144 | {
145 | return $this->setData('store_ids', $storeIds);
146 | }
147 |
148 | /**
149 | * @return array|mixed|null
150 | */
151 | public function getPriority()
152 | {
153 | return $this->getData('priority');
154 | }
155 |
156 | /**
157 | * @return array|mixed|null
158 | */
159 | public function getDisplayContainer()
160 | {
161 | return $this->getData('block_position');
162 | }
163 |
164 | /**
165 | * @return array|mixed|null
166 | */
167 | public function getConditions()
168 | {
169 | return $this->getData('conditions_serialized');
170 | }
171 |
172 | /**
173 | * @return array|mixed|null
174 | */
175 | public function getActions()
176 | {
177 | return $this->getData('actions_serialized');
178 | }
179 |
180 | /**
181 | * @return $this
182 | */
183 | public function getDataModel()
184 | {
185 | return $this;
186 | }
187 |
188 | /**
189 | * @return bool
190 | */
191 | public function isActive(): bool
192 | {
193 | return ($this->getStatus() == self::STATUS_ENABLED);
194 | }
195 |
196 | /**
197 | * @param $storeId
198 | * @return bool
199 | */
200 | public function isVisibleOnStore($storeId): bool
201 | {
202 | return $this->isActive()
203 | && (null === $storeId || array_intersect([0, $storeId], $this->getStoreIds()));
204 | }
205 |
206 | /**
207 | * @return string
208 | */
209 | public function getRuleBlockIdentifier(): string
210 | {
211 | $identifier = $this->getBlockPosition();
212 |
213 | if ((0 !== $this->getData('from_one_category_only') || 0 !== $this->getData('only_with_higher_price')) && 'custom' != $this->getBlockPosition()) {
214 | $identifier .= '_' . '1';
215 |
216 | }
217 | if ($this->getId()) {
218 | $identifier .= '_' . $this->getId();
219 | }
220 |
221 | return $identifier;
222 | }
223 |
224 | /**
225 | * @return array|mixed|null
226 | */
227 | public function getRelatedIds()
228 | {
229 | $key = 'related_ids';
230 |
231 | if (!$this->hasData($key)) {
232 | $ids = $this->getResource()->getRelatedIds($this);
233 | $this->setData($key, $ids);
234 | }
235 |
236 | return $this->getData($key);
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/Model/RuleManager.php:
--------------------------------------------------------------------------------
1 | productCollectionFactory = $productCollectionFactory;
106 | $this->catalogConfig = $catalogConfig;
107 | $this->catalogProductVisibility = $catalogProductVisibility;
108 | $this->stockFilter = $stockFilter;
109 | $this->_eventManager = $_eventManager;
110 | $this->ruleRepository = $ruleRepository;
111 | $this->storeManager = $storeManager;
112 | $this->ruleValidator = $ruleValidator;
113 | $this->ruleCollectionFactory = $ruleCollectionFactory;
114 | $this->getCategoryByProduct = $getCategoryByProduct ?:\Magento\Framework\App\ObjectManager::getInstance()
115 | ->get(\Magefan\Community\Api\GetCategoryByProductInterface::class);
116 | }
117 |
118 | /**
119 | * @param $rule
120 | * @param array $params
121 | * @return array|\Magento\Catalog\Model\ResourceModel\Product\Collection
122 | */
123 | public function getReletedProductsColletion(RuleInterface $rule, array $params = [])
124 | {
125 | if (!$rule) {
126 | return [];
127 | }
128 |
129 | $currentProduct = $params['current_product'] ?? false;
130 | $currentCategory = $params['current_category'] ?? false;
131 | $pageSize = $params['page_size'] ?? false;
132 | $currentPage = $params['current_page'] ?? false;
133 |
134 | if (!$pageSize) {
135 | $pageSize = $rule->getData('number_of_products') ?: 10;
136 | }
137 |
138 | $this->_itemCollection = $this->productCollectionFactory->create()
139 | ->addAttributeToSelect($this->catalogConfig->getProductAttributes())
140 | ->setVisibility($this->catalogProductVisibility->getVisibleInCatalogIds())
141 | ->addStoreFilter()
142 | ->setPageSize((int)$pageSize);
143 |
144 | if ($currentPage) {
145 | $this->_itemCollection->setCurPage((int)$currentPage);
146 | }
147 |
148 | if (!$rule->getData('display_out_of_stock')) {
149 | $this->addOutOfStockFilter($rule);
150 | }
151 |
152 | if ($relatedIds = $rule->getRelatedIds()) {
153 | $this->_itemCollection->addFieldToFilter('entity_id', ['in' => $relatedIds]);
154 | }
155 |
156 | if ($currentProduct) {
157 | $this->_itemCollection->addFieldToFilter('entity_id', ['neq' => $currentProduct->getId()]);
158 | }
159 |
160 | if (!empty($params['skip_ids']) && is_array($params['skip_ids'])) {
161 | $this->_itemCollection->addFieldToFilter('entity_id', ['nin' => $params['skip_ids']]);
162 | }
163 |
164 | $this->addSortBy((int)$rule->getData('sort_by'));
165 |
166 | $this->_eventManager->dispatch('autorp_relatedproducts_block_load_collection_before', [
167 | 'rule' => $rule,
168 | 'collection' => $this->_itemCollection,
169 | 'product' => $currentProduct,
170 | 'category' => $currentCategory
171 | ]);
172 |
173 | $this->_itemCollection->load();
174 |
175 | foreach ($this->_itemCollection as $item) {
176 | $item->setDoNotUseCategoryId(true);
177 | }
178 |
179 | return $this->_itemCollection;
180 | }
181 |
182 | /**
183 | * @param RuleInterface $rule
184 | */
185 | protected function addOutOfStockFilter(RuleInterface $rule): void
186 | {
187 | $this->_itemCollection->addMinimalPrice()
188 | ->addFinalPrice()
189 | ->addTaxPercents()
190 | ->addUrlRewrite();
191 |
192 | $this->stockFilter->addInStockFilterToCollection($this->_itemCollection);
193 | }
194 |
195 | /**
196 | * @param $sortBy.
197 | */
198 | protected function addSortBy(int $sortBy): void
199 | {
200 | switch ($sortBy) {
201 | case SortBy::RANDOM:
202 | $this->_itemCollection->getSelect()->order('rand()');
203 | break;
204 | }
205 | }
206 |
207 | /**
208 | * @param int $ruleId
209 | * @return false|RuleInterface
210 | * @throws \Magento\Framework\Exception\LocalizedException
211 | */
212 | public function getRuleById(int $ruleId)
213 | {
214 | try {
215 | $rule = $this->ruleRepository->get($ruleId);
216 |
217 | if (!$rule->isVisibleOnStore($this->storeManager->getStore()->getId()) || $this->ruleValidator->isRestricted($rule)) {
218 | $rule = false;
219 | }
220 | } catch (NoSuchEntityException $e) {
221 | $rule = false;
222 | }
223 |
224 | return $rule;
225 | }
226 |
227 |
228 | /**
229 | * @param string $blockPosition
230 | * @return false|mixed
231 | * @throws NoSuchEntityException
232 | * @throws \Magento\Framework\Exception\LocalizedException
233 | */
234 | public function getRuleForPosition($blockPosition = '')
235 | {
236 | $rule = false;
237 | $storeId = $this->storeManager->getStore()->getId();
238 |
239 | $rules = $this->ruleCollectionFactory->create()
240 | ->addActiveFilter()
241 | ->addPositionFilter($blockPosition)
242 | ->addStoreFilter($storeId)
243 | ->setOrder('priority', 'ASC');
244 |
245 | foreach ($rules as $item) {
246 | if (!$this->ruleValidator->isRestricted($item)) {
247 | $rule = $item;
248 | break;
249 | }
250 | }
251 |
252 | return $rule;
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/Model/RuleRepository.php:
--------------------------------------------------------------------------------
1 | resource = $resource;
114 | $this->ruleFactory = $ruleFactory;
115 | $this->ruleCollectionFactory = $ruleCollectionFactory;
116 | $this->searchResultsFactory = $searchResultsFactory;
117 | $this->dataObjectHelper = $dataObjectHelper;
118 | $this->dataRuleFactory = $dataRuleFactory;
119 | $this->dataObjectProcessor = $dataObjectProcessor;
120 | $this->storeManager = $storeManager;
121 | $this->collectionProcessor = $collectionProcessor;
122 | $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor;
123 | $this->extensibleDataObjectConverter = $extensibleDataObjectConverter;
124 | }
125 |
126 | /**
127 | * @param \Magefan\AutoRelatedProduct\Api\Data\RuleInterface $rule
128 | * @return \Magefan\AutoRelatedProduct\Api\Data\RuleInterface
129 | * @throws CouldNotSaveException
130 | */
131 | public function save(\Magefan\AutoRelatedProduct\Api\Data\RuleInterface $rule)
132 | {
133 | try {
134 | $this->resource->save($rule);
135 | } catch (\Exception $exception) {
136 | throw new CouldNotSaveException(__(
137 | 'Could not save the rule: %1',
138 | $exception->getMessage()
139 | ));
140 | }
141 |
142 | return $rule;
143 | }
144 |
145 | /**
146 | * @param string $ruleId
147 | * @return \Magefan\AutoRelatedProduct\Api\Data\RuleInterface
148 | * @throws NoSuchEntityException
149 | */
150 | public function get($ruleId)
151 | {
152 | $rule = $this->ruleFactory->create();
153 | $this->resource->load($rule, $ruleId);
154 |
155 | if (!$rule->getId()) {
156 | throw new NoSuchEntityException(__('Rule with id "%1" does not exist.', $ruleId));
157 | }
158 | return $rule;
159 | }
160 |
161 | /**
162 | * @param \Magento\Framework\Api\SearchCriteriaInterface $criteria
163 | * @return \Magefan\AutoRelatedProduct\Api\Data\RuleSearchResultsInterface
164 | */
165 | public function getList(\Magento\Framework\Api\SearchCriteriaInterface $criteria)
166 | {
167 | $collection = $this->ruleCollectionFactory->create();
168 | $this->extensionAttributesJoinProcessor->process(
169 | $collection,
170 | \Magefan\AutoRelatedProduct\Api\Data\RuleInterface::class
171 | );
172 |
173 | $this->collectionProcessor->process($criteria, $collection);
174 |
175 | $searchResults = $this->searchResultsFactory->create();
176 | $searchResults->setSearchCriteria($criteria);
177 | $items = [];
178 |
179 | foreach ($collection as $model) {
180 | $items[] = $model->getDataModel();
181 | }
182 |
183 | $searchResults->setItems($items);
184 | $searchResults->setTotalCount($collection->getSize());
185 |
186 | return $searchResults;
187 | }
188 |
189 | /**
190 | * @param \Magefan\AutoRelatedProduct\Api\Data\RuleInterface $rule
191 | * @return bool
192 | * @throws CouldNotDeleteException
193 | */
194 | public function delete(\Magefan\AutoRelatedProduct\Api\Data\RuleInterface $rule)
195 | {
196 | try {
197 | $ruleModel = $this->ruleFactory->create();
198 | $this->resource->load($ruleModel, $rule->getRuleId());
199 | $this->resource->delete($ruleModel);
200 | } catch (\Exception $exception) {
201 | throw new CouldNotDeleteException(__(
202 | 'Could not delete the Rule: %1',
203 | $exception->getMessage()
204 | ));
205 | }
206 |
207 | return true;
208 | }
209 |
210 | /**
211 | * @param string $ruleId
212 | * @return bool
213 | * @throws CouldNotDeleteException
214 | * @throws NoSuchEntityException
215 | */
216 | public function deleteById($ruleId)
217 | {
218 | return $this->delete($this->get($ruleId));
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/Observer/Frontend/Layout/GenerateBlocksAfter.php:
--------------------------------------------------------------------------------
1 | config = $config;
40 | $this->ruleManager = $ruleManager;
41 | }
42 |
43 | /**
44 | * @param Observer $observer
45 | * @throws \Magento\Framework\Exception\LocalizedException
46 | * @throws \Magento\Framework\Exception\NoSuchEntityException
47 | */
48 | public function execute(Observer $observer)
49 | {
50 | if (!$this->config->isEnabled()) {
51 | return;
52 | }
53 |
54 | $block = $observer->getLayout()->getBlock(self::PARENT_BlOCK_NAME);
55 |
56 | if (!$block || !$rule = $this->ruleManager->getRuleForPosition('product_content_tab')) {
57 | return;
58 | }
59 |
60 | $block->addChild(
61 | 'autorp_tab',
62 | \Magefan\AutoRelatedProduct\Block\RelatedProductList::class,
63 | [
64 | 'title' => $rule->getData('block_title'),
65 | 'isTab'=> 1,
66 | 'rule' => $rule
67 | ]
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Plugin/Frontend/Magento/Catalog/Block/Product/ProductList/Related.php:
--------------------------------------------------------------------------------
1 | relatedItemsProcessor = $relatedItemsProcessor;
25 | }
26 |
27 | /**
28 | * @param $subject
29 | * @param $result
30 | * @return mixed
31 | * @throws \Magento\Framework\Exception\LocalizedException
32 | * @throws \Magento\Framework\Exception\NoSuchEntityException
33 | */
34 | public function afterGetItems($subject, $result)
35 | {
36 | return $this->relatedItemsProcessor->execute($subject, $result, 'product_into_related');
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Plugin/Frontend/Magento/Catalog/Block/Product/ProductList/Upsell.php:
--------------------------------------------------------------------------------
1 | relatedItemsProcessor = $relatedItemsProcessor;
25 | }
26 |
27 | /**
28 | * @param $subject
29 | * @param $result
30 | * @return mixed
31 | * @throws \Magento\Framework\Exception\LocalizedException
32 | * @throws \Magento\Framework\Exception\NoSuchEntityException
33 | */
34 | public function afterGetItemCollection($subject, $result)
35 | {
36 | return $this->relatedItemsProcessor->execute($subject, $result, 'product_into_upsell');
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Plugin/Frontend/Magento/Catalog/Block/Product/View/Details.php:
--------------------------------------------------------------------------------
1 | config = $config;
25 | }
26 |
27 | /**
28 | * @param \Magento\Catalog\Block\Product\View\Details $subject
29 | * @param $result
30 | * @return mixed
31 | * @throws \Magento\Framework\Exception\LocalizedException
32 | */
33 | public function afterGetGroupSortedChildNames(\Magento\Catalog\Block\Product\View\Details $subject, $result)
34 | {
35 | return ($this->config->isEnabled() && $subject->getLayout()->isBlock('product.info.details.autorp_tab'))
36 | ? array_merge($result, [45 => 'product.info.details.autorp_tab'])
37 | : $result;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Plugin/Frontend/Magento/Catalog/Model/ResourceModel/Product/Link/Product/Collection.php:
--------------------------------------------------------------------------------
1 | config = $config;
29 | }
30 |
31 | /**
32 | * @param RelatedCollection $subject
33 | * @param $result
34 | * @return mixed
35 | */
36 | public function afterGetSize(
37 | RelatedCollection $subject,
38 | $result
39 | )
40 | {
41 | if (!$result && $this->config->isEnabled()) {
42 | $backtrace = \Magento\Framework\Debug::backtrace(true, true, false);
43 | if (strpos($backtrace, 'Magento\Catalog\Block\Product\ProductList\Related') !== false) {
44 | $collection = clone $subject;
45 | $result = count($collection->getitems());
46 | unset($collection);
47 | }
48 | }
49 | return $result;
50 | }
51 | }
--------------------------------------------------------------------------------
/Plugin/Frontend/Magento/Framework/View/Result/Layout.php:
--------------------------------------------------------------------------------
1 | positionOptions = $positionOptions;
52 | $this->config = $config;
53 | $this->request = $request;
54 | $this->ruleManager = $ruleManager;
55 | }
56 |
57 | /**
58 | * @param Layout $subject
59 | * @param $response
60 | * @return void
61 | * @throws \Magento\Framework\Exception\LocalizedException
62 | * @throws \Magento\Framework\Exception\NoSuchEntityException
63 | */
64 | public function beforeRenderResult(SubjectLayout $subject, $response)
65 | {
66 | if (!$this->config->isEnabled()) {
67 | return null;
68 | }
69 |
70 | /* @var $layout \Magento\Framework\View\Layout */
71 | $layout = $subject->getLayout();
72 |
73 | $currentLayout = (string)$this->request->getFullActionName();
74 |
75 | $containers = [
76 | 'content.top',
77 | 'content.bottom',
78 | ];
79 |
80 | $parentsArray = [];
81 | $options = $this->positionOptions->toOptionArray();
82 |
83 | for ($i = 0; $i < count($options); $i++) {
84 | if (is_array($options[$i]['value'])) {
85 | foreach ($options[$i]['value'] as $item) {
86 | if (isset($item['parent'])) {
87 | $parentsArray[$item['parent']][][$item['value']] = $item['value'];
88 | }
89 | }
90 | }
91 | }
92 |
93 |
94 | /* Fix for related producs before/after and custom theme */
95 | $blockName = 'catalog.product.related';
96 | if (isset($parentsArray[$blockName]) && !$layout->getBlock($blockName) && $layout->getBlock($blockName . '.theme')) {
97 | $parentsArray[$blockName . '.theme'] = $parentsArray[$blockName];
98 | }
99 | /* End fix */
100 |
101 | $layoutElements = [];
102 | foreach ($parentsArray as $blockName => $positions) {
103 | if ($layout->getBlock($blockName)) {
104 | $layoutElements[] = $blockName;
105 | }
106 | }
107 |
108 | foreach ($containers as $container) {
109 | if (!$layout->isContainer($container)) {
110 | continue;
111 | }
112 | $layoutElements[] = $container;
113 | }
114 |
115 | foreach ($layoutElements as $layoutElement) {
116 | foreach ($parentsArray[$layoutElement] as $position) {
117 | $containerName = $layout->getParentName($layoutElement);
118 |
119 | if (!$containerName || !$this->isBlockAvailableOnCurrentPageType($position, $currentLayout)) {
120 | continue;
121 | }
122 |
123 | if (!$rule = $this->ruleManager->getRuleForPosition($position)) {
124 | continue;
125 | }
126 |
127 | $after = (strpos($rule->getBlockPosition(), 'after') !== false);
128 | $ruleBlockName = $rule->getRuleBlockIdentifier();
129 | $layout
130 | ->addBlock(RelatedProductList::class, $ruleBlockName, $containerName)
131 | ->setData('rule', $rule);
132 | $layout->reorderChild($containerName, $ruleBlockName, $layoutElement, $after);
133 | }
134 | }
135 | }
136 |
137 | /**
138 | * Check block position page type is the same as current page type [category | product | cart]
139 | * @param array $position
140 | * @param string $currentLayout
141 | * @return bool
142 | */
143 | private function isBlockAvailableOnCurrentPageType(array $position, string $currentLayout): bool
144 | {
145 | $position = array_shift($position);
146 | $position = explode('_', $position)[0];
147 | $currentLayout = explode('_', $currentLayout)[1];
148 |
149 | return (bool)($position === $currentLayout);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/Plugin/Frontend/Magento/TargetRule/Block/Catalog/Product/ProductList/Related.php:
--------------------------------------------------------------------------------
1 | relatedItemsProcessor = $relatedItemsProcessor;
26 | }
27 |
28 | /**
29 | * @param $subject
30 | * @param $result
31 | * @return mixed
32 | * @throws \Magento\Framework\Exception\LocalizedException
33 | * @throws \Magento\Framework\Exception\NoSuchEntityException
34 | */
35 | public function afterGetAllItems($subject, $result)
36 | {
37 | return $this->relatedItemsProcessor->execute($subject, $result, 'product_into_related');
38 | }
39 | }
--------------------------------------------------------------------------------
/Plugin/Magento/SalesRule/Model/Rule/Condition/Combine.php:
--------------------------------------------------------------------------------
1 | saleRuleFactory = $saleRuleFactory;
55 | $this->ruleAddress = $ruleAddress;
56 | $this->ruleProduct = $ruleProduct;
57 | $this->request = $request;
58 | }
59 |
60 | /**
61 | * @param Combine $subject
62 | * @param $result
63 | * @return array
64 | */
65 | public function afterGetNewChildSelectOptions(SubjectCombine $subject, $result)
66 | {
67 | if ($this->request->getModuleName() == 'autorp') {
68 | $conditions = [];
69 | $conditions = array_merge_recursive(
70 | $conditions,
71 | [
72 | [
73 | 'value' => \Magento\SalesRule\Model\Rule\Condition\Product\Found::class,
74 | 'label' => __('Product attribute combination')
75 | ],
76 | [
77 | 'value' => \Magento\SalesRule\Model\Rule\Condition\Product\Combine::class,
78 | 'label' => __('Conditions Combination')
79 | ]
80 | ]
81 | );
82 |
83 | $productAttributes = $this->ruleProduct->create()->loadAttributeOptions()->getAttributeOption();
84 | $attributesProduct = [];
85 |
86 | foreach ($productAttributes as $code => $label) {
87 | $attributesProduct[] = [
88 | 'value' => 'Magento\CatalogRule\Model\Rule\Condition\Product|' . $code,
89 | 'label' => $label,
90 | ];
91 | }
92 |
93 | $conditions = array_merge_recursive(
94 | $conditions,
95 | [
96 | ['label' => __('Product Attribute'), 'value' => $attributesProduct]
97 | ]
98 | );
99 |
100 | $result = $conditions;
101 | }
102 |
103 | return $result;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Plugin/Magento/SalesRule/Model/Rule/Condition/Product/Combine.php:
--------------------------------------------------------------------------------
1 | product = $product;
35 | $this->request = $request;
36 | }
37 |
38 | public function afterGetNewChildSelectOptions(SubjectCombine $subject, $result)
39 | {
40 | if ($this->request->getModuleName() == 'autorp' || 'autorp_rule_form' == $this->request->getParam('form_namespace')) {
41 |
42 | $productAttributes = $this->product->loadAttributeOptions()->getAttributeOption();
43 | $pAttributes = [];
44 |
45 | foreach ($productAttributes as $code => $label) {
46 | if (strpos($code, 'quote_item_') !== 0) {
47 | $pAttributes[] = [
48 | 'value' => \Magento\SalesRule\Model\Rule\Condition\Product::class . '|' . $code,
49 | 'label' => $label,
50 | ];
51 | }
52 | }
53 |
54 | $conditions = [];
55 | $conditions = array_merge_recursive(
56 | $conditions,
57 | [
58 | [
59 | 'value' => \Magento\SalesRule\Model\Rule\Condition\Product\Combine::class,
60 | 'label' => __('Conditions Combination'),
61 | ],
62 | ['label' => __('Product Attribute'), 'value' => $pAttributes]
63 | ]
64 | );
65 |
66 | $result = $conditions;
67 | }
68 |
69 | return $result;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Ui/Component/Listing/Column/Actions.php:
--------------------------------------------------------------------------------
1 | urlBuilder = $urlBuilder;
42 | parent::__construct($context, $uiComponentFactory, $components, $data);
43 | }
44 | /**
45 | * Prepare Data Source
46 | *
47 | * @param array $dataSource
48 | * @return array
49 | */
50 | public function prepareDataSource(array $dataSource)
51 | {
52 | if (isset($dataSource['data']['items'])) {
53 | foreach ($dataSource['data']['items'] as & $item) {
54 | $name = $this->getData('name');
55 |
56 | if (isset($item['id'])) {
57 | $item[$name]['edit'] = [
58 | 'href' => $this->urlBuilder->getUrl(self::URL_PATH_EDIT, ['id' => $item['id']]),
59 | 'label' => __('Edit')
60 | ];
61 | $item[$name]['delete'] = [
62 | 'href' => $this->urlBuilder->getUrl(self::URL_PATH_DELETE, ['id' => $item['id']]),
63 | 'label' => __('Delete'),
64 | 'confirm' => [
65 | 'title' => __('Delete %1', $item['name']),
66 | 'message' => __('Are you sure you wan\'t to delete a %1 record?', $item['name'])
67 | ]
68 | ];
69 | }
70 | }
71 | }
72 |
73 | return $dataSource;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Ui/DataProvider/Rule/Form/RuleDataProvider.php:
--------------------------------------------------------------------------------
1 | request = $request;
55 | $this->collection = $ruleCollection;
56 | parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
57 | }
58 |
59 | /**
60 | * @return array
61 | */
62 | public function getMeta()
63 | {
64 | return parent::getMeta();
65 | }
66 |
67 | /**
68 | * Get data
69 | *
70 | * @return array
71 | */
72 | public function getData()
73 | {
74 | if (isset($this->loadedData)) {
75 | return $this->loadedData;
76 | }
77 |
78 | $items = $this->collection->getItems();
79 |
80 | foreach ($items as $rule) {
81 | try {
82 | $rule = $rule->load($rule->getId());
83 | } catch (NoSuchEntityException $e) {
84 | return;
85 | }
86 |
87 | $data = $rule->getData();
88 |
89 | /* Set data */
90 | $this->loadedData[$rule->getId()] = $data;
91 | }
92 |
93 | return $this->loadedData;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Ui/DataProvider/Rule/Rule.php:
--------------------------------------------------------------------------------
1 | _init(Document::class, ResourceModel::class);
27 | }
28 |
29 | /**
30 | * @return \Magento\Framework\Api\Search\AggregationInterface
31 | */
32 | public function getAggregations()
33 | {
34 | return $this->aggregations;
35 | }
36 |
37 | /**
38 | * @param \Magento\Framework\Api\Search\AggregationInterface $aggregations
39 | * @return Rule|void
40 | */
41 | public function setAggregations($aggregations)
42 | {
43 | $this->aggregations = $aggregations;
44 | }
45 |
46 | /**
47 | * @param null $limit
48 | * @param null $offset
49 | * @return array
50 | */
51 | public function getAllIds($limit = null, $offset = null)
52 | {
53 | return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams);
54 | }
55 |
56 | /**
57 | * @return \Magento\Framework\Api\Search\SearchCriteriaInterface|null
58 | */
59 | public function getSearchCriteria()
60 | {
61 | return null;
62 | }
63 |
64 | /**
65 | * @param SearchCriteriaInterface|null $searchCriteria
66 | * @return $this|Rule
67 | */
68 | public function setSearchCriteria(?SearchCriteriaInterface $searchCriteria = null)
69 | {
70 | return $this;
71 | }
72 |
73 | /**
74 | * @return int
75 | */
76 | public function getTotalCount()
77 | {
78 | return $this->getSize();
79 | }
80 |
81 | /**
82 | * @param int $totalCount
83 | * @return $this|Rule
84 | */
85 | public function setTotalCount($totalCount)
86 | {
87 | return $this;
88 | }
89 |
90 | /**
91 | * @param array|null $items
92 | * @return $this|Rule
93 | */
94 | public function setItems(?array $items = null)
95 | {
96 | return $this;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "magefan/module-auto-related-product",
3 | "description": "Extend native Magento Up-Sells, Cross-Sells, and Related products",
4 | "type": "magento2-module",
5 | "version": "2.4.4",
6 | "require": {
7 | "magefan/module-community" : ">=2.2.10"
8 | },
9 | "autoload": {
10 | "psr-4": {
11 | "Magefan\\AutoRelatedProduct\\": ""
12 | },
13 | "files": [
14 | "registration.php"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/etc/acl.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/etc/adminhtml/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/etc/adminhtml/routes.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/etc/adminhtml/system.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 | separator-top
12 | Automatic Related Products
13 | magefan
14 | Magefan_AutoRelatedProduct::config
15 |
16 | Automatic Related Products
17 | 1
18 |
19 | Magefan\AutoRelatedProduct\Block\Adminhtml\System\Config\Form\Info
20 |
21 |
22 |
23 | Enable Extension
24 | Magento\Config\Model\Config\Source\Yesno
25 |
26 |
27 | Product Key
28 | Magefan\Community\Block\Adminhtml\System\Config\Form\ProductKeyField
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/etc/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 | 0
13 | AutoRelatedProduct
14 | 1
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/etc/crontab.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 | 20 */8 * * *
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/etc/db_schema.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/etc/frontend/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/etc/frontend/events.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/autorp_rule_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/autorp_rule_index.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/default.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/related_product_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/form/versionsManager.phtml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 | get(\Magefan\Community\Api\GetModuleVersionInterface::class);
16 |
17 | $currentPlan = 'Basic';
18 |
19 | if ($getModuleVersion->execute('Magefan_AutoRelatedProductExtra')) {
20 | return;
21 | } elseif ($getModuleVersion->execute('Magefan_AutoRelatedProductPlus')) {
22 | $currentPlan = 'Plus';
23 | }
24 | ?>
25 |
26 | {
63 | mutations.forEach(mutation => {
64 | for (let plan in versionsManager._selector) {
65 | let planFeatures = versionsManager._selector[plan];
66 |
67 | planFeatures.forEach(selector => {
68 | if (document.querySelector(selector)) {
69 | const element = document.querySelector(selector);
70 |
71 | if (
72 | element.tagName == 'SELECT'
73 | && Array.from(element.options).some(option => option.text.includes('Plus') || option.text.includes('Extra'))
74 | ) {
75 | element.addEventListener('change', function() {
76 | var selectedOptionText = this.options[this.selectedIndex].text;
77 | if (selectedOptionText.includes('('+ plan +')')) {
78 | versionsManager.showAlert(plan);
79 | this.selectedIndex = 0;
80 | this.dispatchEvent(new Event('change', { bubbles: true }));
81 | }
82 | });
83 | } else {
84 | element.addEventListener('click', function (event) {
85 | this.value = '';
86 |
87 | if (this.tagName == 'SELECT') {
88 | this.dispatchEvent(new Event('change', { bubbles: true }));
89 | }
90 |
91 | event.preventDefault();
92 | event.stopPropagation();
93 |
94 | if (versionsManager._currentPlan != plan) {
95 | versionsManager.showAlert(plan)
96 | }
97 | });
98 | }
99 |
100 | // Remove the selector from _selector
101 | versionsManager._selector[plan] = versionsManager._selector[plan].filter(item => item !== selector);
102 | }
103 | });
104 | }
105 | });
106 | });
107 |
108 | // Start observing the document
109 | observer.observe(document.body, { childList: true, subtree: true });
110 | },
111 |
112 | showAlert: function (extensionPlan) {
113 | require(['jquery', 'Magento_Ui/js/modal/alert'], function($, alert) {
114 | if (extensionPlan === 'Plus') {
115 | extensionPlan = 'Plus or Extra';
116 | }
117 | alert({
118 | title: '" . $escaper->escapeHtml(__('You cannot use this option.')) . "',
119 | content: '" . $escaper->escapeHtml(__('This feature is available in')) . "' + ' ' + extensionPlan + ' " . __('plan only.') . "',
120 | buttons: [{
121 | text: '" . $escaper->escapeHtml(__('Upgrade Plan Now')) . "',
122 | class: 'action primary accept',
123 | click: function () {
124 | window.open('https://magefan.com');
125 | }
126 | }]
127 | });
128 | });
129 | }
130 | };
131 |
132 | versionsManager.initListener();
133 |
134 | ";
135 | ?>
136 |
137 | = /* @noEscape */ $mfSecureRenderer->renderTag('script', [], $script, false) ?>
138 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/info.phtml:
--------------------------------------------------------------------------------
1 |
7 |
12 | isEnabled()) { ?>
13 |
14 |
15 |
16 | Stores > Configuration > Magefan Extensions > Auto Related Products.
18 | ', $block->getUrl('adminhtml/system_config/edit', ['section' => 'autorp']));
19 | ?>
20 | = /**@noEscape**/ $html ?>
21 |
22 |
23 |
24 |
25 |
26 |
45 |
46 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/tmpInfo.phtml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 | isSomeFeaturesRestricted() && $affectedRules = $block->getAffectedRulesByDisplayModes()) { ?>
14 |
15 |
16 |
17 |
18 | = $escaper->escapeHtml(__('Display Modes:')) ?> "From Same Category Only", "Only With Higher Price" = $escaper->escapeHtml(__('and')) ?> "Only With Lower Price"
19 | = $escaper->escapeHtml(__('are no longer available in the Magefan Auto Related Products Basic version.')) ?>
20 |
21 |
22 |
23 | = $escaper->escapeHtml(__('Please')) ?>
24 |
25 | = $escaper->escapeHtml(__('upgrade to Plus or Extra edition ')) ?>
26 |
27 | = $escaper->escapeHtml(__(' or use an older extension version up to 2.4.0 to be able to use these options.')) ?>
28 |
29 |
30 |
31 | = $escaper->escapeHtml(__('Affected rule IDs: %1.', $affectedRules)) ?>
32 |
33 |
34 |
35 |
36 |
37 |
38 | isSomeFeaturesRestricted() && $affectedRules = $block->getAffectedRulesBySortBy()) { ?>
39 |
40 |
41 |
42 |
43 | = $escaper->escapeHtml(__('Sort By:')) ?> "Name", "Newest", "Price (high to low)" = $escaper->escapeHtml(__('and')) ?> "Price (low to high)"
44 | = $escaper->escapeHtml(__('are no longer available in the Magefan Auto Related Products Basic version.')) ?>
45 |
46 |
47 |
48 | = $escaper->escapeHtml(__('Please')) ?>
49 |
50 | = $escaper->escapeHtml(__('upgrade to Plus or Extra edition ')) ?>
51 |
52 | = $escaper->escapeHtml(__(' or use an older extension version up to 2.4.0 to be able to use these options.')) ?>
53 |
54 |
55 |
56 | = $escaper->escapeHtml(__('Affected rule IDs: %1.', $affectedRules)) ?>
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/view/adminhtml/ui_component/autorp_listing.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | -
11 |
- autorp_listing.autorp_listing_data_source
12 | - autorp_listing.autorp_listing_data_source
13 |
14 | - autorp_listing_columns
15 | -
16 |
-
17 |
- add
18 | - Apply Rules
19 | - primary
20 | - */*/apply
21 |
22 | -
23 |
- add
24 | - Add New Rule
25 | - primary
26 | - */*/new
27 |
28 |
29 |
30 |
31 |
32 | Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
33 | autorp_listing_data_source
34 | id
35 | id
36 |
37 | -
38 |
- Magento_Ui/js/grid/provider
39 |
40 | -
41 |
- id
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | -
50 |
- true
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | -
59 |
-
60 |
-
61 |
-
62 |
- Magento_Ui/js/form/element/ui-select
63 | - ui/grid/filters/elements/ui-select
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | -
73 |
- Magento_Ui/js/grid/tree-massactions
74 |
75 |
76 |
77 |
78 | -
79 |
- delete
80 | - Delete
81 |
82 | -
83 |
- Delete items
84 | - Are you sure you want to delete selected items?
85 |
86 |
87 |
88 |
89 |
90 |
91 | -
92 |
- status
93 | - Change status
94 |
95 |
96 |
97 | -
98 |
- enable
99 | - Enable
100 | -
101 |
1
102 |
103 |
104 | -
105 |
- disable
106 | - Disable
107 | -
108 |
0
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | -
120 |
-
121 |
- autorp_listing.autorp_listing.autorp_listing_columns.ids
122 | - true
123 | - id
124 | -
125 |
126 | - false
127 |
131 |
132 |
133 | -
134 |
-
135 |
- autorp_listing.autorp_listing.autorp_listing_columns_editor
136 | - startEdit
137 | -
138 |
- ${ $.$data.rowIndex }
139 | - true
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | -
148 |
- id
149 |
150 |
151 |
152 |
153 |
154 | -
155 |
- ID
156 | - textRange
157 |
158 |
159 |
160 |
161 |
162 | -
163 |
- text
164 | - text
165 | - Rule
166 |
167 |
168 |
169 |
170 |
171 | - Magento\Cms\Model\Block\Source\IsActive
172 | -
173 |
- select
174 | - Magento_Ui/js/grid/columns/select
175 | - select
176 | - select
177 | - Status
178 |
179 |
180 |
181 |
182 |
183 | -
184 |
- text
185 | - textRange
186 | - Priority
187 | - asc
188 |
189 |
190 |
191 |
192 |
193 | - Magento\Cms\Ui\Component\Listing\Column\Cms\Options
194 | -
195 |
- select
196 | - Magento_Ui/js/grid/columns/select
197 | - Store View
198 |
199 |
200 |
201 |
202 |
203 |
204 |
--------------------------------------------------------------------------------
/view/adminhtml/web/js/form/element/is-product-conditions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © Magefan (support@magefan.com). All rights reserved.
3 | * Please visit Magefan.com for license details (https://magefan.com/end-user-license-agreement).
4 | */
5 |
6 | define([
7 | 'jquery',
8 | 'uiRegistry',
9 | 'Magento_Ui/js/form/element/select',
10 | 'domReady!'
11 | ], function ($, uiRegistry, select) {
12 | 'use strict';
13 | return select.extend({
14 | defaults: {
15 | customName: '${ $.parentName }.${ $.index }_input'
16 | },
17 |
18 | initialize: function () {
19 | this._super();
20 | var self = this;
21 | setTimeout(function () {
22 | self.fieldDepend(self.value())
23 | }, 1000);
24 | },
25 |
26 | onUpdate: function(value) {
27 | this.fieldDepend(value);
28 | return this._super();
29 | },
30 |
31 | fieldDepend: function(value) {
32 | if (!value) {
33 | $('div[class="rule-tree"]').hide();
34 | $('.container.sample').hide();
35 | $('div[data-index="merge_type"]').hide();
36 | $('button[data-index="preview_button"]').hide();
37 | $('fieldset[data-index="container_categories"]').hide();
38 | $('#autorp_rule_formrule_same_as_conditions_fieldset_').hide();
39 | $('[data-index="apply_same_as_condition"]').hide();
40 | $('[data-index="display_mode"]').hide();
41 | return;
42 | }
43 |
44 | if (value.startsWith("custom")) {
45 | $('div[class="rule-tree"]').show();
46 | $('.container.sample').show();
47 | $('div[data-index="merge_type"]').hide();
48 | $('button[data-index="preview_button"]').show();
49 | $('fieldset[data-index="container_categories"]').hide();
50 | $('#autorp_rule_formrule_same_as_conditions_fieldset_').show();
51 | $('[data-index="apply_same_as_condition"]').show();
52 |
53 | if (uiRegistry.get('autorp_rule_form.autorp_rule_form.what_to_display').source.data.apply_same_as_condition) {
54 | $('#autorp_rule_formrule_same_as_conditions_fieldset_').show();
55 | } else {
56 | $('#autorp_rule_formrule_same_as_conditions_fieldset_').hide();
57 | }
58 |
59 | $('[data-index="where_to_display_product"] div[class="rule-tree"]').hide();
60 | $('[data-index="display_mode"]').show();
61 | return;
62 | }
63 |
64 | if (value.startsWith("product_")) {
65 | $('div[class="rule-tree"]').show();
66 | $('.container.sample').hide();
67 | if(value.startsWith("product_into")){
68 | $('div[data-index="merge_type"]').show();
69 | }
70 | else{
71 | $('div[data-index="merge_type"]').hide();
72 | }
73 | $('button[data-index="preview_button"]').show();
74 | $('fieldset[data-index="container_categories"]').hide();
75 |
76 | $('[data-index="apply_same_as_condition"]').show();
77 |
78 | if (uiRegistry.get('autorp_rule_form.autorp_rule_form.what_to_display').source.data.apply_same_as_condition) {
79 | $('#autorp_rule_formrule_same_as_conditions_fieldset_').show();
80 | } else {
81 | $('#autorp_rule_formrule_same_as_conditions_fieldset_').hide();
82 | }
83 |
84 | //$('[data-index="where_to_display_product"] [label="Product Attribute"], [data-index="where_to_display_product"] [value="Magento\\\\SalesRule\\\\Model\\\\Rule\\\\Condition\\\\Product\\\\Combine"]').prop( "disabled", false);
85 | $('[data-index="display_mode"]').show();
86 | return;
87 | }
88 |
89 | if (value.startsWith("cart_")) {
90 | $('div[class="rule-tree"]').show();
91 | $('.container.sample').hide();
92 | if(value.startsWith("cart_into")){
93 | $('div[data-index="merge_type"]').show();
94 | }
95 | else{
96 | $('div[data-index="merge_type"]').hide();
97 | }
98 | $('button[data-index="preview_button"]').show();
99 | $('fieldset[data-index="container_categories"]').hide();
100 | $('#autorp_rule_formrule_same_as_conditions_fieldset_').hide();
101 | $('[data-index="apply_same_as_condition"]').hide();
102 | //$('[data-index="where_to_display_product"] [label="Product Attribute"], [data-index="where_to_display_product"] [value="Magento\\\\SalesRule\\\\Model\\\\Rule\\\\Condition\\\\Product\\\\Combine"]').prop( "disabled", true );
103 | $('[data-index="display_mode"]').hide();
104 | return;
105 | }
106 |
107 | if (value.startsWith("category_")) {
108 | $('div[class="rule-tree"]').show();
109 | $('.container.sample').hide();
110 | $('div[data-index="merge_type"]').hide();
111 | $('button[data-index="preview_button"]').show();
112 | $('fieldset[data-index="container_categories"]').show();
113 | $('#autorp_rule_formrule_same_as_conditions_fieldset_').hide();
114 | $('[data-index="apply_same_as_condition"]').hide();
115 | //$('[data-index="where_to_display_product"] [label="Product Attribute"], [data-index="where_to_display_product"] [value="Magento\\\\SalesRule\\\\Model\\\\Rule\\\\Condition\\\\Product\\\\Combine"]').prop( "disabled", true );
116 | $('[data-index="display_mode"]').hide();
117 | return;
118 | }
119 | },
120 | });
121 | });
122 |
--------------------------------------------------------------------------------
/view/adminhtml/web/js/lib/core/collection.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright © Magefan (support@magefan.com). All rights reserved.
3 | * Please visit Magefan.com for license details (https://magefan.com/end-user-license-agreement).
4 | */
5 |
6 | define([
7 | 'underscore',
8 | 'mageUtils',
9 | 'uiRegistry',
10 | 'uiComponent',
11 | 'jquery',
12 | 'Magento_Ui/js/lib/view/utils/async'
13 | ], function (_, utils, registry, Element, $, async) {
14 | 'use strict';
15 |
16 | return Element.extend({
17 | defaults: {
18 | visible: true
19 | },
20 |
21 | /**
22 | * Called when another element was added to current component.
23 | *
24 | * @param {Object} elem - Instance of an element that was added.
25 | * @returns {Collection} Chainable.
26 | */
27 | initElement: function (elem) {
28 |
29 | if (elem.additionalClasses) {
30 | elem.additionalClasses[this.className] = true;
31 | }
32 |
33 | elem.initContainer(this);
34 |
35 | return this;
36 | },
37 |
38 | /**
39 | * Show element.
40 | *
41 | * @returns {Abstract} Chainable.
42 | */
43 | show: function () {
44 | $('.' + this.className).show();
45 |
46 | return this;
47 | },
48 |
49 | /**
50 | * Hide element.
51 | *
52 | * @returns {Abstract} Chainable.
53 | */
54 | hide: function () {
55 | var element = $('.' + this.className),
56 | self = this;
57 |
58 | if (element.length) {
59 | element.hide();
60 | } else {
61 | async.async('.' + self.className, function (item) {
62 | $(item).hide();
63 | }.bind(this));
64 | }
65 |
66 | return this;
67 | }
68 | });
69 | });
70 |
--------------------------------------------------------------------------------