How to define multiple many-to-one relations between two Doctrine tables? - symfony

I've got a problem with doctrine with Symfony when I try to define to relations Many-to-one between the table Challenge and User getting the next error.
Error when I try to define:
The table User has the attribute: id, challengesMaked, challengesReceived and the table Challenge has these atributes: id, idUser1, idUser2 where idUser1 I want to relation with id from User and idUser2 I want to relation with id from User too. The relation between User and Challenge are One to Many (One user can challenge to another user) and between Challenge to User are Many to One (One challenge is received only by a User)
So, how can I define this attributes in my entity to fix my bug. Right now, in my User Entity I have defined these attributes like ...
/**
* #var Challenge ArrayCollection
* #ORM\OneToMany(targetEntity="\MQL\PlayerBundle\Entity\Challenge", mappedBy="idUser1", cascade={"persist"})
*/
protected $challengesMaked;
/**
* #var Challenge ArrayCollection
* #ORM\OneToMany(targetEntity="\MQL\PlayerBundle\Entity\Challenge", mappedBy="idUser2", cascade={"persist"})
*/
protected $challengesReceived;
And in the Challenge Entity I have defined ...
/**
* #var User ArrayCollection
* #ORM\ManyToOne(targetEntity="\FOS\UserBundle\Entity\User", inversedBy="challengesMaked")
**/
protected $idUser1; //Challenger
/**
* #var User ArrayCollection
* #ORM\ManyToOne(targetEntity="\FOS\UserBundle\Entity\User", inversedBy="challengesReceived")
**/
protected $idUser2; //Challenged
What am I doing wrong?

Related

Doctrine throw error: "A new entity was found through the relationship" after removing entity

Let's say I have two entities, Project and User with relation.
Project.php
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(onDelete="SET NULL")
*/
private $creator;
When I remove the User entity, the doctrine leaves the User object(without ID) in the Project entity. In a normal situation, this is fine but I am using DomainEvents. In this scenario, after removing the User entity, DomainEvent triggers saving some data in the DB and secondary saving data(after removing) throw this error. This happens because of now in the Project entity we have the detached(from the EM) User object without ID.
I thought about a listener, that will remove empty objects in the entity after removing, but I am not sure that is a good variant
What is the best variant for solving this error?
The onDelete option doesn't apply a cascade removing.
If you want to do so I think you should have to add the cascade={"remove"} option to the ManyToOne.
Try as following :
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User", cascade={"remove"})
* #ORM\JoinColumn(onDelete="SET NULL")
*/
private $creator;
Removing entity in doctrine

Symfony: Filter ArrayCollection by associated entity id

I have a User entity and a Usecase entity. This 2 entities are associated by a ManyToMany association, but this association also holds another property, called "environment". To implement this relationship I also have an entity called UserUsecase that has a ManyToOne relationship with User, a ManyToOne relationship with Usecase and the extra field "environment". When fetching a user from the database, his usecases are being fetched as well, so the user has an ArrayCollection of objects of type UserUsecase that represent all the usecases a user has. What I want to do is to filter this ArrayCollection by usecase_id. The UserUsecase class has the structure below:
class UserUsecase
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="userUsecases")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #ORM\ManyToOne(targetEntity="Usecase", inversedBy="userUsecases")
* #ORM\JoinColumn(name="usecase_id", referencedColumnName="id")
*/
protected $usecase;
/**
* #ORM\Column(type="integer")
*/
protected $environment;
}
So I tried this inside the User class:
public function filterUsecases($usecase_id){
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('usecase', $usecase_id));
return $this->userUsecases->matching($criteria);
}
It makes sense to me that even the field usecase of the class UserUsecase is an object of type Usecase, it should resolve to its id and the equation would hold when the ids matched. Still this doesn't seem to work, and I cannot find how to implement this filtering. Isn't it possible to be done this way? I found a relevant article that seems to do exactly what I want but this is not working in my case. Here is the article! Am I doing something wrong?
Thanks in advance!
Unless you have many many use cases per user(thousands) I recommend:
public function filterUsecases(Usecase $useCase){
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('usecase', $useCase));
return $this->userUsecases->matching($criteria);
}
Then:
$user->filterUsecases($useCase);
Or passing reference
$user->filterUsecases($em->getReference(Usecase::class, $id));

How to limit OneToMany/ManyToOne associations depth/loop in Doctrine2?

