Error when persisting doctrine entity with OneToMany association - symfony

New entities in a collection using cascade persist will produce an Exception and rollback the flush() operation. The reason is that the "UserGroupPrivilege" entity has identity through a foreign entity "UserGroup".
But if the "UserGroupPrivilege" has its own identity with auto generated value the code works just fine, and I don't want that I want the identity to be a composite key to enforce validation. here is my code:
Entity UserGroup:
class UserGroup
{
/**
* #var integer
*
* #ORM\Column(type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(type="boolean", nullable=false)
* #Type("integer")
*/
private $active;
/**
* #ORM\OneToMany(targetEntity="UserGroupPrivilege", mappedBy="userGroup", cascade={"persist"})
*/
private $privileges;
Entity UserGroupPrivilege:
class UserGroupPrivilege
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer", nullable=false)
*
*/
private $privilegeId;
/**
* #var UserGroup
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="UserGroup", inversedBy="privileges")
* #ORM\JoinColumn(name="userGroupId", referencedColumnName="id")
*/
private $userGroup;
/**
* #var string
* #ORM\Column(type="string", nullable=false)
*/
private $name;
/**
* #var string
* #ORM\Column(type="string", nullable=false)
*/
private $value;
Controller:
$userGroup = new UserGroup();
$userGroupPrivilege = new UserGroupPrivilege();
userGroupPrivilege->setUserGroup($userGroup)
->setName($arrPrivilege['name'])
->setValue($arrPrivilege['value'])
->setPrivilegeId($arrPrivilege['privilegeId']);
$userGroup->addPrivilege($userGroupPrivilege);
$data = $repo->saveUserGroup($userGroup);
return $data;
Repository:
$em = $this->getEntityManager();
$em->persist($userGroup);
$em->flush();
I get the following error:
Entity of type UserGroupPrivilege has identity through a foreign entity UserGroup, however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before trying to persist 'UserGroupPrivilege'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.

Error message is pretty self explanatory. To relate UserGroupPrivilege to UserGroup, UserGroup must have it's ID set. However, since you've just created both entities it has no id because it hasn't been persisted to database yet.
In your case :
$em = $this->getEntityManager();
$em->persist($userGroup);
$em->persist($userGroupPrivilege);
$em->flush();

Can you "enforce validation" with unique constraint:
/**
* #Entity
* #Table(uniqueConstraints={#UniqueConstraint(name="ugppriv_idx", columns={"priviledgeId", "userGroup"})})
*/
class UserGroupPriviledge
{
...

Related

How to modify Symfony ORM insert\update query

I have project that is migrate to Symfony, that project have multiple tables,and also some tables are migrated to ORM, but now i need to incert/update from Symfony to table that have Entity but not managed by ORM. Problem consist in not null columns that require some value and in Entity I cannot define that value because of table relations.
It posible to edit MySql query before they submited to Database.
For example i have Entity:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* #ORM\Table(name="p_user")
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User
{
/**
* #var int
*
* #ORM\Column(name="user_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string|null
*
* #ORM\Column(name="name", type="string", length=55, nullable=true)
*/
private $name;
/**
* #var Permission
*
* #ORM\ManyToOne(targetEntity="Permission", inversedBy="user", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="permission_id", referencedColumnName="permission_id", onDelete="CASCADE")
*/
private $permission;
}
permission_id can be null but in database is not null with default value 0, same for name but with default value ''.
That mean when I make flush, ORM execute INSERT INTO p_user (name, permission_id) VALUES ('name', null), but I want also to execute INSERT INTO p_user (name) VALUES ('name').
It's possible to do that I wanted.
To achieve this you can provide default values.
private $permission = 0;
private $name = '';

With ManyToOne relation entities, how may i access a foreign key id?

I'm trying to access to a foreign key stored in an entity using doctrine and querybuilder.
I got an entity named User which is linked to another entity called Client with a ManyToOne relationship.
I wanted to build a querybuilder that get me the field client_id in user table, that match the id of a client.
My User Entity :
/**
* AppBundle\EntityAppBundle\Entity\User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User extends FOSUser
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*
*
*/
private $nom;
/**
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Etablissement", inversedBy="users")
*
*/
private $etablissements;
/**
*
* #ORM\ManyToOne(targetEntity="Client", inversedBy="users")
*
*/
private $client;
My Client Entity :
/**
* AppBundle\EntityAppBundle\Entity\Client
*
* #ORM\Table(name="client")
* #ORM\Entity()
*/
class Client{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*
*
*/
private $nom;
/**
* #var string
*
* #ORM\Column(name="adresse", type="string", length=255)
*
*
*/
/**
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\User", mappedBy="client",
cascade={"persist"}, orphanRemoval=true)
*
*/
private $users;
In my database, my entity user has the client_id in foreign key column.
So in my queryBuilder in UserRepository, I do :
public function findClientIdViaUserId($myUserId, $myClientID)
{
return $this->createQueryBuilder('e')
->from('AppBundle:User', 'i')
->join('AppBundle:Client', 'c')
->where('c.id = :myClientID')
->andWhere('e.id = :myUserId')
->setParameter('myuserId', $myUserId)
->setParameter('myClientId', $myClientID)
->getQuery()
->getOneOrNullResult();
}
I expect to get the id of the client_id for a user_id.
Let's say that i wanted to get one client_id with the user_id called 1.
With my queryBuilder i got an error like :
[Syntax Error] line 0, col 67: Error: Expected Doctrine\ORM\Query\Lexer::T_WITH, got ','
How may I process to get the client_id from the user_id ?
Thank you for your replies !
Why don't you use EntityManager ?
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find(YOURUSERID);
$client = $user->getClient();
You made a mistake in chaining of Doctrine methods. You've already set an alias for User entity by calling createQueryBuilder('e'). Repository knows about entity it linked to.
When you call from('AppBundle:User', 'i') - an alias for User entity is i now. That's why Doctrine is throwing an error about wrong syntax in resulting DQL.
So, try this piece of code:
return $this
->createQueryBuilder('e')
->join('e.client', 'c')
->where('c.id = :myClientID')
->andWhere('e.id = :myUserId')
->setParameter('myUserId', $myUserId)
->setParameter('myClientId', $myClientID)
->getQuery()
->getOneOrNullResult();

Embeed forms - inserting to database

I created two entities automatically ( using this manual http://symfony.com/doc/2.8/doctrine/reverse_engineering.html) based on ER model generated in Workbench. My intention was to create one-to-one relationship but annotation show it is one-to-many relationship. I created also embeed forms. I would like to insert client and new adress to database. I still get an error:
A new entity was found through the relationship 'UlaBundle\Entity\Client#adres' that was not configured to cascade persist operations for entity: qqq. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
Error is shown even if i set #ManyToOne(..,cascade={"persist"}) and __toString function. What is the problem? Please help. Below my code:
///Client Entity
class Client
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45, nullable=true)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \UlaBundle\Entity\Adres
*
* #ORM\ManyToOne(targetEntity="UlaBundle\Entity\Adres", cascade= {"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="adres_id", referencedColumnName="id")
* })
*/
private $adres;
/// Adres Entity
class Adres
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45, nullable=true)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
///Controller
/**
* #Route("/client", name="client")
*/
public function clientAction(Request $request) {
$c = new Client();
$form = $this->createForm(ClientType::class,$c);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$m = $this->getDoctrine()->getManager();
$m->persist($c);
$m->flush();
return new Response('Added');
}
return $this->render('UlaBundle:Default:client_form.html.twig', array('form' => $form->createView()));
}
I think your problem come from the blank space in cascade= {"persist"}, you should remove it
/**
* #var \UlaBundle\Entity\Adres
*
* #ORM\ManyToOne(targetEntity="UlaBundle\Entity\Adres", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="adres_id", referencedColumnName="id")
* })
*/
private $adres;

