🗂️

Symfony Controllers y Tabs en el BO de PrestaShop 8/9

Actualizado: 2024-12-01

PrestaShop 8+ permite crear paginas de administracion modernas usando Symfony Controllers integrados en el menu lateral del BO. Los Tabs (entradas de menu) se declaran directamente en el modulo y se instalan/desinstalan automaticamente con el modulo.

#Declarar tabs en el constructor del modulo

mymodule.php — declaracion de tabs en el constructor
php
<?php

public function __construct()
{
    $this->name          = 'mymodule';
    $this->tab           = 'administration';
    $this->version       = '1.0.0';
    $this->author        = 'gmartos.es';
    $this->need_instance = 0;
    $this->bootstrap     = true;

    parent::__construct();

    // Tabs declarados aqui se instalan/desinstalan con install()/uninstall()
    $this->tabs = [
        [
            'route_name'        => 'admin_mymodule_index',   // nombre de la ruta
            'class_name'        => 'AdminMyModuleMain',      // class del Tab en DB
            'visible'           => true,                     // visible en el menu
            'name'              => [
                'en' => 'My Module',
                'es' => 'Mi Modulo',
            ],
            'icon'              => 'extension',             // icono Material de Google
            'parent_class_name' => 'IMPROVE',               // padre: SELL, IMPROVE, CONFIGURE, DEFAULT
        ],
        // Tab hijo (submenu sin url propia — solo agrupa)
        [
            'route_name'        => 'admin_mymodule_configure',
            'class_name'        => 'AdminMyModuleConfigure',
            'visible'           => true,
            'name'              => ['en' => 'Configuration', 'es' => 'Configuracion'],
            'parent_class_name' => 'AdminMyModuleMain',     // hijo del tab anterior
        ],
    ];
}

#Instalacion manual de tabs (avanzado)

Instalar un tab manualmente desde install()
php
<?php

private function installTab(string $className, string $routeName, string $parentClassName = 'IMPROVE'): bool
{
    // Si ya existe, actualizarlo
    $tabId = (int) Tab::getIdFromClassName($className);
    $tab = new Tab($tabId ?: null);

    $tab->active     = 1;
    $tab->class_name = $className;
    $tab->route_name = $routeName;
    $tab->module     = $this->name;
    $tab->id_parent  = (int) Tab::getIdFromClassName($parentClassName);

    foreach (Language::getLanguages() as $lang) {
        $tab->name[$lang['id_lang']] = $this->trans(
            'Mi Modulo',
            [],
            'Modules.Mymodule.Admin',
            $lang['locale']
        );
    }

    return (bool) $tab->save();
}

private function uninstallTab(string $className): bool
{
    $tabId = (int) Tab::getIdFromClassName($className);
    if ($tabId) {
        $tab = new Tab($tabId);
        return (bool) $tab->delete();
    }
    return true;
}

#Crear el Symfony Controller

src/Controller/Admin/ConfigurationController.php
php
<?php

declare(strict_types=1);

namespace MyModule\Controller\Admin;

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

class ConfigurationController extends FrameworkBundleAdminController
{
    public const TAB_CLASS_NAME = 'AdminMyModuleConfigure';

    /**
     * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))")
     */
    public function indexAction(Request $request): Response
    {
        return $this->render(
            '@Modules/mymodule/views/templates/admin/configure.html.twig',
            [
                'layoutTitle'         => $this->trans('Configuracion', 'Modules.Mymodule.Admin'),
                'enableSidebar'       => true,
                'help_link'           => $this->generateSidebarLink($request->attributes->get('_legacy_controller')),
            ]
        );
    }
}

#Configurar las rutas (routes.yml)

config/routes.yml — definicion de rutas del modulo
yaml
admin_mymodule_index:
    path: /my-module
    methods: [GET]
    defaults:
        _controller: 'MyModule\Controller\Admin\DashboardController::indexAction'
        _legacy_classname: AdminMyModuleMain
        _legacy_link: AdminMyModuleMain

admin_mymodule_configure:
    path: /my-module/configure
    methods: [GET, POST]
    defaults:
        _controller: 'MyModule\Controller\Admin\ConfigurationController::indexAction'
        _legacy_classname: AdminMyModuleConfigure
        _legacy_link: AdminMyModuleConfigure

# Ruta con parametro
admin_mymodule_item_edit:
    path: /my-module/item/{id}/edit
    methods: [GET, POST]
    requirements:
        id: '\d+'
    defaults:
        _controller: 'MyModule\Controller\Admin\ItemController::editAction'

#Plantilla Twig para el BO

views/templates/admin/configure.html.twig
twig
{% extends '@PrestaShop/Admin/layout.html.twig' %}

{% block content %}
  <div class="card">
    <div class="card-header">
      <h3>{{ 'Configuration'|trans({}, 'Modules.Mymodule.Admin') }}</h3>
    </div>
    <div class="card-body">
      {# Renderizar el form Symfony si lo pasas desde el Controller #}
      {% if form is defined %}
        {{ form_start(form) }}
          {{ form_widget(form) }}
          <button type="submit" class="btn btn-primary">
            {{ 'Save'|trans({}, 'Admin.Actions') }}
          </button>
        {{ form_end(form) }}
      {% endif %}
    </div>
  </div>
{% endblock %}
💡
Padres de menu disponibles

Los valores validos para parent_class_name son: SELL (vender), IMPROVE (mejorar), CONFIGURE (configurar), DEFAULT (raiz), o el class_name de otro tab del modulo para crear submenus.

Descargar en Markdown Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.