66namespace Magento \Catalog \Model \ResourceModel ;
77
88use Magento \Catalog \Model \ResourceModel \Product \Website \Link as ProductWebsiteLink ;
9+ use Magento \Eav \Api \AttributeManagementInterface ;
910use Magento \Framework \App \ObjectManager ;
1011use Magento \Catalog \Model \Indexer \Category \Product \TableMaintainer ;
1112use Magento \Catalog \Model \Product as ProductEntity ;
1213use Magento \Eav \Model \Entity \Attribute \UniqueValidationInterface ;
14+ use Magento \Framework \DataObject ;
1315use Magento \Framework \EntityManager \EntityManager ;
1416use Magento \Framework \Model \AbstractModel ;
1517
@@ -93,6 +95,11 @@ class Product extends AbstractResource
9395 */
9496 private $ tableMaintainer ;
9597
98+ /**
99+ * @var AttributeManagementInterface
100+ */
101+ private $ eavAttributeManagement ;
102+
96103 /**
97104 * @param \Magento\Eav\Model\Entity\Context $context
98105 * @param \Magento\Store\Model\StoreManagerInterface $storeManager
@@ -106,7 +113,7 @@ class Product extends AbstractResource
106113 * @param array $data
107114 * @param TableMaintainer|null $tableMaintainer
108115 * @param UniqueValidationInterface|null $uniqueValidator
109- *
116+ * @param AttributeManagementInterface|null $eavAttributeManagement
110117 * @SuppressWarnings(PHPMD.ExcessiveParameterList)
111118 */
112119 public function __construct (
@@ -121,7 +128,8 @@ public function __construct(
121128 \Magento \Catalog \Model \Product \Attribute \DefaultAttributes $ defaultAttributes ,
122129 $ data = [],
123130 TableMaintainer $ tableMaintainer = null ,
124- UniqueValidationInterface $ uniqueValidator = null
131+ UniqueValidationInterface $ uniqueValidator = null ,
132+ AttributeManagementInterface $ eavAttributeManagement = null
125133 ) {
126134 $ this ->_categoryCollectionFactory = $ categoryCollectionFactory ;
127135 $ this ->_catalogCategory = $ catalogCategory ;
@@ -138,6 +146,8 @@ public function __construct(
138146 );
139147 $ this ->connectionName = 'catalog ' ;
140148 $ this ->tableMaintainer = $ tableMaintainer ?: ObjectManager::getInstance ()->get (TableMaintainer::class);
149+ $ this ->eavAttributeManagement = $ eavAttributeManagement
150+ ?? ObjectManager::getInstance ()->get (AttributeManagementInterface::class);
141151 }
142152
143153 /**
@@ -268,10 +278,10 @@ public function getIdBySku($sku)
268278 /**
269279 * Process product data before save
270280 *
271- * @param \Magento\Framework\ DataObject $object
281+ * @param DataObject $object
272282 * @return $this
273283 */
274- protected function _beforeSave (\ Magento \ Framework \ DataObject $ object )
284+ protected function _beforeSave (DataObject $ object )
275285 {
276286 $ self = parent ::_beforeSave ($ object );
277287 /**
@@ -286,15 +296,73 @@ protected function _beforeSave(\Magento\Framework\DataObject $object)
286296 /**
287297 * Save data related with product
288298 *
289- * @param \Magento\Framework\ DataObject $product
299+ * @param DataObject $product
290300 * @return $this
291301 */
292- protected function _afterSave (\ Magento \ Framework \ DataObject $ product )
302+ protected function _afterSave (DataObject $ product )
293303 {
304+ $ this ->removeNotInSetAttributeValues ($ product );
294305 $ this ->_saveWebsiteIds ($ product )->_saveCategories ($ product );
295306 return parent ::_afterSave ($ product );
296307 }
297308
309+ /**
310+ * Remove attribute values that absent in product attribute set
311+ *
312+ * @param DataObject $product
313+ * @return DataObject
314+ */
315+ private function removeNotInSetAttributeValues (DataObject $ product ): DataObject
316+ {
317+ $ oldAttributeSetId = $ product ->getOrigData (ProductEntity::ATTRIBUTE_SET_ID );
318+ if ($ oldAttributeSetId && $ product ->dataHasChangedFor (ProductEntity::ATTRIBUTE_SET_ID )) {
319+ $ newAttributes = $ product ->getAttributes ();
320+ $ newAttributesCodes = array_keys ($ newAttributes );
321+ $ oldAttributes = $ this ->eavAttributeManagement ->getAttributes (
322+ ProductEntity::ENTITY ,
323+ $ oldAttributeSetId
324+ );
325+ $ oldAttributesCodes = [];
326+ foreach ($ oldAttributes as $ oldAttribute ) {
327+ $ oldAttributesCodes [] = $ oldAttribute ->getAttributecode ();
328+ }
329+ $ notInSetAttributeCodes = array_diff ($ oldAttributesCodes , $ newAttributesCodes );
330+ if (!empty ($ notInSetAttributeCodes )) {
331+ $ this ->deleteSelectedEntityAttributeRows ($ product , $ notInSetAttributeCodes );
332+ }
333+ }
334+
335+ return $ product ;
336+ }
337+
338+ /**
339+ * Clear selected entity attribute rows
340+ *
341+ * @param DataObject $product
342+ * @param array $attributeCodes
343+ * @return void
344+ */
345+ private function deleteSelectedEntityAttributeRows (DataObject $ product , array $ attributeCodes ): void
346+ {
347+ $ backendTables = [];
348+ foreach ($ attributeCodes as $ attributeCode ) {
349+ $ attribute = $ this ->getAttribute ($ attributeCode );
350+ $ backendTable = $ attribute ->getBackendTable ();
351+ if (!$ attribute ->isStatic () && $ backendTable ) {
352+ $ backendTables [$ backendTable ][] = $ attribute ->getId ();
353+ }
354+ }
355+
356+ $ entityIdField = $ this ->getLinkField ();
357+ $ entityId = $ product ->getData ($ entityIdField );
358+ foreach ($ backendTables as $ backendTable => $ attributes ) {
359+ $ connection = $ this ->getConnection ();
360+ $ where = $ connection ->quoteInto ('attribute_id IN (?) ' , $ attributes );
361+ $ where .= $ connection ->quoteInto (" AND {$ entityIdField } = ? " , $ entityId );
362+ $ connection ->delete ($ backendTable , $ where );
363+ }
364+ }
365+
298366 /**
299367 * @inheritdoc
300368 */
@@ -337,12 +405,12 @@ protected function _saveWebsiteIds($product)
337405 /**
338406 * Save product category relations
339407 *
340- * @param \Magento\Framework\ DataObject $object
408+ * @param DataObject $object
341409 * @return $this
342410 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
343411 * @deprecated 101.1.0
344412 */
345- protected function _saveCategories (\ Magento \ Framework \ DataObject $ object )
413+ protected function _saveCategories (DataObject $ object )
346414 {
347415 return $ this ;
348416 }
0 commit comments