Doctrine2 Many-to-one bidirectional relationship not working - symfony

I'm trying to do a bidirectional association between 2 entities. The problem is that from Book I can get their Owner, but from Owner I can't get the books owned.
Here is the important part of the code:
Acme\BookBundle\Entity\Book;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="owned_books")
* #ORM\JoinColumn(name="owner_id", referencedColumnName="id")
*/
protected $owner;
/**
* Get owner
*
* #return Acme\UserBundle\Entity\User
*/
public function getOwner()
{
return $this->owner;
}
Acme\UserBundle\Entity\User;
/**
* #ORM\OneToMany(targetEntity="Acme\BookBundle\Entity\Book", mappedBy="owner")
*/
protected $owned_books;
public function __construct()
{
$this->owned_books = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get owned_books
*
* #return Doctrine\Common\Collections\Collection
*/
public function getOwnedBooks()
{
return $this->owned_books;
}
Then, to get the data:
This Works
$book = $this->getDoctrine()
->getRepository('BookBundle:Book')
->find(1);
$owner = $book->getOwner()->getFirstName();
This Doesn't work ( Gives Fatal error: Call to undefined method Doctrine\ORM\PersistentCollection::getName() )
$owner = $this->getDoctrine()
->getRepository('UserBundle:User')
->find(1);
$books = $owner->getOwnedBooks()->getName();
Does anyone know what I'm doing wrong? Thank you in advance.

$owner->getOwnedBooks() is a collection of Owners. Try to loop through the collection with a foreach loop.
$books = $owner->getOwnedBooks();
foreach ($books as $book) {
echo $book->getName() . ' <br/>';
}

The error message is pretty clear: you're trying to get the name of a collection of book, instead of trying to get the name of a single book.

Related

Type error with ArrayCollection / OneToMany relationship in Symfony 3.4

For the past couple of days I have been trying to create a bidirectionnal ManyToOne-OneToMany relationship in Symfony 3.4
I have two entities. One is Contribution and the other is Source. A Contribution can have several sources. So the relationship should be
Contribution – ManyToOne – Source – OneToMany – Contribution
But I keep getting the following error during $em→flush(); in my controller:
Type error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given, called in /var/www/html/Edebate/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 605
I do not have any set method related to the Array Collection in my Entity Contribution as I could see in other posts here:
Type error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given
Symfony-Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given
And the annotations are ok as mentionned here:
Doctrine OneToMany relationship error
Any help would be appreciate ! :)
Here is my Entity Contribution
use Doctrine\Common\Collections\ArrayCollection;
//annotations
abstract class Contribution
{
/**
* #ORM\OneToMany(targetEntity="Shaker\DebateBundle\Entity\Source", mappedBy="parent")
*/
protected $sources;
//Other attributes and methods
public function __construct() {
$this->sources = new ArrayCollection();
}
/**
* Add source
*
* #param \Shaker\DebateBundle\Entity\Source $source
*
* #return Contribution
*/
public function addSource(\Shaker\DebateBundle\Entity\Source $source)
{
$this->sources[] = $source;
return $this;
}
/**
* Remove source
*
* #param \Shaker\DebateBundle\Entity\Source $source
*/
public function removeSource(\Shaker\DebateBundle\Entity\Source $source)
{
$this->sources->removeElement($source);
}
/**
* Get sources
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getSources()
{
return $this->sources;
}
}
And this is in my Entity Source:
/**
* #ORM\ManyToOne(targetEntity="Shaker\DebateBundle\Entity\Contribution", inversedBy="sources")
*/
protected $parent;
/**
* Set parent
*
* #param \Shaker\DebateBundle\Entity\Contribution $parent
*
* #return Contribution
*/
public function setParent(\Shaker\DebateBundle\Entity\Contribution $parent = null)
{
$this->parent = $parent;
$parent->addSource($this);
return $this;
}
/**
* Get parent
*
* #return \Shaker\JRQBundle\Entity\Contribution
*/
public function getParent()
{
return $this->parent;
}
And in my Controller, the problem arises with flush:
$formsourcebook->handleRequest($request);
$contributionid=$formsourcebook->get('ContributionId')->getData();
if ($formsourcebook->isValid()) {
$topicargtarget=$this->getContribution($contributionid);
$sourcebook->setUser($user);
$sourcebook->setContribution($topicargtarget);
$em->persist($sourcebook);
$em->flush();
}
I don't know your question very well. However, did you try with this syntax in the Source entity?
private $parent;
// ...
public function __construct() {
$this->parent = new ArrayCollection();
// or new \Doctrine\Common\Collections\ArrayCollection();
}
I think you're forgetting the constructor in the class.
I think you "switched" some logic when working with collections. Here's how I think your "add" method should look like:
public function addSource(\Shaker\DebateBundle\Entity\Source $source)
{
$this->sources[] = $source;
$source->setParent($this);
return $this;
}
And in the other entity:
public function setParent(\Shaker\DebateBundle\Entity\Contribution $parent = null)
{
$this->parent = $parent;
return $this;
}
There are missing variables in your controller snippet, together with the form fields definitions, so you shouldn't work that much after submitting the form. Try to directly map as many fields as you can (even via autoguessing), and even if it looks ugly, but works, but then you can beautify later. Just my two cents with several months of delay.

Saving information in controller symfony relation ManyToMany HELPPPPP

Hi I need to save information in my db, what I mean is, how to persist when the relationship is manytomany, I am doing like this but it doesn't work!...I will put some code of entity Menu.
/** Agrega nuevo menù
*
* #Route("/save", name="admin_menu_save")
* #Method({"GET", "POST"})
* #param Request $request
* #return Response
*/
public function saveAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$fecha_menu = $request->get('fecha');
$fecha_comprar = $request->get('fechacomprado');
$fecha_vencimiento = $request->get('fechavencimiento');
$alimentos = $request->get('select_alimentos');
$a = $em->getRepository('AdminBundle:Alimento')->findBy($alimentos);
$menu = new Menu();
$menu->setFecha(new \DateTime($fecha_menu));
$menu->setFechacomprar(new \DateTime($fecha_comprar));
$menu->setFechavence(new \DateTime($fecha_vencimiento));
$menu->setPrecio(6);
$menu->addAlimento($a);
$em->persist($menu);
$em->flush();
return new Response('Guardado OK');
}
//Menu Entity Definition (Just the necessary code):
/**
* #ORM\ManyToMany(targetEntity="Alimento", inversedBy="menu")
* #ORM\JoinTable(name="alimento_menu",
* joinColumns={
* #ORM\JoinColumn(name="menu_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="alimento_id", referencedColumnName="id")
* }
* )
*/
private $alimento;
/**
* Constructor
*/
public function __construct()
{
$this->alimento = new ArrayCollection();
}
/**
* Add alimento
*
* #param \AdminBundle\Entity\Alimento $alimento
*
* #return Menu
*/
public function addAlimento(Alimento $alimento)
{
$this->alimento[] = $alimento;
return $this;
}
/**
* Remove alimento
*
* #param \AdminBundle\Entity\Alimento $alimento
*/
public function removeAlimento(Alimento $alimento)
{
$this->alimento->removeElement($alimento);
}
/**
* Get alimento
*
* #return ArrayCollection
*/
public function getAlimento()
{
return $this->alimento;
}
}
I have not experience working with manytomany relations, I hope to solve this problem, that´s show very good, but I don´t know how to save,edit or remove in that manytomany table!....
First, this bit seems weird.
$alimentos = $request->get('select_alimentos');
$a = $em->getRepository('AdminBundle:Alimento')->findBy($alimentos);
Doctrine findBy takes an array with the entity field as a key and the specific entity value as the value. Like this:
$em-getRepository('AdminBundle:Alimento')->findBy(['id' => $id]);
If that's how your $alimentos variable is structured, that's fine. It just looks strange.
If this is a bi-directional relationship, you have to update both entities. So your controller code is like:
$a = $em->getRepository('AdminBundle:Alimento')->findBy($alimentos);
$menu = new Menu();
// -- more code ---//
$menu->addAlimento($a);
$a->addMenu($menu);
$em->persist($menu);
$em->flush();
Check this documentation.
I just realized what was...This was my solution:
foreach ($alimentos as $item) {
$a = $em->getRepository('AdminBundle:Alimento')->find($item);
$menu->addAlimento($a);
}
And of course later I persist menu.

