THELIA Forum

Welcome to the THELIA support and discusssion forum

Announcement

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

Offline


Je rencontre des problème après avoir validé le formulaire pour mettre à jour les infos d'une image d'un revendeur.
Je vois dans les param de __construct de la classe : core\lib\Thelia\Controller\Admin\AbstractCrudController.php

string $objectName the lower case object name. Example. "message"

Comment connaitre ce nom ?

Pour info, j'ai
ArtabanMarketplace\Controller\Base\SellerImageController.php

....
        parent::__construct(
            "amp_seller_image",
            "manual",
            "order",
            AdminResources::MODULE,
            AmpSellerImageEvents::CREATE,
            AmpSellerImageEvents::UPDATE,
            AmpSellerImageEvents::DELETE,
            AmpSellerImageEvents::TOGGLE_VISIBILITY,
            AmpSellerImageEvents::UPDATE_POSITION,
            "ArtabanMarketplace"
        );
    }
....

J'ai mis "amp_seller_image" car c'est le nom qui me paraissait logique et aussi le nom de la table.

Après avoir validé le formulaire j'obtiens :

LogicException in AbstractCrudController.php line 446:
Aucun amp_seller_image mis à jour

in AbstractCrudController.php line 446
at AbstractCrudController->processUpdateAction()
at call_user_func_array(array(object(SellerImageController), 'processUpdateAction'), array()) in HttpKernel.php line 139
at HttpKernel->handleRaw(object(Request), '1') in HttpKernel.php line 62
at HttpKernel->handle(object(Request), '1', true) in TheliaHttpKernel.php line 76
at TheliaHttpKernel->handle(object(Request), '1', true) in ParamInitMiddleware.php line 87
at ParamInitMiddleware->handle(object(Request), '1', true) in SessionMiddleware.php line 80
at SessionMiddleware->handle(object(Request), '1', true) in StackedHttpKernel.php line 23
at StackedHttpKernel->handle(object(Request), '1', true) in Kernel.php line 185
at Kernel->handle(object(Request)) in index_dev.php line 36

Je ne sais pas si le problème est bien le nom en minuscule de l'objet...

Toute aide sera grandement appréciée !

Offline


C'est parce que la méthode eventContainsObject($event) renvoie false.

En principe, lors d'une mise à jour, l'action place s$dans l'event l'objet qui a été modifié.


OpenStudio Toulouse

Offline


Je vois pas où est le problème... L'objet n'est donc pas créé.

Voici mon code :
ArtabanMarketplace\Form\SellerImageUpdateForm.php

....
class SellerImageUpdateForm extends ImageModification
{
    const FORM_NAME = "seller_image_update";

    public function getName()
    {
        return static::FORM_NAME;
    }

    public function buildForm()
    {
        parent::buildForm();
        $this->formBuilder
            ->add("id")
            ->add("seller_id")
        ;
    }
}

ArtabanMarketplace\Controller\Base\SellerImageController.php

namespace ArtabanMarketplace\Controller\Base;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Thelia\Controller\Admin\AbstractCrudController;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Tools\URL;
use ArtabanMarketplace\Event\AmpSellerImageEvent;
use ArtabanMarketplace\Event\AmpSellerImageEvents;
use ArtabanMarketplace\Model\AmpSellerImageQuery;
use Thelia\Core\Event\ToggleVisibilityEvent;
use Thelia\Core\Event\UpdatePositionEvent;

/**
 * Class SellerImageController
 * @package ArtabanMarketplace\Controller\Base
 */
class SellerImageController extends AbstractCrudController
{
    public function __construct()
    {
        parent::__construct(
            "amp_seller_image",
            "manual",
            "order",
            AdminResources::MODULE,
            AmpSellerImageEvents::CREATE,
            AmpSellerImageEvents::UPDATE,
            AmpSellerImageEvents::DELETE,
            AmpSellerImageEvents::TOGGLE_VISIBILITY,
            AmpSellerImageEvents::UPDATE_POSITION,
            "ArtabanMarketplace"
        );
    }

    /**
     * Return the creation form for this object
     */
    protected function getCreationForm()
    {
        return $this->createForm("seller_image.create");
    }

    /**
     * Return the update form for this object
     */
    protected function getUpdateForm($data = array())
    {
        if (!is_array($data)) {
            $data = array();
        }

        return $this->createForm("seller_image.update", "form", $data);
    }

