Lacier
  • Alternance Lacier
    • Gestion des Canvas dans le Panier
    • Chatbot
      • Étapes pour intégrer la suggestion de produit
    • Monitoring
    • Compression Image Excel
    • Inventaire
    • Gravure Laser
    • Cleaner Module
    • Scripts JavaScript Indépendants
      • Croix pour fermer le widget d'avis
      • Section Instagram
      • Section Panier Commander
      • Chatbot
    • Notification de Retard de Commande
    • Gestion des Templates de Mails
    • Relance Devis
    • Image FIlter
    • Affichage des produits
    • Attribute Replacer
Powered by GitBook
On this page
  • 1. Description du module
  • 2. Localisation des fichiers
  • 3. Fonctionnalités principales
  • 4. Architecture et Code
  • 5. Maintenance du module
  • 6. Résultat final
  1. Alternance Lacier

Image FIlter

1. Description du module

Le module Image Filter permet d'afficher des images cliquables sous forme de filtres sur les pages de catégories de la boutique PrestaShop. Il offre la possibilité de :

  • Définir des filtres par image.

  • Associer ces filtres à des catégories ou sous-catégories spécifiques.

  • Rediriger les utilisateurs vers des pages filtrées ou des URL spécifiques en cliquant sur les images.

Ce module est utile pour améliorer la navigation des utilisateurs et leur permettre d'appliquer des filtres visuels plus intuitifs.


2. Localisation des fichiers

Le module est situé dans le répertoire suivant de PrestaShop :

/modules/myimagefilter/

Fichiers principaux

Fichier

Rôle

myimagefilter.php

Fichier principal du module. Contient toute la logique métier et les hooks.

views/templates/hook/myimagefilter.tpl

Fichier .tpl responsable de l'affichage des filtres sur le front-office.

views/css/styles.css

Fichier CSS permettant de personnaliser l'apparence des images et des filtres.

img/filters/

Répertoire où les images des filtres téléversées via le back-office sont stockées.


3. Fonctionnalités principales

3.1 Affichage des filtres

Le module affiche des images sous forme de filtres sur les pages des catégories et sous-catégories.

  • Page principale : Les filtres sont directement associés aux pages de catégories par leur ID.

  • Sous-catégories : Grâce à une méthode récursive, le module récupère les sous-catégories pour inclure leurs filtres.

3.2 Gestion des filtres dans le back-office

Le module propose une interface dans le back-office pour :

  • Ajouter un filtre :

    • Définir l'ID de la catégorie.

    • Télécharger une image.

    • Définir un titre et une URL cible.

  • Modifier un filtre existant.

  • Supprimer un filtre existant.

Accès au module : Depuis le panneau d'administration de PrestaShop :

Modules > Image Filter Module

3.3 Table de base de données

Le module utilise une table dédiée pour stocker les informations des filtres :

Nom de la table : pss_image_filter

Structure :

Colonne

Type

Description

id_image_filter

INT (PK)

ID unique de l'image filtre.

id_page

INT

ID de la catégorie associée.

image_url

VARCHAR(255)

URL de l'image utilisée pour le filtre.

target_url

VARCHAR(255)

URL de destination lorsque l'image est cliquée.

title

VARCHAR(255)

Titre affiché sous l'image (optionnel).


4. Architecture et Code

4.1 Hooks utilisés

Le module utilise deux hooks principaux :

  • displayBeforeAmazingFilter : Permet d'afficher les filtres avant les filtres classiques de PrestaShop sur les pages de catégories.

  • header : Utilisé pour ajouter le fichier CSS à la page.

4.2 Méthodes importantes

Méthode

Description

getAllSubCategories()

Récupère récursivement toutes les sous-catégories associées à une catégorie donnée.

hookDisplayBeforeAmazingFilter()

Récupère les filtres pour une catégorie et ses sous-catégories, et les passe au fichier .tpl.

renderForm()

