Skip to content

Commit 4afb3db

Browse files
ENGCOM-3826: #16198: Category image remain after deleted. #20178
- Merge Pull Request #20178 from p-bystritsky/magento2:ISSUE-16198 - Merged commits: 1. 3b4581c
2 parents a392746 + 3b4581c commit 4afb3db

File tree

6 files changed

+293
-0
lines changed

6 files changed

+293
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\ResourceModel\Category;
9+
10+
use Magento\Catalog\Api\CategoryListInterface;
11+
use Magento\Framework\Api\SearchCriteriaBuilder;
12+
13+
/**
14+
* Check if Image is currently used in any category as Category Image.
15+
*/
16+
class RedundantCategoryImageChecker
17+
{
18+
/**
19+
* @var SearchCriteriaBuilder
20+
*/
21+
private $searchCriteriaBuilder;
22+
23+
/**
24+
* @var CategoryListInterface
25+
*/
26+
private $categoryList;
27+
28+
public function __construct(
29+
CategoryListInterface $categoryList,
30+
SearchCriteriaBuilder $searchCriteriaBuilder
31+
) {
32+
$this->categoryList = $categoryList;
33+
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
34+
}
35+
36+
/**
37+
* Checks if Image is currently used in any category as Category Image.
38+
*
39+
* Returns true if not.
40+
*
41+
* @param string $imageName
42+
* @return bool
43+
*/
44+
public function execute(string $imageName): bool
45+
{
46+
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
47+
$searchCriteria = $this->searchCriteriaBuilder->addFilter('image', $imageName)->create();
48+
$categories = $this->categoryList->getList($searchCriteria)->getItems();
49+
50+
return empty($categories);
51+
}
52+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Plugin\Model\ResourceModel\Category;
9+
10+
use Magento\Catalog\Model\ResourceModel\Category as CategoryResource;
11+
use Magento\Catalog\Model\ImageUploader;
12+
use Magento\Catalog\Model\ResourceModel\Category\RedundantCategoryImageChecker;
13+
use Magento\Framework\App\Filesystem\DirectoryList;
14+
use Magento\Framework\Filesystem;
15+
use Magento\Framework\Model\AbstractModel;
16+
17+
/**
18+
* Remove old Category Image file from pub/media/catalog/category directory if such Image is not used anymore.
19+
*/
20+
class RemoveRedundantImagePlugin
21+
{
22+
/**
23+
* @var Filesystem
24+
*/
25+
private $filesystem;
26+
27+
/**
28+
* @var ImageUploader
29+
*/
30+
private $imageUploader;
31+
32+
/**
33+
* @var RedundantCategoryImageChecker
34+
*/
35+
private $redundantCategoryImageChecker;
36+
37+
public function __construct(
38+
Filesystem $filesystem,
39+
ImageUploader $imageUploader,
40+
RedundantCategoryImageChecker $redundantCategoryImageChecker
41+
) {
42+
$this->filesystem = $filesystem;
43+
$this->imageUploader = $imageUploader;
44+
$this->redundantCategoryImageChecker = $redundantCategoryImageChecker;
45+
}
46+
47+
/**
48+
* Removes Image file if it is not used anymore.
49+
*
50+
* @param CategoryResource $subject
51+
* @param CategoryResource $result
52+
* @param AbstractModel $category
53+
* @return CategoryResource
54+
*
55+
* @throws \Magento\Framework\Exception\FileSystemException
56+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
57+
*/
58+
public function afterSave(CategoryResource $subject, CategoryResource $result, AbstractModel $category): CategoryResource
59+
{
60+
$originalImage = $category->getOrigData('image');
61+
if (
62+
null !== $originalImage
63+
&& $originalImage !== $category->getImage()
64+
&& $this->redundantCategoryImageChecker->execute($originalImage)
65+
) {
66+
$basePath = $this->imageUploader->getBasePath();
67+
$baseImagePath = $this->imageUploader->getFilePath($basePath, $originalImage);
68+
/** @var \Magento\Framework\Filesystem\Directory\WriteInterface $mediaDirectory */
69+
$mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
70+
$mediaDirectory->delete($baseImagePath);
71+
}
72+
73+
return $result;
74+
}
75+
}

app/code/Magento/Catalog/etc/di.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,14 @@
907907
<type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
908908
<plugin name="copy_quote_files_to_order" type="Magento\Catalog\Model\Plugin\QuoteItemProductOption"/>
909909
</type>
910+
<type name="Magento\Catalog\Model\ResourceModel\Category">
911+
<plugin name="remove_redundant_image" type="Magento\Catalog\Plugin\Model\ResourceModel\Category\RemoveRedundantImagePlugin"/>
912+
</type>
913+
<type name="Magento\Catalog\Plugin\Model\ResourceModel\Category\RemoveRedundantImagePlugin">
914+
<arguments>
915+
<argument name="imageUploader" xsi:type="object">Magento\Catalog\CategoryImageUpload</argument>
916+
</arguments>
917+
</type>
910918
<preference for="Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface" type="Magento\Catalog\Model\ResourceModel\Product\CompositeWithWebsiteProcessor" />
911919
<type name="Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor">
912920
<arguments>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model;
9+
10+
use Magento\Framework\App\Filesystem\DirectoryList;
11+
use Magento\Framework\Filesystem;
12+
use Magento\Framework\Filesystem\Directory\WriteInterface;
13+
use Magento\Framework\ObjectManagerInterface;
14+
15+
/**
16+
* Test removing old Category Image file from pub/media/catalog/category directory if such Image is not used anymore.
17+
*/
18+
class RemoveRedundantImageTest extends \PHPUnit\Framework\TestCase
19+
{
20+
/**
21+
* @var ObjectManagerInterface
22+
*/
23+
private $objectManager;
24+
25+
/**
26+
* @var Filesystem
27+
*/
28+
private $filesystem;
29+
30+
/**
31+
* @var WriteInterface
32+
*/
33+
private $mediaDirectory;
34+
35+
/**
36+
* @var CategoryRepository
37+
*/
38+
private $categoryRepository;
39+
40+
protected function setUp()
41+
{
42+
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
43+
/** @var Filesystem $filesystem */
44+
$this->filesystem = $this->objectManager->get(Filesystem::class);
45+
$this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
46+
$this->categoryRepository = $this->objectManager->get(CategoryRepository::class);
47+
}
48+
49+
/**
50+
* Tests removing Image file if it is not used anymore.
51+
*
52+
* @magentoDataFixture Magento/Catalog/_files/categories_with_image.php
53+
*
54+
* @magentoAppIsolation enabled
55+
* @magentoDbIsolation enabled
56+
*/
57+
public function testRemoveRedundantImage()
58+
{
59+
$imagesPath = 'catalog' . DIRECTORY_SEPARATOR . 'category';
60+
$absoluteImagesPath = $this->mediaDirectory->getAbsolutePath($imagesPath);
61+
$filePath1 = $absoluteImagesPath . DIRECTORY_SEPARATOR . 'test_image_1.jpg';
62+
$filePath2 = $absoluteImagesPath . DIRECTORY_SEPARATOR . 'test_image_2.jpg';
63+
$this->mediaDirectory->create($absoluteImagesPath);
64+
$this->mediaDirectory->touch($filePath1);
65+
$this->mediaDirectory->touch($filePath2);
66+
67+
$category1 = $this->categoryRepository->get(3);
68+
$category1->setImage('test_image_3.jpg');
69+
$this->categoryRepository->save($category1);
70+
$category2 = $this->categoryRepository->get(5);
71+
$category2->setImage('test_image_3.jpg');
72+
$this->categoryRepository->save($category2);
73+
74+
$this->assertTrue($this->mediaDirectory->isExist($filePath1));
75+
$this->assertFalse($this->mediaDirectory->isExist($filePath2));
76+
}
77+
78+
protected function tearDown()
79+
{
80+
$this->mediaDirectory->delete();
81+
}
82+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
7+
8+
/**
9+
* After installation system has two categories: root one with ID:1 and Default category with ID:2
10+
*/
11+
/** @var $category \Magento\Catalog\Model\Category */
12+
$category = $objectManager->create(\Magento\Catalog\Model\Category::class);
13+
$category->isObjectNew(true);
14+
$category->setId(3)
15+
->setName('Category 1')
16+
->setParentId(2)
17+
->setPath('1/2/3')
18+
->setLevel(2)
19+
->setAvailableSortBy('name')
20+
->setDefaultSortBy('name')
21+
->setIsActive(true)
22+
->setPosition(1)
23+
->setImage('test_image_1.jpg')
24+
->save();
25+
26+
$category = $objectManager->create(\Magento\Catalog\Model\Category::class);
27+
$category->isObjectNew(true);
28+
$category->setId(4)
29+
->setName('Category 1.1')
30+
->setParentId(3)
31+
->setPath('1/2/3/4')
32+
->setLevel(3)
33+
->setAvailableSortBy('name')
34+
->setDefaultSortBy('name')
35+
->setIsActive(true)
36+
->setIsAnchor(true)
37+
->setPosition(1)
38+
->setImage('test_image_1.jpg')
39+
->save();
40+
41+
$category = $objectManager->create(\Magento\Catalog\Model\Category::class);
42+
$category->isObjectNew(true);
43+
$category->setId(5)
44+
->setName('Category 2')
45+
->setParentId(2)
46+
->setPath('1/2/5')
47+
->setLevel(2)
48+
->setAvailableSortBy('name')
49+
->setDefaultSortBy('name')
50+
->setIsActive(true)
51+
->setPosition(2)
52+
->setImage('test_image_2.jpg')
53+
->save();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
8+
/** @var \Magento\Framework\Registry $registry */
9+
$registry = $objectManager->get(\Magento\Framework\Registry::class);
10+
11+
$registry->unregister('isSecureArea');
12+
$registry->register('isSecureArea', true);
13+
14+
//Remove categories
15+
/** @var Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
16+
$collection = $objectManager->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class);
17+
$collection
18+
->addAttributeToFilter('level', 2)
19+
->load()
20+
->delete();
21+
22+
$registry->unregister('isSecureArea');
23+
$registry->register('isSecureArea', false);

0 commit comments

Comments
 (0)