I'm trying to do a follower/following system on Symfony, but get these errors:
The field App\Entity\User#following is on the owning side of a bi-directional relationship, but the specified mappedBy association on the target-entity App\Entity\User# does not contain the required 'inversedBy' attribute.
The field App\Entity\User#follower is on the owning side of a bi-directional relationship, but the specified mappedBy association on the target-entity App\Entity\User# does not contain the required 'inversedBy' attribute.
I made it this way:
#[ORM\ManyToMany(targetEntity: self::class, inversedBy: 'following')]
#[JoinTable(name: "user_following")]
private $following;
#[ORM\ManyToMany(targetEntity: self::class, inversedBy: 'follower')]
#[JoinTable(name: "user_follower")]
private $follower;
and (same thing for follower):
/**
* #return Collection<int, self>
*/
public function getFollowing(): Collection
{
return $this->following;
}
public function addFollowing(self $following): self
{
if (!$this->following->contains($following)) {
$this->following[] = $following;
}
return $this;
}
public function removeFollowing(self $following): self
{
$this->following->removeElement($following);
return $this;
}
It's working as it should, but the error is bothering me, and after checking the documentation/google for hours I can't fix it.
Thanks for your help
Related
I have two entities for example:
class Dog
{
/**
* #var House
*
* #ORM\ManyToOne(targetEntity="House")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="house_id", referencedColumnName="id")
* })
*/
private $house;
}
class House
{
/**
* #var ArrayCollection|null
* #ORM\ManyToMany(targetEntity="Dog",cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="dog_id", referencedColumnName="id", nullable=true)
* })
*/
protected $dog;
}
I need to throw an event if field house in Entity Dog was update (set or remove) then add or remove field dog in Entity House.
Can anyone show me how do this ?
Doctrine will do this for you but depending on the cascade option. But your annotations are not correct. In the Dog entity you have annotation for a ManyToOne and in the House entity for a ManyToMany relation. But you should choose between
ManyToOne - OneToMany
ManyToMany - ManyToMany
Take a look into the Doctrine's association mapping to read about all the types of associations and how to define them.
If you are using Symfony (4 or 5) you should use the commandline make tool to add
properties and methods with all the annotations, even for relations.
bin/console make:entity Dog
Type relation when asked for the Field type and you will have to answer some additional questions.
You must call $dog->setHouse($this); from the addDog method. If you used the commandline then below class House would be generated for you.
class House
{
// ...
/**
* #ORM\OneToMany(targetEntity="App\Entity\Dog", mappedBy="house")
*/
private $dogs;
public function __construct()
{
$this->dogs = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* #return Collection|Dog[]
*/
public function getDogs(): Collection
{
return $this->dogs;
}
public function addDog(Dog $dog): self
{
if (!$this->dogs->contains($dog)) {
$this->dogs[] = $dog;
$dog->setHouse($this); // <-- here you go
}
return $this;
}
public function removeDog(Dog $dog): self
{
if ($this->dogs->contains($dog)) {
$this->dogs->removeElement($dog);
// set the owning side to null (unless already changed)
if ($dog->getHouse() === $this) {
$dog->setHouse(null);
}
}
return $this;
}
}
Same thing counts for removeDog() method.
This is one is a bit weird
I'm using symfony3/php7
I have the following ProUser entity linked to a Organization entity, used to identity pro account, (important part is the "isEnabled" method), when I try to login with a ProUser that has a linked Organization (they all have, but I made triple sure to choose one that had in database), I got an error that the organization is null, but if i had a dump method to debug, then the organization is correctly retrieved from database by doctrine...
/**
* Represent a professional owner (i.e a theater owner etc.)
*
* #ORM\Entity
* #ORM\Table(name="pro_user")
*/
class ProUser implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\Column(name="id", type="guid")
* #ORM\Id
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="Organization", cascade={"persist"}, mappedBy="legalRepresentative")
*/
private $organization;
public function getOrganization()
{
return $this->organization;
}
public function setOrganization(Organization $organization)
{
$this->organization = $organization;
return $this;
}
/**
* Note: needed to implement the UserInterface
*/
public function getUsername()
{
return $this->email;
}
// for AdvancedUserInterface
public function isEnabled(): bool
{
$organization = $this->getOrganization();
// when this line is not present,
// it throws an exception that $organization is null,
// no problem when this line is present
dump($organization);
return $organization->isValidated();
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
}
The stacktrace :
Symfony\Component\Debug\Exception\FatalThrowableError:
Call to a member function isValidated() on null
at src/AppBundle/Entity/ProUser.php:151
at AppBundle\Entity\ProUser->isEnabled()
(vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php:277)
at Symfony\Component\Security\Core\Authentication\Token\AbstractToken->hasUserChanged(object(ProUser))
(vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php:101)
at Symfony\Component\Security\Core\Authentication\Token\AbstractToken->setUser(object(ProUser))
(vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/ContextListener.php:176)
at Symfony\Component\Security\Http\Firewall\ContextListener->refreshUser(object(RememberMeToken))
(vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/ContextListener.php:109)
at Symfony\Component\Security\Http\Firewall\ContextListener->handle(object(GetResponseEvent))
(vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Debug/WrappedListener.php:46)
at Symfony\Bundle\SecurityBundle\Debug\WrappedListener->handle(object(GetResponseEvent))
(vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Debug/TraceableFirewallListener.php:35)
at Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener->handleRequest(object(GetResponseEvent), object(RewindableGenerator))
(vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall.php:56)
at Symfony\Component\Security\Http\Firewall->onKernelRequest(object(GetResponseEvent))
(vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/EventListener/FirewallListener.php:48)
Is it due to the code happening in the Security Component, and the entity was unserialized instead of being retrieved by doctrine, so that getOrganization() does not yet return a doctrine proxy ?
This is because of Doctrine's lazy loading of relations (it basically only knows the primary ids of the connected entities untill one or more of them are called, like with dump()).
You can add the fetch attribute to your mapping, where LAZY is default, you can set this to EAGER.
I'm training myself on Symfony and struggling with a problem with bidirectional association (very basic) because by dumping my entity in a twig template I verify that data is correct but the association is always null.
My problem is like this one but the solution is not shared.
I read the documentation here and it seems I follow the right steps.
My db contain a Parent table and a Children table related by children.parent_id as foreign key, both table are popolated and I use DOCTRINE:GENERATE:ENTITIES and DOCTRINE:GENERATE:CRUD.
In Parents class I have:
function __construct() {
$this->lastUpd = new \DateTime();
$this->children = new ArrayCollection();
}
/*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Children", mappedBy="parent_id", cascade={"persist"})
*/
private $children;
public function setChildren(ArrayCollection $children) {
return $this->children = $children;
}
public function getChildren() {
return $this->children;
}
In Children class I have:
/**
* #var \AppBundle\Entity\Parents
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Parents", inversedBy="children")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="parent_id", referencedColumnName="parent_id")
* })
*/
private $parent_id;
/**
* Set parent_id
* #param \AppBundle\Entity\Parents $parent_id
* #return Parents
*/
public function setParentID(\AppBundle\Entity\Parents $parent_id= null) {
$this->parent_id = $parent_id;
return $this;
}
/**
* Get parent_id
* #return \AppBundle\Entity\Parents
*/
public function getParentID() {
return $this->parent_id;
}
As additional info looking at Simfony profiler (of parents list page) -> Doctrine -> Entities Mapping I found (with no errors) AppBundle\Entity\Parents and AppBundle\Entity\Type (a working unidirectional OneToMany association).
I am sorry to post a so basic error and I bet the solution is simple but I can't see it.
note: Im assuming that youre not creating an ArrayCollection of children and adding them en'mass.
you dont have any addChild method (which you need to call).
this is easy with an ArrayCollection.
public function addChild(Children $child) {
$this->children->add($child);
}
you could also do with a removeChild as well.
public function removeChild(Children $child) {
$this->children->removeElement($child);
}
then when in your controller.
$child = new Children();
$parent->addChild($child);
then when you persist the parent object, the children will follow due to the cascade persist. I would also add cascade={"remove"} as well, so when you delete the parent, the children will go to.
I'm currently working on a language assessment project which enables you to take an exam in the language you want and evaluate your level. I use Symfony2 framework and work with Doctrine2 as well. My issue is the following one:
I have two entities Exam and Question linked by a Many-To-Many relation (Exam being the owner). Each exam can be related to several questions, and each question can be related to several exams.
Here is my code:
Exam entity
/**
* Exam
*
* #ORM\Table(name="cids_exam")
* #ORM\Entity(repositoryClass="LA\AdminBundle\Entity\ExamRepository")
*/
class Exam
{
...
/**
* #ORM\ManyToMany(targetEntity="LA\AdminBundle\Entity\Question", cascade={"persist"})
* #ORM\JoinTable(name="cids_exam_question")
*/
private $questions;
...
/**
* Constructor
*/
public function __construct()
{
$this->questions = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add questions
*
* #param \LA\AdminBundle\Entity\Question $questions
* #return Exam
*/
public function addQuestion(\LA\AdminBundle\Entity\Question $questions)
{
$this->questions[] = $questions;
return $this;
}
/**
* Remove questions
*
* #param \LA\AdminBundle\Entity\Question $questions
*/
public function removeQuestion(\LA\AdminBundle\Entity\Question $questions)
{
$this->questions->removeElement($questions);
}
/**
* Get questions
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getQuestions()
{
return $this->questions;
}
}
As long as it is a unidirectional relation, there is no 'exams' attribute in my Question class.
Now, what I want to do is getting all the questions related to a specific exam, calling the getQuestions() method, like this:
$questions = $exam->getQuestions();
But this method returns an empty array, even if I have data in my database. If I var_dump the $exam variable, I can see the questions array is empty:
object(LA\AdminBundle\Entity\Exam)[47]
private 'id' => int 5
...
private 'questions' =>
object(Doctrine\ORM\PersistentCollection)[248]
private 'snapshot' =>
array (size=0)
empty
private 'owner' => null
private 'association' => null
private 'em' => null
private 'backRefFieldName' => null
private 'typeClass' => null
private 'isDirty' => boolean false
private 'initialized' => boolean false
private 'coll' =>
object(Doctrine\Common\Collections\ArrayCollection)[249]
private '_elements' =>
array (size=0)
...
I think I could maybe write a findByExam() function in my QuestionRepository, but I don't really know how to implement the joins in this case.
Any help would be great!
To have a findByExam() method in your QuestionRepository do the following:
public function findByExam($exam)
{
$q = $this->createQueryBuilder('q')
->where('q.exam = :exam')
->setParameter('exam', $exam)
->getQuery();
return $q->getResult();
}
You could also create a bi-directional relationship not uni-directional !
Each exam can be related to several questions, and each question can
be related to several exams.
Create a bi-directional relationship by adding this to your Question entity:
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Vendor\YourExamBundle\Entity\ExamInterface;
class Question
{
protected $exams;
public function __construct()
{
$this->exams = new ArrayCollection();
}
public function getExams()
{
return $this->exams;
}
public function addExam(ExamInterface $exam)
{
if !($this->exams->contains($exam)) {
$this->exams->add($exam);
}
return $this;
}
public function setExams(Collection $exams)
{
$this->exams = $exams;
return $this;
}
// ...
Afterwards you can use...
$question->getExams()
... in your controller.
To automatically join your related entities doctrine's fetch option can be used with:
LAZY ( loads the relations when accessed )
EAGER ( auto-joins the relations )
EXTRA_LAZY ( manual fetching )
example:
/**
* #ManyToMany(targetEntity="Question",inversedBy="exams", cascade={"all"}, fetch="EAGER")
*/
Though eager loading has a downside in terms of performance it might be an option for you.
Doctrine Fetch with EAGER
Whenever you query for an entity that has persistent associations and
these associations are mapped as EAGER, they will automatically be
loaded together with the entity being queried and is thus immediately
available to your application.
Read more about it in the Doctrine Documentation.
Another option you should check when working with relations is the cascade option.
See the Doctrine - Working with Associations chapter of the documentation.
Tip:
You should create interfaces for exams and questions and use them instead of the original entity in your set and add methods to allow easier extending.
Bi-Directional Relations using Doctrine2 ORM with association table exam_questions
exam_id question_id
<?php
class Exams
....OTHER PROPERTIES...
/**
* Owning Side
*
* #ManyToMany(targetEntity="Questions", inversedBy="exams")
* #JoinTable(name="exam_questions",
* joinColumns={#JoinColumn(name="exam_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="question_id", referencedColumnName="id")}
* )
*/
private $questions;
..OTHER CODES..
}
class Questions{
..OTHER CODES..
/**
* Inverse Side
*
* #ManyToMany(targetEntity="Exams", mappedBy="questions")
*/
private $exams;
..OTHER CODES..
}
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#annref-manytomany
Hi i had fully successfully setted my entity onetoMany and ManyToOne i generated setters and getters and in user entity it created this method:
user entity:
/**
* #ORM\OneToMany(targetEntity="TB\RequestsBundle\Entity\Requests", mappedBy="followeeuser")
*/
protected $followees;
requests entity:
/**
* #ORM\ManyToOne(targetEntity="TB\UserBundle\Entity\User", inversedBy="followees")
* #ORM\JoinColumn(name="followee_id", referencedColumnName="id", nullable=false)
*/
protected $followeeuser;
And when i using my own custom queries it works good... but i cant figure out how to use this generated function from symfony:
public function addFollowee(\TB\UserBundle\Entity\User $followee)
{
$this->followees[] = $followee;
}
I dont know what to pass there... i tried first get user object based on id of user from twig... worked good but the error occur:
$user->addFollowee($userRepository->find($target_user_id));
Found entity of type TB\UserBundle\Entity\User on association TB\UserBundle\Entity\User#followees, but expecting TB\RequestsBundle\Entity\Requests
Maybe you should think about what you're trying to before coding it. Grab a pen and a sheet of paper. :)
Tell me if I'm wrong, but here is what I think you're trying to do :
One user can have many "followee".
One "followee" can have one user.
So, a OneToMany relation is ok.
Here is how to write it, from the doc :
Requests.php (btw, you should use Request.php)
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="requests")
**/
private $user;
User.php
/**
* #ORM\OneToMany(targetEntity="Requests", mappedBy="user", cascade={"all"})
**/
private $requests;
public function __construct()
{
$this->requests = new \ArrayCollection();
}
Now you can check if you your relation is ok, and update your schema :
php app/console doctrine:schema:validate
php app/console doctrine:schema:update --force
About getters/setters :
Requests.php
public function getUser()
{
return $this->user;
}
public function setUser(User $user) // Please add a Use statement on top of your document
{
$this->user = $user;
return $this;
}
User.php
public function addRequest(Requests $request)
{
$this->requests->add($request);
return $this;
}
public function removeRequest(Requests $request)
{
$this->requests->removeElement($request);
return $this;
}
// Get requests and set requests (you know how to write those ones)
Now, to set a user to a Request, use
$request->setUser($user);
And to add a Request to a user, use
$user->addRequest($request);