In this tutorial we are going to build module which extends customers list with one extra column which can be toggled. It can have two states - turned on or off. In customer creation and edit form we will add switch which will also manage the same state. By following this tutorial you will learn how to:
The module created within this tutorial can be found here
On module installation the following hooks are being registered:
action
CustomerGridDefinitionModifier
- for adding new column to customers grid.action
CustomerGridQueryBuilderModifier
- for modifying customers grid sql.action
CustomerFormBuilderModifier
- for adding new field to customers create or edit form field.actionAfterCreate
CustomerFormHandler
- to execute the saving process of added field from the module.actionAfterUpdate
CustomerFormHandler
- to execute the update process of added field from the module.<?php
public function install()
{
return parent::install() &&
$this->registerHook('actionCustomerGridDefinitionModifier') &&
$this->registerHook('actionCustomerGridQueryBuilderModifier') &&
$this->registerHook('actionCustomerFormBuilderModifier') &&
$this->registerHook('actionAfterCreateCustomerFormHandler') &&
$this->registerHook('actionAfterUpdateCustomerFormHandler') &&
$this->installTables()
;
}
action
CustomerGridDefinitionModifier
and action
CustomerGridQueryBuilderModifier
are built
using grid id which in this case is Customer. If you want to use hook for any other grid
You can check any definition factory service in PrestaShop\PrestaShop\Core\Grid\Definition\Factory
to see available grid ids. Grid id is returned by getId()
method.
action
CustomerFormBuilderModifier
,actionAfterCreate
CustomerFormHandler
and actionAfterUpdate
CustomerFormHandler
are built using unique identifier which
in this case is Customer is retrieved from its form type CustomerType. E.g id retrieved from ManufacturerType will be Manufacturer. Some types might use
function getBlockPrefix
to retrieve the unique id<?php
use PrestaShop\PrestaShop\Core\Grid\Definition\GridDefinitionInterface;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ToggleColumn;
use PrestaShopBundle\Form\Admin\Type\YesAndNoChoiceType;
class Ps_DemoCQRSHooksUsage extends Module
{
// ...
/**
* Hook allows to modify Customers grid definition.
* This hook is a right place to add/remove columns or actions (bulk, grid).
*
* @param array $params
*/
public function hookActionCustomerGridDefinitionModifier(array $params)
{
/** @var GridDefinitionInterface $definition */
$definition = $params['definition'];
$translator = $this->getTranslator();
$definition
->getColumns()
->addAfter(
'optin',
(new ToggleColumn('is_allowed_for_review'))
->setName($translator->trans('Allowed for review', [], 'Modules.Ps_DemoCQRSHooksUsage'))
->setOptions([
'field' => 'is_allowed_for_review',
'primary_field' => 'id_customer',
'route' => 'ps_democqrshooksusage_toggle_is_allowed_for_review',
'route_param_name' => 'customerId',
])
)
;
$definition->getFilters()->add(
(new Filter('is_allowed_for_review', YesAndNoChoiceType::class))
->setAssociatedColumn('is_allowed_for_review')
);
}
// ...
}
This hook, through $params
array, received GridDefinition
that defines how the grid is rendered. See Grid definition for more information.
In this sample a new toggable column which determines if the customer is eligible to review products is added just after another column which has id optin
. The sample code also demonstrates how add new filter.
ToggleColumn
- used to display booleans, it will display an icon instead of the value. If user clicks on it, this triggers a toggle of the boolean value. More information about this column and all available parameters can be found here.
As in this sample module we are creating ToggleColumn
we need to configure the route in which the toggling action will be performed. Indeed when the end-user clicks on this column, an ajax request is performed and must reach one new controller to handle the action (here: toggle a value on and off).
If you only want to display data then this step can be skipped. E.g you are creating DataColumn
. See Column references for full list of grid columns available.
DemoCQRSHooksUsage\Controller\Admin\CustomerReviewController
:<?php
namespace DemoCQRSHooksUsage\Controller\Admin;
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
class CustomerReviewController extends FrameworkBundleAdminController
{
}
action
:This example has been simplified for practical reasons.
You can find full implementation here which uses CQRS pattern to toggle the reviewer state. More about it here.
<?php
namespace DemoCQRSHooksUsage\Controller\Admin;
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
class CustomerReviewController extends FrameworkBundleAdminController
{
public function toggleIsAllowedForReviewAction($customerId)
{
// updating reviewer state can be handled here
return $this->redirectToRoute('admin_customers_index');
}
}
ps_democqrshooksusage/config/routes.yml
file:ps_democqrshooksusage_toggle_is_allowed_for_review:
path: demo-cqrs-hook-usage/{customerId}/toggle-is-allowed-for-review
methods: [POST]
defaults:
_controller: 'DemoCQRSHooksUsage\Controller\Admin\CustomerReviewController::toggleIsAllowedForReviewAction'
requirements:
customerId: \d+
Route name ps_democqrshooksusage_toggle_is_allowed_for_review
matches the one that was passed as mandatory option when creating the
ToggleColumn
.
By just extending grid definition we won’t be able to display any data since we need to fetch it first. Luckily, we can add additional sql conditions by extending doctrine’s query builder.
<?php
use Doctrine\DBAL\Query\QueryBuilder;
use PrestaShop\PrestaShop\Core\Search\Filters\CustomerFilters;
class Ps_DemoCQRSHooksUsage extends Module
{
// ...
/**
* Hook allows to modify Customers query builder and add custom sql statements.
*
* @param array $params
*/
public function hookActionCustomerGridQueryBuilderModifier(array $params)
{
/** @var QueryBuilder $searchQueryBuilder */
$searchQueryBuilder = $params['search_query_builder'];
/** @var CustomerFilters $searchCriteria */
$searchCriteria = $params['search_criteria'];
$searchQueryBuilder->addSelect(
'IF(dcur.`is_allowed_for_review` IS NULL,0,dcur.`is_allowed_for_review`) AS `is_allowed_for_review`'
);
$searchQueryBuilder->leftJoin(
'c',
'`' . pSQL(_DB_PREFIX_) . 'democqrshooksusage_reviewer`',
'dcur',
'dcur.`id_customer` = c.`id_customer`'
);
if ('is_allowed_for_review' === $searchCriteria->getOrderBy()) {
$searchQueryBuilder->orderBy('dcur.`is_allowed_for_review`', $searchCriteria->getOrderWay());
}
foreach ($searchCriteria->getFilters() as $filterName => $filterValue) {
if ('is_allowed_for_review' === $filterName) {
$searchQueryBuilder->andWhere('dcur.`is_allowed_for_review` = :is_allowed_for_review');
$searchQueryBuilder->setParameter('is_allowed_for_review', $filterValue);
if (!$filterValue) {
$searchQueryBuilder->orWhere('dcur.`is_allowed_for_review` IS NULL');
}
}
}
}
// ...
}
This sample demonstrates how to extend sql of the customers grid. From our custom database table democqrshooksusage_reviewer
we fetch the result of field is_allowed_for_review
. This name must match the id we added in the grid definition. In order for sorting to work we also add orderBy
condition and finally, in order
for filters to work where
conditions are added if the filter exists in $searchCriteria->getFilters()
.
After completing the steps above by going to customers list you should see new column “allowed for review” added.
In this step we are appending to the customers form a new SwitchType
form field - its one of many form types which already exist in PrestaShop. More information
about it can be found here.
This example has been simplified for practical reasons.
You can find full implementation here which uses CQRS pattern to get reviewer state. More about it here.
<?php
// modules/ps_democqrshooksusage/ps_democqrshooksusage.php
use Symfony\Component\Form\FormBuilderInterface;
use PrestaShopBundle\Form\Admin\Type\SwitchType;
class Ps_DemoCQRSHooksUsage extends Module
{
// ...
public function hookActionCustomerFormBuilderModifier(array $params)
{
/** @var FormBuilderInterface $formBuilder */
$formBuilder = $params['form_builder'];
$formBuilder->add('is_allowed_for_review', SwitchType::class, [
'label' => $this->getTranslator()->trans('Allow reviews', [], 'Modules.Ps_DemoCQRSHooksUsage'),
'required' => false,
]);
$customerId = $params['id'];
$params['data']['is_allowed_for_review'] = $this->getIsAllowedForReview($customerId);
$formBuilder->setData($params['data']);
}
private function getIsAllowedForReview($customerId)
{
// implement your data retrieval logic here
return true;
}
// ...
}
In this sample by using Symfony form builder we just added another Form type. To determine if its
on or off we also need to reset its form data by assigning is_allowed_for_review
value to true
or false
.
By completing the steps above newly added switch is now visible in the customers form.
In the previous example we have added a switch field ! But when we want to save its state (on or off) nothing happens. The data is not modified. This is because we have not used the hooks dedicated to handle this topic - lets do that!
This example has been simplified for practical reasons.
You can find full implementation here which uses CQRS pattern to create or update reviewer state. More about it here.
<?php
public function hookActionAfterUpdateCustomerFormHandler(array $params)
{
$this->updateCustomerReviewStatus($params);
}
public function hookActionAfterCreateCustomerFormHandler(array $params)
{
$this->updateCustomerReviewStatus($params);
}
private function updateCustomerReviewStatus(array $params)
{
$customerId = $params['id'];
/** @var array $customerFormData */
$customerFormData = $params['form_data'];
$isAllowedForReview = (bool) $customerFormData['is_allowed_for_review'];
// implement review status saving here
}
when we created the switch type form we named it is_allowed_for_review
. By using the same name we can get the state (on or off).
This hook receives from $params
the form data, that you can retrieve like this: $params['form_data']
.
All the form data is available here, including is_allowed_for_review
data which comes from the switch.