Back to Top

PrestaShop Back Office Controllers: Legacy to Symfony

Updated 13 February 2026

A developer can migrate PrestaShop Back Office controllers from legacy to Symfony by building a demo module wkdemomodule with a simple create, save, and list form.

Migrating PrestaShop legacy Controllers to Symfony

From PrestaShop 1.7, the Back Office uses Symfony. Migrating legacy module controllers to Symfony provides clean routing, dependency injection, and reusable Twig templates for easier maintenance.

We will create wkdemomodule, featuring a Symfony-based grid (list) and a form to manage custom data.

1. Module Structure

Your module folder should look like this:

wkdemomodule/
├── config/
│   └── routes.yml
├── src/
│   └── Controller/
│       └── Admin/
│           └── DemoController.php
├── views/
│   └── templates/
│       └── admin/
│           └── demo/
│               ├── index.html.twig
│               └── form.html.twig
├── wkdemomodule.php
└── composer.json
Module structure

2. Setting up Autoloading

In your composer.json, define the namespace for your src directory:

Searching for an experienced
Prestashop Company ?
Find out More
{
  "name": "wk/wkdemomodule",
  "type": "prestashop-module",
  "autoload": {
    "psr-4": {
      "WkDemoModule\\": "src/"
    }
  },
  "config": {
    "prepend-autoloader": false
  }
}

After creating the file, run composer dump-autoload to refresh the autoloader.

After running the command, the vendor A folder is created inside the module directory.

3. Defining Symfony Routes

Create config/routes.yml. This tells PrestaShop which URL maps to your Symfony controller.

wk_demo_list:
  path: /wk-demo/list
  methods: [GET]
  defaults:
    _controller: 'WkDemoModule\Controller\Admin\DemoController::indexAction'
    _legacy_classname: AdminWkDemo
    _legacy_module_name: wkdemomodule

wk_demo_create:
  path: /wk-demo/create
  methods: [GET, POST]
  defaults:
    _controller: 'WkDemoModule\Controller\Admin\DemoController::createAction'
    _legacy_classname: AdminWkDemo
    _legacy_module_name: wkdemomodule
    
wk_demo_edit:
  path: /wk-demo/edit/{id}
  methods: [GET, POST]
  defaults:
    _controller: 'WkDemoModule\Controller\Admin\DemoController::editAction'
    _legacy_classname: AdminWkDemo
    _legacy_module_name: wkdemomodule

4. The Symfony Controller

Create src/Controller/Admin/DemoController.php. This controller will handle the logic for displaying the list and saving the form.

<?php

namespace WkDemoModule\Controller\Admin;

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

class DemoController extends FrameworkBundleAdminController
{
    public function indexAction(): Response
    {
        $data = $this->getDemoData();

        return $this->render(
            '@Modules/wkdemomodule/views/templates/admin/demo/index.html.twig',
            [
                'gridData' => $data,
                'layoutHeaderToolbarBtn' => [
                    'add' => [
                        'href' => $this->generateUrl('wk_demo_create'),
                        'desc' => $this->trans('Add New', 'Modules.Wkdemomodule.Admin'),
                        'icon' => 'add_circle_outline',
                    ],
                ],
            ]
        );
    }

