So I have the next documents:
/*
* #ODM\Document(collection="role", repositoryClass="AppBundle\Document\Repository\RoleRepository")
*/
class Role implements RoleInterface
{
/**
* #var integer $id
*
* #ODM\Id(strategy="INCREMENT")
*/
private $id;
/**
* #var string $name
*
* #ODM\Field(type="string")
*/
private $name;
/**
* #var ArrayCollection $users
*
* #ODM\ReferenceMany(targetDocument="User", mappedBy="roles", cascade={"all"})
*
* #JMS\Accessor(getter="getUsersToJson")
* #JMS\Expose
* #JMS\Type("array<integer>")
*/
private $users;
and
/**
* #ODM\Document(collection="user", repositoryClass="AppBundle\Document\Repository\UserRepository")
*/
class User implements AdvancedUserInterface
{
/**
* #var integer $id
*
* #ODM\Id(strategy="INCREMENT")
* #ODM\Index(unique=true)
*
* #JMS\Expose
* #JMS\Type("integer")
*/
private $id;
/**
* #var ArrayCollection $roles
*
* #ODM\ReferenceMany(targetDocument="Role", inversedBy="users", cascade={"persist", "refresh"})
*
* #JMS\Expose
* #JMS\Type("ArrayCollection<AppBundle\Document\Role>")
*/
private $roles;
So when I do the patch request to update role I want to set update the list of users which role has. I it completely occurs, but when I go to the database I do not see the role in user which was added in role's user list or I do see the role which I removed from role's user list.
So is this a normal behaviour and I have to delete these references by myself OR I do something wrong?
UPDATE:
Always need to write the question to understand what do I really want...
For editing I use symfony forms.
The main question is: can we have reference with something like both inversedBy relation?
I want to have opportunity to edit user and edit role and they remove or add ids from each other, which they (do not) use.
Currently no, you cannot. One model must own the relationship on one side or the other.
If you have the User model own the relationship to the Role you can set a role(s) to the User while editing it. The users property on the Role model can be an inverse reference to display what users have the Role assigned, but you would have to modify the model with the owning reference to add more users to the role.
Related
I would like to create a notification system. There is a Notification class. A notification can be assigned to more than one users, not just one.
There is a joint table user_notifications, with two columns: user_id and notification_id
The definition of the $notifications in the user class is this:
/**
* #ManyToMany(targetEntity="Notification")
* #JoinTable(name="user_notifications",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="notification_id", referencedColumnName="id", unique=true)}
* )
**/
private $notifications;
Everything works fine. But I would like to add a new column to the user_notifications table, where I would like to store, if the notification is read by the given user, or not. How should I manage it in Doctrine2?
You will have to refactor your entities to introduce a new and transform your user_notifications adjacency table into an entity.
Solution
Transform you table as follows:
Then refactor your associations as follows:
User entity
...
/**
* #OneToMany(targetEntity="UserNotification", mappedBy="notification_id")
**/
private $notifications;
Notification entity
...
/**
* #OneToMany(targetEntity="UserNotification", mappedBy="user_id")
**/
private $users;
UserNotification entity
/** #Entity **/
class UserNotification {
...
/**
* #ManyToOne(targetEntity="User", inversedBy="notifications")
* #JoinColumn(name="user_id", referencedColumnName="id")
**/
private $user_id;
/**
* #ManyToOne(targetEntity="Notification", inversedBy="users")
* #JoinColumn(name="notification_id", referencedColumnName="id")
**/
private $notification_id;
/** #Column(type="boolean") */
private $read;
You'll need to create new entity with this extra column.
You can find details in this answer: https://stackoverflow.com/a/15630665/1348344
Class Comment
/**
* #var \Caerus\AppBundle\Entity\Users
*
* #ORM\ManyToOne(targetEntity="User" , inversedBy="comment")
*
* #ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
*
*/
protected $user;
Class User
/**
* #var mixed
*
* #ORM\OneToMany(targetEntity="Comment", mappedBy="user")
*/
protected $comment;
Basically quite simple. I need the comments class to have a user_id field which is a direct copy of the original user_id field from the users class.
The error is as following:
[Doctrine\ORM\ORMException] ManyToOne Column name id referenced for relation from Comment towards User does not exist
Why exactly is it still saying doesn't exist and how do I solve that ?
Referenced Column name should be the "id" property of the User class.
/**
* #var \Caerus\AppBundle\Entity\Users
*
* #ORM\ManyToOne(targetEntity="User" , inversedBy="comment")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*
*/
protected $user;
P.S.
I would also name the OneToMany property "comments" as it holds many Comment objects.
"#var \Caerus\AppBundle\Entity\Users" should be ...\User as your class is called User
Running php bin/console doctrine:schema:validate will give you more insight into which entity classes and columns are involved in the error condition.
Also you can use this code:
/**
* #var \Caerus\AppBundle\Entity\Users
*
* #ORM\ManyToOne(targetEntity="User" , inversedBy="comment")
*
* #ORM\JoinColumn(nullable=true , referencedColumnName="variable_id_of_comment")
*/
protected $user;
I have an Author entity, which is a Class Table Inheritance containing an AuthorUser and an AuthorGroup.
/**
* Author
*
* #ORM\Table
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"user" = "AuthorUser", "group" = "AuthorGroup"})
*/
class Author {
// ...
}
AuthorUser relates to my User entity and AuthorGroup to my Group entity.
class AuthorUser extends Author
{
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User", inversedBy="?????")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
}
class AuthorGroup extends Author
{
/**
* #var Group
*
* #ORM\ManyToOne(targetEntity="Group", inversedBy="?????")
* #ORM\JoinColumn(name="group_id", referencedColumnName="id")
*/
protected $user;
}
I have no idea how to inverse this. Anyway, the problem is that i have to add this CTI to my Article entity field. How can i relate using ManyToOne to this Article entity field?
class Article
{
/**
* #var Author
*
* #ORM\ManyToOne(targetEntity="Author", inversedBy="?????????")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id")
*/
protected $author;
}
I'm not sure how to make this as transparent as possible. When i create a new Article, i need to provide either an User or Group object to the author field. I followed this behavior, but it doesn't seem to help. It gets even more complicated.
One solution could be to always have AuthorGroups, even when there's only one Author.
Otherwise, take a look at https://github.com/FabienPennequin/DoctrineExtensions-Rateable
You might be able to use that code to provide a similar Authored interface that can discriminate between the AuthorUser and AuthorGroup.
I'm encountering a problem with a Doctrine 2 entity association.
I have a User entity and an Agency entity. One Agency can employ multiple Users (the entities are simplified to show only my problem)
User Entity
/**
* #Entity
* #Table(name="users")
**/
class User
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
* #var integer
**/
protected $id;
/**
* #ManyToOne(targetEntity="Agency", inversedBy="users"})
* #JoinColumn(name="agency_id", referencedColumnName="id")
* #var Agency
*/
protected $agency;
}
Agency Entity
/**
* #Entity
* #Table(name="agencies")
**/
class Agency
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
* #var integer
**/
protected $id;
/**
* #OneToMany(targetEntity="User", mappedBy="agency", cascade={"all"})
* #JoinColumn(name="id", referencedColumnName="agency_id")
* #var User[]
*/
protected $users;
/**
* Add a user to the agency
*
* #param User $user
* #return void
*/
public function addUser(User $user) {
$this->users[] = $user;
}
}
When I now use the following code to create an agency along with a user, Doctrine does not set the agency_id for the user which results in a mysql contraint error that the user's agency_id must not be null.
// $em is the Doctrine EntityManager
$agency = new Agency;
$user = new User;
$agency->addUser($user);
$em->persist($agency);
$em->flush();
so far, the only way I found to make Doctrine set the agency_id for the user is assigning the agency to the user additionally to adding the user to the agency. In my understanding of an ORM it should already set the agency_id when the user is in the users collection of teh agency and it's beeing saved.
$user->agency = $agency;
$agency->addUser($user);
Is there anything wrong or missing with my annotations/metadata?
I found something in the Doctrine 2 documentation:
Changes made only to the inverse side of an association are ignored.
Make sure to update both sides of a bidirectional association (or at
least the owning side, from Doctrine’s point of view)
As in my case the owning side is the User I must update it. Doctrine 1 was able to manage it automatically... too bad.
I'm trying to create a UniqueEntity with 2 fields (both are ManyToOne fields).
The code is as follow:
/*
* #ORM\Table()
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity(fields={"user", "connect"})
*/
class UserConnect
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var boolean $isLeader
*
* #ORM\Column(name="isLeader", type="boolean")
*/
private $isLeader;
/**
* #var date $joinedDate
*
* #ORM\Column(name="joinedDate", type="date")
*/
private $joinedDate;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="userConnects")
*
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="Connect", inversedBy="userConnects")
*
*/
private $connect;
The goal is to ensure that I've got only one Entity that link a USER with a CONNECT.
Should I write something else in my #UniqueEntity declaration?
I understand you want to get an error only when both user and connect fields for one record are duplicated in other record in the database.
The #UniqueEntity annotation is rightly declared for your purpose (multiple column index) but only will be triggered in the form validation and doesn't affects the DDBB schema.
If you want to add the same check at database level you should use the #UniqueConstraint annotation in the Table() declaration and give a name to the new index. Something like:
/*
* #ORM\Table(uniqueConstraints={#ORM\UniqueConstraint(name="IDX_USER_CONNECT", columns={"user_id", "connect_id"})})
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity(fields={"user", "connect"})
*/
class UserConnect
{
In the other hand, if you declare #ORM\Column(unique=true) in each attribute you will get a very different behavior, it won't be a multiple column index but you will have two independent unique columns, if you enter twice the same user_id you will get an error independently of the connect_id value, and the same will happens if you enter twice the same connect_id value.
This works:
/**
* State
*
* #ORM\Table(
* name="general.states",
* uniqueConstraints={
* #ORM\UniqueConstraint(name="states_country_name_code_key", columns={"idcountry", "name","code"}),
* })
* #ORM\Entity(repositoryClass="Fluency\Bundle\GeneralBundle\Entity\Repository\StateRepository")
*/
class State
{.......
Taken from an entity on my system. This way affects Database schema. See where i put #\ORM\UniqueConstraint annotation. Sorry #estopero... next time i must read first the other answers.
you should add the unique declaration in your attributes annotations too.
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="userConnects")
* #ORM\Column(unique=true)
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="Connect", inversedBy="userConnects")
* #ORM\Column(unique=true)
*/
private $connect;
See this symfony doc and this StackOverflow answer.