Home > Software engineering >  problem with a request on 2 tables with symfony
problem with a request on 2 tables with symfony

Time:09-17

I trie to get the documents stored with a message in the message_document table with a doctrine request but the request loops and ends up filling my memory I tried the same request with sql on Dbeaver and it runs with no problem

great thanks for your help

My message.php

enter code here<?php

namespace App\Entity;

use DateTime;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\MessageRepository;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity(repositoryClass=MessageRepository::class)
 */
class Message
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="text")
     */
    private $content;

    /**
     * @ORM\Column(type="datetime")
     */
    private $createdAt;

    /**
    * @ORM\ManyToOne(targetEntity=Conversation::class, inversedBy="messages")
    */
    private $conversation; 

    /**
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="messages")
     */
    private $user;

    /**
     * @ORM\OneToMany(targetEntity=MessageDocument::class, mappedBy="messages")
     */
    private $messageDocuments;


    public function __construct( string $content, User $user, Conversation $converstation) {
        $this->content      = $content;
        $this->user         = $user;
        $this->conversation = $converstation;
        $this->createdAt    = new \DateTime('now');
        $this->messageDocuments = new ArrayCollection();
    }

    public function getId():?int {
        return $this->id;
    }

    public function getContent():?string {
        return $this->content;
    }
    public function setContent(string $content):self {
        $this->content = $content;
        return $this;
    }

    public function getCreatedAt():?\DateTimeInterface {
        return $this->createdAt;
    }
    public function setCreatedAt(\DateTimeInterface $createdAt):self {
        $this->createdAt = $createdAt;
        return $this;
    }
    
    public function getConversation():?Conversation
    {
        return $this->conversation;
    }
    public function setConversation(?Conversation $conversation):self {
        $this->conversation = $conversation;
        return $this;
    }

    public function getUser():?User {
        return $this->user;
    }
    public function setUser(?User $user):self {
        $this->user = $user;
        return $this;
    }

    /**
     * @return Collection|MessageDocument[]
     */
    public function getMessageDocuments(): Collection
    {
        return $this->messageDocuments;
    }

    public function addMessageDocument(MessageDocument $messageDocument): self
    {
        if (!$this->messageDocuments->contains($messageDocument)) {
            $this->messageDocuments[] = $messageDocument;
            $messageDocument->setMessages($this);
        }

        return $this;
    }

    public function removeMessageDocument(MessageDocument $messageDocument): self
    {
        if ($this->messageDocuments->removeElement($messageDocument)) {
            // set the owning side to null (unless already changed)
            if ($messageDocument->getMessages() === $this) {
                $messageDocument->setMessages(null);
            }
        }

        return $this;
    }

    
}

my MessageDocument.php

enter code here<?php

namespace App\Entity;

use App\Repository\MessageDocumentRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=MessageDocumentRepository::class)
 */
class MessageDocument
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $fileName;

    /**
     * @ORM\Column(type="datetime")
     */
    private $updatedAt;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $type;

    /**
     * @ORM\ManyToOne(targetEntity=Message::class, inversedBy="messageDocuments")
     */
    private $message;

    /**
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="messageDocuments")
     */
    private $sender;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getFileName(): ?string
    {
        return $this->fileName;
    }

    public function setFileName(string $fileName): self
    {
        $this->fileName = $fileName;

        return $this;
    }

    public function getUpdatedAt(): ?\DateTimeInterface
    {
        return $this->updatedAt;
    }

    public function setUpdatedAt(\DateTimeInterface $updatedAt): self
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    public function getType(): ?string
    {
        return $this->type;
    }

    public function setType(?string $type): self
    {
        $this->type = $type;

        return $this;
    }

    public function getMessage(): ?Message
    {
        return $this->message;
    }

    public function setMessage(?Message $message): self
    {
        $this->message = $message;

        return $this;
    }

    public function getSender(): ?User
    {
        return $this->sender;
    }

    public function setSender(?User $sender): self
    {
        $this->sender = $sender;

        return $this;
    }
}

