Symfony2 entity foreign keys - symfony

I can't figure it out..
Why I haven't access to Country table?
countryName should show Great Britain but it doesn't.
This is my dump($User):
My my piece of code of User entity:
/**
*
* #ORM\ManyToOne(targetEntity="Dashboard\MainBundle\Entity\Country", cascade={"persist"})
* #ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=true)
*
*/
private $countryId;
And my piece of code of Country Entity:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

Depending on how you get the user maybe it is a lazy load that you are using which will get the country only if you call the getter explicitly, to always get a country with the user try :
/**
*
* #ORM\ManyToOne(targetEntity="Dashboard\MainBundle\Entity\Country", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="country_id", referencedColumnName="id", nullable=true)
*
*/
private $countryId;
But still we need to know how you are getting the user the lazy load may override the fetch eager.

Your Country object is now only a Proxy object - dump function don't call a Doctrine to get a related object. Try before dump get your object for example:
dump($User->getCountry()):
dump($User);
OR try left join you Country in QueryBuilder
OR find a information about lazy load in Doctrine2 here

Related

How to generalize the mappping of a symfony entity that is potentially linked to many entities types

In a previous question i wanted to know how to prevent a user to edit a form if another user was already using it.
Since i'm using SF 2.8, i can't use the lock component (> SF3.4) so i was thinking about doing it manually, with an entity managing the locks.
for my entity, i need :
user_id (the user that edit the form, create the lock)
entity_id (the id of the edited entity)
entity_class (FQCN of the entityType)
createdAt (date of the lock)
moreover, i need a UniqueEntity constraint on (user_id, entity_id and entity_class)
This is where i have a problem of mapping : the entity (id) can be of different type (i have Profession, Module, Institution, User...)
So from a Doctrine point of view, i don"t see how i can do it.
maybe i can use the entity id, but loosing the very power of docrine/symfony relationships.
/**
* Lockit.
*
* #ORM\Table(name="lockit")
*
* #UniqueEntity(
* fields={"entityClass", "entityId", "user"}
* )
*/
class Lockit
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* FQCN of the entity associated with the form to be locked.
*
* #var string
* #ORM\Column(name="entity_class", type="string")
*/
private $entityClass;
/**
* Entity id associated with the form to be locked.
* #ORM\Column(name="entity_id", type="integer")
*/
private $entityId;
/**
* #var \Simusante\SimustoryBundle\Entity\User
*
* #ORM\ManyToOne(targetEntity="Simusante\SimustoryUserBundle\Entity\User")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="userId", referencedColumnName="id", nullable=true)
* })
*/
private $user;
/**
* Date of the lock creation.
*
* #var \DateTime
* #ORM\Column(name="createdAt", type="datetime", nullable=true)
* #Assert\Date()
*/
private $createdAt;
Another solution would be to create as many lockEntities as i can lock entity with.
i would create a base Lock, and then a ProfessionLock, a InstitutionLock... where i could use the "correct" mapping.
/**
* #ORM\ManyToOne(targetEntity="Institution")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="institutionId", referencedColumnName="id", nullable=true)
* })
*/
private $user;
it would work, but this doesn't feel as "optimized" as it could.
maybe there's another way to to it, where i don't have to create as many entities as i have form type to lock.
Thank you in advance
This is where i have a problem of mapping : the entity (id) can be of different type (i have Profession, Module, Institution, User...)
As I can see, just mapping the entityId field as a text field instead of integer should solve your issue.
Your UniqueEntity constraint would still be relevant, and you would still be able to recover any locked entity Lockit instance via a simple entity repository method or whatever query method you'd like.

User Information on entities symfony

I started using symfony not long ago and at the moment I'm struggling with this problem:
I decided to have "who" information at entity level so I have defined these additional 4 prameters for every entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* #ORM\JoinColumn(name="created_by", referencedColumnName="id")
*/
private $createdBy;
/**
* #var \DateTime
*
* #ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* #ORM\JoinColumn(name="updated_by", referencedColumnName="id")
*/
private $updatedBy;
/**
* #var \DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=true)
*/
private $updatedAt;
My problem is now where and how I should populate createdBy and updatedBy. ATM I do that in my controller before persisting to the database. Thou I encountered a problem when a entity is a property of another entity and lets say I have an entity called Post that has a property images of type Document the entities Post and Document both have "who" information on them and images property inside Post is defined as follows:
/**
* #var array
*
* #ORM\ManyToMany(targetEntity="Nisand\DocumentsBundle\Entity\Document", cascade={"persist"})
* #ORM\JoinTable(name="blog_documents",
* joinColumns={#ORM\JoinColumn(name="post_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="document_id", referencedColumnName="id")}
* )
*/
private $images;
For Post suppose I set createdBy in the controller before persisting but on Document how should that work cause that will be persisted by the cascade rule?
How do you guys handle in your applications the "who" columns?
Try this bundle: StofDoctrineExtensionsBundle and use Blameable extension.
You will need set current user with BlameableListener. And it will cover your use case.
Documentation for Blameable is here: https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/blameable.md

Symfony + Doctrine - UnitOfWork commit order