    /**
     * Hydrate the update form for this object, before passing it to the update template
     *
     * @param mixed $object
     */
    protected function hydrateObjectForm($object)
    {
        $data = array(
            "id" => $object->getId(),
            "seller_id" => $object->getAmpSellerId(),
            "file" => $object->getFile(),
            "visible" => (bool) $object->getVisible(),
            "position" => $object->getPosition(),
            "title" => $object->getTitle(),
            "description" => $object->getDescription(),
            "chapo" => $object->getChapo(),
            "postscriptum" => $object->getPostscriptum(),
        );

        return $this->getUpdateForm($data);
    }

    /**
     * Creates the creation event with the provided form data
     *
     * @param mixed $formData
     * @return \Thelia\Core\Event\ActionEvent
     */
    protected function getCreationEvent($formData)
    {
        $event = new AmpSellerImageEvent();

        $event->setAmpSellerId($formData["seller_id"]);
        $event->setFile($formData["file"]);
        $event->setVisible($formData["visible"]);
        $event->setTitle($formData["title"]);
        $event->setDescription($formData["description"]);
        $event->setChapo($formData["chapo"]);
        $event->setPostscriptum($formData["postscriptum"]);

        return $event;
    }

    /**
     * Creates the update event with the provided form data
     *
     * @param mixed $formData
     * @return \Thelia\Core\Event\ActionEvent
     */
    protected function getUpdateEvent($formData)
    {
        $event = new AmpSellerImageEvent();

        $event->setId($formData["id"]);
        $event->setAmpSellerId($formData["seller_id"]);
        $event->setFile($formData["file"]);
        $event->setVisible($formData["visible"]);
        $event->setTitle($formData["title"]);
        $event->setDescription($formData["description"]);
        $event->setChapo($formData["chapo"]);
        $event->setPostscriptum($formData["postscriptum"]);

        return $event;
    }

    /**
     * Creates the delete event with the provided form data
     */
    protected function getDeleteEvent()
    {
        $event = new AmpSellerImageEvent();

        $event->setId($this->getRequest()->request->get("seller_image_id"));

        return $event;
    }

    /**
     * Return true if the event contains the object, e.g. the action has updated the object in the event.
     *
     * @param mixed $event
     */
    protected function eventContainsObject($event)
    {
        return null !== $this->getObjectFromEvent($event);
    }

    /**
     * Get the created object from an event.
     *
     * @param mixed $event
     */
    protected function getObjectFromEvent($event)
    {
        return $event->getAmpSellerImage();
    }

    /**
     * Load an existing object from the database
     */
    protected function getExistingObject()
    {
        return AmpSellerImageQuery::create()
            ->findPk($this->getRequest()->query->get("seller_image_id"))
        ;
    }

    /**
     * Returns the object label form the object event (name, title, etc.)
     *
     * @param mixed $object
     */
    protected function getObjectLabel($object)
    {
        return $object->getTitle();
    }

    /**
     * Returns the object ID from the object
     *
     * @param mixed $object
     */
    protected function getObjectId($object)
    {
        return $object->getId();
    }

    /**
     * Render the main list template
     *
     * @param mixed $currentOrder , if any, null otherwise.
     */
    protected function renderListTemplate($currentOrder)
    {
        $this->getParser()
            ->assign("order", $currentOrder)
        ;

        return $this->render("diaporama-images");
    }

    /**
     * Render the edition template
     */
    protected function renderEditionTemplate()
    {
        $this->getParserContext()
            ->set(
                "seller_image_id",
                $this->getRequest()->query->get("seller_image_id")
            )
        ;

        return $this->render("seller-image-edit");
    }

