💻 DéveloppementAvancé PS 1.6 PS 1.7 PS 8.x

Ajouter un champ image personnalisé aux catégories PrestaShop

Guide complet pour ajouter un second champ image aux catégories PrestaShop 1.7 et 8.x : override de classe, migration SQL, back-office et affichage front.

En bref : Ajoutez un second champ image aux catégories PrestaShop via un override de la classe Category (PS 1.6/1.7) ou un module Symfony (PS 8.x), en modifiant la base de données, le back-office et les templates front-end.

Publié le 21 mars 2026 8 min de lecture Alexandre Carette

Pourquoi ajouter une seconde image aux catégories ?

Le système natif de PrestaShop ne propose qu'une seule image par catégorie, accompagnée éventuellement d'une vignette (thumbnail). C'est souvent insuffisant pour les besoins de webdesign modernes : image de fond pleine largeur dans le header, visuel d'ambiance distinct de la vignette de navigation, bannière promotionnelle saisonnière…

L'ajout d'un champ id_image2 aux catégories permet de gérer deux visuels indépendants directement depuis le back-office, sans recourir à un module tiers. Ce tutoriel détaille chaque étape, du schéma SQL jusqu'à l'affichage front-end.

Prérequis et préparation

Avant toute modification structurelle :

  • **Activez le mode debug** pour ne jamais rester bloqué sur une page blanche sans message d'erreur
  • Sauvegardez votre base de données
  • Travaillez sur un environnement de développement ou de préprod

Activer le mode développeur

PrestaShop 1.6 :


// config/defines.inc.php
if (!defined('_PS_MODE_DEV_'))
    define('_PS_MODE_DEV_', true);

PrestaShop 1.7 / 8.x :


// .env ou app/config/parameters.php
// Passez PS_DEV_MODE à true
define('_PS_MODE_DEV_', true);

Sur PrestaShop 8.x, vous pouvez également utiliser le fichier .env à la racine :


APP_DEBUG=1
APP_ENV=dev

Cette activation est indispensable : sans elle, une erreur PHP produit une page blanche inexploitable.

Étape 1 : Modification de la base de données

Ajoutez la colonne id_image2 à la table des catégories :


ALTER TABLE `ps_category`
  ADD `id_image2` INT(10) UNSIGNED DEFAULT NULL AFTER `id_parent`;

Cette colonne stockera l'identifiant de la seconde image. Le préfixe ps_ correspond au préfixe par défaut de vos tables — adaptez-le si nécessaire.

Étape 2 : Override de la classe Category

Créez le fichier d'override pour étendre le modèle Category avec la gestion de la nouvelle image.

Chemin : override/classes/Category.php


<?php
/**
 * Override Category - Ajout d'un second champ image
 */
class Category extends CategoryCore
{
    /** @var int|null Identifiant de la seconde image */
    public $id_image2;

    /**
     * Définition enrichie avec le nouveau champ
     */
    public function __construct($id_category = null, $id_lang = null, $id_shop = null)
    {
        // Déclaration du champ avant l'appel parent
        self::$definition['fields']['id_image2'] = [
            'type' => self::TYPE_INT,
            'validate' => 'isUnsignedId',
        ];

        parent::__construct($id_category, $id_lang, $id_shop);
    }

    /**
     * Ajoute (upload) la seconde image
     *
     * @return bool
     */
    public function addImage2()
    {
        if (!isset($_FILES['image2']) || $_FILES['image2']['error'] !== UPLOAD_ERR_OK) {
            return false;
        }

        $tmp_name = $_FILES['image2']['tmp_name'];
        $target_dir = _PS_CAT_IMG_DIR_;
        $image_name = (int)$this->id . '-2';

        // Génération des différents formats d'image
        $image_types = ImageType::getImagesTypes('categories');

        if (!ImageManager::resize($tmp_name, $target_dir . $image_name . '.jpg')) {
            return false;
        }

        foreach ($image_types as $image_type) {
            ImageManager::resize(
                $tmp_name,
                $target_dir . $image_name . '-' . stripslashes($image_type['name']) . '.jpg',
                (int)$image_type['width'],
                (int)$image_type['height']
            );
        }

        // Mise à jour de l'identifiant en base
        $this->id_image2 = 1;
        return $this->update();
    }

    /**
     * Supprime la seconde image et toutes ses déclinaisons
     *
     * @return bool
     */
    public function deleteImage2()
    {
        $target_dir = _PS_CAT_IMG_DIR_;
        $image_name = (int)$this->id . '-2';

        // Suppression de l'image originale
        $path = $target_dir . $image_name . '.jpg';
        if (file_exists($path)) {
            @unlink($path);
        }

        // Suppression des déclinaisons
        $image_types = ImageType::getImagesTypes('categories');
        foreach ($image_types as $image_type) {
            $path = $target_dir . $image_name . '-' . stripslashes($image_type['name']) . '.jpg';
            if (file_exists($path)) {
                @unlink($path);
            }
        }

        $this->id_image2 = null;
        return $this->update();
    }

