Skip to content

Commit

Permalink
Merge pull request #9906 from Zales0123/product-images-deletion-fix
Browse files Browse the repository at this point in the history
Thank you, Mateusz! 🌵
  • Loading branch information
bartoszpietrzak1994 authored Nov 12, 2018
2 parents 6cc76e5 + 8852676 commit c27fe04
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ Feature: Prevent deletion of purchased product
When I try to delete the "Toyota GT86 model" product
Then I should be notified that this product is in use and cannot be deleted
And this product should still exist in the product catalog

@ui
Scenario: Purchased product images should be kept
Given the store has a product "Lamborghini Gallardo Model"
And this product has an image "lamborghini.jpg" with "thumbnail" type
And there is a customer "jane.doe@gmail.com" that placed an order "#00000026"
And the customer bought a single "Lamborghini Gallardo Model"
And the customer chose "Free" shipping method to "United States" with "Cash on Delivery" payment
When I try to delete the "Lamborghini Gallardo Model" product
Then I should be notified that this product is in use and cannot be deleted
And this product should still exist in the product catalog
And the product "Lamborghini Gallardo Model" should still have an accessible image
8 changes: 8 additions & 0 deletions src/Sylius/Behat/Context/Ui/Admin/ManagingProductsContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,14 @@ public function thisProductShouldHaveAnImageWithType($type)
Assert::true($currentPage->isImageWithTypeDisplayed($type));
}

/**
* @Then /^the (product "[^"]+") should still have an accessible image$/
*/
public function productShouldStillHaveAnAccessibleImage(ProductInterface $product): void
{
Assert::true($this->indexPage->hasProductAccessibleImage($product->getCode()));
}

/**
* @Then /^(?:this product|it)(?:| also) should not have any images with "([^"]*)" type$/
*/
Expand Down
28 changes: 28 additions & 0 deletions src/Sylius/Behat/Page/Admin/Product/IndexPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,30 @@

namespace Sylius\Behat\Page\Admin\Product;

use Behat\Mink\Session;
use Sylius\Behat\Page\Admin\Crud\IndexPage as CrudIndexPage;
use Sylius\Behat\Service\Accessor\TableAccessorInterface;
use Sylius\Behat\Service\Checker\ImageExistenceCheckerInterface;
use Symfony\Component\Routing\RouterInterface;

final class IndexPage extends CrudIndexPage implements IndexPageInterface
{
/** @var ImageExistenceCheckerInterface */
private $imageExistenceChecker;

public function __construct(
Session $session,
array $parameters,
RouterInterface $router,
TableAccessorInterface $tableAccessor,
string $routeName,
ImageExistenceCheckerInterface $imageExistenceChecker
) {
parent::__construct($session, $parameters, $router, $tableAccessor, $routeName);

$this->imageExistenceChecker = $imageExistenceChecker;
}

/**
* {@inheritdoc}
*/
Expand All @@ -25,6 +45,14 @@ public function filterByTaxon($taxonName)
$this->getElement('taxon_filter', ['%taxon%' => $taxonName])->click();
}

public function hasProductAccessibleImage(string $productCode): bool
{
$productRow = $this->getTableAccessor()->getRowWithFields($this->getElement('table'), ['code' => $productCode]);
$imageUrl = $productRow->find('css', 'img')->getAttribute('src');

return $this->imageExistenceChecker->doesImageWithUrlExist($imageUrl, 'sylius_admin_product_thumbnail');
}

/**
* {@inheritdoc}
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Sylius/Behat/Page/Admin/Product/IndexPageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ interface IndexPageInterface extends CrudIndexPageInterface
* @param string $taxonName
*/
public function filterByTaxon($taxonName);

public function hasProductAccessibleImage(string $productCode): bool;
}
5 changes: 5 additions & 0 deletions src/Sylius/Behat/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@

<service id="sylius.behat.table_accessor" class="Sylius\Behat\Service\Accessor\TableAccessor" public="false" />

<service id="sylius.behat.checker.image_existence" class="Sylius\Behat\Service\Checker\ImageExistenceChecker">
<argument type="service" id="__symfony__.sylius.liip.filter_service" />
<argument type="string">%__symfony__.sylius_core.public_dir%</argument>
</service>