    /**
     * Must return a RedirectResponse instance
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    protected function redirectToEditionTemplate()
    {
        $id = $this->getRequest()->query->get("seller_image_id");

        return new RedirectResponse(
            URL::getInstance()->absoluteUrl(
                "/admin/module/artabanmarketplace/seller_image/edit",
                [
                    "seller_image_id" => $id,
                ]
            )
        );
    }

    /**
     * Must return a RedirectResponse instance
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    protected function redirectToListTemplate()
    {
        return new RedirectResponse(
            URL::getInstance()->absoluteUrl("/admin/module/artabanmarketplace/seller_image")
        );
    }

    protected function createToggleVisibilityEvent()
    {
        return new ToggleVisibilityEvent($this->getRequest()->query->get("seller_image_id"));
    }

    protected function createUpdatePositionEvent($positionChangeMode, $positionValue)
    {
        return new UpdatePositionEvent(
            $this->getRequest()->query->get("seller_image_id"),
            $positionChangeMode,
            $positionValue
        );
    }
}

ArtabanMarketplace\Controller\SellerImageController.php

namespace ArtabanMarketplace\Controller;

use ArtabanMarketplace\Controller\Base\SellerImageController as BaseSellerImageController;
use ArtabanMarketplace\Event\AmpSellerImageEvent;
use ArtabanMarketplace\Event\AmpSellerImageEvents;
use ArtabanMarketplace\Event\AmpSellerEvent;
use ArtabanMarketplace\Model\AmpSellerImage;
use ArtabanMarketplace\Model\AmpSellerImageQuery;
use Propel\Runtime\Exception\PropelException;
use Thelia\Core\Event\File\FileCreateOrUpdateEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Core\HttpFoundation\Response;
use Thelia\Core\Security\AccessManager;
use Thelia\Core\Security\Resource\AdminResources;
use Thelia\Log\Tlog;
use Thelia\Tools\URL;

/**
 * Class SellerImageController
 * @package ArtabanMarketplace\Controller
 */
class SellerImageController extends BaseSellerImageController
{
    /**
     * Load a object for modification, and display the edit template.
     *
     * @return \Thelia\Core\HttpFoundation\Response the response
     */
    public function updateAction()
    {
        // Check current user authorization
        if (null !== $response = $this->checkAuth($this->resourceCode, $this->getModuleCode(), AccessManager::UPDATE)) {
            return $response;
        }

        $image = $this->getExistingObject();

        if (is_null($image)) {
            return $this->pageNotFound();
        }

        $redirectUrl = $image->getRedirectionUrl();

        return $this->render('seller-image-edit', array(
            'seller_image_id' => $image->getId(),
            'redirectUrl' => $redirectUrl
        ));
    }

    /**
     * Put in this method post object update processing if required.
     *
     * @param  DiaporamaImageEvent  $updateEvent the update event
     * @return Response a response, or null to continue normal processing
     */
    protected function performAdditionalUpdateAction($updateEvent)
    {
        $this->updateImageFile($updateEvent->getAmpSellerImage());
        return null;
    }

    /**
     * Updating the image. Inspired by FileController::updateFileAction().
     *
     * @param DiaporamaImage $file   Diaporama Image ID.
     * @param string $eventName  the event type.
     *
     * @return DiaporamaImage
     */
    protected function updateImageFile(AmpSellerImage $file)
    {
        $fileUpdateForm = $this->createForm($file->getUpdateFormId());
        try {
            if (null === $file) {
                throw new \InvalidArgumentException(sprintf('%d image id does not exist', $file->getId()));
            }

            $event = new FileCreateOrUpdateEvent(null);

            $event->setModel($file);
            $event->setOldModel($file);

            $fileForm = $this->getRequest()->files->get($fileUpdateForm->getName());

            if (isset($fileForm['file'])) {
                $event->setUploadedFile($fileForm['file']);
            }

            $this->dispatch(TheliaEvents::IMAGE_SAVE, $event);

            $fileUpdated = $event->getModel();

            $this->adminLogAppend(
                AdminResources::MODULE,
                AccessManager::UPDATE,
                sprintf('Image with Ref %s (ID %d) modified', $fileUpdated->getTitle(), $fileUpdated->getId())
            );

            if ($this->getRequest()->get('save_mode') == 'close') {
                return $this->generateRedirect(
                    URL::getInstance()->absoluteUrl($file->getRedirectionUrl(), ['current_tab' => 'images'])
                );
            } else {
                return $this->generateSuccessRedirect($fileUpdateForm);
            }
        } catch (PropelException $e) {
            $message = $e->getMessage();
        } catch (\Exception $e) {
            $message = sprintf('Sorry, an error occurred: %s', $e->getMessage().' '.$e->getFile());
        }

        if (isset($message)) {
            Tlog::getInstance()->error(sprintf('Error during image editing : %s.', $message));

            $fileUpdateForm->setErrorMessage($message);

            $this->getParserContext()->addForm($fileUpdateForm)->setGeneralError($message);
        }

        return $file;
    }