    /**
     * Retourne le chemin de la seconde image
     *
     * @param string $type Format d'image (ex: 'medium_default')
     * @return string|false
     */
    public function getImage2Url($type = 'category_default')
    {
        $path = _PS_CAT_IMG_DIR_ . (int)$this->id . '-2-' . $type . '.jpg';
        if (file_exists($path)) {
            return _THEME_CAT_DIR_ . (int)$this->id . '-2-' . $type . '.jpg';
        }
        return false;
    }
}

Point critique : le cache des classes

Après avoir créé ou modifié un override, vous devez supprimer le fichier d'index des classes pour que PrestaShop prenne en compte vos modifications :


# PrestaShop 1.6
rm var/cache/*/class_index.php

# PrestaShop 1.7 / 8.x
rm var/cache/dev/class_index.php
rm var/cache/prod/class_index.php

Sans cette suppression, vous obtiendrez l'erreur :


Call to undefined method Category::deleteImage2

Cette erreur signifie que PHP utilise encore la classe CategoryCore en cache au lieu de votre override. C'est le piège le plus fréquent de cette manipulation.

Étape 3 : Modification du back-office

Pour que l'administrateur puisse uploader la seconde image, il faut ajouter le champ dans le formulaire de gestion des catégories.

PrestaShop 1.6 (AdminCategoriesController)

Créez l'override du contrôleur :

Chemin : override/controllers/admin/AdminCategoriesController.php


<?php
class AdminCategoriesController extends AdminCategoriesControllerCore
{
    public function renderForm()
    {
        // Appel du formulaire parent
        $form = parent::renderForm();

        // Injection du champ image2 dans le HTML
        // (méthode simplifiée — en production, utilisez le système de hooks)
        $image2_field = '<div class="form-group">';
        $image2_field .= '<label class="control-label col-lg-3">Image secondaire</label>';
        $image2_field .= '<div class="col-lg-9">';
        $image2_field .= '<input type="file" name="image2" accept="image/*" />';

        if ($this->object && $this->object->id_image2) {
            $image2_field .= '<p class="help-block"><img src="'
                . _THEME_CAT_DIR_ . (int)$this->object->id . '-2-medium_default.jpg'
                . '" style="max-width:200px" /></p>';
        }

        $image2_field .= '</div></div>';

        // Insertion avant le bouton de soumission
        $form = str_replace('id="category_form_submit_btn"', $image2_field . 'id="category_form_submit_btn"', $form);

        return $form;
    }

    public function postProcess()
    {
        parent::postProcess();

        if (Tools::isSubmit('submitAddcategory') || Tools::isSubmit('submitAddcategoryAndStay')) {
            $category = new Category((int)Tools::getValue('id_category'));
            if (Validate::isLoadedObject($category)) {
                $category->addImage2();
            }
        }
    }
}

PrestaShop 8.x (Symfony)

Sur PrestaShop 8.x, le back-office utilise Symfony. L'approche recommandée est de créer un module qui utilise les hooks de formulaire :


<?php
// modules/mymodule/mymodule.php (extrait)

public function hookActionCategoryFormBuilderModifier(array $params)
{
    /** @var FormBuilderInterface $formBuilder */
    $formBuilder = $params['form_builder'];

    $formBuilder->add('image2', FileType::class, [
        'label' => $this->trans('Image secondaire', [], 'Modules.Mymodule.Admin'),
        'required' => false,
        'attr' => ['accept' => 'image/*'],
    ]);

    // Pré-remplissage si l'image existe
    $categoryId = $params['id'];
    $params['data']['image2'] = null;
    $formBuilder->setData($params['data']);
}

public function hookActionAfterUpdateCategoryFormHandler(array $params)
{
    $this->handleImage2Upload($params);
}

public function hookActionAfterCreateCategoryFormHandler(array $params)
{
    $this->handleImage2Upload($params);
}

private function handleImage2Upload(array $params)
{
    $categoryId = $params['id'];
    $category = new Category((int)$categoryId);

    if (Validate::isLoadedObject($category)) {
        $category->addImage2();
    }
}

Cette approche par module est nettement plus propre et survivra aux mises à jour de PrestaShop.

Étape 4 : Affichage front-end

Affichage dans le template de catégorie

PrestaShop 1.6 (Smarty) :


