Doctrine #UniqueEntity with ManyToOne fields? - php

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.

Related

is it possible in symfony to propagate a variable from one table to another and that same variable to another table?

From service to the user table:
/**
* #var \Servicio
*
*
* #ORM\GeneratedValue(strategy="NONE")
* #ORM\ManyToOne(targetEntity="Servicio", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="Servicio_idServicio", referencedColumnName="idServicio")
* })
*/
private $servicioIdservicio;
From the user table to another one:
/**
* #var \Usuario
*
* #ORM\GeneratedValue(strategy="NONE")
* #ORM\OneToOne(targetEntity="Usuario")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="idUsuario", referencedColumnName="idUsuario")
* })
*/
private $Idusuario;
/**
* #var \Usuario
*
* #ORM\GeneratedValue(strategy="NONE")
* #ORM\OneToOne(targetEntity="Usuario")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="Usuario_Servicio_idServicio", referencedColumnName="Servicio_idServicio")
* })
*/
private $usuarioServicioIdservicio;
when I want to create an object I am asked for two user objects when the second one comes from service but when I travel from user it comes out as a user object.
How can I solve this?
When I create an Incidence object that drags the user it asks me to add a user object for $iduser and another user object for $userServiceIdservice, because I get from the user table the $idservice. what it should ask me are integers not objects no? I don't know if the migration has been done wrong or what I should do to create an object incidence.

How to establish one-to-one relation through IDs between doctrine entities

I was hoping this be a straight forward process but it seems Doctrine doesn't really like the idea of linking entities through their IDs.
All I intended to do was normalising a table by shipping some fields from it to a new table and instead of adding a new reference field to the original table to hold the ID of the new corresponding record in the, make sure the new record in the child table will have identical ID to its parent row.
Here is an example of what I have:
A User entity, with annotated field $user to reference column ID in the UserDetail entity to itself's ID
/**
* #ORM\Table(name="user", options={"collate"="utf8_general_ci", "charset"="utf8", "engine"="InnoDB"})
* #ORM\Entity
*/
class User extends Entity
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id
/**
* #ORM\OneToOne(targetEntity="UserDetail", cascade={"persist"})
* #ORM\JoinColumn(name="id", referencedColumnName="id", nullable=true)
*/
private $userDetail;
...
}
and here is the UserDetail with its ID's #GeneratedValue removed
/**
* #ORM\Table(name="user_detail", options={"collate"="utf8_general_ci", "charset"="utf8", "engine"="InnoDB"})
* #ORM\Entity
*/
class UserDetail extends Entity
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
*/
private $id;
...
}
At this point what my expectation was to be able to do something like:
$user = new User();
$userDetail = new UserDetail();
$user->setUserDetail($userDetail)
$entityManager->persist($user);
$entityManager->flush();
And get two records persisted to the user and user_detail tables with identical IDs, but the reality is, not having any strategy defined for the UserDetail's identifier, doctrine will complaint about the missing ID, Entity of type UserDetail is missing an assigned ID for field 'id'.
Of course it is possible to do the job manually and in more than one call
$user = new User();
$entityManager->persist($user);
$entityManager->flush();
$userDetail = new UserDetail();
$userDetail->setId($user->getId)
$user->setUserDetail($userDetail)
$entityManager->persist($user);
$entityManager->flush();
But I'm still hoping there is a correct configuration (annotation) that can help me to avoid such extra steps and leave handling of a one-to-one relationship through the entity's IDs to Doctrine.
This is untested but I think the following might work, according to the docs (http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html):
/**
* #ORM\Table(name="user_detail", options={"collate"="utf8_general_ci", "charset"="utf8", "engine"="InnoDB"})
* #ORM\Entity
*/
class UserDetail extends Entity
{
/**
* #var integer
*
* #ORM\OneToOne(targetEntity="User")
* #ORM\JoinColumn(name="id", referencedColumnName="id")
* #ORM\Id
*/
private $user;
...
}

Weird issue with related entity in Doctrine