I have a form that shows entity:
class Event
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\OneToMany(targetEntity="EventAttendee", mappedBy="event", cascade={"all"})
*/
private $attendees;
}
and a collection within it:
class EventAttendee
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="Event", inversedBy="attendees")
* #ORM\JoinColumn(name="event_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $event;
/**
*
* #ORM\OneToOne(targetEntity="Employee")
* #ORM\JoinColumn(name="employee_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $employee;
}
If I delete an employee from the collection and add it again, I'm getting integrity constraint violation. This is because Doctrine's UnitOfWork first executes Inserts and then Deletes. Therefore, when it inserts a new record db still has the old one with the same employee.
Doctrine2 developers did not provide any working solution for Symfony2 users (here is the thread: http://www.doctrine-project.org/jira/browse/DDC-601).
And thus, I'm asking the question here: is it anyhow possible to avoid this issue?
EDIT:
My current workaround is:
find all not-persisted colletion items ready to insert
remove them from the collection and save to a variable
remove all the items that were really deleted in the form
call flush()
add all the items for insert back to the collection
call flush()
This works for me, however doesn't look good. Maybe someone has a better solution.

Doctrine2: how do I link entities using two columns (or composite keys)?

I've been trying to find an answer to this question relating to composite from both Doctrine2 documentation and across the internet, but so far have had no firm leads.
To keep it brief, essentially I need to link 3 entities: Object, Grid and GriddingSquare. An object is found in a Grid and, more specifically, within one GriddingSquare. A Grid is composed of 1 or more GriddingSquares.
In the source database (which cannot easily be modified), the GriddingSquare is uniquely identified by 'GridID' (integer) and 'GridSquareLabel' (string). These two columns are thus both in the Object table (& entity) and GriddingSquare table (& entity).
I tried the following, but can't seem to get it work:
Object entity:
/**
* #var \Grid
*
* #ORM\ManyToOne(targetEntity="Grid")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="GridID", referencedColumnName="GridID")
* })
*/
private $gridid;
/**
* #var string
*
* #ORM\Column(name="GridSquareLabel", type="string", length=20, nullable=true)
*/
private $gridsquarelabel;
/**
* #var \CdbGriddingsquares
*
* #ORM\ManyToOne(targetEntity="CdbGriddingsquares")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="GridID", referencedColumnName="GridID"),
* #ORM\JoinColumn(name="GridSquareLabel", referencedColumnName="GridSquareLabel")
* })
*/
private $griddingsquare;
GriddingSquare entity:
/**
* #var \Grid
*
* #ORM\ManyToOne(targetEntity="Grid")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="GridID", referencedColumnName="GridID")
* })
*/
private $gridid;
/**
* #var string
* #ORM\Column(name="GridSquareLabel", type="string", nullable=false)
*/
private $gridsquarelabel;
Tried to use the following suggestions from Doctrine2 documentation, but although it is technically a 'composite key' question, I can't seem to find an example which helps me understand what I need to do http://docs.doctrine-project.org/en/latest/reference/annotations-reference.html#annref-joincolumn, and http://docs.doctrine-project.org/en/2.0.x/tutorials/composite-primary-keys.html

Symfony2 Doctrine2 column being set to null

I have a system that took form information detailing a project, added it to a project table and is meant to add an entry into an assigned projects table to associate user with project (point of this is allowing multiple users for each project). Anyway I got this working without foreign keys, struggled to add them but eventually got them.
Unfortunately this additional has caused this error 'SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'projectId' cannot be null' whenever something is added to the assignedProjects table.
So my question is, have I missed something in my codes?
The code to add a new row to assignedProjects:
$assignedProject = new AssignedProjects();
$assignedProject->setProjectId($project->getId());
$assignedProject->setUserId($user[0]['id']);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
The code for the assignProjects entity:
class AssignedProjects
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer $projectId
*
* #ORM\Column(name="projectId", type="integer")
*/
private $projectId;
/**
* #ORM\ManyToOne(targetEntity="Projects", inversedBy="assignment")
* #ORM\JoinColumn(name="projectId", referencedColumnName="id")
*/
private $project;
/**
* #var integer $UserId
*
* #ORM\Column(name="userId", type="integer")
*/
private $userId;
/**
* #ORM\ManyToOne(targetEntity="Dev\UserBundle\Entity\User", inversedBy="assignment")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
(followed by the usual getters and setters)
and the project tables entity is:
class Projects
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $projectName
*
* #ORM\Column(name="projectName", type="string", length=255)
*/
private $projectName;
/**
* #ORM\OneToMany(targetEntity="AssignedProjects", mappedBy="project")
*/
protected $assignment;
Any help would be much appreciated!
Either you use the ProjectId and UserId columns and manage the relationship manually (not recommended) or you use the doctrine relationships(recommended), but don´t do both things. If you go for the second option, don´t include the projectId and userId columns, they are automatically created for you by doctrine. So, your AssignedProjects class should be:
class AssignedProjects {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id * #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Projects", inversedBy="assignment")
* #ORM\JoinColumn(name="projectId", referencedColumnName="id")
*/
private $project;
/**
* #ORM\ManyToOne(targetEntity="Dev\UserBundle\Entity\User", inversedBy="assignment")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
and in your controller you would do:
$assignedProject = new AssignedProjects();
$assignedProject->setProject($project);
$assignedProject->setUser($user[0]);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
Note that I am setting the Project and User fields, not the ids
By the way, unless you need to save extra data about this project assignement (things like the date or similar), you can declare a direct ManyToMany relationship between User and Project and do away with this class, Doctrine would generate the needed table by itself
With Doctrine2, you don't have to declare the foreign key (projectId) but only the association (project). So you can delete $projectId property, as well as setProjectId ans getProjectId methods. Same fix for $user...
Instead, you will use setProject like that :
$assignedProject = new AssignedProjects();
$assignedProject->setProject($project);
$assignedProject->setUser($user[0]);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
Have a look to Doctrine2 documentation, it will help you, for sure !
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.1/reference/association-mapping.html

Resources