1
votes

obtenir l'ID généré automatiquement pour un champ supplémentaire, en utilisant post-persist

J'ai un produit avec un identifiant autogenarete et j'ai également un champ de code produit, qui saisit des valeurs basées sur les choix de l'utilisateur combinés avec la clé autogénérée pour créer le code produit. Cependant, je ne peux pas saisir l'identifiant d'autogénat lors de l'insertion d'un nouveau produit.

J'ai utilisé le premier prépersiste et la mise à jour, mais cela ne saisit pas l'identifiant lors de l'insertion d'un nouveau produit. seulement lors de la mise à jour, il saisit l'identifiant

<?php 
# src/EventSubscriber/EasyAdminSubscriber.php
namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use App\Entity\Product;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
class EasyAdminSubscriber implements EventSubscriberInterface
{


    public static function getSubscribedEvents()
    {
        return array(
            'easy_admin.post_persist' => array('setProductcode'),
        );
    }


    /**
     * @param LifecycleEventArgs $args
     */
    public function setProductcode(GenericEvent $event, LifecycleEventArgs $args)
    {

      $entityManager = $args->getEntityManager();


        $entity = $event->getSubject();



        if (!($entity instanceof Product)) {
            return;
        }


        $whole = 'yooo'; 


        $entityManager->flush();

        $entity->setProductcode($whole);

        $event['entity'] = $entity;
    }
}

J'essaie d'utiliser postpersist, et j'ai changé mon champ pour être nullablae true mais cela enregistre le code produit comme nul.

Argument 2 passed to App\EventSubscriber\EasyAdminSubscriber::setProductcode() must be an instance of Doctrine\Common\Persistence\Event\LifecycleEventArgs, string given, called in 

J'ai utilisé postload et postpersist ensemble et il affiche le code produit en sortie .. mais il ne l'enregistre pas dans la base de données.

 * @ORM\PostLoad
 * @ORM\PostPersist

Comment puis-je récupérer l'identifiant dans l'entité pour le mettre dans un champ supplémentaire? Merci d'avance!


modifier

J'ai créé un easyadminsubcriber et cela fonctionne lorsque j'utilise le retour pre_persist. Cependant, le code ci-dessous est mis à jour vers post_persist. mais j'ai du mal à implémenter la fonction flush avec lifecycleeventargs.

J'ai reçu l'erreur suivante

/**
 * @var string
 *
 * @ORM\Column(type="string", length=191, unique=true, nullable=true)
 */
private $productcode;

ci-dessous est mon code post_persist

XXX


0 commentaires

3 Réponses :


2
votes

par défaut, l'id n'est défini que lorsque l'entité est vidée dans la base de données. cela signifie que vous devez générer votre code produit après avoir vidé l'entité, puis à nouveau purger. doctrine ne peut pas utiliser une magie sophistiquée pour déterminer l'identifiant avant qu'il n'entende réellement de la base de données, il n'y a donc pas vraiment d'autre moyen. (si vous voulez faire tout cela dans l'entité, je ne peux pas imaginer une autre façon pratique et propre de le faire)

mise à jour

vous devriez utiliser PostPersist ( tout en conservant PreUpdate).

L'événement postPersist se produit pour une entité après que l'entité a été rendue persistante. Il sera appelé après les opérations d'insertion de la base de données. Les valeurs de clé primaire générées sont disponibles dans l'événement postPersist. ( source ) p >

donc, la clé primaire générée y est disponible. Cependant, ce n'est que après que vous avez vidé l'entité. Donc, vous devrez vider à nouveau pour écrire le code produit dans la base de données également.

créer des gestionnaires d'événements appropriés (car "setProductcode" est un setter, pas un gestionnaire d'événements , au moins en termes de nom)

/**
 * PostPersist triggers after the _creation_ of entities in db
 * @ORM\PostPersist
 */
public function postPersist(LifecycleEventArgs $args) {
    $this->setProductcode();
    // need to flush, so that changes are written to database
    $args->getObjectManager()->flush();
}   

/**
 * PreUpdate triggers before changes are written to db
 * @ORM\PreUpdate
 */
public function preUpdate() {
    $this->setProductcode(); 
    // don't need to flush, this happens before the database calls
}

(voir https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html# lifecycle-callbacks-event-argument pour plus d'informations)

(avertissement: cette réponse a été fortement modifiée depuis sa création, laissant les commentaires connectés en partie sans références pertinentes)


7 commentaires

Merci pour les commentaires. 1) -> pouvez-vous vider le fichier enity lui-même? Je suis un peu confus car vous pouvez appeler * @ORM \ PostPersist mais vous ne pouvez pas vider? je ne connais pas toutes les options avaialbel .. ou dois-je écrire cela dans le contrôleur? 2) Je ne suis pas très familier avec la transaction. 3) c'est en effet une possibilité. mais je ne sais pas comment faire 4) ouais, je suppose que je peux le faire, mais j'ai pensé que pour l'avenir, il serait peut-être pratique de l'enregistrer dans la base de données 5) comment ajouteriez-vous un numéro au code produit? qui est ascendant .. Merci pour l'instant cependant! j'apprécie les commentaires


