Search code examples
symfonydoctrine-odmsymfony5

Symfony 5 Save Object in MongoDB Collection


I have never worked with MongoDb and I am also new to symfony and ODM. I have a collection called userlist. In userlist I want to save multiple books. The book Id's get selected on a Form which I send to the server, in my controller I want to use all the selected BookIds use find($id) and save the returned Book details in my Userlist.

Document\Userlist.php

class Userlist{

/**
 * @MongoDB\Id
 */
private $id;

/**
 * @MongoDB\Field(type="string")
 */
private $name;


/**
 * @MongoDB\Field(type="collection")
 */
private $books;
}


public function __construct()
{
    $this->books = new ArrayCollection();
}

public function getBooks(): ?array
{
    return array_unique($this->books);
}

public function setBooks(array $books): self
{
    $this->books= $books;

    return $this;
}

MyController.php

 foreach ($_POST['books'] as $bookObjectId) {

        $bookData= $dm->getRepository(Book::class)->find($bookObjectId);

        $userlist->setBooks($bookData);
        $dm->persist($userlist);
        $dm->flush();
}

If I use above I get Collection type requires value of type array or null, Doctrine\Common\Collections\ArrayCollection given. So if I take $this->books = new ArrayCollection(); out this error disappears. Unfortunately all I get saved to the document is

books [0]

No data is saved. If I use

$bookData= $dm->getRepository(Book::class)->find($bookObjectId);
$books = array('_id' => $bookData->getId(),
                'name' =>$bookData->getBookName());
$userlist->setBooks($bookData);
    $dm->persist($userlist);
    $dm->flush();

Then the data will be all saved, but I would like to just get my 1 Document and save it straight into another document without re-creating an Array with calling all the getters and then save that. I have tried to read all over the internet, but I just don't get it. I also tried

public function addBooks(Book $books)
{
    $this->books[] = $books;
    return $this;
}

but again no luck. The Form by the way has got no BookId form field. Instead I create this dynamic with jQuery, but I doubt that this is a problem. Can anyone point in the right direction by any chance, this would be brilliant. Thank you very much.

UPDATE

My repository file:

public function findReturnArray($id)
{
    return $this->createQueryBuilder()
        ->hydrate(false)
        ->field('_id')->equals($id)
        ->getQuery()
        ->execute()->toArray();
}

In the controller:

 foreach ($_POST['bookIds'] as $orderObjectId) {

            $bookData[] = $dm->getRepository(Book::class)->findReturnArray($bookObjectId);
           
        } 
$userlist->setBooks($bookData);
$dm->persist($userlist);

That does save both books into books but I have 1 issue. It is saved like this

books
  [] [0]
    {}[0]
      bookId
      bookName etc
  [] [1]
    {}[0]
      bookId
      bookName

what I would like is:

   books
    {}[0]
      bookId
      bookName etc
    {}[1]
      bookId
      bookName
  

setBooks is still the same but I took the $this->books = new ArrayCollection(); out. any idea how I can stop this now?


Solution

  • If you want to persist references to Book documents in your Userlist document, you have to use the ReferenceMany annotation (see docs).

    class Userlist
    {
        // ...
    
        /**
         * @MongoDB\ReferenceMany(targetDocument="My\Namespace\Book", storeAs="id")
         */
        private $books;
    
    
        public function __construct()
        {
            $this->books = new ArrayCollection();
        }
    
        public function getBooks(): array
        {
            return $this->books->getValues();
        }
    
        public function addBook(Book $book)
        {
            if ($this->books->contains($book)) {
                return;
            }
    
            $this->books->add($book);
        }
    }
    
    foreach ($_POST['books'] as $bookObjectId) {
        $book = $dm->getRepository(Book::class)->find($bookObjectId);
    
        $userlist->addBook($book);
    }
    
    $dm->persist($userlist);
    $dm->flush();
    

    UPDATE

    After reading your update, I guess this is the change you are looking for:

    public function findReturnArray($id)
    {
        return $this->createQueryBuilder()
            ->hydrate(false)
            ->field('_id')->equals($id)
            ->getQuery()
            ->getSingleResult();
    }