I've 3 doctrine entities. One is User, second is Product and third is ProductUsers.
So, User have OneToMany association with ProductUsers and the same Product have OneToMany association with ProductUsers. ProductUsers has ManyToOne association with both User and Product. Like so:
class Product
{
/**
* #var ProductUsers
*
* #ORM\OneToMany(targetEntity="ProductUsers", mappedBy="product")
*/
private $productUsers;
}
class ProductUsers
{
/**
* #var Product
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Product", inversedBy="productUsers")
* #ORM\JoinColumn(name="product_ID", referencedColumnName="ID")
*/
private $product;
/**
* #var User
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="User", inversedBy="productUsers")
* #ORM\JoinColumn(name="user_ID", referencedColumnName="ID")
*/
private $user;
// extra fields ...
}
class User
{
/**
* #var ProductUsers
*
* #ORM\OneToMany(targetEntity="ProductUsers", mappedBy="user")
*/
private $productUsers;
}
A user can use multiple products and a product can have multiple users. ProductUsers has some extra info about the relation other than just the relation.
The problem is when I fetch one User object it comes with associated ProductUsers and it's associated Product. Not only that but the Product also comes with all it's associated ProductUsers and it's respective User objects which is quite an overhead.
This question closely relates to my problem.
I'm looking to limit that at doctrine level just like what JMSSerializerBundle MaxDepth does. Is there a way to limit such overhead in doctrine?
I faced this issue long time back. I tried lazy loading. Which didn't work as expected and that was not a proper solution to my issue. So I did some R&D and came up with a solution that I don't need a bidirectional relationship from Product to ProductUsers.
I can manage same relationship with unidirectional handling only from ProductUsers side. You will need One-To-Many Association when you need a cascade-persist or similar feature. I wrote a small blog regarding this as well.
So, for your solution, just have Many-To-One association from ProductUsers with both Product and User entity. You will not need any change in your database association.
And when you need Products associated for a single user, you can always save a Querybuilder in Repository to use when you need associated data.
It will save a lot of performance. Hope it helps!

Symfony2 + Doctrine2 onDelete="restrict"

I want to restrict a delete action on my object, but it's not working.
My code from my first entity:
/**
* #ORM\ManyToMany(targetEntity="Season", inversedBy="clubs")
* #ORM\JoinTable(
* name="clubs_to_seasons",
* joinColumns={
* #ORM\JoinColumn(name="club_id", referencedColumnName="id", onDelete="cascade")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="season_id", referencedColumnName="id", onDelete="restrict")
* }
* )
**/
private $seasons;
And second:
/**
* #ORM\ManyToOne(targetEntity="League")
* #Assert\NotBlank(message="validation.custom.not_blank")
*/
private $league;
On database side everything is fine - I cannot delete a Season object, because it has a reference to Club object, but when I use a remove fuction from Entity Manager, Season object is deleted.
I know that on ORM side is a cascade={..} but I need a restrict, not cascade.
BUMP:
If I'm using one-to-many, many-to-one or one-to-one, everything is ok - when I try to delete an object (and I'm using onDelete="restrict" on the database side), then exceptions appears with message, that I cannot delete because I have other object connected with. But when I have many-to-many (as in my example), and I have a season associated with any club (and I have onDelete="restrict"), still I can delete a season using entity manager. But when I'm deleting season directly on the db side, message appears, and I cannot delete this.
What is wrong with many-to-many?
EDIT:
OK, now it's working.
My solution for the future:
I removed many-to-many relation from both entities. I have made a third entity - clubToSeason. Previously this entity was autogenerated, now it's not. And inside file of this third entity I have two many-to-one relations:
class ClubToSeason {
/**
* #ORM\ManyToOne(targetEntity="season", inversedBy="clubs")
**/
private $season;
/**
* #ORM\ManyToOne(targetEntity="club", inversedBy="seasons")
**/
private $club;
...
}

Symfony: How do I annotate entity properties that are objects to get Doctrine to store a foreign key?

I'm still getting to grips with Symfony and Doctine and I appreciate this might sound overly simple.
I have at present two basic entities: WebSite (having id and canonicalUrl properties) and Job which has, as one property, a WebSite.
A Job has one WebSite; a WebSite can be referenced by many Jobs. Both are under the same namespace.
Relevant here is the Job entity:
/**
*
* #ORM\Entity
*/
class Job
{
/**
*
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #var WebSite
*/
protected $website;
}
In database terms, a persisted Job should be storing the id of the relevant WebSite.
Without any changes to the above, calling php app/console doctrine:migrations:diff generates a new migration for a table named Job with a single id field.
How do I annotate Job::website such that Doctrine knows to create an integer field and to get the value as the id of the Website object?
You must explicitly define the relationship. The shortest would be
/**
* #ORM\Entity
*/
class Job
{
/**
* #var WebSite
*
* #ORM\ManyToOne(targetEntity="Website")
*/
protected $website;
}
However, should you find yourself wanting to tweak the relationship to better suit your needs, have a look at the annotation reference (ManyToOne and JoinColumn for this particular case). There's also quite a comprehensive article about association mapping, which you might find interesting.

Resources