THELIA Forum

Welcome to the THELIA support and discusssion forum

Announcement

Rejoignez la communauté sur le Discord Thelia : https://discord.gg/YgwpYEE3y3

Offline


Bonsoir,

J'ai besoin de mettre à jour le contenu du panier lorsqu'un produit y est ajouté.

Lorsque le produit est déjà ajouté au panier je ne veux pas que l'utilisateur rajoute le même produit au panier (je fais ça pour un type de produit en particulier)

<?php

// event CART_ADDITEM
public function addItemToCart(CartItem $event)
{
    $cartItem = $event->getCartItem();

    if($cartItem->getQuantity() > 1) {
        $cartItem->setQuantity(1);
        exit();
    }
}

Le exit est bien pris en compte pour ne pas mettre à jour le DOM (donc le DOM n'est pas mis à jour) mais si je rafraichi la page alors ma quantité est augmenté quand même de +1.

Comment garder la valeur à 1 quantité lorsque le produit est déjà au panier ?

Par la même occasion, j'ai besoin d'effectué cette condition seulement pour des produit particulier, est-ce que l'on peut récupérer le gabarit du produit dans l'$event ? En faisant un dump de $event je n'ai rien trouvé de concluant. Sinon je vais devoir faire une requête pour récupérer le produit qui est en train de s'ajouter au panier.

Merci d'avance.

Offline


Trois choses :

1) Si tu ne fais pas de save(), rien ne sera persisté en base. Donc : $cartItem->setQuantity(1)->save()

2) Le exit() est inutile (qu'est ce qui sera affiché dans le navigateur ?) , laisse donc la page panier se ré-afficher, tu verras que la quantité est bien à 1.
En règle général, Il est déconseillé d'intervenir sur le cycle de vie de la requête depuis un EventListener, il faut faire ça depuis un contrôleur. Dans les cas de force majeure, on peut lever une Thelia\Core\HttpKernel\Exception\RedirectException, qui redirigera le navigateur vers une URL de ton choix, mais ce n'est pas une bonne pratique.

3) Vérifie que la priorité de ton EventListener est basse (genre 10), afin d'être sur que la ise à jour de la quantité se fait bien en fin de traitement de la requête.

Pour récupérer le "gabarit" (le modèle ?) du produit, il te suffit d'aller chercher l'objet Product via le CartItem :

$product = $cartItem->getProduct();

Et voilà.


OpenStudio Toulouse

Offline


Merci pour ces précisions wink

Je trouve qu'intervenir sur le cycle de la requête depuis un event listener n'est pas top non plus. Mais dans mon cas je dois traiter ça lorsque l'utilisateur ajoute un produit à son panier.

Comment ferais tu intervenir un controller pour gérer cette événement ? (dans le contexte d'un module Thelia). J'aurais eu une appli custom j'aurais en effet gérer l'appel d'un service depuis mon controller mais depuis un module Thelia j'avoue que je ne sais pas encore trop comment coupler tout ça.

Offline


Comment ferais tu intervenir un controller pour gérer cette événement ?

A vue de nez, je conserverais l'event listener tel que tu l'as écrit (sauf le exit()), mais en plus je surchargerais la route /car/add, pour l'amener vers un contrôleur qui hérite de Front\Controller\CartController et surcharge addItem() pour implémenter la partie cycle de vie spécifique de la requête.

Mais bon, c'est à vue de nez... Le diable se cache dans les détails smile


OpenStudio Toulouse

Offline


Donc concrètement tu garde l'event tel que je l'ai mais tu ferais la sauvegarde de la quantité depuis le controller ?

Pourrais-tu m'expliquer concrètement le cycle de vie depuis CartController ? Parce que à aucun moment je vois la méthode save qui est appelé pour mettre à jour le panier et à vu de nez je ne vois pas dans quel objet la méthode save pourrait être appelé.
Une explication sur le cycle de vie de la requête et des events serait top wink

PS : est-ce qu'un cookbook ou une mise à jour de la doc est prévu ? Actuellement la doc est en grande partie le code source ^^. C'est pas que c'est mal mais des fois je Ctrl+click à n'en plus finir ^^

Offline


save(), c'est une méthode Propel. Tous les objets Propel (et donc l'instance de CartItem que tu manipules) sont enregistrés en base avec save().

Donc concrètement tu garde l'event tel que je l'ai mais tu ferais la sauvegarde de la quantité depuis le controller ?

Non, je garde l'event tel que tu l'as codé. Et si la réponse HTTP ne doit pas être celle retournée par le CartController, je renvoie ma propre réponse dans un contrôleur de mon module.

Pourrais-tu m'expliquer concrètement le cycle de vie depuis CartController ? Parce que à aucun moment je vois la méthode save qui est appelé