{* Image secondaire de la catégorie courante *}
{if $category->id_image2}
    <img
        class="category-header-bg"
        src="{$link->getCatImageLink($category->link_rewrite, $category->id_image2, 'category_default')|escape:'html':'UTF-8'}"
        alt="{$category->name|escape:'html':'UTF-8'}"
        loading="lazy"
    />
{/if}

Attention à la variable utilisée : $category pour la catégorie en cours, $subcategory dans une boucle sur les sous-catégories. Confondre les deux est une source d'erreur classique :


{* CORRECT — dans une boucle de sous-catégories *}
{foreach from=$subcategories item=subcategory}
    <div class="subcategory-card">
        {* Image principale *}
        <img src="{$link->getCatImageLink($subcategory.link_rewrite, $subcategory.id_image, 'medium_default')|escape:'html':'UTF-8'}" alt="{$subcategory.name|escape:'html':'UTF-8'}" />

        {* Image secondaire *}
        {if isset($subcategory.id_image2) && $subcategory.id_image2}
            <img src="{$link->getCatImageLink($subcategory.link_rewrite, $subcategory.id_image2, 'medium_default')|escape:'html':'UTF-8'}" alt="" />
        {/if}
    </div>
{/foreach}

PrestaShop 8.x (Twig) :

Sur les versions modernes, l'image peut être exposée via un module qui enrichit les variables de template :


{# templates/catalog/listing/category.html.twig #}
{% if category.image2_url is defined and category.image2_url %}
    <div class="category-banner">
        <img
            src="{{ category.image2_url }}"
            alt="{{ category.name }}"
            loading="lazy"
            class="img-fluid"
        />
    </div>
{% endif %}

Étape 5 : Convention de nommage des fichiers image

Les images sont stockées dans /img/c/ avec la convention suivante :

FichierDescription `{id}-2.jpg`Image secondaire originale `{id}-2-category_default.jpg`Format catégorie par défaut `{id}-2-medium_default.jpg`Format moyen `{id}-2-small_default.jpg`Format miniature

Par exemple, pour la catégorie 5 : 5-2.jpg, 5-2-category_default.jpg, etc.

Bonnes pratiques et pièges à éviter

1. Toujours vider le cache des classes

L'erreur Call to undefined method après un override est quasi systématique lors du premier essai. Supprimez class_index.php dans tous les dossiers de cache.

2. Gestion robuste des uploads

Vérifiez toujours la validité du fichier uploadé (type MIME, taille, dimensions) avant traitement. Utilisez ImageManager::validateUpload() fourni par PrestaShop.

3. Privilégiez un module sur PrestaShop 8.x

Les overrides fonctionnent encore sur PS 8.x, mais la tendance est à leur suppression progressive. Pour un projet pérenne, encapsulez cette fonctionnalité dans un module dédié utilisant les hooks Symfony.

4. Ne confondez pas $category et $subcategory

Dans les templates Smarty, $category désigne la catégorie courante (la page consultée), tandis que $subcategory est l'itérateur dans un foreach sur les sous-catégories. Utiliser l'un à la place de l'autre affichera la mauvaise image — ou rien du tout.

5. Régénération des miniatures

Après avoir ajouté les images, pensez à régénérer les miniatures depuis Préférences > Images dans le back-office, ou via la ligne de commande :


# PrestaShop 8.x
php bin/console prestashop:image:regenerate --type=categories

Aller plus loin : image WebP et lazy loading

Sur PrestaShop 8.x, le support natif du format WebP est activable. Pour vos images secondaires, appliquez la même logique :


// Génération WebP en plus du JPEG
if (function_exists('imagewebp')) {
    ImageManager::resize(
        $tmp_name,
        $target_dir . $image_name . '-' . $image_type['name'] . '.webp',
        (int)$image_type['width'],
        (int)$image_type['height'],
        'webp'
    );
}

Combinez avec l'élément en front pour servir le format optimal :


<picture>
    <source srcset="/img/c/5-2-category_default.webp" type="image/webp">
    <img src="/img/c/5-2-category_default.jpg" alt="" loading="lazy">
</picture>
#override #catégories #personnalisation #back-office #images

Questions fréquentes

Tout ce que vous devez savoir sur ce sujet.

Un projet PrestaShop ?

Discutons-en directement.

★★★★★

193 projets livrés

Gratuit & sans engagement — réponse sous 24h

Alexandre Carette

Alexandre Carette

Expert PrestaShop & Architecture E-commerce

Développeur PrestaShop depuis 2014, 193 projets livrés. Je conçois des architectures headless Nuxt + PrestaShop et des outils d'automatisation IA pour les e-commerçants.