    public function createAction(Request $request): Response
    {
        if ($request->isMethod('POST')) {
            $name = $request->request->get('demo_name');

            // Normally you would save data here
            // This is kept simple for demo purposes

            $this->addFlash(
                'success',
                $this->trans('Item created successfully.', 'Admin.Notifications.Success')
            );

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

        return $this->render(
            '@Modules/wkdemomodule/views/templates/admin/demo/form.html.twig'
        );
    }

    public function editAction(Request $request, int $id): Response
    {
        $data = $this->getDemoData();

        if (!isset($data[$id])) {
            throw $this->createNotFoundException('Item not found');
        }

        $item = $data[$id];

        if ($request->isMethod('POST')) {
            $name = $request->request->get('demo_name');

            // Demo update logic (normally DB update here)
            $item['name'] = $name;

            $this->addFlash(
                'success',
                $this->trans('Item updated successfully.', 'Admin.Notifications.Success')
            );

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

        return $this->render(
            '@Modules/wkdemomodule/views/templates/admin/demo/form.html.twig',
            [
                'demoItem' => $item,
                'isEdit' => true,
            ]
        );
    }

    private function getDemoData()
    {
        return [
            1 => ['id' => 1, 'name' => 'Demo Item 1', 'status' => 'Active'],
            2 => ['id' => 2, 'name' => 'Demo Item 2', 'status' => 'Inactive'],
        ];
    }
}

5. Main Module file

In wkdemomodule.php, you must register the tab so it appears in the Back Office menu, linking it to your Symfony route.

<?php

if (!defined('_PS_VERSION_')) {
    exit;
}

class WkDemoModule extends Module
{
    public function __construct()
    {
        $this->name = 'wkdemomodule';
        $this->tab = 'administration';
        $this->version = '1.0.0';
        $this->author = 'WK';
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('WK Demo Module');
        $this->description = $this->l('Demo module using Symfony Back Office controller.');
    }

    public function install()
    {
        return parent::install() && $this->installTab();
    }

    public function uninstall()
    {
        return parent::uninstall() && $this->uninstallTab();
    }

    private function installTab()
    {
        $tab = new Tab();
        $tab->active = 1;
        $tab->class_name = 'AdminWkDemo';
        $tab->route_name = 'wk_demo_list';
        $tab->module = $this->name;
        $tab->id_parent = (int) Tab::getIdFromClassName('AdminParentCustomer');

        foreach (Language::getLanguages(true) as $lang) {
            $tab->name[$lang['id_lang']] = 'WK Demo Module';
        }

        return $tab->add();
    }

    private function uninstallTab()
    {
        $idTab = (int) Tab::getIdFromClassName('AdminWkDemo');
        if ($idTab) {
            $tab = new Tab($idTab);
            return $tab->delete();
        }
        return true;
    }
}

6. List View (Twig)

Create views/templates/admin/demo/index.html.twig. Symfony controllers use Twig instead of Smarty.

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

{% block content %}
<div class="card">
  <h3 class="card-header">
    <i class="material-icons">list</i>
    {{ 'Demo Data List'|trans({}, 'Modules.Wkdemomodule.Admin') }}
  </h3>

  <div class="card-body">
    <table class="table">
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Status</th>
          <th>Actions</th>
        </tr>
        </thead>
        <tbody>
        {% for item in gridData %}
        <tr>
          <td>{{ item.id }}</td>
          <td>{{ item.name }}</td>
          <td>{{ item.status }}</td>
          <td>
            <a href="{{ path('wk_demo_edit', {id: item.id}) }}"
              class="btn btn-sm btn-primary">
              <i class="material-icons">edit</i>
              Edit
            </a>
          </td>
        </tr>
        {% endfor %}
        </tbody>
    </table>
  </div>
</div>
{% endblock %}

7. Create Form View

Create views/templates/admin/demo/form.html.twig. Symfony controllers use Twig instead of Smarty.

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

{% block content %}
<div class="card">
  <h3 class="card-header">
    <i class="material-icons">edit</i>
    {{ isEdit is defined ? 'Edit Demo Item' : 'Add Demo Item' }}
  </h3>

  <div class="card-body">
    <form method="post">
      <div class="form-group">
        <label>{{ 'Name'|trans({}, 'Admin.Global') }}</label>
        <input type="text"
               name="demo_name"
               class="form-control"
               value="{{ demoItem.name|default('') }}"
               required>
      </div>

      <button type="submit" class="btn btn-primary">
        {{ 'Save'|trans({}, 'Admin.Actions') }}
      </button>
    </form>
  </div>
</div>
{% endblock %}

Reference Screenshots for the PrestaShop Module

Module installed successfully.
Symfony admin controller listing page.
form
Symfony edit form page.

Support

If you are facing any issues or have any doubts about the above process, please feel free to contact us through the comment section.

Also, you can explore our PrestaShop Development Services and a large range of quality PrestaShop Modules.

For any doubt, contact us at [email protected]

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home