---
title: Formulario Symfony en modulo PS 1.7+ — patron moderno
section: examples
slug: symfony-form-module
description: "Crear un formulario de configuracion con Symfony FormType en modulo PrestaShop 1.7+: form types, data providers, form handlers y templates Twig."
keywords: prestashop symfony form formtype modulo configuracion twig data provider handler moderno
last_updated: 2025-01-15
source_url: "https://ayudaprestashop.es/examples/symfony-form-module"
---

# Formulario Symfony en modulo PS 1.7+ — patron moderno

> Crear un formulario de configuracion con Symfony FormType en modulo PrestaShop 1.7+: form types, data providers, form handlers y templates Twig.

## Cuando usar Symfony Forms

Usa Symfony Forms cuando: (1) desarrollas para PS 1.7.7+ o 8.x, (2) quieres un formulario en el BO moderno con validacion server-side, (3) necesitas integrar con el sistema de tabs/configuracion de Symfony. Para modulos que deben ser compatibles con PS 1.6, sigue usando HelperForm.

## Estructura del modulo

*Arbol de archivos para Symfony Forms*

```text
modules/ecom_settings/
├── ecom_settings.php                   # Modulo principal
├── config/
│   ├── routes.yml                      # Rutas Symfony
│   └── services.yml                    # Servicios DI
├── src/
│   ├── Controller/
│   │   └── Admin/
│   │       └── SettingsController.php  # Controller Symfony
│   ├── Form/
│   │   ├── Type/
│   │   │   └── SettingsFormType.php    # FormType
│   │   └── DataProvider/
│   │       └── SettingsFormDataProvider.php
│   └── Form/
│       └── Handler/
│           └── SettingsFormHandler.php  # (opcional, si no usas DataProvider)
└── views/
    └── templates/
        └── admin/
            └── settings.html.twig       # Template del formulario
```

## FormType — definir los campos

*src/Form/Type/SettingsFormType.php*

```php
<?php
namespace EcomSettings\Form\Type;

use PrestaShopBundle\Form\Admin\Type\SwitchType;
use PrestaShopBundle\Form\Admin\Type\TranslateType;
use PrestaShopBundle\Form\Admin\Type\TranslatableType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\ColorType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;

class SettingsFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            // Switch (boolean) — usa el tipo PS nativo
            ->add('enabled', SwitchType::class, [
                'label' => 'Activar modulo',
                'required' => false,
            ])
            // Texto simple
            ->add('title', TextType::class, [
                'label' => 'Titulo del banner',
                'required' => true,
                'attr' => [
                    'placeholder' => 'Ej: Envio gratis +50 EUR',
                    'maxlength' => 255,
                ],
                'help' => 'Texto que se muestra en el banner superior',
            ])
            // Campo multilang (traducible)
            ->add('description', TranslatableType::class, [
                'label' => 'Descripcion',
                'type' => TextareaType::class,
                'required' => false,
                'options' => [
                    'attr' => ['rows' => 4],
                ],
            ])
            // Select
            ->add('position', ChoiceType::class, [
                'label' => 'Posicion del banner',
                'choices' => [
                    'Arriba de la pagina' => 'top',
                    'Debajo del header'   => 'below_header',
                    'Encima del footer'   => 'above_footer',
                ],
                'required' => true,
            ])
            // Color picker
            ->add('background_color', ColorType::class, [
                'label' => 'Color de fondo',
                'required' => true,
            ])
            ->add('text_color', ColorType::class, [
                'label' => 'Color del texto',
                'required' => true,
            ])
            // Numero
            ->add('display_delay', IntegerType::class, [
                'label' => 'Retraso antes de mostrar (ms)',
                'required' => false,
                'attr' => ['min' => 0, 'max' => 10000],
                'help' => '0 = mostrar inmediatamente',
            ])
        ;
    }
}
```

## FormDataProvider — leer/escribir datos

*src/Form/DataProvider/SettingsFormDataProvider.php*

```php
<?php
namespace EcomSettings\Form\DataProvider;

use PrestaShop\PrestaShop\Core\Form\FormDataProviderInterface;
use Configuration;

class SettingsFormDataProvider implements FormDataProviderInterface
{
    /**
     * Leer datos actuales para rellenar el formulario
     */
    public function getData(): array
    {
        return [
            'enabled'          => (bool) Configuration::get('ECOM_BANNER_ENABLED'),
            'title'            => Configuration::get('ECOM_BANNER_TITLE') ?: '',
            'description'      => $this->getMultilangValue('ECOM_BANNER_DESC'),
            'position'         => Configuration::get('ECOM_BANNER_POSITION') ?: 'top',
            'background_color' => Configuration::get('ECOM_BANNER_BG_COLOR') ?: '#2fb5d2',
            'text_color'       => Configuration::get('ECOM_BANNER_TXT_COLOR') ?: '#ffffff',
            'display_delay'    => (int) Configuration::get('ECOM_BANNER_DELAY'),
        ];
    }

    /**
     * Guardar datos del formulario
     */
    public function setData(array $data): array
    {
        $errors = [];

        // Validaciones custom
        if (empty($data['title']) && $data['enabled']) {
            $errors[] = 'El titulo es obligatorio cuando el modulo esta activado.';
        }

        if (empty($errors)) {
            Configuration::updateValue('ECOM_BANNER_ENABLED', (int) $data['enabled']);
            Configuration::updateValue('ECOM_BANNER_TITLE', $data['title']);
            Configuration::updateValue('ECOM_BANNER_POSITION', $data['position']);
            Configuration::updateValue('ECOM_BANNER_BG_COLOR', $data['background_color']);
            Configuration::updateValue('ECOM_BANNER_TXT_COLOR', $data['text_color']);
            Configuration::updateValue('ECOM_BANNER_DELAY', (int) $data['display_delay']);

            // Guardar campo multilang
            if (isset($data['description'])) {
                $this->setMultilangValue('ECOM_BANNER_DESC', $data['description']);
            }
        }

        return $errors;
    }

    protected function getMultilangValue(string $key): array
    {
        $result = [];
        foreach (\Language::getLanguages(true) as $lang) {
            $result[$lang['id_lang']] = Configuration::get($key, $lang['id_lang']) ?: '';
        }
        return $result;
    }

    protected function setMultilangValue(string $key, array $values): void
    {
        foreach ($values as $idLang => $value) {
            Configuration::updateValue($key, $value, false, 0, 0);
            // Para multilang con Configuration hay que usar el truco:
            Configuration::updateValue($key, [$idLang => $value]);
        }
    }
}
```

