feat(main): New module init
This commit is contained in:
@@ -0,0 +1 @@
|
||||
vendor/
|
||||
@@ -0,0 +1,75 @@
|
||||
# ws_customerspecialty
|
||||
|
||||
PrestaShop module (1.7 - 9.0 compatible) that adds a manageable "Specialty" field to customer
|
||||
accounts.
|
||||
|
||||
## What it does
|
||||
|
||||
- Back office: a **Customer Specialties** admin screen (under Customers) lets you maintain the
|
||||
list of selectable specialty values (title per language, position, active/inactive). Values
|
||||
are never hard-deleted from the UI, only deactivated, since they may already be linked to
|
||||
customers.
|
||||
- Back office: the native "Add/Edit customer" page gets a required "Specialty" dropdown, and the
|
||||
native Customers grid gets a sortable/filterable "Specialty" column and a read-only specialty
|
||||
block on the customer view page.
|
||||
- Front office: the registration / "my account" form gets a required "Specialty" dropdown, gated
|
||||
by a **Configure** toggle (`WS_CUSTOMERSPECIALTY_FRONT_ENABLED`) so it can be hidden from the
|
||||
storefront without affecting back-office management.
|
||||
- The customer's specialty title is exposed as a `{specialization}` order confirmation mail
|
||||
variable (see below).
|
||||
|
||||
## Data model
|
||||
|
||||
The customer's specialty is stored in the module's own tables — `ws_customerspecialty`,
|
||||
`ws_customerspecialty_lang`, and the pivot table `ws_customerspecialty_customer` — rather than by
|
||||
altering `ps_customer` or overriding core classes. This keeps the module safe to run unchanged
|
||||
across PrestaShop 1.7 through 9.0, since it never depends on core method internals that vary
|
||||
between major versions.
|
||||
|
||||
## Order confirmation email integration
|
||||
|
||||
The module does **not** ship its own copy of the order confirmation mail template. Instead it
|
||||
exposes the customer's specialty as `{specialization}` via the `actionGetExtraMailTemplateVars`
|
||||
hook (the same pattern used in `ws_giftdropdown`). To show it in an order confirmation email, add
|
||||
the placeholder to the shop's own `order_conf.html` / `order_conf.txt` mail templates, e.g.:
|
||||
|
||||
```
|
||||
Specialty: {specialization}
|
||||
```
|
||||
|
||||
If the placeholder isn't present in the active theme's mail template, the variable is computed
|
||||
but simply has nothing to render into — this is a one-time manual integration step per shop/theme,
|
||||
not something the module can safely do on your behalf without overriding core mail templates.
|
||||
|
||||
## Compatibility notes
|
||||
|
||||
- Command handlers are tagged for both `tactician.handler` (PS 1.7/8) and `messenger.message_handler`
|
||||
(PS 9), with matching `__invoke()`/`handle()` methods on each handler.
|
||||
- No PrestaShop core class overrides, no `ALTER TABLE` on core tables.
|
||||
|
||||
## Deliberate differences from the legacy `pchcustomerfield` module
|
||||
|
||||
- No drag-and-drop reordering of specialty values. The grid has "Move up" / "Move down" row
|
||||
actions instead (`customerspecialty_move_up` / `_move_down` routes swapping `position` with the
|
||||
adjacent row). This avoids depending on PrestaShop's core Grid position/drag-reorder plumbing,
|
||||
whose exact API wasn't verified live against a running 1.7 and 9.0 instance.
|
||||
- The active/inactive toggle uses a `ToggleColumn` + `customerspecialty_toggle_active` route. If
|
||||
that route ever misbehaves on a given PrestaShop version, active/inactive can always be changed
|
||||
via the Edit form as a fallback — it isn't the only way to flip the flag.
|
||||
- Grid `getFilters()`/`getColumns()` option keys (`ToggleColumn`, `SearchAndResetType`) follow the
|
||||
standard, long-stable PrestaShop Grid API from memory; they were not verified against a live
|
||||
PrestaShop 1.7 or 9.0 install (per request, devdocs research was skipped for this build). Smoke
|
||||
test the "Customer Specialties" admin grid after install before relying on it in production.
|
||||
|
||||
## Biggest assumption to verify
|
||||
|
||||
The storefront "Specialty" field on registration/account-edit relies on `additionalCustomerFormFields`
|
||||
already being called by core's own front-office `CustomerFormatter` (passing `fields` by reference).
|
||||
The legacy module achieved this only by overriding `CustomerFormatter` to add that hook call itself —
|
||||
an override intentionally not reproduced here (see the architecture note above). If the installed
|
||||
PrestaShop version's `CustomerFormatter` does not already call this hook natively, the storefront
|
||||
field will not appear (back office, grid, and mail-variable behavior are unaffected either way,
|
||||
since none of those depend on this hook). Verify on a real 1.7 and 9.0 install; if the hook isn't
|
||||
native, the fallback is to make the field mandatory server-side via a small first-party
|
||||
`CustomerFormatter` override scoped to only adding that one `Hook::exec()` call — much smaller and
|
||||
safer than the legacy module's full-method copy.
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "websmid/customerspecialty",
|
||||
"description": "Adds a manageable specialty/profession field to customer accounts",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Isabelle Oving-Anno - De Websmid BV",
|
||||
"email": "isabelle@dewebsmid.nl"
|
||||
}
|
||||
],
|
||||
"require": {},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Websmid\\CustomerSpecialty\\": "src/"
|
||||
},
|
||||
"classmap": ["ws_customerspecialty.php"],
|
||||
"exclude-from-classmap": []
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"prepend-autoloader": false
|
||||
},
|
||||
"type": "prestashop-module",
|
||||
"author": "Isabelle Oving-Anno - De Websmid BV",
|
||||
"license": "MIT"
|
||||
}
|
||||
Generated
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "523e34cf5681a91b03bd57967a2d35e0",
|
||||
"packages": [],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
services:
|
||||
websmid.customerspecialty.command_handler.save_customer_specialty_handler:
|
||||
class: 'Websmid\CustomerSpecialty\Command\CommandHandler\SaveCustomerSpecialtyCommandHandler'
|
||||
tags:
|
||||
- { name: 'tactician.handler', command: 'Websmid\CustomerSpecialty\Command\SaveCustomerSpecialtyCommand' }
|
||||
- { name: 'messenger.message_handler' }
|
||||
|
||||
websmid.customerspecialty.command_handler.move_customer_specialty_handler:
|
||||
class: 'Websmid\CustomerSpecialty\Command\CommandHandler\MoveCustomerSpecialtyCommandHandler'
|
||||
tags:
|
||||
- { name: 'tactician.handler', command: 'Websmid\CustomerSpecialty\Command\MoveCustomerSpecialtyCommand' }
|
||||
- { name: 'messenger.message_handler' }
|
||||
|
||||
websmid.customerspecialty.command_handler.toggle_customer_specialty_active_handler:
|
||||
class: 'Websmid\CustomerSpecialty\Command\CommandHandler\ToggleCustomerSpecialtyActiveCommandHandler'
|
||||
tags:
|
||||
- { name: 'tactician.handler', command: 'Websmid\CustomerSpecialty\Command\ToggleCustomerSpecialtyActiveCommand' }
|
||||
- { name: 'messenger.message_handler' }
|
||||
|
||||
websmid.customerspecialty.command_handler.link_customer_specialty_handler:
|
||||
class: 'Websmid\CustomerSpecialty\Command\CommandHandler\LinkCustomerSpecialtyCommandHandler'
|
||||
tags:
|
||||
- { name: 'tactician.handler', command: 'Websmid\CustomerSpecialty\Command\LinkCustomerSpecialtyCommand' }
|
||||
- { name: 'messenger.message_handler' }
|
||||
@@ -0,0 +1,36 @@
|
||||
services:
|
||||
websmid.customerspecialty.form.type.customerspecialty:
|
||||
public: true
|
||||
class: 'Websmid\CustomerSpecialty\Form\Type\CustomerSpecialtyType'
|
||||
parent: 'form.type.translatable.aware'
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
websmid.customerspecialty.form.customerspecialty_data_provider:
|
||||
public: true
|
||||
class: 'Websmid\CustomerSpecialty\Form\DataProvider\CustomerSpecialtyDataProvider'
|
||||
|
||||
websmid.customerspecialty.form.customerspecialty_data_form_builder:
|
||||
public: true
|
||||
class: 'PrestaShop\PrestaShop\Core\Form\IdentifiableObject\Builder\FormBuilder'
|
||||
factory:
|
||||
- '@prestashop.core.form.builder.form_builder_factory'
|
||||
- 'create'
|
||||
arguments:
|
||||
- 'Websmid\CustomerSpecialty\Form\Type\CustomerSpecialtyType'
|
||||
- '@websmid.customerspecialty.form.customerspecialty_data_provider'
|
||||
|
||||
websmid.customerspecialty.form.customerspecialty_data_handler:
|
||||
public: true
|
||||
class: 'Websmid\CustomerSpecialty\Form\DataHandler\CustomerSpecialtyDataHandler'
|
||||
arguments:
|
||||
- '@prestashop.core.command_bus'
|
||||
|
||||
websmid.customerspecialty.form.customerspecialty_form_handler:
|
||||
public: true
|
||||
class: 'PrestaShop\PrestaShop\Core\Form\IdentifiableObject\Handler\FormHandler'
|
||||
factory:
|
||||
- '@prestashop.core.form.identifiable_object.handler.form_handler_factory'
|
||||
- 'create'
|
||||
arguments:
|
||||
- '@websmid.customerspecialty.form.customerspecialty_data_handler'
|
||||
@@ -0,0 +1,34 @@
|
||||
services:
|
||||
websmid.customerspecialty.grid.definition.factory.customerspecialty_definition_factory:
|
||||
public: true
|
||||
class: 'Websmid\CustomerSpecialty\Grid\Definition\Factory\CustomerSpecialtyGridDefinitionFactory'
|
||||
parent: 'prestashop.core.grid.definition.factory.abstract_grid_definition'
|
||||
arguments:
|
||||
- '@prestashop.adapter.legacy.configuration'
|
||||
|
||||
websmid.customerspecialty.grid.query.customerspecialty_query_builder:
|
||||
public: true
|
||||
class: 'Websmid\CustomerSpecialty\Grid\Query\CustomerSpecialtyQueryBuilder'
|
||||
parent: 'prestashop.core.grid.abstract_query_builder'
|
||||
arguments:
|
||||
- '@prestashop.core.query.doctrine_search_criteria_applicator'
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext().language.id"
|
||||
- '@prestashop.core.grid.query.filter.doctrine_filter_applicator'
|
||||
|
||||
websmid.customerspecialty.grid.data.factory.customerspecialty_data_factory:
|
||||
public: true
|
||||
class: '%prestashop.core.grid.data.factory.doctrine_grid_data_factory%'
|
||||
arguments:
|
||||
- '@websmid.customerspecialty.grid.query.customerspecialty_query_builder'
|
||||
- '@prestashop.core.hook.dispatcher'
|
||||
- '@prestashop.core.grid.query.doctrine_query_parser'
|
||||
- 'CustomerSpecialty'
|
||||
|
||||
websmid.customerspecialty.grid.customerspecialty_grid_factory:
|
||||
public: true
|
||||
class: 'PrestaShop\PrestaShop\Core\Grid\GridFactory'
|
||||
arguments:
|
||||
- '@websmid.customerspecialty.grid.definition.factory.customerspecialty_definition_factory'
|
||||
- '@websmid.customerspecialty.grid.data.factory.customerspecialty_data_factory'
|
||||
- '@prestashop.core.grid.filter.form_factory'
|
||||
- '@prestashop.core.hook.dispatcher'
|
||||
@@ -0,0 +1,4 @@
|
||||
services:
|
||||
websmid.customerspecialty.form.choice_provider.customer_specialty_choice_provider:
|
||||
public: true
|
||||
class: 'Websmid\CustomerSpecialty\Form\ChoiceProvider\CustomerSpecialtyChoiceProvider'
|
||||
@@ -0,0 +1,49 @@
|
||||
customerspecialty_index:
|
||||
path: customerspecialty
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::indexAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
_legacy_link: AdminCustomerSpecialtyOverview:index
|
||||
|
||||
customerspecialty_search:
|
||||
path: customerspecialty
|
||||
methods: [POST]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::searchAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
|
||||
customerspecialty_create:
|
||||
path: customerspecialty/create
|
||||
methods: [POST, GET]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::createAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
|
||||
customerspecialty_edit:
|
||||
path: customerspecialty/edit/{idCustomerSpecialty}
|
||||
methods: [POST, GET]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::editAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
|
||||
customerspecialty_toggle_active:
|
||||
path: customerspecialty/toggle-active/{idCustomerSpecialty}
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::toggleActiveAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
|
||||
customerspecialty_move_up:
|
||||
path: customerspecialty/move-up/{idCustomerSpecialty}
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::moveUpAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
|
||||
customerspecialty_move_down:
|
||||
path: customerspecialty/move-down/{idCustomerSpecialty}
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: 'Websmid\CustomerSpecialty\Controller\CustomerSpecialtyController::moveDownAction'
|
||||
_legacy_controller: AdminCustomerSpecialtyOverview
|
||||
@@ -0,0 +1,13 @@
|
||||
imports:
|
||||
- { resource: form.yml }
|
||||
- { resource: grid.yml }
|
||||
- { resource: command.yml }
|
||||
- { resource: provider.yml }
|
||||
|
||||
services:
|
||||
websmid.customerspecialty.entity.table_prefix_subscriber:
|
||||
class: 'Websmid\CustomerSpecialty\Entity\TablePrefixSubscriber'
|
||||
arguments:
|
||||
- '%database_prefix%'
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber }
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command\CommandHandler;
|
||||
|
||||
use Websmid\CustomerSpecialty\Command\LinkCustomerSpecialtyCommand;
|
||||
use Websmid\CustomerSpecialty\Entity\CustomerSpecialtyCustomer;
|
||||
use DateTime;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
|
||||
final class LinkCustomerSpecialtyCommandHandler
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __invoke(LinkCustomerSpecialtyCommand $command)
|
||||
{
|
||||
return $this->handle($command);
|
||||
}
|
||||
|
||||
public function handle(LinkCustomerSpecialtyCommand $command)
|
||||
{
|
||||
$this->entityManager = SymfonyContainer::getInstance()->get('doctrine.orm.entity_manager');
|
||||
$repository = $this->entityManager->getRepository(CustomerSpecialtyCustomer::class);
|
||||
|
||||
$link = $repository->findOneBy(['idCustomer' => $command->getIdCustomer()]);
|
||||
|
||||
if (!$link) {
|
||||
$link = new CustomerSpecialtyCustomer();
|
||||
$link->setIdCustomer($command->getIdCustomer());
|
||||
$link->setDateAdd(new DateTime());
|
||||
}
|
||||
|
||||
$link->setIdCustomerSpecialty($command->getIdCustomerSpecialty());
|
||||
$link->setDateUpd(new DateTime());
|
||||
|
||||
$this->entityManager->persist($link);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command\CommandHandler;
|
||||
|
||||
use Websmid\CustomerSpecialty\Command\MoveCustomerSpecialtyCommand;
|
||||
use Websmid\CustomerSpecialty\Entity\CustomerSpecialty;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
use PrestaShopException;
|
||||
|
||||
final class MoveCustomerSpecialtyCommandHandler
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __invoke(MoveCustomerSpecialtyCommand $command)
|
||||
{
|
||||
return $this->handle($command);
|
||||
}
|
||||
|
||||
public function handle(MoveCustomerSpecialtyCommand $command)
|
||||
{
|
||||
$this->entityManager = SymfonyContainer::getInstance()->get('doctrine.orm.entity_manager');
|
||||
$repository = $this->entityManager->getRepository(CustomerSpecialty::class);
|
||||
|
||||
$customerSpecialty = $repository->find($command->getIdCustomerSpecialty());
|
||||
|
||||
if (!$customerSpecialty) {
|
||||
throw new PrestaShopException('Customer specialty not found.');
|
||||
}
|
||||
|
||||
$neighbor = $this->findNeighbor($repository, $customerSpecialty, $command->getDirection());
|
||||
|
||||
if (!$neighbor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$currentPosition = $customerSpecialty->getPosition();
|
||||
$customerSpecialty->setPosition($neighbor->getPosition());
|
||||
$neighbor->setPosition($currentPosition);
|
||||
|
||||
$this->entityManager->persist($customerSpecialty);
|
||||
$this->entityManager->persist($neighbor);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function findNeighbor(EntityRepository $repository, CustomerSpecialty $customerSpecialty, string $direction): ?CustomerSpecialty
|
||||
{
|
||||
$queryBuilder = $repository->createQueryBuilder('cs');
|
||||
|
||||
if (MoveCustomerSpecialtyCommand::DIRECTION_UP === $direction) {
|
||||
$queryBuilder
|
||||
->where('cs.position < :position')
|
||||
->orderBy('cs.position', 'DESC');
|
||||
} else {
|
||||
$queryBuilder
|
||||
->where('cs.position > :position')
|
||||
->orderBy('cs.position', 'ASC');
|
||||
}
|
||||
|
||||
return $queryBuilder
|
||||
->setParameter('position', $customerSpecialty->getPosition())
|
||||
->setMaxResults(1)
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command\CommandHandler;
|
||||
|
||||
use Websmid\CustomerSpecialty\Command\SaveCustomerSpecialtyCommand;
|
||||
use Websmid\CustomerSpecialty\Entity\CustomerSpecialty;
|
||||
use Websmid\CustomerSpecialty\Entity\CustomerSpecialtyLang;
|
||||
use DateTime;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
use PrestaShopException;
|
||||
|
||||
final class SaveCustomerSpecialtyCommandHandler
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __invoke(SaveCustomerSpecialtyCommand $command)
|
||||
{
|
||||
return $this->handle($command);
|
||||
}
|
||||
|
||||
public function handle(SaveCustomerSpecialtyCommand $command)
|
||||
{
|
||||
if (empty(array_filter($command->getTitles()))) {
|
||||
throw new PrestaShopException('At least one title is required.');
|
||||
}
|
||||
|
||||
$this->entityManager = SymfonyContainer::getInstance()->get('doctrine.orm.entity_manager');
|
||||
|
||||
$customerSpecialty = $this->findOrCreateCustomerSpecialty($command->getIdCustomerSpecialty());
|
||||
$customerSpecialty->setActive($command->isActive());
|
||||
$this->stampDates($customerSpecialty);
|
||||
|
||||
$this->entityManager->persist($customerSpecialty);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->saveTitles($customerSpecialty->getId(), $command->getTitles());
|
||||
|
||||
return $customerSpecialty->getId();
|
||||
}
|
||||
|
||||
private function findOrCreateCustomerSpecialty(int $idCustomerSpecialty): CustomerSpecialty
|
||||
{
|
||||
if ($idCustomerSpecialty > 0) {
|
||||
$customerSpecialty = $this->entityManager->getRepository(CustomerSpecialty::class)->find($idCustomerSpecialty);
|
||||
|
||||
if ($customerSpecialty) {
|
||||
return $customerSpecialty;
|
||||
}
|
||||
}
|
||||
|
||||
$customerSpecialty = new CustomerSpecialty();
|
||||
$customerSpecialty->setPosition($this->getNextPosition());
|
||||
|
||||
return $customerSpecialty;
|
||||
}
|
||||
|
||||
private function getNextPosition(): int
|
||||
{
|
||||
$result = $this->entityManager->getRepository(CustomerSpecialty::class)
|
||||
->createQueryBuilder('cs')
|
||||
->select('MAX(cs.position) as maxPosition')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
|
||||
return null === $result ? 0 : ((int) $result + 1);
|
||||
}
|
||||
|
||||
private function stampDates(CustomerSpecialty $customerSpecialty): void
|
||||
{
|
||||
$now = new DateTime();
|
||||
|
||||
if (null === $customerSpecialty->getId()) {
|
||||
$customerSpecialty->setDateAdd($now);
|
||||
}
|
||||
|
||||
$customerSpecialty->setDateUpd($now);
|
||||
}
|
||||
|
||||
private function saveTitles(int $idCustomerSpecialty, array $titles): void
|
||||
{
|
||||
$repository = $this->entityManager->getRepository(CustomerSpecialtyLang::class);
|
||||
|
||||
foreach ($repository->findBy(['idCustomerSpecialty' => $idCustomerSpecialty]) as $existingLang) {
|
||||
$this->entityManager->remove($existingLang);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
foreach ($titles as $idLang => $title) {
|
||||
if ('' === trim((string) $title)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lang = new CustomerSpecialtyLang();
|
||||
$lang->setIdCustomerSpecialty($idCustomerSpecialty);
|
||||
$lang->setIdLang((int) $idLang);
|
||||
$lang->setTitle($title);
|
||||
|
||||
$this->entityManager->persist($lang);
|
||||
}
|
||||
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command\CommandHandler;
|
||||
|
||||
use Websmid\CustomerSpecialty\Command\ToggleCustomerSpecialtyActiveCommand;
|
||||
use Websmid\CustomerSpecialty\Entity\CustomerSpecialty;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
use PrestaShopException;
|
||||
|
||||
final class ToggleCustomerSpecialtyActiveCommandHandler
|
||||
{
|
||||
public function __invoke(ToggleCustomerSpecialtyActiveCommand $command)
|
||||
{
|
||||
return $this->handle($command);
|
||||
}
|
||||
|
||||
public function handle(ToggleCustomerSpecialtyActiveCommand $command)
|
||||
{
|
||||
$entityManager = SymfonyContainer::getInstance()->get('doctrine.orm.entity_manager');
|
||||
$repository = $entityManager->getRepository(CustomerSpecialty::class);
|
||||
|
||||
$customerSpecialty = $repository->find($command->getIdCustomerSpecialty());
|
||||
|
||||
if (!$customerSpecialty) {
|
||||
throw new PrestaShopException('Customer specialty not found.');
|
||||
}
|
||||
|
||||
$customerSpecialty->setActive(!$customerSpecialty->isActive());
|
||||
|
||||
$entityManager->persist($customerSpecialty);
|
||||
$entityManager->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command;
|
||||
|
||||
class LinkCustomerSpecialtyCommand
|
||||
{
|
||||
private $idCustomer;
|
||||
private $idCustomerSpecialty;
|
||||
|
||||
public function __construct(int $idCustomer, int $idCustomerSpecialty)
|
||||
{
|
||||
$this->idCustomer = $idCustomer;
|
||||
$this->idCustomerSpecialty = $idCustomerSpecialty;
|
||||
}
|
||||
|
||||
public function getIdCustomer(): int
|
||||
{
|
||||
return $this->idCustomer;
|
||||
}
|
||||
|
||||
public function getIdCustomerSpecialty(): int
|
||||
{
|
||||
return $this->idCustomerSpecialty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command;
|
||||
|
||||
class MoveCustomerSpecialtyCommand
|
||||
{
|
||||
const DIRECTION_UP = 'up';
|
||||
const DIRECTION_DOWN = 'down';
|
||||
|
||||
private $idCustomerSpecialty;
|
||||
private $direction;
|
||||
|
||||
public function __construct(int $idCustomerSpecialty, string $direction)
|
||||
{
|
||||
$this->idCustomerSpecialty = $idCustomerSpecialty;
|
||||
$this->direction = $direction;
|
||||
}
|
||||
|
||||
public function getIdCustomerSpecialty(): int
|
||||
{
|
||||
return $this->idCustomerSpecialty;
|
||||
}
|
||||
|
||||
public function getDirection(): string
|
||||
{
|
||||
return $this->direction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command;
|
||||
|
||||
class SaveCustomerSpecialtyCommand
|
||||
{
|
||||
private $idCustomerSpecialty;
|
||||
private $active;
|
||||
private $titles;
|
||||
|
||||
public function __construct(int $idCustomerSpecialty, bool $active, array $titles)
|
||||
{
|
||||
$this->idCustomerSpecialty = $idCustomerSpecialty;
|
||||
$this->active = $active;
|
||||
$this->titles = $titles;
|
||||
}
|
||||
|
||||
public function getIdCustomerSpecialty(): int
|
||||
{
|
||||
return $this->idCustomerSpecialty;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function getTitles(): array
|
||||
{
|
||||
return $this->titles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Command;
|
||||
|
||||
class ToggleCustomerSpecialtyActiveCommand
|
||||
{
|
||||
private $idCustomerSpecialty;
|
||||
|
||||
public function __construct(int $idCustomerSpecialty)
|
||||
{
|
||||
$this->idCustomerSpecialty = $idCustomerSpecialty;
|
||||
}
|
||||
|
||||
public function getIdCustomerSpecialty(): int
|
||||
{
|
||||
return $this->idCustomerSpecialty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Config;
|
||||
|
||||
class Config
|
||||
{
|
||||
const HOOKS = [
|
||||
'additionalCustomerFormFields',
|
||||
'actionCustomerFormBuilderModifier',
|
||||
'actionAfterCreateCustomerFormHandler',
|
||||
'actionAfterUpdateCustomerFormHandler',
|
||||
'actionObjectCustomerAddAfter',
|
||||
'actionObjectCustomerUpdateAfter',
|
||||
'actionCustomerGridDefinitionModifier',
|
||||
'actionCustomerGridQueryBuilderModifier',
|
||||
'displayAdminCustomers',
|
||||
'actionGetExtraMailTemplateVars',
|
||||
];
|
||||
|
||||
const TABS = [
|
||||
[
|
||||
'class_name' => 'AdminCustomerSpecialtyOverview',
|
||||
'parent_class_name' => 'AdminParentCustomer',
|
||||
'name' => [
|
||||
'en' => 'Customer Specialties',
|
||||
'nl' => 'Klantspecialisaties',
|
||||
],
|
||||
'module' => 'ws_customerspecialty',
|
||||
'icon' => 'work',
|
||||
'route_name' => 'customerspecialty_index',
|
||||
'active' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
const FRONT_ENABLED_CONFIG_KEY = 'WS_CUSTOMERSPECIALTY_FRONT_ENABLED';
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Controller;
|
||||
|
||||
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
|
||||
use PrestaShopBundle\Security\Voter\PageVoter;
|
||||
use Websmid\CustomerSpecialty\Grid\Definition\Factory\CustomerSpecialtyGridDefinitionFactory;
|
||||
use Websmid\CustomerSpecialty\Grid\Filters\CustomerSpecialtyFilters;
|
||||
use Websmid\CustomerSpecialty\Command\MoveCustomerSpecialtyCommand;
|
||||
use Websmid\CustomerSpecialty\Command\ToggleCustomerSpecialtyActiveCommand;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use PrestaShopException;
|
||||
use Exception;
|
||||
|
||||
class CustomerSpecialtyController extends FrameworkBundleAdminController
|
||||
{
|
||||
public function indexAction(Request $request, CustomerSpecialtyFilters $filters): Response
|
||||
{
|
||||
$gridFactory = $this->get('websmid.customerspecialty.grid.customerspecialty_grid_factory');
|
||||
$grid = $gridFactory->getGrid($filters);
|
||||
|
||||
return $this->render('@Modules/ws_customerspecialty/views/templates/admin/grid/customerspecialty.html.twig', [
|
||||
'customerSpecialtyGrid' => $this->presentGrid($grid),
|
||||
'layoutHeaderToolbarBtn' => $this->getToolbarButtons($request->get('_legacy_controller')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function searchAction(Request $request): RedirectResponse
|
||||
{
|
||||
return $this->get('prestashop.bundle.grid.response_builder')->buildSearchResponse(
|
||||
$this->get('websmid.customerspecialty.grid.definition.factory.customerspecialty_definition_factory'),
|
||||
$request,
|
||||
CustomerSpecialtyGridDefinitionFactory::GRID_ID,
|
||||
'customerspecialty_index'
|
||||
);
|
||||
}
|
||||
|
||||
public function createAction(Request $request): Response
|
||||
{
|
||||
$formBuilder = $this->get('websmid.customerspecialty.form.customerspecialty_data_form_builder');
|
||||
$formHandler = $this->get('websmid.customerspecialty.form.customerspecialty_form_handler');
|
||||
|
||||
$form = $formBuilder->getForm();
|
||||
$form->handleRequest($request);
|
||||
|
||||
try {
|
||||
$handlerResult = $formHandler->handle($form);
|
||||
|
||||
if (null !== $handlerResult->getIdentifiableObjectId()) {
|
||||
$this->addFlash('success', $this->trans('Successful creation', [], 'Admin.Notifications.Success'));
|
||||
|
||||
return $this->redirectToRoute('customerspecialty_index');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages()));
|
||||
}
|
||||
|
||||
return $this->render('@Modules/ws_customerspecialty/views/templates/admin/form/customerspecialty.html.twig', [
|
||||
'customerSpecialtyForm' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function editAction(Request $request, int $idCustomerSpecialty): Response
|
||||
{
|
||||
$formBuilder = $this->get('websmid.customerspecialty.form.customerspecialty_data_form_builder');
|
||||
$formHandler = $this->get('websmid.customerspecialty.form.customerspecialty_form_handler');
|
||||
|
||||
$form = $formBuilder->getFormFor($idCustomerSpecialty);
|
||||
|
||||
try {
|
||||
$form->handleRequest($request);
|
||||
$handlerResult = $formHandler->handleFor($idCustomerSpecialty, $form);
|
||||
|
||||
if ($handlerResult->isSubmitted() && $handlerResult->isValid()) {
|
||||
$this->addFlash('success', $this->trans('Successful update', [], 'Admin.Notifications.Success'));
|
||||
|
||||
return $this->redirectToRoute('customerspecialty_index');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages()));
|
||||
}
|
||||
|
||||
return $this->render('@Modules/ws_customerspecialty/views/templates/admin/form/customerspecialty.html.twig', [
|
||||
'customerSpecialtyForm' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function toggleActiveAction(int $idCustomerSpecialty): RedirectResponse
|
||||
{
|
||||
try {
|
||||
$this->getCommandBus()->handle(new ToggleCustomerSpecialtyActiveCommand($idCustomerSpecialty));
|
||||
$this->addFlash('success', $this->trans('Successful update', [], 'Admin.Notifications.Success'));
|
||||
} catch (Exception $e) {
|
||||
$this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages()));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('customerspecialty_index');
|
||||
}
|
||||
|
||||
public function moveUpAction(int $idCustomerSpecialty): RedirectResponse
|
||||
{
|
||||
return $this->move($idCustomerSpecialty, MoveCustomerSpecialtyCommand::DIRECTION_UP);
|
||||
}
|
||||
|
||||
public function moveDownAction(int $idCustomerSpecialty): RedirectResponse
|
||||
{
|
||||
return $this->move($idCustomerSpecialty, MoveCustomerSpecialtyCommand::DIRECTION_DOWN);
|
||||
}
|
||||
|
||||
private function move(int $idCustomerSpecialty, string $direction): RedirectResponse
|
||||
{
|
||||
try {
|
||||
$this->getCommandBus()->handle(new MoveCustomerSpecialtyCommand($idCustomerSpecialty, $direction));
|
||||
} catch (Exception $e) {
|
||||
$this->addFlash('error', $this->getErrorMessageForException($e, $this->getErrorMessages()));
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('customerspecialty_index');
|
||||
}
|
||||
|
||||
private function getToolbarButtons(string $securitySubject): array
|
||||
{
|
||||
$toolbarButtons = [];
|
||||
|
||||
if (!$this->isGranted(PageVoter::CREATE, $securitySubject)) {
|
||||
return $toolbarButtons;
|
||||
}
|
||||
|
||||
$toolbarButtons['customerspecialty_create'] = [
|
||||
'href' => $this->generateUrl('customerspecialty_create'),
|
||||
'desc' => $this->trans('New specialty', [], 'Admin.Actions'),
|
||||
'icon' => 'add_circle_outline',
|
||||
'class' => 'btn-primary',
|
||||
'floating_class' => '',
|
||||
];
|
||||
|
||||
return $toolbarButtons;
|
||||
}
|
||||
|
||||
private function getErrorMessages(): array
|
||||
{
|
||||
return [
|
||||
PrestaShopException::class => [
|
||||
'key' => 'An error occurred while saving the customer specialty.',
|
||||
'domain' => 'Modules.Wscustomerspecialty.Admin',
|
||||
'parameters' => [],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getCommandBus()
|
||||
{
|
||||
return $this->get('prestashop.core.command_bus');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Entity;
|
||||
|
||||
use DateTime;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="ws_customerspecialty")
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class CustomerSpecialty
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id_ws_customerspecialty", type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="position", type="integer")
|
||||
*/
|
||||
private $position;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="active", type="boolean")
|
||||
*/
|
||||
private $active;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="date_add", type="datetime")
|
||||
*/
|
||||
private $dateAdd;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="date_upd", type="datetime")
|
||||
*/
|
||||
private $dateUpd;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPosition(): int
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function setPosition(int $position): self
|
||||
{
|
||||
$this->position = $position;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): self
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateAdd(): ?DateTime
|
||||
{
|
||||
return $this->dateAdd;
|
||||
}
|
||||
|
||||
public function setDateAdd(DateTime $dateAdd): self
|
||||
{
|
||||
$this->dateAdd = $dateAdd;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateUpd(): ?DateTime
|
||||
{
|
||||
return $this->dateUpd;
|
||||
}
|
||||
|
||||
public function setDateUpd(DateTime $dateUpd): self
|
||||
{
|
||||
$this->dateUpd = $dateUpd;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id_ws_customerspecialty' => $this->id,
|
||||
'position' => $this->position,
|
||||
'active' => $this->active,
|
||||
'date_add' => $this->dateAdd ? $this->dateAdd->format('Y-m-d H:i:s') : null,
|
||||
'date_upd' => $this->dateUpd ? $this->dateUpd->format('Y-m-d H:i:s') : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Entity;
|
||||
|
||||
use DateTime;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="ws_customerspecialty_customer")
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class CustomerSpecialtyCustomer
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id_ws_customerspecialty_customer", type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="id_customer", type="integer", unique=true)
|
||||
*/
|
||||
private $idCustomer;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="id_ws_customerspecialty", type="integer")
|
||||
*/
|
||||
private $idCustomerSpecialty;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="date_add", type="datetime")
|
||||
*/
|
||||
private $dateAdd;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="date_upd", type="datetime")
|
||||
*/
|
||||
private $dateUpd;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getIdCustomer(): int
|
||||
{
|
||||
return $this->idCustomer;
|
||||
}
|
||||
|
||||
public function setIdCustomer(int $idCustomer): self
|
||||
{
|
||||
$this->idCustomer = $idCustomer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIdCustomerSpecialty(): int
|
||||
{
|
||||
return $this->idCustomerSpecialty;
|
||||
}
|
||||
|
||||
public function setIdCustomerSpecialty(int $idCustomerSpecialty): self
|
||||
{
|
||||
$this->idCustomerSpecialty = $idCustomerSpecialty;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateAdd(): ?DateTime
|
||||
{
|
||||
return $this->dateAdd;
|
||||
}
|
||||
|
||||
public function setDateAdd(DateTime $dateAdd): self
|
||||
{
|
||||
$this->dateAdd = $dateAdd;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateUpd(): ?DateTime
|
||||
{
|
||||
return $this->dateUpd;
|
||||
}
|
||||
|
||||
public function setDateUpd(DateTime $dateUpd): self
|
||||
{
|
||||
$this->dateUpd = $dateUpd;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id_ws_customerspecialty_customer' => $this->id,
|
||||
'id_customer' => $this->idCustomer,
|
||||
'id_ws_customerspecialty' => $this->idCustomerSpecialty,
|
||||
'date_add' => $this->dateAdd ? $this->dateAdd->format('Y-m-d H:i:s') : null,
|
||||
'date_upd' => $this->dateUpd ? $this->dateUpd->format('Y-m-d H:i:s') : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="ws_customerspecialty_lang")
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class CustomerSpecialtyLang
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(name="id_ws_customerspecialty_lang", type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="id_ws_customerspecialty", type="integer")
|
||||
*/
|
||||
private $idCustomerSpecialty;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="id_lang", type="integer")
|
||||
*/
|
||||
private $idLang;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="title", type="string", length=255)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getIdCustomerSpecialty(): int
|
||||
{
|
||||
return $this->idCustomerSpecialty;
|
||||
}
|
||||
|
||||
public function setIdCustomerSpecialty(int $idCustomerSpecialty): self
|
||||
{
|
||||
$this->idCustomerSpecialty = $idCustomerSpecialty;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIdLang(): int
|
||||
{
|
||||
return $this->idLang;
|
||||
}
|
||||
|
||||
public function setIdLang(int $idLang): self
|
||||
{
|
||||
$this->idLang = $idLang;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): self
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id_ws_customerspecialty_lang' => $this->id,
|
||||
'id_ws_customerspecialty' => $this->idCustomerSpecialty,
|
||||
'id_lang' => $this->idLang,
|
||||
'title' => $this->title,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Entity;
|
||||
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
|
||||
use Doctrine\ORM\Events;
|
||||
|
||||
class TablePrefixSubscriber implements EventSubscriber
|
||||
{
|
||||
private $prefix;
|
||||
|
||||
public function __construct(string $prefix)
|
||||
{
|
||||
$this->prefix = $prefix ?: 'ps_';
|
||||
}
|
||||
|
||||
public function getSubscribedEvents(): array
|
||||
{
|
||||
return [Events::loadClassMetadata];
|
||||
}
|
||||
|
||||
public function loadClassMetadata(LoadClassMetadataEventArgs $args): void
|
||||
{
|
||||
$classMetadata = $args->getClassMetadata();
|
||||
|
||||
if (strpos($classMetadata->getName(), 'Websmid\\CustomerSpecialty\\') !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tableName = $classMetadata->getTableName();
|
||||
|
||||
if (strpos($tableName, $this->prefix) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classMetadata->setPrimaryTable(['name' => $this->prefix . $tableName]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Form\ChoiceProvider;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Form\ConfigurableFormChoiceProviderInterface;
|
||||
use PrestaShop\PrestaShop\Core\Form\FormChoiceAttributeProviderInterface;
|
||||
use PrestaShop\PrestaShop\Core\Form\FormChoiceProviderInterface;
|
||||
use Context;
|
||||
use Db;
|
||||
|
||||
final class CustomerSpecialtyChoiceProvider implements
|
||||
FormChoiceProviderInterface,
|
||||
FormChoiceAttributeProviderInterface,
|
||||
ConfigurableFormChoiceProviderInterface
|
||||
{
|
||||
public function getChoices(array $options = []): array
|
||||
{
|
||||
$includeInactive = $options['include_inactive'] ?? false;
|
||||
$idLang = (int) Context::getContext()->language->id;
|
||||
|
||||
$sql = '
|
||||
SELECT cs.`id_ws_customerspecialty`, csl.`title`
|
||||
FROM `' . _DB_PREFIX_ . 'ws_customerspecialty` cs
|
||||
INNER JOIN `' . _DB_PREFIX_ . 'ws_customerspecialty_lang` csl
|
||||
ON csl.`id_ws_customerspecialty` = cs.`id_ws_customerspecialty`
|
||||
AND csl.`id_lang` = ' . $idLang . '
|
||||
' . ($includeInactive ? '' : 'WHERE cs.`active` = 1') . '
|
||||
ORDER BY cs.`position` ASC
|
||||
';
|
||||
|
||||
$rows = Db::getInstance()->executeS($sql);
|
||||
$choices = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$choices[$row['title']] = (int) $row['id_ws_customerspecialty'];
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
public function getChoicesAttributes(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Form\DataHandler;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Form\FormDataHandlerInterface;
|
||||
use Websmid\CustomerSpecialty\Command\SaveCustomerSpecialtyCommand;
|
||||
use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface;
|
||||
|
||||
final class CustomerSpecialtyDataHandler implements FormDataHandlerInterface
|
||||
{
|
||||
private $commandBus;
|
||||
|
||||
public function __construct(CommandBusInterface $commandBus)
|
||||
{
|
||||
$this->commandBus = $commandBus;
|
||||
}
|
||||
|
||||
public function create(array $data)
|
||||
{
|
||||
return $this->save(0, $data);
|
||||
}
|
||||
|
||||
public function update($id, array $data)
|
||||
{
|
||||
return $this->save((int) $id, $data);
|
||||
}
|
||||
|
||||
private function save(int $id, array $data)
|
||||
{
|
||||
return $this->commandBus->handle(new SaveCustomerSpecialtyCommand(
|
||||
$id,
|
||||
(bool) ($data['active'] ?? false),
|
||||
$data['title'] ?? []
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Form\DataProvider;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Form\FormDataProviderInterface;
|
||||
use Websmid\CustomerSpecialty\Entity\CustomerSpecialty;
|
||||
use Websmid\CustomerSpecialty\Entity\CustomerSpecialtyLang;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
|
||||
final class CustomerSpecialtyDataProvider implements FormDataProviderInterface
|
||||
{
|
||||
public function getDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'title' => [],
|
||||
'active' => true,
|
||||
];
|
||||
}
|
||||
|
||||
public function getData($id): array
|
||||
{
|
||||
$entityManager = SymfonyContainer::getInstance()->get('doctrine.orm.entity_manager');
|
||||
|
||||
$customerSpecialty = $entityManager->getRepository(CustomerSpecialty::class)->find((int) $id);
|
||||
|
||||
if (!$customerSpecialty) {
|
||||
return $this->getDefaultData();
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => $this->loadTitles($entityManager, (int) $id),
|
||||
'active' => $customerSpecialty->isActive(),
|
||||
];
|
||||
}
|
||||
|
||||
private function loadTitles(EntityManagerInterface $entityManager, int $idCustomerSpecialty): array
|
||||
{
|
||||
$titles = [];
|
||||
|
||||
foreach ($entityManager->getRepository(CustomerSpecialtyLang::class)->findBy(['idCustomerSpecialty' => $idCustomerSpecialty]) as $lang) {
|
||||
$titles[$lang->getIdLang()] = $lang->getTitle();
|
||||
}
|
||||
|
||||
return $titles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use PrestaShopBundle\Form\Admin\Type\SwitchType;
|
||||
use PrestaShopBundle\Form\Admin\Type\TranslatableType;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class CustomerSpecialtyType extends AbstractType
|
||||
{
|
||||
private $translator;
|
||||
private $locales;
|
||||
|
||||
public function __construct(TranslatorInterface $translator, array $locales)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $formBuilder, array $options): void
|
||||
{
|
||||
$formBuilder
|
||||
->add('title', TranslatableType::class, [
|
||||
'type' => TextType::class,
|
||||
'label' => $this->trans('Title'),
|
||||
'required' => true,
|
||||
])
|
||||
->add('active', SwitchType::class, [
|
||||
'label' => $this->trans('Active'),
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
private function trans(string $label): string
|
||||
{
|
||||
return $this->translator->trans($label, [], 'Modules.Wscustomerspecialty.Admin');
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Grid\Definition\Factory;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\AbstractGridDefinitionFactory;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\ColumnCollection;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\DataColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ToggleColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ActionColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Action\Row\RowActionCollection;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Action\Row\Type\LinkRowAction;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Action\GridActionCollection;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Action\Type\SimpleGridAction;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Filter\Filter;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Filter\FilterCollection;
|
||||
use PrestaShopBundle\Form\Admin\Type\SearchAndResetType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
|
||||
class CustomerSpecialtyGridDefinitionFactory extends AbstractGridDefinitionFactory
|
||||
{
|
||||
const GRID_ID = 'customerspecialty';
|
||||
|
||||
protected function getId()
|
||||
{
|
||||
return self::GRID_ID;
|
||||
}
|
||||
|
||||
protected function getName()
|
||||
{
|
||||
return $this->trans('Customer Specialties', [], 'Modules.Wscustomerspecialty.Admin');
|
||||
}
|
||||
|
||||
protected function getColumns()
|
||||
{
|
||||
return (new ColumnCollection())
|
||||
->add(
|
||||
(new DataColumn('id_ws_customerspecialty'))
|
||||
->setName($this->trans('ID', [], 'Admin.Global'))
|
||||
->setOptions([
|
||||
'field' => 'id_ws_customerspecialty',
|
||||
])
|
||||
)
|
||||
->add(
|
||||
(new DataColumn('title'))
|
||||
->setName($this->trans('Title', [], 'Modules.Wscustomerspecialty.Admin'))
|
||||
->setOptions([
|
||||
'field' => 'title',
|
||||
])
|
||||
)
|
||||
->add(
|
||||
(new ToggleColumn('active'))
|
||||
->setName($this->trans('Active', [], 'Admin.Global'))
|
||||
->setOptions([
|
||||
'field' => 'active',
|
||||
'primary_field' => 'id_ws_customerspecialty',
|
||||
'route' => 'customerspecialty_toggle_active',
|
||||
'route_param_name' => 'idCustomerSpecialty',
|
||||
])
|
||||
)
|
||||
->add(
|
||||
(new ActionColumn('actions'))
|
||||
->setName($this->trans('Actions', [], 'Admin.Global'))
|
||||
->setOptions([
|
||||
'actions' => (new RowActionCollection())
|
||||
->add(
|
||||
(new LinkRowAction('edit'))
|
||||
->setName($this->trans('Edit', [], 'Admin.Actions'))
|
||||
->setIcon('edit')
|
||||
->setOptions([
|
||||
'route' => 'customerspecialty_edit',
|
||||
'route_param_name' => 'idCustomerSpecialty',
|
||||
])
|
||||
)
|
||||
->add(
|
||||
(new LinkRowAction('move_up'))
|
||||
->setName($this->trans('Move up', [], 'Modules.Wscustomerspecialty.Admin'))
|
||||
->setIcon('arrow_upward')
|
||||
->setOptions([
|
||||
'route' => 'customerspecialty_move_up',
|
||||
'route_param_name' => 'idCustomerSpecialty',
|
||||
])
|
||||
)
|
||||
->add(
|
||||
(new LinkRowAction('move_down'))
|
||||
->setName($this->trans('Move down', [], 'Modules.Wscustomerspecialty.Admin'))
|
||||
->setIcon('arrow_downward')
|
||||
->setOptions([
|
||||
'route' => 'customerspecialty_move_down',
|
||||
'route_param_name' => 'idCustomerSpecialty',
|
||||
])
|
||||
),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
protected function getFilters()
|
||||
{
|
||||
return (new FilterCollection())
|
||||
->add(
|
||||
(new Filter('title', TextType::class))
|
||||
->setAssociatedColumn('title')
|
||||
)
|
||||
->add(
|
||||
(new Filter('active', ChoiceType::class))
|
||||
->setTypeOptions([
|
||||
'choices' => [
|
||||
$this->trans('Active', [], 'Admin.Global') => 1,
|
||||
$this->trans('Inactive', [], 'Admin.Global') => 0,
|
||||
],
|
||||
'required' => false,
|
||||
])
|
||||
->setAssociatedColumn('active')
|
||||
)
|
||||
->add(
|
||||
(new Filter('actions', SearchAndResetType::class))
|
||||
->setTypeOptions([
|
||||
'reset_route' => 'customerspecialty_index',
|
||||
'redirect_route' => 'customerspecialty_index',
|
||||
])
|
||||
->setAssociatedColumn('actions')
|
||||
);
|
||||
}
|
||||
|
||||
protected function getGridActions()
|
||||
{
|
||||
return (new GridActionCollection())
|
||||
->add(
|
||||
(new SimpleGridAction('common_refresh_list'))
|
||||
->setName($this->trans('Refresh list', [], 'Admin.Catalog.Feature'))
|
||||
->setIcon('refresh')
|
||||
)
|
||||
->add(
|
||||
(new SimpleGridAction('common_show_query'))
|
||||
->setName($this->trans('Show SQL query', [], 'Admin.Catalog.Feature'))
|
||||
->setIcon('code')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Grid\Filters;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Grid\Filter\Filters;
|
||||
use Websmid\CustomerSpecialty\Grid\Definition\Factory\CustomerSpecialtyGridDefinitionFactory;
|
||||
|
||||
class CustomerSpecialtyFilters extends Filters
|
||||
{
|
||||
protected $filterId = CustomerSpecialtyGridDefinitionFactory::GRID_ID;
|
||||
|
||||
public static function getDefaults()
|
||||
{
|
||||
return [
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
'orderBy' => 'cs.position',
|
||||
'sortOrder' => 'asc',
|
||||
'filters' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Grid\Query;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Grid\Query\AbstractDoctrineQueryBuilder;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Search\SearchCriteriaInterface;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Query\QueryBuilder;
|
||||
|
||||
class CustomerSpecialtyQueryBuilder extends AbstractDoctrineQueryBuilder
|
||||
{
|
||||
private $searchCriteriaApplicator;
|
||||
private $contextLanguageId;
|
||||
private $filterApplicator;
|
||||
|
||||
public function __construct(
|
||||
Connection $connection,
|
||||
$dbPrefix,
|
||||
$searchCriteriaApplicator,
|
||||
int $contextLanguageId,
|
||||
$filterApplicator
|
||||
) {
|
||||
parent::__construct($connection, $dbPrefix);
|
||||
|
||||
$this->searchCriteriaApplicator = $searchCriteriaApplicator;
|
||||
$this->contextLanguageId = $contextLanguageId;
|
||||
$this->filterApplicator = $filterApplicator;
|
||||
}
|
||||
|
||||
public function getSearchQueryBuilder(SearchCriteriaInterface $searchCriteria)
|
||||
{
|
||||
$queryBuilder = $this->getBaseQuery();
|
||||
$queryBuilder->select('cs.id_ws_customerspecialty, cs.active, cs.position, csl.title');
|
||||
|
||||
$this->applyFilters($queryBuilder, $searchCriteria);
|
||||
|
||||
$this->searchCriteriaApplicator
|
||||
->applyPagination($searchCriteria, $queryBuilder)
|
||||
->applySorting($searchCriteria, $queryBuilder);
|
||||
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
public function getCountQueryBuilder(SearchCriteriaInterface $searchCriteria)
|
||||
{
|
||||
$queryBuilder = $this->getBaseQuery();
|
||||
$queryBuilder->select('COUNT(cs.id_ws_customerspecialty)');
|
||||
|
||||
$this->applyFilters($queryBuilder, $searchCriteria);
|
||||
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
private function getBaseQuery(): QueryBuilder
|
||||
{
|
||||
$queryBuilder = $this->connection->createQueryBuilder();
|
||||
|
||||
$queryBuilder
|
||||
->from($this->dbPrefix . 'ws_customerspecialty', 'cs')
|
||||
->leftJoin(
|
||||
'cs',
|
||||
$this->dbPrefix . 'ws_customerspecialty_lang',
|
||||
'csl',
|
||||
'csl.id_ws_customerspecialty = cs.id_ws_customerspecialty AND csl.id_lang = :contextLanguageId'
|
||||
)
|
||||
->setParameter('contextLanguageId', $this->contextLanguageId);
|
||||
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
private function applyFilters(QueryBuilder $queryBuilder, SearchCriteriaInterface $searchCriteria): void
|
||||
{
|
||||
foreach ($searchCriteria->getFilters() as $filterName => $filterValue) {
|
||||
if ('' === $filterValue || null === $filterValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($filterName) {
|
||||
case 'title':
|
||||
$queryBuilder->andWhere('csl.title LIKE :title')
|
||||
->setParameter('title', '%' . pSQL($filterValue) . '%');
|
||||
break;
|
||||
case 'active':
|
||||
$queryBuilder->andWhere('cs.active = :active')
|
||||
->setParameter('active', (int) $filterValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Module;
|
||||
|
||||
use Websmid\CustomerSpecialty\Config\Config;
|
||||
use Websmid\CustomerSpecialty\Command\LinkCustomerSpecialtyCommand;
|
||||
use Db;
|
||||
use Configuration;
|
||||
use Tools;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\DataColumn;
|
||||
use PrestaShop\PrestaShop\Core\Grid\Filter\Filter;
|
||||
|
||||
trait AdminHooks
|
||||
{
|
||||
public function isUsingNewTranslationSystem(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
$confirmation = '';
|
||||
|
||||
if (Tools::isSubmit('submitWsCustomerSpecialty')) {
|
||||
Configuration::updateValue(Config::FRONT_ENABLED_CONFIG_KEY, (bool) Tools::getValue('front_enabled'));
|
||||
$confirmation = $this->displayConfirmation($this->trans('Settings saved.', [], 'Admin.Notifications.Success'));
|
||||
}
|
||||
|
||||
$this->context->smarty->assign([
|
||||
'front_enabled' => (bool) Configuration::get(Config::FRONT_ENABLED_CONFIG_KEY),
|
||||
'form_action' => $this->context->link->getAdminLink('AdminModules') . '&configure=' . $this->name,
|
||||
'confirmation' => $confirmation,
|
||||
]);
|
||||
|
||||
return $this->fetch('module:ws_customerspecialty/views/templates/admin/configuration.tpl');
|
||||
}
|
||||
|
||||
public function hookActionCustomerFormBuilderModifier(array $params): void
|
||||
{
|
||||
/** @var FormBuilderInterface $formBuilder */
|
||||
$formBuilder = $params['form_builder'];
|
||||
$idCustomer = (int) $params['id'];
|
||||
|
||||
$formBuilder->add('specialization', ChoiceType::class, [
|
||||
'label' => $this->trans('Specialty', [], 'Modules.Wscustomerspecialty.Admin'),
|
||||
'required' => true,
|
||||
'placeholder' => $this->trans('Please select', [], 'Admin.Global'),
|
||||
'choices' => $this->getCustomerSpecialtyChoiceProvider()->getChoices([
|
||||
'include_inactive' => $idCustomer > 0,
|
||||
]),
|
||||
]);
|
||||
|
||||
if ($idCustomer > 0) {
|
||||
$params['data']['specialization'] = $this->loadCustomerSpecialtyId($idCustomer);
|
||||
$formBuilder->setData($params['data']);
|
||||
}
|
||||
}
|
||||
|
||||
public function hookActionAfterCreateCustomerFormHandler(array $params): void
|
||||
{
|
||||
$this->persistBackOfficeCustomerSpecialty($params);
|
||||
}
|
||||
|
||||
public function hookActionAfterUpdateCustomerFormHandler(array $params): void
|
||||
{
|
||||
$this->persistBackOfficeCustomerSpecialty($params);
|
||||
}
|
||||
|
||||
public function hookActionCustomerGridDefinitionModifier(array $params): void
|
||||
{
|
||||
$definition = $params['definition'];
|
||||
|
||||
$definition->getColumns()->addBefore(
|
||||
'social_title',
|
||||
(new DataColumn('specialization'))
|
||||
->setName($this->trans('Specialty', [], 'Modules.Wscustomerspecialty.Admin'))
|
||||
->setOptions([
|
||||
'field' => 'specialization',
|
||||
])
|
||||
);
|
||||
|
||||
$choices = ['--' => 0] + $this->getCustomerSpecialtyChoiceProvider()->getChoices(['include_inactive' => true]);
|
||||
|
||||
$definition->getFilters()->add(
|
||||
(new Filter('specialization', ChoiceType::class))
|
||||
->setTypeOptions([
|
||||
'choices' => $choices,
|
||||
'required' => false,
|
||||
])
|
||||
->setAssociatedColumn('specialization')
|
||||
);
|
||||
}
|
||||
|
||||
public function hookActionCustomerGridQueryBuilderModifier(array $params): void
|
||||
{
|
||||
$searchQueryBuilder = $params['search_query_builder'];
|
||||
$searchCriteria = $params['search_criteria'];
|
||||
$idLang = (int) $this->context->language->id;
|
||||
|
||||
$searchQueryBuilder->addSelect(
|
||||
'(SELECT csl.title
|
||||
FROM ' . _DB_PREFIX_ . 'ws_customerspecialty_customer cscu
|
||||
INNER JOIN ' . _DB_PREFIX_ . 'ws_customerspecialty_lang csl
|
||||
ON csl.id_ws_customerspecialty = cscu.id_ws_customerspecialty AND csl.id_lang = ' . $idLang . '
|
||||
WHERE cscu.id_customer = c.id_customer
|
||||
) as specialization'
|
||||
);
|
||||
|
||||
$this->applyCustomerSpecialtySorting($searchCriteria, $searchQueryBuilder);
|
||||
$this->applyCustomerSpecialtyFilter($searchCriteria, $searchQueryBuilder);
|
||||
}
|
||||
|
||||
public function hookDisplayAdminCustomers(array $params)
|
||||
{
|
||||
$title = $this->loadCustomerSpecialtyTitle((int) $params['id_customer']);
|
||||
|
||||
if (null === $title) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$this->context->smarty->assign(['specialization' => $title]);
|
||||
|
||||
return $this->fetch('module:ws_customerspecialty/views/templates/hook/display_admin_customers.tpl');
|
||||
}
|
||||
|
||||
private function persistBackOfficeCustomerSpecialty(array $params): void
|
||||
{
|
||||
$idCustomer = (int) $params['id'];
|
||||
$specialization = (int) ($params['form_data']['specialization'] ?? 0);
|
||||
|
||||
if ($idCustomer <= 0 || $specialization <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->get('prestashop.core.command_bus')->handle(
|
||||
new LinkCustomerSpecialtyCommand($idCustomer, $specialization)
|
||||
);
|
||||
}
|
||||
|
||||
private function applyCustomerSpecialtySorting($searchCriteria, $searchQueryBuilder): void
|
||||
{
|
||||
if ('specialization' !== $searchCriteria->getOrderBy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$searchQueryBuilder->orderBy('specialization', $searchCriteria->getOrderWay());
|
||||
}
|
||||
|
||||
private function applyCustomerSpecialtyFilter($searchCriteria, $searchQueryBuilder): void
|
||||
{
|
||||
$filters = $searchCriteria->getFilters();
|
||||
|
||||
if (!isset($filters['specialization']) || '' === $filters['specialization']) {
|
||||
return;
|
||||
}
|
||||
|
||||
$idCustomerSpecialty = (int) $filters['specialization'];
|
||||
|
||||
if (0 === $idCustomerSpecialty) {
|
||||
$searchQueryBuilder->andWhere(
|
||||
'NOT EXISTS (SELECT 1 FROM ' . _DB_PREFIX_ . 'ws_customerspecialty_customer cscu WHERE cscu.id_customer = c.id_customer)'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$searchQueryBuilder
|
||||
->andWhere(
|
||||
'EXISTS (
|
||||
SELECT 1 FROM ' . _DB_PREFIX_ . 'ws_customerspecialty_customer cscu
|
||||
WHERE cscu.id_customer = c.id_customer AND cscu.id_ws_customerspecialty = :idCustomerSpecialtyFilter
|
||||
)'
|
||||
)
|
||||
->setParameter('idCustomerSpecialtyFilter', $idCustomerSpecialty);
|
||||
}
|
||||
|
||||
private function loadCustomerSpecialtyId(int $idCustomer): int
|
||||
{
|
||||
return (int) Db::getInstance()->getValue(
|
||||
'SELECT `id_ws_customerspecialty` FROM `' . _DB_PREFIX_ . 'ws_customerspecialty_customer`
|
||||
WHERE `id_customer` = ' . $idCustomer
|
||||
);
|
||||
}
|
||||
|
||||
private function loadCustomerSpecialtyTitle(int $idCustomer): ?string
|
||||
{
|
||||
$idLang = (int) $this->context->language->id;
|
||||
|
||||
$title = Db::getInstance()->getValue('
|
||||
SELECT csl.title
|
||||
FROM `' . _DB_PREFIX_ . 'ws_customerspecialty_customer` cscu
|
||||
INNER JOIN `' . _DB_PREFIX_ . 'ws_customerspecialty_lang` csl
|
||||
ON csl.id_ws_customerspecialty = cscu.id_ws_customerspecialty AND csl.id_lang = ' . $idLang . '
|
||||
WHERE cscu.id_customer = ' . $idCustomer . '
|
||||
');
|
||||
|
||||
return false !== $title && null !== $title ? (string) $title : null;
|
||||
}
|
||||
|
||||
private function getCustomerSpecialtyChoiceProvider()
|
||||
{
|
||||
return $this->get('websmid.customerspecialty.form.choice_provider.customer_specialty_choice_provider');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Module;
|
||||
|
||||
use Websmid\CustomerSpecialty\Config\Config;
|
||||
use Websmid\CustomerSpecialty\Command\LinkCustomerSpecialtyCommand;
|
||||
use Configuration;
|
||||
use Tools;
|
||||
use Db;
|
||||
use PrestaShop\PrestaShop\Core\Form\FormField;
|
||||
|
||||
trait DisplayHooks
|
||||
{
|
||||
public function hookAdditionalCustomerFormFields(array $params)
|
||||
{
|
||||
if (!$this->isFrontEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$choices = $this->get('websmid.customerspecialty.form.choice_provider.customer_specialty_choice_provider')->getChoices();
|
||||
|
||||
if (empty($choices)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$specializationField = (new FormField())
|
||||
->setName('specialization')
|
||||
->setType('select')
|
||||
->setLabel($this->trans('Specialty', [], 'Modules.Wscustomerspecialty.Shop'))
|
||||
->setRequired(true)
|
||||
->setAvailableValues($choices);
|
||||
|
||||
if (isset($params['fields'])) {
|
||||
$this->insertFieldAfterEmail($params['fields'], $specializationField);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return $specializationField;
|
||||
}
|
||||
|
||||
public function hookActionObjectCustomerAddAfter(array $params): void
|
||||
{
|
||||
$this->persistFrontOfficeCustomerSpecialty($params);
|
||||
}
|
||||
|
||||
public function hookActionObjectCustomerUpdateAfter(array $params): void
|
||||
{
|
||||
$this->persistFrontOfficeCustomerSpecialty($params);
|
||||
}
|
||||
|
||||
public function hookActionGetExtraMailTemplateVars(array $params): void
|
||||
{
|
||||
$specialization = $this->loadOrderCustomerSpecialization((int) $params['template_vars']['{id_order}']);
|
||||
|
||||
if (null === $specialization) {
|
||||
return;
|
||||
}
|
||||
|
||||
$params['extra_template_vars']['{specialization}'] = $specialization;
|
||||
}
|
||||
|
||||
private function insertFieldAfterEmail(array &$fields, FormField $field): void
|
||||
{
|
||||
$keys = array_keys($fields);
|
||||
$emailPosition = array_search('email', $keys, true);
|
||||
$insertAt = false === $emailPosition ? count($fields) : $emailPosition + 1;
|
||||
|
||||
$before = array_slice($fields, 0, $insertAt, true);
|
||||
$after = array_slice($fields, $insertAt, null, true);
|
||||
|
||||
$fields = $before + ['specialization' => $field] + $after;
|
||||
}
|
||||
|
||||
private function persistFrontOfficeCustomerSpecialty(array $params): void
|
||||
{
|
||||
if (!$this->isFrontEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$customer = $params['object'];
|
||||
$idCustomer = (int) $customer->id;
|
||||
$specialization = (int) Tools::getValue('specialization');
|
||||
|
||||
if ($idCustomer <= 0 || $specialization <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->get('prestashop.core.command_bus')->handle(
|
||||
new LinkCustomerSpecialtyCommand($idCustomer, $specialization)
|
||||
);
|
||||
}
|
||||
|
||||
private function loadOrderCustomerSpecialization(int $idOrder): ?string
|
||||
{
|
||||
$idCustomer = (int) Db::getInstance()->getValue(
|
||||
'SELECT `id_customer` FROM `' . _DB_PREFIX_ . 'orders` WHERE `id_order` = ' . $idOrder
|
||||
);
|
||||
|
||||
if (!$idCustomer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$idLang = (int) $this->context->language->id;
|
||||
|
||||
$title = Db::getInstance()->getValue('
|
||||
SELECT csl.title
|
||||
FROM `' . _DB_PREFIX_ . 'ws_customerspecialty_customer` cscu
|
||||
INNER JOIN `' . _DB_PREFIX_ . 'ws_customerspecialty_lang` csl
|
||||
ON csl.id_ws_customerspecialty = cscu.id_ws_customerspecialty AND csl.id_lang = ' . $idLang . '
|
||||
WHERE cscu.id_customer = ' . $idCustomer . '
|
||||
');
|
||||
|
||||
return false !== $title && null !== $title ? (string) $title : null;
|
||||
}
|
||||
|
||||
private function isFrontEnabled(): bool
|
||||
{
|
||||
return (bool) Configuration::get(Config::FRONT_ENABLED_CONFIG_KEY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Websmid\CustomerSpecialty\Module;
|
||||
|
||||
use Websmid\CustomerSpecialty\Config\Config;
|
||||
use Db;
|
||||
use Language;
|
||||
use Tab;
|
||||
use Configuration;
|
||||
|
||||
trait Install
|
||||
{
|
||||
public function install()
|
||||
{
|
||||
if (!parent::install()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->createTables()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->registerTabs(Config::TABS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->registerHooks()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Configuration::updateValue(Config::FRONT_ENABLED_CONFIG_KEY, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function createTables(): bool
|
||||
{
|
||||
$sqlToExec = [
|
||||
'
|
||||
CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ws_customerspecialty` (
|
||||
`id_ws_customerspecialty` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`position` INT(10) UNSIGNED NOT NULL DEFAULT 0,
|
||||
`active` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
|
||||
`date_add` DATETIME NOT NULL,
|
||||
`date_upd` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`id_ws_customerspecialty`)
|
||||
);
|
||||
',
|
||||
'
|
||||
CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ws_customerspecialty_lang` (
|
||||
`id_ws_customerspecialty_lang` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`id_ws_customerspecialty` INT(11) NOT NULL,
|
||||
`id_lang` INT(11) NOT NULL,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`id_ws_customerspecialty_lang`),
|
||||
UNIQUE KEY `ws_customerspecialty_lang_unique` (`id_ws_customerspecialty`, `id_lang`)
|
||||
);
|
||||
',
|
||||
'
|
||||
CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'ws_customerspecialty_customer` (
|
||||
`id_ws_customerspecialty_customer` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`id_customer` INT(11) NOT NULL,
|
||||
`id_ws_customerspecialty` INT(11) NOT NULL,
|
||||
`date_add` DATETIME NOT NULL,
|
||||
`date_upd` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`id_ws_customerspecialty_customer`),
|
||||
UNIQUE KEY `ws_customerspecialty_customer_unique` (`id_customer`),
|
||||
KEY `id_ws_customerspecialty` (`id_ws_customerspecialty`)
|
||||
);
|
||||
',
|
||||
];
|
||||
|
||||
foreach ($sqlToExec as $sql) {
|
||||
if (!Db::getInstance()->execute($sql)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function registerHooks(): bool
|
||||
{
|
||||
foreach (Config::HOOKS as $hook) {
|
||||
if (!$this->registerHook($hook)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function registerTabs(array $moduleTabs): bool
|
||||
{
|
||||
$languages = Language::getLanguages(false);
|
||||
|
||||
foreach ($moduleTabs as $moduleTab) {
|
||||
if (Tab::getIdFromClassName($moduleTab['class_name'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tab = new Tab();
|
||||
$tab->class_name = $moduleTab['class_name'];
|
||||
$tab->module = $moduleTab['module'];
|
||||
$tab->active = (int) $moduleTab['active'];
|
||||
$tab->route_name = $moduleTab['route_name'];
|
||||
$tab->icon = $moduleTab['icon'];
|
||||
$tab->id_parent = (int) Tab::getIdFromClassName($moduleTab['parent_class_name']);
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$tab->name[(int) $language['id_lang']] = $moduleTab['name'][$language['iso_code']]
|
||||
?? $moduleTab['name']['en'];
|
||||
}
|
||||
|
||||
$tab->add();
|
||||
|
||||
if (!$tab->id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{if $confirmation}
|
||||
{$confirmation nofilter}
|
||||
{/if}
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<i class="icon-user"></i>
|
||||
{l s='Front office display' d='Modules.Wscustomerspecialty.Admin'}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="help-block">
|
||||
{l s='When enabled, a required "Specialty" field is added to the registration and account edit forms on the storefront. Back-office management of specialty values and customer records is unaffected by this setting.' d='Modules.Wscustomerspecialty.Admin'}
|
||||
</p>
|
||||
<form method="post" action="{$form_action|escape:'htmlall':'UTF-8'}">
|
||||
<div class="form-group">
|
||||
<label class="control-label">
|
||||
<input type="checkbox" name="front_enabled" value="1" {if $front_enabled}checked="checked"{/if}>
|
||||
{l s='Show the specialty field on the storefront' d='Modules.Wscustomerspecialty.Admin'}
|
||||
</label>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<button type="submit" name="submitWsCustomerSpecialty" class="btn btn-default pull-right">
|
||||
<i class="process-icon-save"></i>
|
||||
{l s='Save' d='Modules.Wscustomerspecialty.Admin'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
{% import '@PrestaShop/Admin/macros.html.twig' as ps %}
|
||||
{% form_theme customerSpecialtyForm '@PrestaShop/Admin/TwigTemplateForm/prestashop_ui_kit.html.twig' %}
|
||||
|
||||
{% block customerspecialty_form_block %}
|
||||
<div class="card">
|
||||
{{ form_start(customerSpecialtyForm) }}
|
||||
<div class="card-header">
|
||||
<h3 class="card-header-title">{{ 'Customer specialty'|trans({}, 'Modules.Wscustomerspecialty.Admin') }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{ form_rest(customerSpecialtyForm) }}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{{ path('customerspecialty_index') }}" class="btn btn-outline-secondary">
|
||||
{{ 'Cancel'|trans({}, 'Admin.Actions') }}
|
||||
</a>
|
||||
<button class="btn btn-primary float-right">
|
||||
{{ 'Save'|trans({}, 'Admin.Actions') }}
|
||||
</button>
|
||||
</div>
|
||||
{{ form_end(customerSpecialtyForm) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends '@PrestaShop/Admin/layout.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% block customerspecialty_form %}
|
||||
{% include '@Modules/ws_customerspecialty/views/templates/admin/form/blocks/form.html.twig' %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
window.prestashop.component.initComponents([
|
||||
'TranslatableField',
|
||||
'TranslatableInput',
|
||||
]);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,14 @@
|
||||
{% extends '@PrestaShop/Admin/layout.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% include '@PrestaShop/Admin/Common/Grid/grid_panel.html.twig' with {'grid': customerSpecialtyGrid} %}
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
new window.prestashop.component.Grid('customerspecialty');
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{if $specialization}
|
||||
<div class="panel" id="ws-customerspecialty-panel">
|
||||
<div class="panel-heading">
|
||||
<i class="icon-user-md"></i>
|
||||
{l s='Specialty' d='Modules.Wscustomerspecialty.Admin'}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p class="ws-customerspecialty-title">{$specialization|escape:'htmlall':'UTF-8'}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* 2007-2026 De Websmid BV
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This file is not open source! Each license that you purchased is only available for 1 website only.
|
||||
* If you want to use this file on more websites (or projects), you need to purchase additional licenses.
|
||||
* You are not allowed to redistribute, resell, lease, license, sub-license or offer our resources to any third party.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please contact us for extra customization service at an affordable price
|
||||
*
|
||||
* @author Isabelle Oving-Anno | De Websmid <isabelle@dewebsmid.nl>
|
||||
* @copyright 2007-2026 De Websmid BV
|
||||
* @license Valid for 1 website (or project) for each purchase of license
|
||||
* International Registered Trademark & Property of De Websmid BV
|
||||
*/
|
||||
|
||||
use Websmid\CustomerSpecialty\Module\Install;
|
||||
use Websmid\CustomerSpecialty\Module\AdminHooks;
|
||||
use Websmid\CustomerSpecialty\Module\DisplayHooks;
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if (file_exists(__DIR__ . '/vendor/autoload.php')) {
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
}
|
||||
|
||||
class Ws_CustomerSpecialty extends Module
|
||||
{
|
||||
use Install;
|
||||
use AdminHooks;
|
||||
use DisplayHooks;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->name = 'ws_customerspecialty';
|
||||
$this->version = '1.0.0';
|
||||
$this->author = 'Isabelle Oving-Anno | De Websmid BV';
|
||||
$this->need_instance = 0;
|
||||
$this->bootstrap = true;
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->displayName = $this->trans('Customer Specialty', [], 'Modules.Wscustomerspecialty.Admin');
|
||||
$this->description = $this->trans('Adds a manageable specialty/profession field to customer accounts, shown in the back office and exposed to order confirmation emails.', [], 'Modules.Wscustomerspecialty.Admin');
|
||||
$this->ps_versions_compliancy = ['min' => '1.7.0.0', 'max' => _PS_VERSION_];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user