Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12110: Missing cascade into attribute set deletion. #12167

Merged
merged 10 commits into from
Dec 7, 2017
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

namespace Magento\Catalog\Plugin\Model\AttributeSetRepository;

use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Eav\Api\Data\AttributeSetInterface;

/**
* Delete related products after attribute set successfully removed.
*/
class RemoveProducts
{
/**
* Retrieve products related to specific attribute set.
*
* @var CollectionFactory
*/
private $collectionFactory;

/**
* RemoveProducts constructor.
*
* @param CollectionFactory $collectionFactory
*/
public function __construct(CollectionFactory $collectionFactory)
{
$this->collectionFactory = $collectionFactory;
}

/**
* Delete related to specific attribute set products, if attribute set was removed successfully.
*
* @param AttributeSetRepositoryInterface $subject
* @param bool $result
* @param AttributeSetInterface $attributeSet
* @return bool
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function afterDelete(
AttributeSetRepositoryInterface $subject,
bool $result,
AttributeSetInterface $attributeSet
) {
/** @var Collection $productCollection */
$productCollection = $this->collectionFactory->create();
$productCollection->addFieldToFilter('attribute_set_id', ['eq' => $attributeSet->getId()]);
$productCollection->delete();

return $result;
}
}
22 changes: 22 additions & 0 deletions app/code/Magento/Catalog/Setup/UpgradeSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class UpgradeSchema implements UpgradeSchemaInterface
/**
* {@inheritdoc}
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
Expand Down Expand Up @@ -126,6 +128,10 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con
$this->fixCustomerGroupIdColumn($setup);
}

if (version_compare($context->getVersion(), '2.2.4', '<')) {
$this->removeAttributeSetRelation($setup);
}

$setup->endSetup();
}

Expand Down Expand Up @@ -699,4 +705,20 @@ private function addReplicaTable(SchemaSetupInterface $setup, $existingTable, $r
);
$setup->getConnection()->query($sql);
}

/**
* Remove foreign key between catalog_product_entity and eav_attribute_set tables.
* Drop foreign key to delegate cascade on delete to plugin.
* @see \Magento\Catalog\Plugin\Model\AttributeSetRepository\RemoveProducts
*
* @param SchemaSetupInterface $setup
* @return void
*/
private function removeAttributeSetRelation(SchemaSetupInterface $setup)
{
$setup->getConnection()->dropForeignKey(
$setup->getTable('catalog_product_entity'),
$setup->getFkName('catalog_product_entity', 'attribute_set_id', 'eav_attribute_set', 'attribute_set_id')
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

namespace Magento\Catalog\Test\Unit\Plugin\Model\AttributeSetRepository;

use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\Catalog\Plugin\Model\AttributeSetRepository\RemoveProducts;
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Eav\Api\Data\AttributeSetInterface;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use PHPUnit\Framework\TestCase;

/**
* Provide tests for RemoveProducts plugin.
*/
class RemoveProductsTest extends TestCase
{
/**
* @var RemoveProducts
*/
private $testSubject;

/**
* @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject
*/
private $collectionFactory;

/**
* @inheritdoc
*/
protected function setUp()
{
$objectManager = new ObjectManager($this);
$this->collectionFactory = $this->getMockBuilder(CollectionFactory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
$this->testSubject = $objectManager->getObject(
RemoveProducts::class,
[
'collectionFactory' => $this->collectionFactory,
]
);
}

/**
* Test plugin will delete all related products for given attribute set.
*/
public function testAfterDelete()
{
$attributeSetId = '1';

/** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collection */
$collection = $this->getMockBuilder(Collection::class)
->disableOriginalConstructor()
->getMock();
$collection->expects(self::once())
->method('addFieldToFilter')
->with(self::identicalTo('attribute_set_id'), self::identicalTo(['eq' => $attributeSetId]));
$collection->expects(self::once())
->method('delete');

$this->collectionFactory->expects(self::once())
->method('create')
->willReturn($collection);

/** @var AttributeSetRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $attributeSetRepository */
$attributeSetRepository = $this->getMockBuilder(AttributeSetRepositoryInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();

/** @var AttributeSetInterface|\PHPUnit_Framework_MockObject_MockObject $attributeSet */
$attributeSet = $this->getMockBuilder(AttributeSetInterface::class)
->setMethods(['getId'])
->disableOriginalConstructor()
->getMockForAbstractClass();
$attributeSet->expects(self::once())
->method('getId')
->willReturn($attributeSetId);

self::assertTrue($this->testSubject->afterDelete($attributeSetRepository, true, $attributeSet));
}
}
3 changes: 3 additions & 0 deletions app/code/Magento/Catalog/etc/adminhtml/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,7 @@
<argument name="scopeOverriddenValue" xsi:type="object">Magento\Catalog\Model\Attribute\ScopeOverriddenValue</argument>
</arguments>
</type>
<type name="Magento\Eav\Api\AttributeSetRepositoryInterface">
<plugin name="remove_products" type="Magento\Catalog\Plugin\Model\AttributeSetRepository\RemoveProducts"/>
</type>
</config>
2 changes: 1 addition & 1 deletion app/code/Magento/Catalog/etc/module.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Magento_Catalog" setup_version="2.2.3">
<module name="Magento_Catalog" setup_version="2.2.4">
<sequence>
<module name="Magento_Eav"/>
<module name="Magento_Cms"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

namespace Magento\Catalog\Plugin\Model\AttributeSetRepository;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Eav\Model\Entity\Attribute\Set;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Interception\PluginList;
use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory;
use PHPUnit\Framework\TestCase;

/**
* Provide tests for RemoveProducts plugin.
* @magentoAppArea adminhtml
*/
class RemoveProductsTest extends TestCase
{
/**
* @return void
*/
public function testRemoveProductsIsRegistered()
{
$pluginInfo = Bootstrap::getObjectManager()->get(PluginList::class)
->get(AttributeSetRepositoryInterface::class, []);
self::assertSame(RemoveProducts::class, $pluginInfo['remove_products']['instance']);
}

/**
* Test related to given attribute set products will be removed, if attribute set will be deleted.
*
* @magentoDataFixture Magento/Catalog/_files/attribute_set_with_product.php
*/
public function testAfterDelete()
{
$attributeSet = Bootstrap::getObjectManager()->get(Set::class);
$attributeSet->load('empty_attribute_set', 'attribute_set_name');

$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
$product = $productRepository->get('simple');

$productCollection = Bootstrap::getObjectManager()->get(CollectionFactory::class)->create();
$productCollection->addIdFilter($product->getId());
$urlRewriteCollection = Bootstrap::getObjectManager()->get(UrlRewriteCollectionFactory::class)->create();
$urlRewriteCollection->addFieldToFilter('entity_type', 'product');
$urlRewriteCollection->addFieldToFilter('entity_id', $product->getId());

self::assertSame(1, $urlRewriteCollection->getSize());
self::assertSame(1, $productCollection->getSize());

$attributeSetRepository = Bootstrap::getObjectManager()->get(AttributeSetRepositoryInterface::class);
$attributeSetRepository->deleteById($attributeSet->getAttributeSetId());

$productCollection = Bootstrap::getObjectManager()->get(CollectionFactory::class)->create();
$productCollection->addIdFilter($product->getId());
$urlRewriteCollection = Bootstrap::getObjectManager()->get(UrlRewriteCollectionFactory::class)->create();
$urlRewriteCollection->addFieldToFilter('entity_type', 'product');
$urlRewriteCollection->addFieldToFilter('entity_id', $product->getId());

self::assertSame(0, $urlRewriteCollection->getSize());
self::assertSame(0, $productCollection->getSize());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

require __DIR__ . '/../../Eav/_files/empty_attribute_set.php';
require __DIR__ . '/../../Catalog/_files/product_simple.php';

$product->setAttributeSetId($attributeSet->getId());
$product->save();
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

require __DIR__ . '/../../Catalog/_files/product_simple_rollback.php';
require __DIR__ . '/../../Eav/_files/empty_attribute_set_rollback.php';