diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index 6c9867359d40..fa68ae3f865e 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -11,6 +11,8 @@ */ namespace Magento\Catalog\Model\ResourceModel; +use Magento\Catalog\Model\Indexer\Category\Product\Processor; +use Magento\Framework\DataObject; use Magento\Framework\EntityManager\EntityManager; /** @@ -82,6 +84,11 @@ class Category extends AbstractResource */ protected $aggregateCount; + /** + * @var Processor + */ + private $indexerProcessor; + /** * Category constructor. * @param \Magento\Eav\Model\Entity\Context $context @@ -90,6 +97,7 @@ class Category extends AbstractResource * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param Category\TreeFactory $categoryTreeFactory * @param Category\CollectionFactory $categoryCollectionFactory + * @param Processor $indexerProcessor * @param array $data * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer */ @@ -100,6 +108,7 @@ public function __construct( \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Catalog\Model\ResourceModel\Category\TreeFactory $categoryTreeFactory, \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory, + Processor $indexerProcessor, $data = [], \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { @@ -113,6 +122,7 @@ public function __construct( $this->_categoryCollectionFactory = $categoryCollectionFactory; $this->_eventManager = $eventManager; $this->connectionName = 'catalog'; + $this->indexerProcessor = $indexerProcessor; $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); } @@ -197,6 +207,18 @@ protected function _beforeDelete(\Magento\Framework\DataObject $object) $this->deleteChildren($object); } + /** + * Mark Category indexer as invalid to be picked up by cron. + * + * @param DataObject $object + * @return $this + */ + protected function _afterDelete(DataObject $object) + { + $this->indexerProcessor->markIndexerAsInvalid(); + return parent::_afterDelete($object); + } + /** * Delete children categories of specific category * diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/CategoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/CategoryTest.php index 4812751792f1..b7d05fd2b70e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/CategoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/CategoryTest.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Test\Unit\Model\ResourceModel; use Magento\Catalog\Model\Factory; +use Magento\Catalog\Model\Indexer\Category\Product\Processor; use Magento\Catalog\Model\ResourceModel\Category; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; use Magento\Eav\Model\Config; @@ -91,6 +92,11 @@ class CategoryTest extends \PHPUnit\Framework\TestCase */ private $serializerMock; + /** + * @var Processor|\PHPUnit_Framework_MockObject_MockObject + */ + private $indexerProcessorMock; + /** * {@inheritDoc} */ @@ -121,6 +127,9 @@ protected function setUp() $this->collectionFactoryMock = $this->getMockBuilder(CollectionFactory::class) ->disableOriginalConstructor() ->getMock(); + $this->indexerProcessorMock = $this->getMockBuilder(Processor::class) + ->disableOriginalConstructor() + ->getMock(); $this->serializerMock = $this->getMockBuilder(Json::class)->getMock(); @@ -131,6 +140,7 @@ protected function setUp() $this->managerMock, $this->treeFactoryMock, $this->collectionFactoryMock, + $this->indexerProcessorMock, [], $this->serializerMock ); diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Model/Plugin/Category.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Model/Plugin/Category.php new file mode 100644 index 000000000000..ed841996ea07 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Model/Plugin/Category.php @@ -0,0 +1,45 @@ +fulltextIndexerProcessor = $fulltextIndexerProcessor; + } + + /** + * Mark fulltext indexer as invalid post-deletion of category. + * + * @param Resource $subjectCategory + * @param Resource $resultCategory + * @return Resource + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterDelete(Resource $subjectCategory, Resource $resultCategory) : Resource + { + $this->fulltextIndexerProcessor->markIndexerAsInvalid(); + + return $resultCategory; + } +} diff --git a/app/code/Magento/CatalogSearch/etc/adminhtml/di.xml b/app/code/Magento/CatalogSearch/etc/adminhtml/di.xml index d6c72d883fed..2d41d17889e4 100644 --- a/app/code/Magento/CatalogSearch/etc/adminhtml/di.xml +++ b/app/code/Magento/CatalogSearch/etc/adminhtml/di.xml @@ -31,4 +31,7 @@ Magento\CatalogSearch\Ui\DataProvider\Product\AddFulltextFilterToCollection + + + diff --git a/app/code/Magento/CatalogSearch/etc/webapi_rest/di.xml b/app/code/Magento/CatalogSearch/etc/webapi_rest/di.xml new file mode 100644 index 000000000000..c7293783dc60 --- /dev/null +++ b/app/code/Magento/CatalogSearch/etc/webapi_rest/di.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/code/Magento/CatalogSearch/etc/webapi_soap/di.xml b/app/code/Magento/CatalogSearch/etc/webapi_soap/di.xml new file mode 100644 index 000000000000..c7293783dc60 --- /dev/null +++ b/app/code/Magento/CatalogSearch/etc/webapi_soap/di.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php index 6efd2638c0d5..dab47c818ece 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php @@ -27,6 +27,11 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ protected $productResource; + /** + * @var \Magento\Catalog\Api\CategoryRepositoryInterface + */ + private $categoryRepository; + protected function setUp() { /** @var \Magento\Framework\Indexer\IndexerInterface indexer */ @@ -39,6 +44,10 @@ protected function setUp() $this->productResource = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Catalog\Model\ResourceModel\Product::class ); + + $this->categoryRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Api\CategoryRepositoryInterface::class + ); } /** @@ -200,6 +209,25 @@ public function testCategoryCreate() } } + /** + * @magentoAppArea adminhtml + * @depends testReindexAll + */ + public function testCatalogCategoryProductIndexInvalidateAfterDelete() + { + $indexerShouldBeValid = (bool)$this->indexer->isInvalid(); + + $categories = $this->getCategories(1); + $this->categoryRepository->delete(array_pop($categories)); + + $state = $this->indexer->getState(); + $state->loadByIndexer($this->indexer->getId()); + $status = $state->getStatus(); + + $this->assertFalse($indexerShouldBeValid); + $this->assertEquals(\Magento\Framework\Indexer\StateInterface::STATUS_INVALID, $status); + } + /** * @param int $count * @return Category[] diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Model/Plugin/CategoryTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Model/Plugin/CategoryTest.php new file mode 100644 index 000000000000..ec2a14abafc1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Model/Plugin/CategoryTest.php @@ -0,0 +1,69 @@ +indexerProcessor = Bootstrap::getObjectManager()->create(Processor::class); + $this->categoryRepository = Bootstrap::getObjectManager()->create(CategoryRepositoryInterface::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/indexer_catalog_category.php + * @magentoAppArea adminhtml + */ + public function testIndexerInvalidatedAfterCategoryDelete() + { + $this->indexerProcessor->reindexAll(); + $isIndexerValid = (bool)$this->indexerProcessor->getIndexer()->isValid(); + + $category = $this->getCategories(1); + $this->categoryRepository->delete(array_pop($category)); + + $state = $this->indexerProcessor->getIndexer()->getState(); + $state->loadByIndexer($this->indexerProcessor->getIndexerId()); + $status = $state->getStatus(); + + $this->assertTrue($isIndexerValid); + $this->assertEquals(\Magento\Framework\Indexer\StateInterface::STATUS_INVALID, $status); + } + + /** + * @param int $count + * @return Category[] + */ + private function getCategories($count) + { + /** @var Category $category */ + $category = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\Category::class + ); + + $result = $category->getCollection()->addAttributeToSelect('name')->getItems(); + $result = array_slice($result, 2); + + return array_slice($result, 0, $count); + } +}