Génère le formulaire pour ajouter ou modifier un filtre dans le back-office.

renderList()

Affiche la liste des filtres existants dans le back-office. Inclut les sous-catégories associées.

<?php

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

class MyImageFilter extends Module
{
    public function __construct()
    {
        $this->name = 'myimagefilter';
        $this->tab = 'front_office_features';
        $this->version = '1.0.0';
        $this->author = 'Aleksandre';
        $this->need_instance = 0;

        parent::__construct();

        $this->bootstrap = true;

        $this->displayName = $this->l('Image Filter Module');
        $this->description = $this->l('Gérez des images cliquables pour appliquer des filtres spécifiques à des pages.');

        $this->ps_versions_compliancy = array('min' => '1.6.0.0', 'max' => '1.6.999.999');
    }

    public function install()
    {
        return parent::install()
            && $this->registerHook('displayBeforeAmazingFilter')
            && $this->registerHook('header')
            && $this->installDb();
    }

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

    private function installDb()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'image_filter` (
            `id_image_filter` INT(11) NOT NULL AUTO_INCREMENT,
            `id_page` INT(11) NOT NULL,
            `image_url` VARCHAR(255) NOT NULL,
            `target_url` VARCHAR(255) NOT NULL,
            `title` VARCHAR(255) NOT NULL,
            PRIMARY KEY (`id_image_filter`)
        ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;';
        return Db::getInstance()->execute($sql);
    }

    private function uninstallDb()
    {
        // $sql = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'image_filter`;';
        // return Db::getInstance()->execute($sql);
    }

    public function getContent()
    {
        $output = '';

        if (Tools::isSubmit('submitImageFilter')) {
            $id_image_filter = (int)Tools::getValue('id_image_filter', 0);
            $id_page = (int)Tools::getValue('id_page');
            $title = Tools::getValue('title');
            $target_url = Tools::getValue('target_url');
            $image_url = Tools::getValue('current_image_url', '');

            if (isset($_FILES['image_file']) && !empty($_FILES['image_file']['name'])) {
                $image_name = Tools::str2url($_FILES['image_file']['name']);
                $upload_dir = _PS_IMG_DIR_ . 'filters/';
                if (!is_dir($upload_dir)) {
                    mkdir($upload_dir, 0755, true);
                }
                $image_path = $upload_dir . $image_name;
                if (move_uploaded_file($_FILES['image_file']['tmp_name'], $image_path)) {
                    $image_url = _PS_BASE_URL_ . '/img/filters/' . $image_name;
                } else {
                    $output .= $this->displayError($this->l('Erreur lors du téléversement de l\'image.'));
                }
            }

            $data = [
                'id_page' => $id_page,
                'title' => $title,
                'target_url' => $target_url,
                'image_url' => $image_url,
            ];

            if ($id_image_filter) {
                Db::getInstance()->update('image_filter', $data, 'id_image_filter = ' . (int)$id_image_filter);
                $output .= $this->displayConfirmation($this->l('Configuration modifiée avec succès.'));
            } else {
                Db::getInstance()->insert('image_filter', $data);
                $output .= $this->displayConfirmation($this->l('Configuration ajoutée avec succès.'));
            }
        }

        if (Tools::isSubmit('deleteImageFilter')) {
            $id_image_filter = (int)Tools::getValue('id_image_filter');
            if ($id_image_filter && Db::getInstance()->delete('image_filter', 'id_image_filter = ' . $id_image_filter)) {
                $output .= $this->displayConfirmation($this->l('Configuration supprimée avec succès.'));
            } else {
                $output .= $this->displayError($this->l('Erreur lors de la suppression.'));
            }
        }

        $output .= $this->renderForm();
        $output .= $this->renderList();

        return $output;
    }

    public function renderForm()
    {
        $id_image_filter = (int)Tools::getValue('id_image_filter', 0);
        $filter = $id_image_filter ? Db::getInstance()->getRow('SELECT * FROM `' . _DB_PREFIX_ . 'image_filter` WHERE id_image_filter = ' . $id_image_filter) : [];

        $helper = new HelperForm();
        $helper->module = $this;
        $helper->name_controller = $this->name;
        $helper->token = Tools::getAdminTokenLite('AdminModules');
        $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name;
        $helper->title = $this->displayName;
        $helper->show_toolbar = true;
        $helper->toolbar_scroll = false;
        $helper->bootstrap = true; // S'assurer que le helper utilise bootstrap
        $helper->submit_action = 'submitImageFilter';

        $helper->fields_value = [
            'id_image_filter' => isset($filter['id_image_filter']) ? $filter['id_image_filter'] : '',
            'id_page' => isset($filter['id_page']) ? $filter['id_page'] : '',
            'title' => isset($filter['title']) ? $filter['title'] : '',
            'target_url' => isset($filter['target_url']) ? $filter['target_url'] : '',
            'current_image_url' => isset($filter['image_url']) ? $filter['image_url'] : '',
        ];

        $helper->tpl_vars = [
            'fields_value' => $helper->fields_value,
            'languages' => $this->context->controller->getLanguages(),
            'id_language' => $this->context->language->id
        ];

        $fields_form = [
            'form' => [
                'legend' => [
                    'title' => $this->l('Ajouter ou modifier une image pour le filtre'),
                    'icon' => 'icon-picture',
                ],
                'input' => [
                    ['type' => 'hidden', 'name' => 'id_image_filter'],
                    ['type' => 'text', 'label' => $this->l('ID de la page'), 'name' => 'id_page', 'required' => true],
                    ['type' => 'file', 'label' => $this->l('Image'), 'name' => 'image_file'],
                    ['type' => 'hidden', 'name' => 'current_image_url'],
                    ['type' => 'text', 'label' => $this->l('Titre'), 'name' => 'title'],
                    ['type' => 'text', 'label' => $this->l('URL cible'), 'name' => 'target_url', 'required' => true],
                ],
                'submit' => [
                    'name' => 'submitImageFilter',
                    'title' => $this->l('Enregistrer'),
                    'class' => 'btn btn-primary pull-right',
                ],
            ],
        ];

        return $helper->generateForm([$fields_form]);
    }



    public function renderList()
    {
        // 1. Récupération des images et des pages associées (tableau principal)
        $fields_list = [
            'id_image_filter' => ['title' => $this->l('ID'), 'type' => 'text', 'align' => 'center'],
            'id_page' => ['title' => $this->l('ID de la Page'), 'type' => 'text'],
            'title' => ['title' => $this->l('Titre'), 'type' => 'text'],
            'target_url' => ['title' => $this->l('URL cible'), 'type' => 'text'],
            'image_url' => ['title' => $this->l('Image'), 'type' => 'image'],
            'delete_link' => [
                'title' => $this->l('Action'),
                'callback' => 'renderDeleteLink',
                'callback_object' => $this
            ],
        ];
    
        $helper = new HelperList();
        $helper->actions = ['edit'];
        $helper->identifier = 'id_image_filter';
        $helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name;
        $helper->token = Tools::getAdminTokenLite('AdminModules');
    
        $filters = Db::getInstance()->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'image_filter`');
    
        // On ajoute le lien de suppression à chaque ligne
        foreach ($filters as &$f) {
            $f['delete_link'] = AdminController::$currentIndex.'&configure='.$this->name.'&deleteImageFilter&id_image_filter='.$f['id_image_filter'].'&token='.Tools::getAdminTokenLite('AdminModules');
        }
    
        // 2. Affichage des sous-catégories associées
        $subCategoryFields = [
            'id_page' => ['title' => $this->l('ID de la Page'), 'type' => 'text'],
            'subcategories' => ['title' => $this->l('Sous-catégories associées'), 'type' => 'text'],
        ];
    
        $subCategoryData = [];
        foreach ($filters as $filter) {
            $subCategories = $this->getAllSubCategories($filter['id_page']);
            $subCategoryData[] = [
                'id_page' => $filter['id_page'],
                'subcategories' => !empty($subCategories) ? implode(', ', $subCategories) : $this->l('Aucune sous-catégorie'),
            ];
        }
    
        $helperSubCategories = new HelperList();
        $helperSubCategories->identifier = 'id_page';
        $helperSubCategories->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name;
        $helperSubCategories->token = Tools::getAdminTokenLite('AdminModules');
    
        // 3. Génération des deux tableaux distincts
        $output = '<h3>' . $this->l('Liste des filtres d\'images') . '</h3>';
        $output .= $helper->generateList($filters, $fields_list);
    
        $output .= '<h3>' . $this->l('Sous-catégories associées aux pages') . '</h3>';
        $output .= $helperSubCategories->generateList($subCategoryData, $subCategoryFields);
    
        return $output;
    }
    

    public function hookDisplayBeforeAmazingFilter($params)
    {
        $id_category = (int)Tools::getValue('id_category');
    
        if (!$id_category) {
            return ''; // Sortir si aucune catégorie n'est spécifiée
        }
    
        // Récupérer l'ID parent (si nécessaire)
        $parentCategory = Db::getInstance()->getValue('SELECT id_parent FROM `' . _DB_PREFIX_ . 'category` WHERE id_category = ' . $id_category);
    
        // Récupération des sous-catégories
        $categoryIds = $this->getAllSubCategories($id_category);
        $categoryIds[] = $id_category; // Inclure la catégorie elle-même
    
        // Ajouter l'ID parent si nécessaire
        if ($parentCategory && $parentCategory != 0) {
            $categoryIds[] = $parentCategory;
        }
    
        // Requête SQL
        $sql = 'SELECT * FROM `' . _DB_PREFIX_ . 'image_filter` WHERE `id_page` IN (' . implode(',', array_map('intval', $categoryIds)) . ')';
        $images = Db::getInstance()->executeS($sql);
    
        if (empty($images)) {
            return ''; // Sortir si aucun filtre n'est trouvé
        }
    
        // Assignation des images à Smarty
        $this->context->smarty->assign(['images' => $images]);
        return $this->display(__FILE__, 'views/templates/hook/myimagefilter.tpl');
    }
    
    

    private function getAllSubCategories($id_category)
    {
        $sql = 'SELECT id_category FROM `' . _DB_PREFIX_ . 'category` WHERE `id_parent` = ' . (int)$id_category;
        $subCategories = Db::getInstance()->executeS($sql);

        $categoryIds = [];
        foreach ($subCategories as $category) {
            $categoryIds[] = $category['id_category'];
            
            // Appel récursif pour récupérer les sous-catégories enfants
            $categoryIds = array_merge($categoryIds, $this->getAllSubCategories($category['id_category']));
        }

        return $categoryIds;
    }

    public function hookHeader($params)
    {
        $this->context->controller->addCSS($this->_path . 'views/css/styles.css');
    }

    public function renderDeleteLink($echo, $tr)
    {
        return '<a href="'.$tr['delete_link'].'" class="btn btn-danger" onclick="return confirm(\''.$this->l('Êtes-vous sûr ?').'\');">'.$this->l('Supprimer').'</a>';
    }

}
<div class="image-filter-container">
    {foreach from=$images item=image}
        <div class="image-filter-item">
            <a href="{$image.target_url}">
                <img src="{$image.image_url}" alt="{$image.title}" class="img-responsive" />
            </a>
            <p class="image-filter-title">{$image.title}</p>
        </div>
    {/foreach}