C'est l'action Thelia\Action\Cart qui gère le panier. Dans T2 , les contrôleurs génèrent des events qui sont dispatchés, et traités par les actions (= event listeners) abonnées à ces events. C'est ce que fait le CartController : il contrôle les données en entrée, les met dans un event, dispatche cet event, l'event est traité par les event listeners abonnés dans l'ordre des priorités, et le contrôleur retourne une réponse HTTP, le plus souvent une vue HTML.

Pour le cookbook, ce serait cool, c'est vrai. Il faudrait trouver du temps pour ça, ou l'écrire à plusieurs mains.


OpenStudio Toulouse

Offline


D'accord, merci pour cette réponse wink.
La chose qui me "gène" ici c'est que je vais avoir du code dupliqué dans l'event et le controller ?
Dans le controller on est d'accord que je dois faire la même condition que dans l'event sauf que je retourne simplement une autre réponse HTTP ?

(Je pourrais écrire le cookbook mais seulement quand j'aurais une meilleure compréhension du fonctionnement Thelia wink)

Offline

Offline


Dans le controller il faut que je renvois une réponse HTTP différente, avec un message d'erreur par exemple. Le truc c'est que si dans l'event je check la quantité, dans le controller pour renvoyer ma réponse je dois vérifier la même chose après cette ligne ?

Offline


Ben non, pourquoi ? Dans l'event listener, tu mets à jour ta quantité. Je ne comprends pas le problème.


OpenStudio Toulouse

Offline


Pour renvoyer ma propre réponse HTTP selon le cas ou la personne essaye d'ajouter à nouveau le produit pour renvoyer du JSON si c'est de l'AJAX en lui indiquant qu'il ne peut pas rajouter ce produit.

Tu as un exemple de la méthode addItem overridé ? selon la condition dans mon event ($cartItem->getQuantity() > 1) je veux renvoyer une réponse HTTP différente dans mon controller.

Offline


Pour que je comprenne bien, le controller doit gérer la réponse suite à mon event ? Donc renvoyer une erreur si la condition de mon event est respecté ?

Offline


selon la condition dans mon event ($cartItem->getQuantity() > 1) je veux renvoyer une réponse HTTP différente dans mon controller.

Pourquoi tu voudrais faire ça ?


OpenStudio Toulouse

Offline


Pour afficher une modale avec le contenu souhaité pour informer l'utilisateur qu'il ne peut pas ajouter une nouvelle quantité plutôt qu'afficher le récapitulatif du panier dans la modale.

Si on ne comprend pas la même chose, qu'est ce que tu voulais dire par :

Non, je garde l'event tel que tu l'as codé. Et si la réponse HTTP ne doit pas être celle retournée par le CartController, je renvoie ma propre réponse dans un contrôleur de mon module.

Comment tu gères l'envoie de ta nouvelle réponse ?

Offline


Je suppose que tu ecris un template, et que le controlleur le renvoie.


OpenStudio Toulouse

Offline


Actuellement je n'ai pas de template, mais en effet maintenant que tu le dis, ça a du sens pour renvoyer la réponse que je veux wink
Par contre je reviens à la même chose dans mon event manager j'aurais :

public function itemAddedToCart(CartEvent $event)
{
    $cartItem = $event->getCartItem();

    if ($cartItem->getProduct()->getTemplateId() === MonModule::getConfigValue(MonModule::CONFIG_TEMPLATE_ID)) {
        // Si le produit est déjà présent dans le panier on ne le rajoute pas une seconde fois
        if ($cartItem->getQuantity() > 1) {
            $cartItem->setQuantity(1)->save();
        }
    }
}

Et dans le controller je vais devoir refaire la vérification (le if) sur la quantité pour adapter la réponse à renvoyer ?


Autre petite question pendant que j'y pense. Dans la configuration des valeurs d'un module (module_configuration.html)

Dans quel contexte il faut utiliser soit MonModule::getConfigValue / MonModule::setConfigValue ou ConfigQuery::read / ConfigQuery::write ?

Je pense que getConfigValue est mieux dans le contexte du module. Dans les modules existant je compare et des fois il y a les 2 mais c'est souvent ConfigQuery qui ressort et je me demande pourquoi car MonModule::getConfigValue fait son boulot ^^

Last edited by Rtransat (13-10-2016 11:21:25)

Offline


Je viens de remarquer que c'est la route /ajax/addCartMessage qu'il faut que j'override pour gérer l'affichage du récapitulatif pour affiche autre chose que le fichier includes/addedToCart.html

C'est la 2ème requête ajax qui m’intéresse.

<route id="monmodule.ajax.addCartMessage" path="/ajax/addCartMessage">
    <default key="_controller">MonModule\Controller\CartController::noAction</default>
    <default key="_view">cantAddProduct</default>
</route>
public function noAction(Request $request)
{
    $view = null;

    if (! $view = $request->query->get('view')) {
        if ($request->request->has('view')) {
            $view = $request->request->get('view');
        }
    }
    if (null !== $view) {
        //$productQuantity = ????
        if ($productQuantity > 1) {
            // custom view
        }
        else {
            // addedToCart
        }
        
        $request->attributes->set('_view', $view);
    }

    if (null === $view && null === $request->attributes->get("_view")) {
        $request->attributes->set("_view", "index");
    }

    if (ConfigQuery::isRewritingEnable()) {
        if ($request->attributes->get('_rewritten', false) === false) {
            /* Does the query GET parameters match a rewritten URL ? */
            $rewrittenUrl = URL::getInstance()->retrieveCurrent($request);

            if ($rewrittenUrl->rewrittenUrl !== null) {
                /* 301 redirection to rewritten URL */
                throw new RedirectException($rewrittenUrl->rewrittenUrl, 301);
            }
        }
    }
}

Le problème ici c'est que dans mon action j'ai besoin de des infos de l’événement (qui sont surement en session) pour faire la même condition que dans mon listener et modifier la vue en fonction de la quantité. Comment je peux faire cela ?

PS : je n'arrive pas à inclure une vue de mon module dans _view du rooting.xml, je suis obligé de créer la vue dans mon thème, c'est normal ?

Offline


Ce n'est pas nécessaire de ré-écrire le DefaultController, il marche très bien. Dans le routing.xml de ton module, tu surcharges la route comme ceci :

<route id="monmodule.ajax.addCartMessage" path="/ajax/addCartMessage">
    <default key="_controller">Thelia\Controller\Front\DefaultController::noAction</default>
    <default key="_view">cantAddProduct</default>
</route>

Par contre, le fichier templates/frontOffice/default/cantAddProduct.html doit exister dans ton module.


OpenStudio Toulouse

Offline


Le module font utilise la route

<route id="ajax.addCartMessage" path="/ajax/addCartMessage">
    <default key="_controller">Thelia\Controller\Front\DefaultController::noAction</default>
    <default key="_view">includes/addedToCart</default>
</route>

J'ai besoin de renvoyer soit la vue par défaut du module Front ou si je suis dans la condition d'une quantité > 1 alors je dois retourner une autre vue.

Comment je peux faire ça ? Selon moi je dois overridé la méthode noAction pour checker la quantité d'un produit qui a été ajouté à mon panier.

Quel est le meilleur moyen ?

Offline

Offline


C'est l'implémentation du cycle de vie de la requête qui me pose problème justement.

On peut altérer la session des items dans le panier ?

public function addItem()
{
    $request = $this->getRequest();

    $cartAdd = $this->getAddCartForm($request);
    $message = null;

    try {
        $form = $this->validateForm($cartAdd);

        $cartEvent = $this->getCartEvent();

        $cartEvent->bindForm($form);

        $this->getDispatcher()->dispatch(TheliaEvents::CART_ADDITEM, $cartEvent);
        /*
        *
        * C'était ici que je parlais de duplication de code entre l'event et le controller
        */
        if ($cartEvent->getCartItem()->getProduct()->getTemplateId() === (int)MonModule::getConfigValue(MonModule::CONFIG_TEMPLATE_ID)) {
            $cart = $this->getSession()->getSessionCart();
            // altération de la session
            var_dump($cart); die();
        }

        .......

Last edited by Rtransat (13-10-2016 15:43:45)

Offline


Altérer la session ?

Le cart est stocké en base de données. le $cart que tu récupères avec $this->getSession()->getSessionCart() est un object Propel Cart...


OpenStudio Toulouse

Offline


C'est ce que j'ai remarqué par la suite.
Je vais faire une table pour stocker mes données le temps que l'utilisateur passe sa commande.

Par contre pour la gestion du cycle de vie de la requête dans le controller je ne vois pas comment gérer le cas dans l'override du controller hmm

Tu as pas un exemple sous le coude pour que je comprenne le principe de la gestion de la requête dans la méthode addItem du controller CartController ?

Offline


Y'a pas de méthode, tu renvoie juste le template qui te convient. Regarde la méthode changeViewForAjax() et comment elle est utilisée dans le CartController.


OpenStudio Toulouse

Offline


Oui mais ça c'est pour l'ajout au panier et pas la requête ajax/addCartMessage qui se situe ici

La vue de /cart/add j'arrive à la changer de la façon dont tu le dit mais pas pour la route /ajax/addCartMessage (il faut que je change le template en fonction de ma condition)