the request on MessageDocument join Message

/**
*  @return MessageDocument[] Returns an array of MessageDocument objects
*/
 //$qb->expr()->eq('md.id = :val')
//,Join::WITH,$qb->expr()->eq('md.id = :val') 
public function findDocByMessageId($messageId)
{
    return $this->createQueryBuilder('md')
        ->select('md')
        ->join('md.message','m')        
        ->where('m.id =:val')
        ->setParameter('val', $messageId)
        ->setMaxResults(20)
        ->getQuery()    
        ->getResult();
}

the calling of the repo request

$allMessages = new ArrayCollection();
    $docs=[];
    foreach ($messages as $messageUnique) {
        $messId = $messageUnique->getId();
        $documentsMessages = $messageRepository->findDocByMessageId($messId);

        if($documentsMessages !== null){
            foreach($documentsMessages as $document){
                $docs=$document;
            }
                //$messageUnique->addMessageDocument($document);
        }

        $conversation->setLastMessage($messageUnique);
        
        $messageUnique = array(
            'id'        => $messageUnique->getId(),
            'author'    => $messageUnique->getUser()->getFullName(),
            'authorId'  => $messageUnique->getUser()->getId(),
            'content'   => $messageUnique->getContent(),
            'createdAt' => $messageUnique->getCreatedAt()
        );
        $allMessages->add($messageUnique);

    }

CodePudding user response:

I'm not sure at all about setting $messageUnique = array() with value of the same variable, you should probably use another name of variable. If you have an infinite loop problem it's coming from the PHP in your case.

CodePudding user response:

I have changed my code and now error message is about circular reference

Message entity <?php

namespace App\Entity;

use DateTime;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\MessageRepository;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity(repositoryClass=MessageRepository::class)
 */
class Message
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="text")
     */
    private $content;

    /**
     * @ORM\Column(type="datetime")
     */
    private $createdAt;

    /**
    * @ORM\ManyToOne(targetEntity=Conversation::class, inversedBy="messages")
    */
    private $conversation; 

    /**
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="messages")
     */
    private $user;

    /**
     * @ORM\OneToMany(targetEntity=MessageDocument::class, mappedBy="message")
     */
    private $messageDocument;


    public function __construct( string $content, User $user, Conversation $converstation) {
        $this->content      = $content;
        $this->user         = $user;
        $this->conversation = $converstation;
        $this->createdAt    = new \DateTime('now');
        $this->messageDocuments = new ArrayCollection();
    }

    public function getId():?int {
        return $this->id;
    }

    public function getContent():?string {
        return $this->content;
    }
    public function setContent(string $content):self {
        $this->content = $content;
        return $this;
    }

    public function getCreatedAt():?\DateTimeInterface {
        return $this->createdAt;
    }
    public function setCreatedAt(\DateTimeInterface $createdAt):self {
        $this->createdAt = $createdAt;
        return $this;
    }
    
    public function getConversation():?Conversation
    {
        return $this->conversation;
    }
    public function setConversation(?Conversation $conversation):self {
        $this->conversation = $conversation;
        return $this;
    }

    public function getUser():?User {
        return $this->user;
    }
    public function setUser(?User $user):self {
        $this->user = $user;
        return $this;
    }

    /**
     * @return Collection|MessageDocument[]
     */
    public function getMessageDocument(): Collection
    {
        return $this->messageDocument;
    }

    public function addMessageDocument(MessageDocument $messageDocument): self
    {
        if (!$this->messageDocument->contains($messageDocument)) {
            $this->messageDocument[] = $messageDocument;
            $messageDocument->setMessages($this);
        }

        return $this;
    }

    public function removeMessageDocument(MessageDocument $messageDocument): self
    {
        if ($this->messageDocuments->removeElement($messageDocument)) {
            // set the owning side to null (unless already changed)
            if ($messageDocument->getMessages() === $this) {
                $messageDocument->setMessages(null);
            }
        }

        return $this;
    }

    
}

MessageDocument entity

<?php

namespace App\Entity;

