In this blog, we will learn how to create configurations using Symfony services and components and will define our own Symfony services in the module, and will use existing components.
Here, we are showing the process step by step with an example module ‘wkcreatedemoproduct‘
Step 1: Create the module class
here, we have created the module class file “wkcreatedemoproduct/wkcreatedemoproduct.php“
In this class, we are initializing the module, controllers tab, and redirecting the configuration page to modern page routes.
Also, note that we are using a new translation system in this example module. Please refer to the documentation for more information.
declare(strict_types=1); use PrestaShop\PrestaShop\Adapter\SymfonyContainer; class WkCreateDemoProduct extends Module { public function __construct() { $this->name = 'wkcreatedemoproduct'; $this->author = 'Webkul'; $this->version = '1.0.0'; $this->need_instance = 0; $this->bootstrap = true; parent::__construct(); $this->displayName = $this->trans('Create demo products', [], 'Modules.Wkcreatedemoproduct.Admin'); $this->description = $this->trans( 'Module created for the Demo product creation purpose on PrestaShop store', [], 'Modules.Wkcreatedemoproduct.Admin' ); $this->ps_versions_compliancy = ['min' => '8.0.0', 'max' => '8.99.99']; } public function getTabs() { return [ [ 'class_name' => 'AdminWkCreateDemoProduct', // Creating tab in admin menu 'visible' => true, 'name' => 'Demo Product Data', 'parent_class_name' => 'CONFIGURE', ] ]; } public function getContent() { $route = SymfonyContainer::getInstance()->get('router')->generate('demo_product_configuration_form'); // redirecting to controller, symfony route Tools::redirectAdmin($route); } public function isUsingNewTranslationSystem() { // Used this for new translations system, It will avoid redirection of translation page on legacy controller return true; } }
Step 2: Define routes
Create routes.yml file inside the wkcreatedemoproduct/config folder
In this file, we have provided the path from the legacy controller to the modern page controller
demo_product_configuration_form: path: /wkcreatedemoproduct/configuration methods: [GET, POST] defaults: _controller: 'PrestaShop\Module\WkCreateDemoProduct\Controller\DemoProductConfigurationController::index' # Needed to work with tab system _legacy_controller: AdminWkCreateDemoProduct _legacy_link: AdminWkCreateDemoProduct
Step 3: Define services
Create a services.yml file inside the wkcreatedemoproduct/config folder
In the given code, we have defined our own services. These will be used for form creation and form handling. Each service is created inside the wkcreatedemoproduct\Form folder
services: _defaults: public: true prestashop.module.wkcreatedemoproduct.form.type.demo_configuration_text: class: 'PrestaShop\Module\WkCreateDemoProduct\Form\DemoConfigurationTextType' parent: 'form.type.translatable.aware' public: true tags: - { name: form.type } prestashop.module.wkcreatedemoproduct.form.demo_configuration_text_form_data_provider: class: 'PrestaShop\Module\WkCreateDemoProduct\Form\DemoConfigurationTextFormDataProvider' arguments: - '@prestashop.module.wkcreatedemoproduct.form.demo_configuration_text_data_configuration' prestashop.module.wkcreatedemoproduct.form.demo_configuration_text_form_data_handler: class: 'PrestaShop\PrestaShop\Core\Form\Handler' arguments: - '@form.factory' - '@prestashop.core.hook.dispatcher' - '@prestashop.module.wkcreatedemoproduct.form.demo_configuration_text_form_data_provider' - 'PrestaShop\Module\WkCreateDemoProduct\Form\DemoConfigurationTextType' - 'DemoConfiguration' prestashop.module.wkcreatedemoproduct.form.demo_configuration_text_data_configuration: class: PrestaShop\Module\WkCreateDemoProduct\Form\DemoConfigurationTextDataConfiguration arguments: ['@prestashop.adapter.legacy.configuration']
Step: 4 Create services class
Now create a service class in the wkcreatedemoproduct\Form folder
i) First service (demo_configuration_text) class DemoConfigurationTextType is created for form view
declare(strict_types=1); namespace PrestaShop\Module\WkCreateDemoProduct\Form; use PrestaShopBundle\Form\Admin\Type\FormattedTextareaType; use PrestaShopBundle\Form\Admin\Type\GeneratableTextType; use PrestaShopBundle\Form\Admin\Type\MoneyWithSuffixType; use Symfony\Component\Form\Extension\Core\Type\NumberType; use PrestaShopBundle\Form\Admin\Type\TranslatableType; use PrestaShopBundle\Form\Admin\Type\TranslatorAwareType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\Length; class DemoConfigurationTextType extends TranslatorAwareType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('product_name', TranslatableType::class, [ 'label' => $this->trans('First product name', 'Modules.Wkcreatedemoproduct.Admin'), 'required' => true, 'options' => [ 'constraints' => [ new Length([ 'max' => 128, ]), ], ], ]) ->add('product_prefix', GeneratableTextType::class, [ 'label' => $this->trans('Name prefix', 'Modules.Wkcreatedemoproduct.Admin'), 'generated_value_length' => 5, ]) ->add('product_short_desc', TranslatableType::class, [ 'label' => $this->trans('Product short description', 'Modules.Wkcreatedemoproduct.Admin'), 'type' => FormattedTextareaType::class, 'help' => $this->trans('Character limit < 21844 and text does not contains <>={}', 'Modules.Wkcreatedemoproduct.Admin'), 'required' => true, 'options' => [ 'constraints' => [ new Length([ 'max' => 21844, ]), ], ], ]) ->add('product_qty', NumberType::class, [ 'label' => $this->trans('Product quantity', 'Modules.Wkcreatedemoproduct.Admin'), 'unit' => 'unit', ]); } }
ii) Second service (demo_configuration_text_data_configuration) class DemoConfigurationTextDataConfiguration is created for the data process
declare(strict_types=1); namespace PrestaShop\Module\WkCreateDemoProduct\Form; use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface; use PrestaShop\PrestaShop\Core\ConfigurationInterface; use PrestaShop\PrestaShop\Adapter\Language\LanguageDataProvider; use Validate; use Tools; /** * Configuration is used to save data to configuration table and retrieve from it */ final class DemoConfigurationTextDataConfiguration implements DataConfigurationInterface { public const WK_PRODUCT_NAME = 'WK_DEMO_PRODUCT_WK_PRODUCT_NAME'; public const WK_PRODUCT_SHORT_DESC = 'WK_DEMO_PRODUCT_WK_PRODUCT_SHORT_DESC'; public const WK_PRODUCT_PREFIX = 'WK_DEMO_PRODUCT_WK_PRODUCT_PREFIX'; public const WK_PRODUCT_QTY = 'WK_DEMO_PRODUCT_WK_PRODUCT_QTY'; /** * @var ConfigurationInterface */ private $configuration; /** * @param ConfigurationInterface $configuration */ public function __construct(ConfigurationInterface $configuration) { $this->configuration = $configuration; } /** * {@inheritdoc} */ public function getConfiguration(): array { $return = []; if ($productName = $this->configuration->get(static::WK_PRODUCT_NAME)) { $return['product_name'] = $productName; } if ($productShortDesc = $this->configuration->get(static::WK_PRODUCT_SHORT_DESC)) { $return['product_short_desc'] = $productShortDesc; } if ($productPrefix = $this->configuration->get(static::WK_PRODUCT_PREFIX)) { $return['product_prefix'] = $productPrefix; } if ($productQty = $this->configuration->get(static::WK_PRODUCT_QTY)) { $return['product_qty'] = $productQty; } return $return; } /** * {@inheritdoc} */ public function updateConfiguration(array $configuration): array { $errors = $this->validateConfiguration($configuration); if (!empty($errors)) { return $errors; } $this->configuration->set(static::WK_PRODUCT_NAME, $configuration['product_name']); $this->configuration->set(static::WK_PRODUCT_SHORT_DESC, $configuration['product_short_desc'], null, ['html' => true]); /* Adding html => true allows for configuration->set to save HTML values */ $this->configuration->set(static::WK_PRODUCT_PREFIX, $configuration['product_prefix']); $this->configuration->set(static::WK_PRODUCT_QTY, $configuration['product_qty']); /* Errors are returned here. */ return []; } /** * Ensure the parameters passed are valid. * * @param array $configuration * * @return bool Returns array */ public function validateConfiguration(array $configuration): array { $errors = []; $objLangDataProvider = new LanguageDataProvider(); $languages = $objLangDataProvider->getLanguages(false); foreach ($languages as $lang) { if (!trim($configuration['product_name'][$lang['id_lang']])) { $errors[] = sprintf( $this->l('Product name is required in %s.'), $lang['name'] ); } elseif (!Validate::isCatalogName($configuration['product_name'][$lang['id_lang']])) { $errors[] = sprintf( $this->l('Product name is not valid in %s.'), $lang['name'] ); } elseif (Tools::strlen($configuration['product_name'][$lang['id_lang']]) > 128) { $this->errors[] = sprintf( $this->trans('Product name is too long. It must have 128 character or less in %s.', 'Modules.Wkcreatedemoproduct.Admin'), $lang['name'] ); } if (!trim($configuration['product_short_desc'][$lang['id_lang']])) { $errors[] = sprintf( $this->trans('Product short description is required in %s.', 'Modules.Wkcreatedemoproduct.Admin'), $lang['name'] ); } elseif (!Validate::isCleanHtml($configuration['product_short_desc'][$lang['id_lang']])) { $errors[] = sprintf( $this->trans('Product short description is not valid in %s.', 'Modules.Wkcreatedemoproduct.Admin'), $lang['name'] ); } elseif (Tools::strlen($configuration['product_short_desc'][$lang['id_lang']]) > 21844) { $this->errors[] = sprintf( $this->trans('Product short description too long. It must have 21844 character or less in %s.', 'Modules.Wkcreatedemoproduct.Admin'), $lang['name'] ); } if (!$configuration['product_qty']) { $errors[] = $this->trans('Product quantity is required.', 'Modules.Wkcreatedemoproduct.Admin'); } elseif (!Validate::isUnsignedInt($configuration['product_qty'])) { $errors[] = $this->trans('Product quantity is invalid.', 'Modules.Wkcreatedemoproduct.Admin'); } return $errors; } } }
iii) Third service (demo_configuration_text_form_data_provider) class DemoConfigurationTextFormDataProvider is created for getter and setter purpose
declare(strict_types=1); namespace PrestaShop\Module\WkCreateDemoProduct\Form; use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface; use PrestaShop\PrestaShop\Core\Form\FormDataProviderInterface; /** * Provider ir responsible for providing form data, in this case it's as simple as using configuration to do that * * Class DemoConfigurationTextFormDataProvider */ class DemoConfigurationTextFormDataProvider implements FormDataProviderInterface { /** * @var DataConfigurationInterface */ private $demoConfigurationTextDataConfiguration; /** * @param DataConfigurationInterface $demoConfigurationTextDataConfiguration */ public function __construct(DataConfigurationInterface $demoConfigurationTextDataConfiguration) { $this->demoConfigurationTextDataConfiguration = $demoConfigurationTextDataConfiguration; } /** * {@inheritdoc} */ public function getData(): array { return $this->demoConfigurationTextDataConfiguration->getConfiguration(); } /** * {@inheritdoc} */ public function setData(array $data): array { return $this->demoConfigurationTextDataConfiguration->updateConfiguration($data); } }
Step 5: Create a controller
DemoProductConfigurationController controller is created to handle the request, It must be inside the wkcreatedemoproduct/src/Controller folder
declare(strict_types=1); namespace PrestaShop\Module\WkCreateDemoProduct\Controller; use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; class DemoProductConfigurationController extends FrameworkBundleAdminController { public function index(Request $request): Response { $textFormDataHandler = $this->get('prestashop.module.wkcreatedemoproduct.form.demo_configuration_text_form_data_handler'); $textForm = $textFormDataHandler->getForm(); $textForm->handleRequest($request); if ($textForm->isSubmitted() && $textForm->isValid()) { /** You can return array of errors in form handler and they can be displayed to user with flashErrors */ $errors = $textFormDataHandler->save($textForm->getData()); if (empty($errors)) { $this->addFlash('success', $this->trans('Successful update.', 'Admin.Notifications.Success')); return $this->redirectToRoute('demo_product_configuration_form'); } $this->flashErrors($errors); } return $this->render('@Modules/wkcreatedemoproduct/views/templates/admin/form.html.twig', [ 'demoConfigurationTextForm' => $textForm->createView(), ]); } }
Step 6: Create view
Twig file form.html.twig path: wkcreatedemoproduct/views/templates/admin/
{% extends '@PrestaShop/Admin/layout.html.twig' %} {# PrestaShop made some modifications to the way forms are displayed to work well with PrestaShop in order to benefit from those you need to use ui kit as theme#} {% form_theme demoConfigurationTextForm '@PrestaShop/Admin/TwigTemplateForm/prestashop_ui_kit.html.twig' %} {% block content %} {{ form_start(demoConfigurationTextForm) }} <div class="card"> <h3 class="card-header"> <i class="material-icons">settings</i> {{ 'Demo products settings'|trans({}, 'Modules.Wkcreatedemoproduct.Admin') }} </h3> <div class="card-body"> <div class="form-wrapper"> {{ form_widget(demoConfigurationTextForm) }} </div> </div> <div class="card-footer"> <div class="d-flex justify-content-end"> <button class="btn btn-primary float-right" id="save-button"> {{ 'Create sample products'|trans({}, 'Modules.Wkcreatedemoproduct.Admin') }} </button> </div> </div> </div> {{ form_end(demoConfigurationTextForm) }} {% endblock %} {% block javascripts %} {{ parent() }} <script src="{{ asset('../modules/wkcreatedemoproduct/views/js/admin.js') }}"></script> {% endblock %}
Step 7: Define the composer file
Create composer.json file for autoloading
{ "name": "prestashop/wkcreatedemoproduct", "description": "PrestaShop - DEMO Products", "license": "AFL-3.0", "authors": [{ "name": "Webkul" }], "autoload": { "psr-4": { "PrestaShop\\Module\\WkCreateDemoProduct\\": "src/" } }, "require": { "php": ">=7.1.0" }, "config": { "preferred-install": "dist", "prepend-autoloader": false }, "type": "prestashop-module" }
Step 8: Installation
Copy created a module on the modules directory of PrestaShop and run the composer command composer install to install all dependencies. The complete module folder structure is attached in the screenshot.
After installing the module, you will controller tab in the left menu sidebar and the configuration form
In the above example, we have created some fields using components, check the documentation for more information.
That’s all about this blog.
If any issues or doubts please feel free to mention them in the comment section.
I would be happy to help.
Also, you can explore our PrestaShop Development Services & a large range of quality PrestaShop Modules.
For any doubt contact us at [email protected]
Be the first to comment.