## services.yml — registrar servicios

*config/services.yml*

```yaml
services:
  _defaults:
    public: true

  # FormType
  ecom_settings.form.type.settings:
    class: 'EcomSettings\Form\Type\SettingsFormType'
    tags:
      - { name: 'form.type' }

  # DataProvider
  ecom_settings.form.data_provider.settings:
    class: 'EcomSettings\Form\DataProvider\SettingsFormDataProvider'

  # FormHandler — usa el FormHandler generico de PS
  ecom_settings.form.handler.settings:
    class: 'PrestaShop\PrestaShop\Core\Form\Handler'
    arguments:
      - '@form.factory'
      - '@prestashop.core.hook.dispatcher'
      - '@ecom_settings.form.data_provider.settings'
      - 'EcomSettings\Form\Type\SettingsFormType'
      - 'EcomSettingsConfiguration'  # Hook name suffix

  # Controller
  ecom_settings.controller.admin.settings:
    class: 'EcomSettings\Controller\Admin\SettingsController'
    arguments:
      - '@ecom_settings.form.handler.settings'
    tags:
      - { name: 'controller.service_arguments' }
```

## Admin Controller Symfony

*src/Controller/Admin/SettingsController.php*

```php
<?php
namespace EcomSettings\Controller\Admin;

use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class SettingsController extends FrameworkBundleAdminController
{
    public function indexAction(Request $request): Response
    {
        /** @var \PrestaShop\PrestaShop\Core\Form\Handler $formHandler */
        $formHandler = $this->get('ecom_settings.form.handler.settings');

        $form = $formHandler->getForm();
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $errors = $formHandler->save($form->getData());

            if (empty($errors)) {
                $this->addFlash('success', $this->trans(
                    'Settings updated successfully.', 'Admin.Notifications.Success'
                ));
            } else {
                foreach ($errors as $error) {
                    $this->addFlash('error', $error);
                }
            }

            return $this->redirectToRoute('ecom_settings_admin');
        }

        return $this->render(
            '@Modules/ecom_settings/views/templates/admin/settings.html.twig',
            [
                'settingsForm' => $form->createView(),
                'help_link' => false,
                'enableSidebar' => true,
                'layoutTitle' => 'Ecom Settings',
            ]
        );
    }
}
```

## Template Twig

*views/templates/admin/settings.html.twig*

```twig
{% extends '@PrestaShop/Admin/layout.html.twig' %}

{% block content %}
  {{ form_start(settingsForm) }}
  <div class="card">
    <div class="card-header">
      <i class="material-icons">settings</i>
      {{ 'Configuracion del banner'|trans({}, 'Modules.Ecomsettings.Admin') }}
    </div>
    <div class="card-body">
      <div class="form-wrapper">
        {{ form_row(settingsForm.enabled) }}
        {{ form_row(settingsForm.title) }}
        {{ form_row(settingsForm.description) }}
        {{ form_row(settingsForm.position) }}

        <div class="row">
          <div class="col-md-6">
            {{ form_row(settingsForm.background_color) }}
          </div>
          <div class="col-md-6">
            {{ form_row(settingsForm.text_color) }}
          </div>
        </div>

        {{ form_row(settingsForm.display_delay) }}
      </div>
    </div>
    <div class="card-footer">
      <div class="d-flex justify-content-end">
        <button type="submit" class="btn btn-primary">
          <i class="material-icons">save</i>
          {{ 'Guardar'|trans({}, 'Admin.Actions') }}
        </button>
      </div>
    </div>
  </div>
  {{ form_end(settingsForm) }}
{% endblock %}
```

## Routing YAML

*config/routes.yml*

```yaml
ecom_settings_admin:
  path: /ecom-settings
  methods: [GET, POST]
  defaults:
    _controller: 'ecom_settings.controller.admin.settings::indexAction'
    _legacy_controller: AdminEcomSettings
    _legacy_link: AdminEcomSettings
```

> **[TIP] Compatibilidad legacy**
>
> Las claves `_legacy_controller` y `_legacy_link` permiten que el sistema de permisos legacy de PS funcione con tu controller Symfony. Asi el admin puede gestionar permisos desde Empleados > Perfiles.


---

*Fuente: [https://ayudaprestashop.es/examples/symfony-form-module](https://ayudaprestashop.es/examples/symfony-form-module). Version Markdown generada automaticamente para consumo por LLMs.*