oh, vous avez raison sur le post-persist. cependant, vous devrez à nouveau vider, car la modification de votre code produit n'est effectuée qu'après l'avoir écrite dans la base de données. ...


@Missblues tu as raison, j'étais dans le mauvais sens là-bas ... j'ai mis à jour ma réponse.


Pas de soucis, j'essaie d'être clair dans mes questions, mais généralement j'écris trop et ça peut devenir compliqué. Quoi qu'il en soit, je comprends que je dois rincer mais je ne sais pas où / comment mettre ça. Pour donner un peu plus de contexte. J'utilise easyadmin pour gérer tous les admincontrollers et l'extension des admincontrollers avec une configuration multilingue n'est pas idéale avec easyadmin. C'est pourquoi je recherche des solutions pour gérer tout ce qui se trouve dans le fichier d'entité / ou peut-être d'autres solutions?


@Missblues easyadmin bundle ... mh ... vous pouvez ajouter un écouteur d'événement pour l'événement post_create, injecter le entitymanger et appeler simplement flush là-bas ... peut-être ... symfony.com/doc/master/bundles/EasyAdminBundle/book/…


J'ai mis à jour mon code initial avec l'écouteur d'événements mais j'ai du mal à injecter le entitymanager pour le vidage.


continuons cette discussion dans le chat .



2
votes

Avez-vous vraiment besoin de conserver le code produit s'il ne s'agit que d'une concaténation d'autres colonnes? Et si vous utilisiez simplement un getter efficace?

public function getProductcode() 
{
  if(!empty($this->productcode)){
    return $this->productcode;
  }

  if(empty($this->id)){
    return "to be determined";
  }

  $this->productcode = $this->option1 . $this->option2 . $this->id;

  return $this->productcode;
}


3 commentaires

Merci pour les commentaires. ouais, je pensais juste que pour les futures références / extensions, ce serait peut-être mieux s'il est réellement stocké. n'est-ce pas courant?


Il est déjà stocké dans ses composants, ce qui rend quelque peu redondant la répétition des données dans un format différent, à moins que vous ne l'utilisiez comme index, mais que vous ayez déjà la clé primaire id qui devrait être plus efficace pour rechercher des résultats, et les index peuvent être définis sur plusieurs colonnes, donc je ne vois pas la nécessité d'une colonne séparée dans ce cas, surtout si cela cause autant de maux de tête.


Je vois. si je ne fais rien d'autre avec l'identifiant, alors oui, ce n'est peut-être pas nécessaire. Cependant, j'aime effectuer certaines fonctions sur l'identifiant pour changer la sortie, etc. (également pour les futurs addons). et si un postpersiste existe pour pouvoir obtenir l'identifiant pourquoi ne pas l'utiliser :)



0
votes

Très bien, j'ai maintenant 2 solutions pour définir l'ID de génération automatique dans un autre champ (en n'utilisant pas le contrôleur). La première est directement dans le fichier d'entité lui-même, comme indiqué dans la réponse @jakumi.

/**
 * @ORM\PostPersist
 * @ORM\PreUpdate
 */
public function setProductcode() 
{


    $part1 = $entity->getProducttype()->getTypenumber();
    $id1 = $entity->getId();
    $part2 = sprintf("%03d", $id1);
    $whole = $part1.$part2; 

  $this->productcode = $whole;


}

Une autre solution consiste à utiliser l'abonné d'événements.

<?php 
# src/EventSubscriber/EasyAdminSubscriber.php
namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\Product;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
class EasyAdminSubscriber implements EventSubscriberInterface
{




    public function __construct(EntityManagerInterface $em) {

        $this->em = $em;
    }



    public static function getSubscribedEvents()
    {
        return array(
            'easy_admin.post_persist' => array('setProductcode'),
        );
    }



    public function setProductcode(GenericEvent $event)
    {


        $entity = $event->getSubject();

        if (!($entity instanceof Product)) {
            return;
        }


        $this->em->flush();

        $entity->setProductcode();

        $this->em->flush();

    }
}

et mon code d'entité avec postpersiste et pré-mise à jour

   public function setProductcode()
    {

      $part = $this->producttype->gettypenumber();
      $id1 = $this->id;
      $part = sprintf("%03d", $id1);

      $whole = $part1.''.$part2;    

      return $this->productcode= $whole;

    }


/**
 * PostPersist triggers after the _creation_ of entities in db
 * @ORM\PostPersist
 */
public function postPersist(LifecycleEventArgs $args) {


    $this->setPoductcode();
    // need to flush, so that changes are written to database
    $args->getObjectManager()->flush();
}   

/**
 * PreUpdate triggers before changes are written to db
 * @ORM\PreUpdate
 */
public function preUpdate() {


    $this->setProductcode();
    // don't need to flush, this happens before the database calls
}

Merci @Jakumi pour l'explication et les directives pour les deux solutions.


0 commentaires