    public function deleteImageAction($imageId)
    {
        // Check current user authorization
        if (null !== $response = $this->checkAuth($this->resourceCode, $this->getModuleCode(), AccessManager::DELETE)) {
            return $response;
        }
          
        try {
            $seller_image = AmpSellerImageQuery::create()->findPk($imageId);
            $event = new AmpSellerImageEvent($imageId);
            $event->setId($imageId);
            $this->dispatch($this->deleteEventIdentifier, $event);
            $this->performAdditionalDeleteAction($event);
            return $this->generateRedirect($seller_image->getRedirectionUrl());
        } catch (\Exception $e) {
            return $this->renderAfterDeleteError($e);
        }
    }
}

ArtabanMarketplace\Config\routing.xml

....
    <route id="ArtabanMarketplace.seller_image.edit" path="/admin/module/artabanmarketplace/seller_image/edit" methods="post">
        <default key="_controller">ArtabanMarketplace:SellerImage:processUpdate</default>
    </route>
....

En fait la seule différence avec diaporama est

Diaporamas\Form\DiaporamaImageUpdateForm.php

....
    public function buildForm()
    {
        parent::buildForm();
        $this->formBuilder
            ->add("id", DiaporamaImageIdType::TYPE_NAME)
            ->add("diaporama_id", DiaporamaIdType::TYPE_NAME)
        ;
    }
....

Mais quand je code quelque chose du même genre, j'ai une erreur. J'ai essayé de la résoudre, mais sans succès.
Du coup, je ne sais pas si ça ne marche pas à cause de ça ou d'autre chose.

Est ce que quelqu'un aurait une piste ?

Merci d'avance !

Offline


Mais ou crée tu ton objet ? Quelle est l'action qui traite l'évènement AmpSellerImageEvents::CREATE ?


OpenStudio Toulouse

Offline


C'est bien là que c'est mystérieux pour moi...
C'est donc la méthode processUpdateAction de la class AbstractCrudController qui renvoit l'erreur. Cette erreur dit que l'objet n'existe pas. Cette vérification est faite depuis les données du formulaire. Peut être que l'objet doit être renvoyé par le formulaire et qu'il serait créé à la génération du formulaire...
Je vais regarder cela après avoir mangé.

Comme je l'ai dit, je m'inspire du module Diaporama et c'est touffu au niveau de la gestion des image et la doc de Thelia qui est franchement légère, n'aborde pas ce sujet il me semble... En plus la communauté Thelia semble bien réduite, il y a que toi qui répond...

Mais bon, je vais m'accrocher. En persévèrent et avec ton aide, je devrais finir par y arriver.

Offline


Il ne faut pas plutôt chercher AmpSellerImageEvents::UPDATE ?

Offline


Bon, j'ai une piste avec les action qui sont des services. Ils en parlaient sur le doc de Symfony !
Allez hop ! C'est parti !

Offline


Globalement, le principe est le suivant : un contrôleur récupère les données d'une form, les place dans un event et dispatche cet event.

Une classe action (un service) est abonné à cet event, et réalise l'action demandée (creation, modif, suppression, etc.)

Dans le cas spécifique des images, Thelia fournit un service spécialisé, regarde la méthode \Carousel\Controller\ConfigurationController::uploadImage()

Il suffit que ton model (l'objet sauvé en base) implémente FileModelInterface pour que tu puisses l'utiliser.


OpenStudio Toulouse

Offline


Merci de tes explications.

En fait, en lisant la doc symfony, j'avais vu que le problème pouvait être lié aux services. Du coup, j'ai cherché un répertoire sevice comme je l'avais vu dans d'autres modules. N'en voyant pas, j'en ai déduit que c'était déjà géré par Thelia ( Dieu seul sait comment, soit... ) et j'ai chercher ailleurs... Et en fait une partie des services et dans le répertoire Action !

Du coup, j'ai recopié et adapté les fichiers dont j'avais besoin et les ai déclaré dans config comme service et zou ! J'ai eu d'autres problèmes que je suis parvenu à résoudre tout seul ! Comme un grand ! LOL !

Encore merci Roaster, tu m'a mis remis sur les rails en me demandant où était déclaré mon objet. Ca m'a fait repartir depuis le début et permis de trouver la solution !

Last edited by GillesL (07-06-2018 16:44:44)

Offline