Doctrine one to many - persisting multiple files from owning side not working

I have 2 entities Submission and Documents. 1 Submission can have Multiple documents.
Submission Entity:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Document", mappedBy="submission",cascade={"persist", "remove" })
* #ORM\JoinColumn(name="id", referencedColumnName="submission_id")
*/
protected $document;
/**
* #return mixed
*/
public function getDocument()
{
return $this->document->toArray();
}
public function setDocument(Document $document)
{
$this->document[] = $document;
return $this;
}
Document Entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Submission", inversedBy="document")
* #ORM\JoinColumn(name="submission_id", referencedColumnName="id",onDelete="cascade", nullable=true)
*/
protected $submission;
public function getSubmission()
{
return $this->submission;
}
/**
* #param mixed $submission
*/
public function setSubmission($submission)
{
$this->submission = $submission;
}
After receiving files dropzonejs - I'm saving them into Document object, and then, i'm try to save this object into Submission, and persist.
$document = new Document();
$em = $this->getDoctrine()->getManager();
$media = $request->files->get('file');
foreach($media as $req){
$document->setFile($req);
$document->setPath($req->getPathName());
$document->setName($req->getClientOriginalName());
$em->persist($document);
}
$submission->setSubmissionStatus(true);
foreach($document as $item){
$submission->setDocument($item);
}
$submission->setUser($user);
$em = $this->getDoctrine()->getManager();
$em->persist($submission);
$em->flush();
Problem is that all the time, i'm receiving error that submission_title is not set, but that's not true, because i have set this field before. I haven't got idea, what is wrong.
I think you'll get some mileage out of following the tutorial over at http://symfony.com/doc/current/doctrine/associations.html, if you haven't already.
I can see that your getters / setters aren't optimal for associating more than one Document with your Submission.
As they write in the Symfony docs, where they want to associate one category with many products, they have the following code:
// src/AppBundle/Entity/Category.php
// ...
use Doctrine\Common\Collections\ArrayCollection;
class Category
{
// ...
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
}
From the docs:
The code in the constructor is important. Rather than being
instantiated as a traditional array, the $products property must be of
a type that implements Doctrine's Collection interface. In this case,
an ArrayCollection object is used. This object looks and acts almost
exactly like an array, but has some added flexibility. If this makes
you uncomfortable, don't worry. Just imagine that it's an array and
you'll be in good shape.
So, you'll want to be sure the constructor for your Document entity has something like $this->submissions = new ArrayCollection();. I've changed the property to a plural name, because I think it's more semantically correct. But you can keep your $submission property name, if you like.
Next is to add a addSubmission, removeSubmission, and a getSubmissions method.
Then, your class might end up looking like this:
<?php
// src/AppBundle/Entity/Submission.php
namespace AppBundle\Entity
use Doctrine\Common\Collections\ArrayCollection;
class Submission
{
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Document", mappedBy="submission",cascade={"persist", "remove" })
* #ORM\JoinColumn(name="id", referencedColumnName="submission_id")
*
* #var ArrayCollection()
*/
protected $documents;
...
/**
* Instantiates the Submission Entity
*
* #return void
*/
public function __construct()
{
$this->documents = new ArrayCollection();
}
/**
* Returns all documents on the Submission
*
* #return mixed
*/
public function getDocuments()
{
return $this->documents;
}
/**
* Add document to this Submission
*
* #param Document $document The object to add to the $documents collection.
*
* #return Submission
*/
public function setDocument(Document $document)
{
$this->documents[] = $document;
return $this;
}
/**
* Remove a document from this Submission
*
* #param Document $document The object to remove from the $documents collection.
*
* #return Submission
*/
public function removeDocument(Document $document)
{
$this->documents->removeElement($document);
return $this;
}
}