I'm using Doctrine2 inside Symfony and I have the following setup:
An Item class:
/**
* Class Item
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="OneShortly\CommonBundle\Entity\ItemRepository")
*/
class Item
{
/**
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumn(name="primaryCategory", referencedColumnName="foreignId")
*/
private $primaryCategory;
}
And a Category class:
/**
* Category
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="OneShortly\CommonBundle\Entity\CategoryRepository")
*/
class Category
{
/**
* #var integer
*
* #ORM\Column(name="foreignId", type="integer", unique=true)
*/
private $foreignId;
}
Now when I do this:
$item = new Item();
$item->setPrimaryCategory($category);
$this->em->persist($item);
$this->em->flush();
I get this error:
[Symfony\Component\Debug\Exception\ContextErrorException] Notice:
Undefined index: foreignId in
home/www/project/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php
line 692
Now, I've been looking at this from all angles and still cannot see what is wrong with this code. Can you help?
After some more digging I figured out myself by using doctrine:schema:validate:
[Mapping] FAIL - The entity-class
'Acme\CommonBundle\Entity\Item' mapping is invalid:
* The referenced column name 'foreignId' has to be a primary key column on the target entity class
'Acme\CommonBundle\Entity\Category'.
[Database] FAIL - The database schema is not in sync with the current
mapping file.
So, I changed the foreign key from foreignId to id (which happens to be the primary key) and it works. I could, of course, just use foreignId as a primary key, but I realized actually I don't need that.
Take a look at http://symfony.com/doc/current/book/doctrine.html#relationship-mapping-metadata.
You should rather have:
/**
* Class Item
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="OneShortly\CommonBundle\Entity\ItemRepository")
*/
class Item
{
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="items")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $primaryCategory;
}
and:
/**
* Category
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="OneShortly\CommonBundle\Entity\CategoryRepository")
*/
class Category
{
/**
* #ORM\OneToMany(targetEntity="Item", mappedBy="primaryCategory")
*/
private $items;
}
Forget ID in ORM.

doctrine:schema:update doesn't respect column order

I have this Entity in Symfony2 :
<?php
namespace Project\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entity
*/
class Users
{
/**
* #var integer
*
* #ORM\Column(name="user_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $userId;
/**
* #var integer
*
* #ORM\Column(name="test", type="integer", nullable=false)
*/
private $test;
}
I add the following line between {{userId}} and {{test}} :
/**
* #var integer
*
* #ORM\Column(name="superbanana", type="integer", nullable=false)
*/
private $superbanana;
Then I execute in console :
php app/console doctrine:schema:update --dump-sql
It give me the response :
ALTER TABLE users ADD superbanana INT NOT NULL
**How can I do to have instead ? **
ALTER TABLE users ADD superbanana INT NOT NULL AFTER user_id
If you don't want to drop/create the table, you can use #columnDefinition attribute and define the column definition yourself.
/**
* #var integer
*
* #ORM\Column(type="integer", columnDefinition="INT NOT NULL AFTER `user_id`")
*/
private $superbanana;
I don't think this is possible because using Doctrine means that you don't care about how the Table is managed anymore (apparently someone tried it before).
And since you never use MySQL directly, I think there is no utility to specify column orders for Doctrine.
But you can always delete your table so Doctrine will completely rebuild the table, respecting your order.

adding created timestamp to join table in doctrine2

I have the following property in my User entity to track followers and following. Basically a user can follow other user as well. I have a join column called app_user_follow_user, however I also wanted to add a timestamp of whenever someone follows another user, when did it happen. How can I specify a created timestamp via this ORM?
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="following")
*/
protected $followers;
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="followers")
* #ORM\JoinTable(name="app_user_follow_user",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="follow_user_id", referencedColumnName="id")}
* )
*/
protected $following;
Doctrine ManyToMany relationships are used when your join table has two columns. If you need to add another column you have to convert the relationship to OneToMany on both sides and ManyToOne on the joined entity.
This is entirely untested but it will hopefully give you the gist.
User Entity
/**
* #ORM\OneToMany(targetEntity="AppUserFollowUser", mappedBy="appUser")
*/
protected $followers;
/**
* #ORM\OneToMany(targetEntity="AppUserFollowUser", mappedBy="followUser")
*/
protected $following;
AppUserFollowUser Entity
/**
* #ORM\Table(name = "app_user_follow_user")
*/
class AppUserFollowUser
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="User", inversedBy="followers")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $appUser;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="User", inversedBy="following")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="follow_user_id", referencedColumnName="id")
* })
*/
private $followUser;
/**
* #ORM\Column(name="created_date", type="datetime", nullable=false)
*/
private $createdDate;
}
I think that you will have to create a link entity manually (entiy1 onetomany linkEntity manytoone entity2.
Because, the usual link entity are automated and should be as simple and (data less) as possible, so doctrine can take all the controle over it,
imagine you need to get the timestamp, how can you do it on an (none hard coded) entity, you will need a getter, and the annotations are not supposed to contains code.

Categories