Symfony 2 Doctrine persist Entity with related entities - php

I have a problem trying to persist a new entity with Symfony 2.7.11 that have a related Entity.
I need to create a Landing that can have many universities so I created 3 tables
landing
landingId (primary Key)
university
universityId (primary Key)
landingUniversity
landingId (both are primary Key) (foreign Key Landing)
universityId (foreign Key University)
And I have just 2 Entities (Landing & University) and a Many To Many relation (unidirectional, because I just want to know the universities added to a landing, so University hasn't got anything about landing)
First, I find each University on my database and I save them. Then I create the new Landing and I add all of them.
$universityRepository = $this->em->getRepository('University');
$universities = array();
foreach ($listUniversities as $universityId){
$university= $cursosRepository->findById($universityId);
$universities[] = $university[0];
}
$newLanding = new Landing();
$newLanding->setName($landing["name"]);
foreach ($universities as $university){
$newLanding->addUniversity($university);
}
$em = $this->getEntityManager();
$em->persist($newLanding);
$em->flush();
And I'm getting this error when symfony executes flush():
Could not resolve type of column "landingId" of class University
What I'm doing wrong?
My Entity:
/**
* Landing
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="LandingRepository")
*/
class Landing
{
/**
* #var integer
*
* #ORM\Column(name="landingid", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=250)
*/
private $name;
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="Language")
* #ORM\JoinColumn(name="languageId", referencedColumnName="languageId")
*/
private $languageId;
/**
* #ORM\ManyToMany(targetEntity="University")
* #ORM\JoinTable(name="landingUniversity",
* joinColumns={#ORM\JoinColumn(name="landingId", referencedColumnName="landingId")},
* inverseJoinColumns={#ORM\JoinColumn(name="unversityId", referencedColumnName="unversityId")}
* )
*/
private $universities;
Thank you so much!!!

The error message is really explicit. Your error is here :
joinColumns={#ORM\JoinColumn(name="landingId", referencedColumnName="landingId")}
should be
joinColumns={#ORM\JoinColumn(name="landingId", referencedColumnName="landingid")}
because your Landing entity doesn't contain any landingId database field, but landingid.
But you'd rather edit the column name of your $id property :
/**
* #var integer
*
* #ORM\Column(name="landingId", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

Related

ManyToMany Relationship With Type Symfony Doctrine

I have this structure of Entities
class Group
{
/**
* #var int
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private int $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=50, nullable=false)
*/
private string $name;
}
class GroupUser
{
/**
* #var int
*
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private int $id;
/**
* #var Group
* #ORM\ManyToOne(targetEntity="Group", inversedBy="GroupUser")
* #ORM\JoinColumn(name="group_id", referencedColumnName="id", nullable=false)
*/
private Group $group;
/**
* #var string
* #ORM\Column(type="string", length=50, nullable=false)
*/
private string $type;
/**
* #var int
* #ORM\Column(type="integer", nullable=false)
*/
private int $user;
}
And there are two types of users. Admins and Clients. There's a ManyToMany relationship between Group and User. And the property in GroupUser $type is saving the Class of either Admin or Client and the property $user is saving the id.
id
group_id
user
type
1
1
1
Entity\Admin
2
2
1
Entity\Client
How do I join it using doctrine from the Admin and Client-side? Or maybe someone could point out to some resources how this kind of relationship works on doctrine? As I'm having a hard time googling anything out.
I imagine it could be like a conditional leftJoin, but I can't seem to figure it out.
Normaly, you cannot do that on database, because it not safe.
Why is it not safe? because you can have an id in the user column of userGroup table that refer to nothing as it is not linked.
I will write what you should have done, and how you can achieve what you want using your own method:
What you should have done:
In your UserGroup entities, have 2 columns (admin and client) which are linked to the related entities. They can be null (Client is null and admin contain the id of admin entity if it is an admin and vice versa). Then you can delete the type column
How to achieve what you using your method:
As it cannot be done in the database, you will have to do it in some manager. Have a method getUser which will check on your type attribute and return the associated entity from the current id stored in $user
example:
public function getUserFromGroupUser(GroupUser $groupUser){
if('Entity\Admin' ===$groupUser->getType()){
return $this->adminRepository->find($groupUser->getUser());
}
if('Entity\Client' ===$groupUser->getType()){
return $this->ClientRepository->find($groupUser->getUser());
}
throw new \RuntimeException('the type does not exist');
}

Set foreign key of table1 on part of consist primary key of table2

Firstly sorry for my English)
I have following tables:
1) rejection_reasons
rejection_reason_id
locale_code
title
Primary key: rejection_reason_id, locale_code
2) order_rejection_reasons
order_id
rejection_reason_id
Primary key: order_id
Foreign key: rejection_reason_id (Note! without field locale_code)
Entities:
class RejectionReason
{
/**
* #var int
*
* #ORM\Id
* #ORM\Column(name="rejection_reason_id", type="smallint", length=1, nullable=false)
*/
private $id;
/**
* #var string
*
* #ORM\Id
* #ORM\Column(name="locale_code", type="string", length=2, nullable=false, options={"fixed"=true})
*/
private $localeCode;
/**
* #ORM\OneToMany(targetEntity="OrderRejectionReason", mappedBy="rejectionReason", cascade={"remove", "persist"}, orphanRemoval=true)
*/
private $orderRejectionReasons;
/**
* #param int $id
* #param string $localeCode
*/
public function __construct($id, $localeCode)
{
$this->id = $id;
$this->localeCode = $localeCode;
$this->orderRejectionReasons = new ArrayCollection();
}
}
class OrderRejectionReason
{
/**
* #var int
*
* #ORM\Column(name="order_id", type="integer", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var RejectionReason
*
* #ORM\ManyToOne(targetEntity="RejectionReason", inversedBy="orderRejectionReasons")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="rejection_reason_id", referencedColumnName="rejection_reason_id", nullable=false, onDelete="CASCADE")
* })
*/
private $rejectionReason;
}
Doctrine returns error:
The join columns of the association 'rejectionReason' have to match to ALL identifier columns of the target entity 'App\Entity\RejectionReason', however 'locale_code' are missing.
Could you help me set relation between these tables?
The join columns of the association 'rejectionReason' have to match to ALL identifier columns, so you should take a look at the identifier columns. As you can see localcode is marked as an id (#ORM\Id) aswell as id, this means that you created a composite primary key.
Take a look at: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/tutorials/composite-primary-keys.html
Because it is a composite primary key you cannot relate to only one of the two identifiers (JoinColumn, referencedColumnName="id").
You may consider making localeCode unique instead of an id which should solve you problem.(so you have to decide if localCode should be an id) You could also try to add localCode to the JoinColumn annotation.
Try without the extra curly braces and JoinColumn statement
class OrderRejectionReason
{
/**
* #var RejectionReason
*
* #ORM\ManyToOne(targetEntity="RejectionReason", inversedBy="orderRejectionReasons")
* #ORM\JoinColumn(name="rejection_reason_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
private $rejectionReason;
}
EDIT I:
Added name="rejection_reason_id", referencedColumnName="rejection_reason_id"
EDIT II:
Changed referencedColumnName="rejection_reason_id" to referencedColumnName="id"

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;
...
}

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.

Doctrine #UniqueEntity with ManyToOne fields?

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.

Categories