use App\Repository\MessageDocumentRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=MessageDocumentRepository::class)
 */
class MessageDocument
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $fileName;

    /**
     * @ORM\Column(type="datetime")
     */
    private $updatedAt;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $type;

    /**
     * @ORM\ManyToOne(targetEntity=Message::class, inversedBy="messageDocument")
     */
    private $message;

    /**
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="messageDocument")
     */
    private $sender;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getFileName(): ?string
    {
        return $this->fileName;
    }

    public function setFileName(string $fileName): self
    {
        $this->fileName = $fileName;

        return $this;
    }

    public function getUpdatedAt(): ?\DateTimeInterface
    {
        return $this->updatedAt;
    }

    public function setUpdatedAt(\DateTimeInterface $updatedAt): self
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    public function getType(): ?string
    {
        return $this->type;
    }

    public function setType(?string $type): self
    {
        $this->type = $type;

        return $this;
    }

    public function getMessage(): ?Message
    {
        return $this->message;
    }

    public function setMessage(?Message $message): self
    {
        $this->message = $message;

        return $this;
    }

    public function getSender(): ?User
    {
        return $this->sender;
    }

    public function setSender(?User $sender): self
    {
        $this->sender = $sender;

        return $this;
    }
}

js use to call controller from twig

function getMessages(conversationId , userId) {
    superConversationId = conversationId;
    userIdEnCours       = userId;
    //* On vide ce qu'il y avait avant
    removeAllChildNodes(document.querySelector('.msg_history'));

    //* On remet toutes les conversations en blanc
    let allDivs = document.getElementsByClassName('chat_list');
    for (let div of allDivs) {
        div.style.background = '#f8f8f8';
    }

    //* background-color light-grey quand conversation selectionné
    $("#"  conversationId).css('background', "#e1e1e1")

    let xmlhttp = new XMLHttpRequest();

    xmlhttp.onreadystatechange = function () {
        //si requête validé on traite la réponse
        if (this.readyState == 4 && this.status == 200) {

            let messages   = JSON.parse(this.response);
            let discussion = document.querySelector('.msg_history');

            for (let message of messages) {
                let dateMessage = new Date(message.createdAt)
                var lastMessageId = message.id;

                //* Affichage selon envoyé ou reçu
                if (message.authorId == userIdEnCours) {
                    discussion.innerHTML  = "<div class=\"outgoing_msg col-12 \"\><div class=\"sent_msg col-6 m-0\">"
                          "<p class='m-0'>"   message.content   "</p>"
                          "<span class=\"time_date\"> De "   message.author   " | "   dateMessage.toLocaleString()   "</span>"
                          "</div>"
                    ;
                } else {
                    discussion.innerHTML  = "<div class=\"incoming_msg col-12 \">"
                          "<div class=\"received_msg col-12\"\><div class=\"received_withd_msg col-6\">"
                          "<p>"   message.content   "</p>"
                          "<span class=\"time_date_receiver\"> De "   message.author   " | "   dateMessage.toLocaleString()   "</span>"
                          "</div></div>"
                        
                    ;
                }
            }

            //* scroll dernier message
            let divMessagerie       = document.querySelector(".msg_history");
            divMessagerie.scrollTop = divMessagerie.scrollHeight;
//vl le 13/09
// ne voyant pas l'utilité ...

/*             Interval = setInterval( () => {
                $.ajax({
                    type: "POST",
                    url : "/checkMessage/"   lastMessageId,
                    success: function (response) {
                        if (response === true) {
                            clearInterval(Interval);
                            getMessages(
                                superConversationId, 
                                userIdEnCours
                            );
                        }
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown) {
                        alert(errorThrown);
                    }
                });
            }, 5000); */
        }
    };

    //! Ouverture de la requete (MOCK VERS PROD)
    //* PROD 
    // xmlhttp.open("GET", "http://www.generation-boomerang.com/messagerie/conv/"   conversationId);
    //* DEV
    xmlhttp.open("GET", "/messagerie/conv/"   conversationId);

    xmlhttp.send();
}