symfony 2 Doctrine leftJoin NOT IN

How can I solve a query where I want to get all data from the left table which isn't existing in the right table?
left table: ID | NAME | DATE
right table: ID | ID_left_table | NAME | DATE
It is confusing me a bit since I haven't got that experience with doctrine.
My entitys look like:
class NameData
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
.
.
.
and
class ValueData
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="TestBundle\Entity\NameData")
*/
private $nameid;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
.
.
.
1) First of all, fix $nameid property annotation in ValueData entity to this:
/**
* #ORM\ManyToOne(targetEntity="NameData")
* #ORM\JoinColumn(name="name_id", referencedColumnName="id")
**/
private $nameData;
This is basic Doctrine annotation mapping for relationships, in this case ManyToOne.
2) Modify valueData table to add foreign key, by typing php app/console doctrine:schema:update --force or with migrations
3) let Symfony generate the right setters and getters for ValueData entity, by running command in your console php app/console doctrine:generate:entities TestBundle:ValueData.
4) And then, if you need to get data in controller:
$valueData = $this->getDoctrine()->getRepository('TestBundle:ValueData')->find(1);//Find by ID 1 OR ->findAll() to get all records
$nameData = $valueData->getNameData(); //This line of code behind the scenes will
//join the valueData table with nameData, and get associated data
Add a comment if you will need help along the way.
Edit:
Below is the query builder to select all NameDatas which dont have any ValueData:
$nameDataRepo = $this->getDoctrine()->getRepository('TestBundle:NameData');
$nameDatasWithoutDatavalues = $nameDataRepo->createQueryBuilder('nameData')
->leftJoin('nameData.dataValues', 'dataValue')
->where('dataValue.id IS NULL')
->getQuery()
->getResult
Also, make sure to write bi-directional part of doctrine relationships, to be able to access children from parent entity NameData.php:
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="ValueData", mappedBy="nameData")
*/
private $valueDatas;
/**
* Constructor
*/
public function __construct()
{
$this->valueDatas = new \Doctrine\Common\Collections\ArrayCollection();
}
And in ValueData.php, edit $nameData property annotation to this:
/**
* #ORM\ManyToOne(targetEntity="NameData", inversedBy="valueDatas")
* #ORM\JoinColumn(name="name_id", referencedColumnName="id")
**/
private $nameData;

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