Skip to content

Commit

Permalink
Batch managing product categories (add, replace, remove) (#9)
Browse files Browse the repository at this point in the history
* Batch managing product categories (add, replace, remove)
  • Loading branch information
ondrej-kuhnel authored Sep 22, 2020
1 parent 70ef715 commit fcaf712
Show file tree
Hide file tree
Showing 9 changed files with 341 additions and 46 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## v1.3.0 (2020-09-22)

#### Details

- Batch managing product categories (add, replace, remove)

## v1.2.0 (2020-09-03)

#### Details
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"require": {
"ext-json": "*",
"php": "^7.3",
"sylius/sylius": "1.7.*"
"sylius/sylius": "^1.7"
},
"require-dev": {
"behat/behat": "^3.4",
Expand Down
2 changes: 1 addition & 1 deletion src/Controller/DuplicateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class DuplicateController
{
Expand Down
162 changes: 142 additions & 20 deletions src/Controller/ManageProductCategoriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
use Doctrine\ORM\EntityManagerInterface;
use MangoSylius\ExtendedChannelsPlugin\Form\Type\BulkManageProductCategoriesType;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Model\ProductTaxonInterface;
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Sylius\Component\Product\Factory\ProductFactoryInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Symfony\Component\Form\FormFactoryInterface;
Expand All @@ -17,7 +19,8 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class ManageProductCategoriesController
{
Expand Down Expand Up @@ -55,6 +58,14 @@ class ManageProductCategoriesController
* @var EventDispatcherInterface
*/
private $eventDispatcher;
/**
* @var ProductFactoryInterface
*/
private $productFactory;
/**
* @var FactoryInterface
*/
private $productTaxonFactory;

public function __construct(
TranslatorInterface $translator,
Expand All @@ -64,7 +75,9 @@ public function __construct(
EngineInterface $templatingEngine,
EntityManagerInterface $entityManager,
FormFactoryInterface $formFactory,
EventDispatcherInterface $eventDispatcher
EventDispatcherInterface $eventDispatcher,
ProductFactoryInterface $productFactory,
FactoryInterface $productTaxonFactory
) {
$this->router = $router;
$this->flashBag = $flashBag;
Expand All @@ -74,22 +87,23 @@ public function __construct(
$this->entityManager = $entityManager;
$this->formFactory = $formFactory;
$this->eventDispatcher = $eventDispatcher;
$this->productFactory = $productFactory;
$this->productTaxonFactory = $productTaxonFactory;
}

public function bulkManageProductCategories(Request $request): Response
{
$productIds = array_filter(explode(',', $request->get('bulkProductsIds')));
/** @var array<int> $productIds */
$productIds = array_filter(explode(',', $request->get('bulkProductsIds', [])));
assert(count($productIds) > 0);

if ($request->isMethod('POST')) {
foreach ($productIds as $productId) {
$product = $this->productRepository->find($productId);
assert($product instanceof ProductInterface);
$product->getProductTaxons()->clear();
$form = $this->formFactory->create(BulkManageProductCategoriesType::class, $product);
$form->handleRequest($request);
}
$this->entityManager->flush();
$dummyProduct = $this->productFactory->createNew();
assert($dummyProduct instanceof ProductInterface);
$form = $this->formFactory->create(BulkManageProductCategoriesType::class, $dummyProduct);
$form->handleRequest($request);

if ($form->isSubmitted()) {
$this->manageProducts($request, $dummyProduct, $productIds);

$message = $this->translator->trans('mango-sylius.admin.manage_product_categories.saved');
$this->flashBag->add('success', $message);
Expand All @@ -101,17 +115,125 @@ public function bulkManageProductCategories(Request $request): Response
return new RedirectResponse($this->router->generate('sylius_admin_product_index'));
}

$mainProduct = $this->productRepository->find(current($productIds));
assert($mainProduct instanceof ProductInterface);
$mainProduct->getProductTaxons()->clear();
$form = $this->formFactory->create(BulkManageProductCategoriesType::class, $mainProduct);

return $this->templatingEngine->renderResponse('@MangoSyliusExtendedChannelsPlugin/ManageProductCategories/form.html.twig', [
return new Response($this->templatingEngine->render('@MangoSyliusExtendedChannelsPlugin/ManageProductCategories/form.html.twig', [
'form' => $form->createView(),
'productIds' => implode(',', $productIds),
'productIdsCount' => count($productIds),
'paths' => [
'cancel' => $this->router->generate('sylius_admin_product_index'),
],
]);
]));
}

/**
* @param array<int> $productIds
*/
protected function manageProducts(Request $request, ProductInterface $dummyProduct, array $productIds): void
{
$mainTaxonAction = $request->request->get('main_taxon_action');
$taxonAction = $request->request->get('taxons_action');

assert($mainTaxonAction !== null);
assert($taxonAction !== null);

foreach ($productIds as $productId) {
$product = $this->productRepository->find($productId);
assert($product instanceof ProductInterface);

$this->manageMainTaxon($product, $dummyProduct, $mainTaxonAction);
$this->manageCategories($product, $dummyProduct, $taxonAction);
}

$this->entityManager->flush();
}

protected function manageMainTaxon(ProductInterface $product, ProductInterface $dummyProduct, string $action): void
{
if ($action === 'replace') {
$product->setMainTaxon($dummyProduct->getMainTaxon());

return;
}

if ($action === 'add') {
if ($product->getMainTaxon() === null) {
$product->setMainTaxon($dummyProduct->getMainTaxon());
}

return;
}

if ($action === 'remove') {
if ($product->getMainTaxon() === $dummyProduct->getMainTaxon()) {
$product->setMainTaxon(null);
}

return;
}

if ($action === 'remove_all') {
$product->setMainTaxon(null);

return;
}
}

protected function manageCategories(ProductInterface $product, ProductInterface $dummyProduct, string $action): void
{
if ($action === 'replace') {
$product->getProductTaxons()->clear();
$this->addTaxon($product, $dummyProduct);

return;
}

if ($action === 'add') {
$this->addTaxon($product, $dummyProduct);

return;
}

if ($action === 'remove') {
$this->removeTaxon($product, $dummyProduct);

return;
}

if ($action === 'remove_all') {
$product->getProductTaxons()->clear();

return;
}
}

protected function addTaxon(ProductInterface $product, ProductInterface $dummyProduct): void
{
foreach ($dummyProduct->getProductTaxons() as $productTaxon) {
$taxon = $productTaxon->getTaxon();
if ($taxon !== null && !$product->hasTaxon($taxon)) {
$dummyProductTaxon = $this->productTaxonFactory->createNew();
assert($dummyProductTaxon instanceof ProductTaxonInterface);
$dummyProductTaxon->setProduct($product);
$dummyProductTaxon->setTaxon($taxon);

$product->addProductTaxon($dummyProductTaxon);
}
}
}

protected function removeTaxon(ProductInterface $product, ProductInterface $dummyProduct): void
{
foreach ($dummyProduct->getProductTaxons() as $productTaxon) {
$taxon = $productTaxon->getTaxon();
if ($taxon !== null && $product->hasTaxon($taxon)) {
foreach ($product->getProductTaxons() as $pt) {
if ($pt->getTaxon() === $taxon) {
$product->removeProductTaxon($pt);

break;
}
}
}
}
}
}
2 changes: 2 additions & 0 deletions src/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ services:
$entityManager: '@doctrine.orm.default_entity_manager'
$formFactory: '@form.factory'
$eventDispatcher: '@event_dispatcher'
$productFactory: '@sylius.custom_factory.product'
$productTaxonFactory: '@sylius.factory.product_taxon'

MangoSylius\ExtendedChannelsPlugin\Service\ProductDuplicator:
arguments:
Expand Down
33 changes: 31 additions & 2 deletions src/Resources/translations/messages.cs.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mango-sylius:
admin:
selected_products_count: Vybrané produkty
form:
channel:
bccEmail: BCC email
Expand All @@ -10,7 +11,7 @@ mango-sylius:
product:
duplicate: Duplikovat
success: Produkt byl zduplikován
manage_categories: Spravovat kategorie
manage_categories: Spravovat Taxony
product_variant:
duplicate: Duplikovat
success: Varianta produktu byla zduplikována
Expand All @@ -22,7 +23,35 @@ mango-sylius:
grid:
bulk_actions: Hromadné akce
manage_product_categories:
saved: Kategorie produktů byly uloženy
saved: Taxony produktů byly uloženy
main_taxon_actions_label: Akce
main_taxon_actions:
ignore: Ignorovat
replace: Změnit
add: Přidat
remove: Odebrat zvolené
remove_all: Odebrat vše
main_taxon_actions_help_title: Akce hlavního taxonu
main_taxon_actions_help:
ignore: Neudělá nic. Použijte, když je potřeba změnit pouze strom taxonů.
replace: Změní hlavní taxon bez ohledu na existující hodnotu.
add: Přidá zvolený taxon k produktům, které hlavní taxon nemají.
remove: Odebere hlavní taxon u produktů, kde je hlavní taxon stejný jako zvolený.
remove_all: Odebere hlavní taxon bez ohledu na existující nebo zvolený.
taxons_actions_label: Akce
taxons_actions:
ignore: Ignorovat
replace: Změnit
add: Přidat
remove: Odebrat zvolené
remove_all: Odebrat vše
taxons_actions_help_title: Akce taxonů
taxons_actions_help:
ignore: Neudělá nic. Použijte, když je potřeba změnit pouze hlavní taxon.
replace: Odebere všechny existující a přidá zvolené.
add: Přidá zvolené taxony a zachová ostatní existující.
remove: Odebere zvolené taxony, ostatní existující zachová.
remove_all: Odebere všechny taxony bez ohledu na vybrané.

mangoweb_extended_channels_plugin:
ui:
Expand Down
33 changes: 31 additions & 2 deletions src/Resources/translations/messages.en.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mango-sylius:
admin:
selected_products_count: Selected products
form:
channel:
bccEmail: BCC email
Expand All @@ -10,7 +11,7 @@ mango-sylius:
product:
duplicate: Duplicate
success: The product was successfully duplicated
manage_categories: Manage categories
manage_categories: Manage Taxons
product_variant:
duplicate: Duplicate
success: The product variant was successfully duplicated
Expand All @@ -22,7 +23,35 @@ mango-sylius:
grid:
bulk_actions: Bulk actions
manage_product_categories:
saved: The product categories were successfully saved
saved: The product Taxons were successfully saved
main_taxon_actions_label: Action
main_taxon_actions:
ignore: Ignore
replace: Replace
add: Add
remove: Remove selected
remove_all: Remove all
main_taxon_actions_help_title: Main taxon actions
main_taxon_actions_help:
ignore: Do nothing. Use when you want to change taxon tree only.
replace: Overwrite the main taxon for all products regardless of its existing value.
add: Add selected taxon for products with empty main taxon.
remove: Remove value when product taxon equans to selected taxon.
remove_all: Remove the main taxon regardless of its existing value or selected main taxon.
taxons_actions_label: Action
taxons_actions:
ignore: Ignore
replace: Replace
add: Add
remove: Remove selected
remove_all: Remove all
taxons_actions_help_title: Taxon tree actions
taxons_actions_help:
ignore: Do nothing. Use when you want to change main taxon only.
replace: Remove all taxons and add selected.
add: Add selected taxons preserving existing taxons as well.
remove: Remove selected taxons preserving other existing taxons.
remove_all: Remove all taxons regardless of what you have selected.

mangoweb_extended_channels_plugin:
ui:
Expand Down
2 changes: 1 addition & 1 deletion src/Resources/views/Grid/Action/manageCategories.html.twig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<form action="{{ options.link.url|default(path(options.link.route)) }}" method="get" id="bulk-setProductCategories">
{# hack - get bulk items ids via JS #}
<button class="ui labeled icon button" type="submit" disabled onclick="$('#bulkProductsIds').val($('input[name=\'ids[]\']:checked').map(function (index, input) { return input.value; }).toArray().join(','))">
<button class="ui labeled icon button" type="submit" disabled onclick="$('#bulkProductsIds').val($('input.bulk-select-checkbox:checked').map(function (index, input) { return input.value; }).toArray().join(','))">
<i class="icon folder"></i> {{ action.label|trans }}
</button>
<input type="hidden" name="bulkProductsIds" value="" id="bulkProductsIds" />
Expand Down
Loading

0 comments on commit fcaf712

Please sign in to comment.