The controller to get the messages from a conversation

/**
 * @Route("/messagerie/conv/{id}" , name="messagerie_getMessagesOfConv")
 * @Security("is_granted('ROLE_ABONNE') or is_granted('ROLE_ADMIN')", message="Merci de vous abonner au portail pour bénéficier de cette super fonctionnalité !")
 */
public function getMessagesOfConv(int $id, EntityManagerInterface $entityManager, ConversationRepository $conversationRepository, ParticipantRepository $participantRepository,MessageDocumentRepository $messageRepository) {
    //* Récup du user   check à faire
    $userEncours = $this->getUser();
    $userId = $userEncours->getId();
            //* Ckeck si la conversation appartient bien au user
    $check = $participantRepository->checkBelongs($id, $userId);
    if ($check != 1) {
        return $this->json('cette conv ne te regarde pas !');
    }

    $conversation = $conversationRepository->find($id);
    $messages = $conversation->getMessages();

 //   $documentsMessages = new ArrayCollection();//vl
    $allMessages = new ArrayCollection();
   // $docs=;
    foreach ($messages as $messageUnique) {
        $messId = $messageUnique->getId();
        $documentsMessages = $messageRepository->findDocByMessageId($messId);
 
        if($documentsMessages !== null){
            foreach($documentsMessages as $document){
              //  $docs=$document;
              $messageUnique->addMessageDocument($document);
            } 
            
        }

        $conversation->setLastMessage($messageUnique);
        
        $messageUnique = array(
            'id'        => $messageUnique->getId(),
            'author'    => $messageUnique->getUser()->getFullName(),
            'authorId'  => $messageUnique->getUser()->getId(),
            'content'   => $messageUnique->getContent(),
            'createdAt' => $messageUnique->getCreatedAt(),
            'messageDocuments'=>$messageUnique->getMessageDocument()
        );
        $allMessages->add($messageUnique);

    }
    

    $entityManager->persist($conversation);
    $entityManager->flush();
    //echo '<pre>';  var_dump( $conversation);exit;echo '</pre>';//
    return $this->json($allMessages);
}

conversation entity

<?php

namespace App\Entity;

use App\Repository\ConversationRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=ConversationRepository::class)
 */
class Conversation {
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\OneToOne(targetEntity=Message::class, cascade={"persist", "remove"})
     */
    private $lastMessage;

    /**
     * @ORM\OneToMany(targetEntity=Message::class, mappedBy="conversation")
     */
    private $messages;

    /**
     * @ORM\OneToMany(targetEntity="Participant", mappedBy="conversation")
     */
    private $participants;

    /**
     * @ORM\Column(type="string" , length=50)
     */
    private $title;

    /**
     * @ORM\Column(type="datetime")
     */
    private $createdAt;

    public function __construct() {
        $this->participants = new ArrayCollection();
        $this->messages     = new ArrayCollection();
        $this->createdAt    = new \DateTime('now');
    }

    public function getId():?int {
        return $this->id;
    }

    public function getLastMessage():?Message {
        return $this->lastMessage;
    }
    public function setLastMessage(?Message $lastMessage):self {
        $this->lastMessage = $lastMessage;
        return $this;
    }

    /**
     * @return Collection|Message[]
     */
    public function getMessages():Collection {
        return $this->messages;
    }
    public function addMessage(Message $message):self {
        if (!$this->messages->contains($message)) {
            $this->messages[] = $message;
            $message->setConversation($this);
        }
        return $this;
    }
    public function removeMessage(Message $message):self {
        if ($this->messages->contains($message)) {
            $this->messages->removeElement($message);
            // set the owning side to null (unless already changed)
            if ($message->getConversation() === $this) {
                $message->setConversation(null);
            }
        }
        return $this;
    }

     /**
     * @return Collection|Participant[]
     */
    public function getParticipants():Collection {
        return $this->participants;
    }