<service id="sylius.behat.paypal_api_mocker" class="Sylius\Behat\Service\Mocker\PaypalApiMocker" public="false">
<argument type="service" id="sylius.behat.mocker" />
<argument type="service" id="sylius.behat.response_loader" />
Expand Down
1 change: 1 addition & 0 deletions src/Sylius/Behat/Resources/config/services/contexts/ui.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
<argument type="service" id="sylius.behat.page.admin.product.index_per_taxon" />
<argument type="service" id="sylius.behat.current_page_resolver" />
<argument type="service" id="sylius.behat.notification_checker" />
<argument type="service" id="__symfony__.liip_imagine.cache.manager" />
<tag name="fob.context_service" />
</service>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
</service>
<service id="sylius.behat.page.admin.product.index" class="%sylius.behat.page.admin.product.index.class%" parent="sylius.behat.page.admin.crud.index" public="false">
<argument type="string">sylius_admin_product_index</argument>
<argument type="service" id="sylius.behat.checker.image_existence" />
</service>
<service id="sylius.behat.page.admin.product.index_per_taxon" class="%sylius.behat.page.admin.product.index_per_taxon.class%" parent="sylius.behat.page.admin.crud.index" public="false">
<argument type="string">sylius_admin_product_per_taxon_index</argument>
Expand Down
40 changes: 40 additions & 0 deletions src/Sylius/Behat/Service/Checker/ImageExistenceChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Behat\Service\Checker;

use Liip\ImagineBundle\Service\FilterService;