Fatal error: Call to undefined method contains() in Symfony 2.7

I have a problem of recognition constrains() method in Symfony2. I have a relationship between the groups & Roles entities: So a group must have a mandatory role and the role may or may not have one or more groups. So in my addRoles function ( Groups $grp) I have checked each time if the group has a role so we joust if not assigning a role. But when inserting,
I encounter a problem:
PHP Fatal error: Call to undefined method
MemberShipManagement\GroupsBundle\Entity\Roles::contains() in
/var/www/Project_Console/src/MemberShipManagement/GroupsBundle/Entity/Roles.php
on line 118,
Class Groups:
/**
* #var Roles $role
*
* #ORM\ManyToOne(targetEntity="Roles", inversedBy="groups")
* #ORM\JoinColumn(name="role_id", referencedColumnName="id", nullable=false)
*
* #Assert\Valid()
*/
protected $role;
Class Roles:
/**
* #var ArrayCollection $groups
*
* #ORM\OneToMany(targetEntity="Groups", mappedBy="role", cascade={"remove"} )
*#Assert\Valid()
*/
protected $groups;
/**
* Add group
* #param Groups $grp
*/
public function addRoles(Groups $grp) {
// $grp->setRole($this);
if (!$this->groups->contains($grp)) {
$this->groups->add($grp);
}
return $this;
}
/**
* Remove groups
* #param Groups $groups
*/
public function removeRoles(Groups $groups)
{
if ($this->groups->contains($groups)) {
$this->groups->removeElement($groups);
}
return $this;
}
public function __construct()
{
$this->groups = new ArrayCollection();
}
thank you :)
Maybe you could try to check if the data exists before calling the contains method like,
public function addRoles(Groups $grp) {
// $grp->setRole($this);
if ( !isEmpty($this->groups) ){
if (!$this->groups->contains($grp)) {
$this->groups->add($grp);
}
}
return $this;
}
since the error you get seems to be derived from the fact that $groups are not interpreted as ArrayCollection defined in doctrine.(if so, you could've called contains method defined there)
http://www.doctrine-project.org/api/common/2.4/class-Doctrine.Common.Collections.ArrayCollection.html
As long as other examples using contains method are found like this(https://groups.google.com/forum/#!topic/doctrine-user/i6IhBPHALkk) your approach looks correct.
Otherwise you could try to change $role to private instead of protected?

Symfony2-Doctrine -- Selecting data with WHERE from DoctrineArray

In an entity I have a field that looks like this:
/**
* #ORM\Column(type="array")
*/
protected $category;
and QueryBuilder
$qb = $this->createQueryBuilder('s');
$qb->select($fields)
->where( 's.category IN (:category)') //////////// <----
->orderBy('s.name', 'ASC')
->setParameter('category', $category_id);
So in database field category is Doctrine2 Array. I want to select records from database with QueryBuilder. My question is, how can i do this, with WHERE clause that will be checking fields from that array ?
A look here may help you
// Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1 (see section above)
public function in($x, $y); // Returns Expr\Func instance
$qb->select($fields)
->where($qb->expr()->in('s.category', $categories))
#Cerad gave you a perfectly valid comment. One of the problem of storing arrays is that you don't have any chance of searching.
See PHP/MySQL - Storing array in database, and Storing arrays in the database. As you can see, it is a terrible practice.
The best way is to simply create a Category entity, and to have a OneToMany relation with that category.
Here is an example of an entity Book that has many categories:
1 Create your category entity:
class Category implements CategoryInterface
{
//.....
/**
* Title of the category
*
* #ORM\Column(type="string", length=100)
*/
protected $title;
/**
* Relation with your book entity for example
*
* #ORM\ManyToOne(targetEntity="Book", inversedBy="categories")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $book;
/**
* Set book
*
* #param BookInterface $book
*/
public function setBook(BookInterface $book)
{
$this->book = $book;
}
/**
* Get book
*
* #return BookInterface
*/
public function getBook()
{
return $this->book;
}
}
2 Your book entity:
use Doctrine\Common\Collections\ArrayCollection;
class Book implements BookInterface
{
/**
* Categories for the books
*
* #ORM\OneToMany(targetEntity="Category", mappedBy="book")
* #var CategoryInterface[]
*/
protected $categories ;
public function __construct()
{
$this->categories = new ArrayCollection();
}
/**
* Add Categories
*
* #param CategoryInterface $category
*/
public function addCategory(CategoryInterface $category)
{
$category->setBook($this);
$this->categories->add($category);
}
/**
* Remove Category
*
* #param CategoryInterface $category
* #return bool
*/
public function removeCategory(CategoryInterface $category)
{
return $this->categories->removeElement($category);
}
/**
* Get Categories
*
* #return Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
/**
* Set Categories
*
* #param ArrayCollection $categories
*/
public function setCategories($categories) {
$this->categories->clear();
foreach ($categories as $category) {
$this->addCategory($category);
}
return $this;
}
3 Your can now search properly.

Resources