</div>

<script>
document.addEventListener('DOMContentLoaded', function () {
    // Sélectionne toutes les images cliquables
    const filterImages = document.querySelectorAll('.image-filter-item a');

    // Récupère l'URL actuelle de la page
    const currentUrl = window.location.href;

    filterImages.forEach((link) => {
        const targetUrl = new URL(link.href); // Création de l'URL cible
        const targetParams = targetUrl.searchParams;

        // **Vérification de la correspondance avec l'URL actuelle**
        let isSelected = false;

        // Si l'URL du filtre est une URL absolue, on compare l'URL complète
        if (link.href.startsWith('http')) {
            if (link.href === currentUrl) {
                isSelected = true;
            }
        } else {
            // Si c'est une URL relative, on compare les paramètres de l'URL
            const currentParams = new URL(window.location.href).searchParams;
            let allParamsMatch = true;
            targetParams.forEach((value, key) => {
                if (currentParams.get(key) !== value) {
                    allParamsMatch = false;
                }
            });
            isSelected = allParamsMatch;
        }

        // **Ajout de la classe "selected" au hover actif**
        const imgElement = link.querySelector('img');
        if (isSelected) {
            imgElement.classList.add('selected');
        }

        // **Gère le clic sur les liens**
        link.addEventListener('click', function (event) {
            event.preventDefault();

            // Si le lien est une URL complète (ex: http:// ou https://), on redirige vers la page
            if (link.href.startsWith('http')) {
                window.location.href = link.href;
                return; // On arrête l'exécution du reste de la fonction
            }

            // Sinon, on applique les filtres en utilisant les paramètres de l'URL cible
            const newUrl = new URL(window.location.href);
            targetParams.forEach((value, key) => {
                newUrl.searchParams.set(key, value);
            });

            // Met à jour l'URL et recharge la page
            window.history.replaceState({}, '', newUrl);
            window.location.reload();
        });
    });

    // **Gestion du bouton "Effacer les filtres"**
    const clearButton = document.querySelector('.clearAll .all');
    if (clearButton) {
        clearButton.addEventListener('click', function (event) {
            event.preventDefault();

            // Retire la classe "selected"
            document.querySelectorAll('.image-filter-item img.selected').forEach((img) => {
                img.classList.remove('selected');
            });

            // Supprime tous les paramètres de l'URL
            const clearUrl = new URL(window.location.href);
            clearUrl.search = '';

            // Met à jour l'URL et recharge la page
            window.location.href = clearUrl.href;
        });
    }
});
</script>
.image-filter-container {
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
    justify-content: center;
    margin-top: 20px;
    padding: 0 80px 0 80px;
}