final class ImageExistenceChecker implements ImageExistenceCheckerInterface
{
/** @var FilterService */
private $filterService;

/** @var string */
private $mediaRootPath;

public function __construct(FilterService $filterService, string $mediaRootPath)
{
$this->filterService = $filterService;
$this->mediaRootPath = $mediaRootPath;
}

public function doesImageWithUrlExist(string $imageUrl, string $liipImagineFilter): bool
{
$imageUrl = str_replace($liipImagineFilter . '/', '', substr($imageUrl, strpos($imageUrl, $liipImagineFilter), strlen($imageUrl)));

$browserImagePath = $this->filterService->getUrlOfFilteredImage($imageUrl, $liipImagineFilter);

return file_exists($this->mediaRootPath . parse_url($browserImagePath, PHP_URL_PATH));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Behat\Service\Checker;

interface ImageExistenceCheckerInterface
{
public function doesImageWithUrlExist(string $imageUrl, string $liipImagineImagineFilter): bool;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@

namespace Sylius\Bundle\CoreBundle\EventListener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
use Sylius\Component\Core\Model\ImageInterface;
use Sylius\Component\Core\Uploader\ImageUploaderInterface;

/**
* @internal
*/
final class ImagesRemoveListener
{
/** @var ImageUploaderInterface */
Expand All @@ -30,20 +34,35 @@ final class ImagesRemoveListener
/** @var FilterManager */
private $filterManager;

/** @var string[] */
private $imagesToDelete = [];

public function __construct(ImageUploaderInterface $imageUploader, CacheManager $cacheManager, FilterManager $filterManager)
{
$this->imageUploader = $imageUploader;
$this->cacheManager = $cacheManager;
$this->filterManager = $filterManager;
}

public function postRemove(LifecycleEventArgs $event): void
public function onFlush(OnFlushEventArgs $event): void
{
$image = $event->getEntity();
foreach ($event->getEntityManager()->getUnitOfWork()->getScheduledEntityDeletions() as $entityDeletion) {
if (!$entityDeletion instanceof ImageInterface) {
continue;
}

if (!in_array($entityDeletion->getPath(), $this->imagesToDelete)) {
$this->imagesToDelete[] = $entityDeletion->getPath();
}
}
}

if ($image instanceof ImageInterface) {
$this->imageUploader->remove($image->getPath());
$this->cacheManager->remove($image->getPath(), array_keys($this->filterManager->getFilterConfiguration()->all()));
public function postFlush(PostFlushEventArgs $event): void
{
foreach ($this->imagesToDelete as $key => $imagePath) {
$this->imageUploader->remove($imagePath);
$this->cacheManager->remove($imagePath, array_keys($this->filterManager->getFilterConfiguration()->all()));
unset($this->imagesToDelete[$key]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ parameters:
sylius.cache:
type: file_system
sylius.uploader.filesystem: sylius_image
sylius_core.public_dir: "%kernel.project_dir%/web"
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
<argument type="service" id="sylius.image_uploader" />
<argument type="service" id="liip_imagine.cache.manager" />
<argument type="service" id="liip_imagine.filter.manager" />
<tag name="doctrine.event_listener" event="postRemove" lazy="true" />
<tag name="doctrine.event_listener" event="onFlush" lazy="true" />
<tag name="doctrine.event_listener" event="postFlush" lazy="true" />
</service>
<service id="sylius.listener.order_recalculation" class="Sylius\Bundle\CoreBundle\EventListener\OrderRecalculationListener">
<argument type="service" id="sylius.order_processing.order_processor" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<service id="sylius.test.factory.promotion" class="Sylius\Component\Core\Test\Factory\TestPromotionFactory">
<argument type="service" id="sylius.factory.promotion" />
</service>
</services>

<service id="sylius.liip.filter_service" alias="liip_imagine.service.filter" public="true" />
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@

namespace spec\Sylius\Bundle\CoreBundle\EventListener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\UnitOfWork;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Bundle\CoreBundle\EventListener\ImagesRemoveListener;
use Sylius\Component\Core\Model\ImageInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Uploader\ImageUploaderInterface;

final class ImagesRemoveListenerSpec extends ObjectBehavior
Expand All @@ -35,36 +38,80 @@ function it_is_initializable(): void
$this->shouldHaveType(ImagesRemoveListener::class);
}

function it_removes_file_on_post_remove_event(
function it_saves_scheduled_entity_deletions_images_paths(
OnFlushEventArgs $event,
EntityManagerInterface $entityManager,
UnitOfWork $unitOfWork,
ImageInterface $image,
ProductInterface $product
): void {
$event->getEntityManager()->willReturn($entityManager);
$entityManager->getUnitOfWork()->willReturn($unitOfWork);
$unitOfWork->getScheduledEntityDeletions()->willReturn([$image, $product]);

$image->getPath()->shouldBeCalled();

$this->onFlush($event);
}

function it_removes_saved_images_paths(
ImageUploaderInterface $imageUploader,
CacheManager $cacheManager,
FilterManager $filterManager,
LifecycleEventArgs $event,
OnFlushEventArgs $onFlushEvent,
PostFlushEventArgs $postFlushEvent,
EntityManagerInterface $entityManager,
UnitOfWork $unitOfWork,
ImageInterface $image,
ProductInterface $product,
FilterConfiguration $filterConfiguration
): void {
$event->getEntity()->willReturn($image);
$onFlushEvent->getEntityManager()->willReturn($entityManager);
$entityManager->getUnitOfWork()->willReturn($unitOfWork);
$unitOfWork->getScheduledEntityDeletions()->willReturn([$image, $product]);

$image->getPath()->willReturn('image/path');

$filterManager->getFilterConfiguration()->willReturn($filterConfiguration);
$filterConfiguration->all()->willReturn(['sylius_small' => 'thumbnalis']);
$this->onFlush($onFlushEvent);

$imageUploader->remove('image/path')->shouldBeCalled();
$cacheManager->remove('image/path', ['sylius_small'])->shouldBeCalled();

$this->postRemove($event);
$filterManager->getFilterConfiguration()->willReturn($filterConfiguration);
$filterConfiguration->all()->willReturn(['test' => 'Test']);

$cacheManager->remove('image/path', ['test'])->shouldBeCalled();

$this->postFlush($postFlushEvent);
}

function it_does_nothing_if_entity_is_not_image(
function it_removes_saved_images_paths_from_both_filesystem_and_service_property(
ImageUploaderInterface $imageUploader,
CacheManager $cacheManager,
FilterManager $filterManager,
LifecycleEventArgs $event
OnFlushEventArgs $onFlushEvent,
PostFlushEventArgs $postFlushEvent,
EntityManagerInterface $entityManager,
UnitOfWork $unitOfWork,
ImageInterface $image,
ProductInterface $product,
FilterConfiguration $filterConfiguration
): void {
$event->getEntity()->willReturn(new \stdClass());
$filterManager->getFilterConfiguration()->shouldNotBeCalled();
$imageUploader->remove(Argument::any())->shouldNotBeCalled();
$cacheManager->remove(Argument::any(), Argument::any())->shouldNotBeCalled();
$onFlushEvent->getEntityManager()->willReturn($entityManager);
$entityManager->getUnitOfWork()->willReturn($unitOfWork);
$unitOfWork->getScheduledEntityDeletions()->willReturn([$image, $product]);

$image->getPath()->willReturn('image/path');

$this->onFlush($onFlushEvent);

$imageUploader->remove('image/path')->shouldBeCalledOnce();

$filterManager->getFilterConfiguration()->willReturn($filterConfiguration);
$filterConfiguration->all()->willReturn(['test' => 'Test']);

$cacheManager->remove('image/path', ['test'])->shouldBeCalledOnce();

$this->postRemove($event);
$this->postFlush($postFlushEvent);
$this->postFlush($postFlushEvent);
}
}

0 comments on commit c27fe04

Please sign in to comment.