    public function addParticipant(Participant $participant):self {
        if (!$this->participants->contains($participant)) {
            $this->participants[] = $participant;
            $participant->setConversation($this);
        }
        return $this;
    }
    public function removeParticipant(Participant $participant):self {
        if ($this->participants->contains($participant)) {
            $this->participants->removeElement($participant);
            // set the owning side to null (unless already changed)
            if ($participant->getConversation() === $this) {
                $participant->setConversation(null);
            }
        }
        return $this;
    }

    /**
     * Get the value of title
     */ 
    public function getTitle() {
        return $this->title;
    }
    /**
     * Set the value of title
     * @return  self
     */ 
    public function setTitle($title) {
        $this->title = $title;
        return $this;
    }

    public function getCreatedAt():?\DateTimeInterface {
        return $this->createdAt;
    }
    public function setCreatedAt(\DateTimeInterface $createdAt):self {
        $this->createdAt = $createdAt;
        return $this;
    }
}

and participant entity

<?php

namespace App\Entity;

use App\Repository\ParticipantRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=ParticipantRepository::class)
 */
class Participant
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="User", inversedBy="participants")
     */
    private $user;

    /**
     * @ORM\ManyToOne(targetEntity="Conversation", inversedBy="participants")
     */
    private $conversation;

    private $messageReadAt;

    /**
     * Get the value of id
     */ 
    public function getId()
    {
        return $this->id;
    }

    /**
     * Get the value of user
     */ 
    public function getUser()
    {
        return $this->user;
    }

    /**
     * Set the value of user
     *
     * @return  self
     */ 
    public function setUser($user)
    {
        $this->user = $user;
        return $this;
    }

    /**
     * Get the value of conversation
     */ 
    public function getConversation()
    {
        return $this->conversation;
    }

    /**
     * Set the value of conversation
     *
     * @return  self
     */ 
    public function setConversation($conversation)
    {
        $this->conversation = $conversation;
        return $this;
    }

    /**
     * Get the value of messageReadAt
     */ 
    public function getMessageReadAt()
    {
        return $this->messageReadAt;
    }

    /**
     * Set the value of messageReadAt
     *
     * @return  self
     */ 
    public function setMessageReadAt($messageReadAt)
    {
        $this->messageReadAt = $messageReadAt;
        return $this;
    }
}

there is also a CreateMessageHandler and GetMessageHanler (but I don't understand how it works)

CreateMessageHandler <?php

namespace App\MessageHandler;
use App\Entity\User;
use App\Entity\Message;
use App\Entity\Conversation;
use App\Message\CreateMessage;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

class CreateMessageHandler implements MessageHandlerInterface
{
    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager) {
        $this->entityManager = $entityManager;
    }

    public function __invoke(CreateMessage $createMessage) 
    {
        $conversation = $this->entityManager->getRepository(Conversation::class)->find($createMessage->getConversationId());

        if(is_null($conversation))
        {
            $conversation = new Conversation();
            $this->entityManager->persist($conversation);
            $this->entityManager->flush();
        } else {
            $message = new Message(
                $createMessage->getContent(),
                $this->entityManager->getRepository(User::class)->find($createMessage->getUserId()),
                $conversation,
            );
           // if ()
        }

        // Debug
        // echo $createMessage->getContent();
        // echo $message->getUser()->getId();
        // echo $message->getConversation()->getId();

        $this->entityManager->persist($message);
        $this->entityManager->flush();
    }
}

GetMessageHandler <?php

namespace App\MessageHandler;

use App\Entity\Message;
use App\Message\GetMessages;
use App\Repository\MessageRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

class GetMessagesHandler implements MessageHandlerInterface
{
    private $entityManager;
    private $messageRepository;

    public function __construct(EntityManagerInterface $entityManager, MessageRepository $messageRepository) {
        $this->entityManager = $entityManager;
    }

    public function __invoke(GetMessages $message)
    {
        //Récupérer les messages de la conversation
        return $this->entityManager->getRepository(Message::class)->findBy(['conversation' => $message->getConversationId()]);
    }
}

I think everything is there. A little long sorry... Hope somebody could find the reason why I have this circular reference

Thanks

  • Related