.image-filter-item {
    flex: 0 1 calc(15.66% - 10px); /* 6 images par ligne */
    box-sizing: border-box;
    text-align: center;
}

.image-filter-item img {
    max-width: 100%;
    height: auto;
    border: 1px solid #ddd;
    padding: 10px;
    background-color: #fff;
    transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.image-filter-item img:hover {
    transform: scale(1.05);
    box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
}

.image-filter-item img.selected {
    border: 2px solid #d8673f;
    box-shadow: 0 6px 10px rgba(0, 0, 0, 0.25);
}

.image-filter-title {
    margin-top: 10px;
    font-size: 14px;
    font-weight: bold;
    color: #333;
    text-align: center;
}

5. Maintenance du module

5.1 Ajouter un nouveau filtre

  1. Accède au module via Modules > Image Filter Module.

  2. Remplis les champs requis dans le formulaire :

    • ID de la page : ID de la catégorie où le filtre sera affiché.

    • Image : Téléverse une image pour le filtre.

    • Titre : (Optionnel) Le texte affiché sous l'image.

    • URL cible : URL vers laquelle l'utilisateur sera redirigé en cliquant sur l'image.

  3. Clique sur Enregistrer.

5.2 Modifier ou supprimer un filtre

  • Modifier : Clique sur le bouton Modifier dans la liste des filtres.

  • Supprimer : Clique sur le bouton Supprimer et confirme.


6. Résultat final

PreviousRelance DevisNextAffichage des produits

Last updated 5 months ago