I am using Zend 2 Framework and I am trying to get data using Doctrine 2.
However the following error is coming in the Entity file.
Doctrine\ORM\Mapping\MappingException
No identifier/primary key specified for Entity "Acl\Entity\Permission". Every Entity must have an identifier/primary key.
How can I specify the primary key?
I am using the following code.
/**
* User Permissions
*
* #ORM\Entity
* #ORM\Table(name="acl_permissions")
* #property int $id
* #property int $role_id
* #property int $resource_id
* #property string $action
*/
class Permission
{
/**
* #ORM\Column(type="integer")
*/
public $id;
/**
* #ORM\Column(type="integer")
* #ORM\OneToOne(targetEntity="Role")
* #ORM\JoinColumn(name="role_id", referencedColumnName="id")
*/
public $role;
/**
* #ORM\Column(type="integer")
* #ORM\OneToOne(targetEntity="Resource")
* #ORM\JoinColumn(name="resource_id", referencedColumnName="id")
*/
public $resource;
/**
* #ORM\Column(type="string")
*/
public $action;
public function getRole()
{
return $this->role;
}
public function getResource()
{
return $this->resource;
}
}
Have you checked the docs?
You can define a primary key by using the #ORM\Id annotation. In case the value is generated automatically (e.g. if using auto_increment), you also need to set the #ORM\GeneratedValue(strategy="IDENTITY") annotation.
Related
How can I set the task_id on the TaskLine entity?
Similar Questions:
Doctrine: How to insert foreign key value
Best practice for inserting objects with foreign keys in symfony2
Doctrine 2 entity association does not set value for foreign key
I get this error:
Neither the property "task_id" nor one of the methods "getTaskId()", "taskId()", "isTaskId()", "hasTaskId()", "__get()" exist and have public access in class "AppBundle\Entity\TaskLine"
NOTE: Normally I work with PropelORM.
I'm trying to save a new TaskLine entity which is related to Task entity. I'm posting JSON payload which look something like this.
{
"id": null,
"task_id": 1,
"note" : "new note"
}
In the controller I json_decode the request payload and submit that to $form->submit($note_data), $form is an instance of:
class TaskNoteType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add( 'task_id', NumberType::class )
->add( 'note', TextType::class )
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\TaskLine'
));
}
}
Here is my Task entity
class Task
{
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=150, nullable=true)
*/
private $description;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
}
TaskLine entity
class TaskLine
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \AppBundle\Entity\Task
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Task")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="task_id", referencedColumnName="id")
* })
*/
private $task;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set task
*
* #param \AppBundle\Entity\Task $task
*
* #return TaskLine
*/
public function setTask(\AppBundle\Entity\Task $task = null)
{
$this->task = $task;
return $this;
}
/**
* Get task
*
* #return \AppBundle\Entity\Task
*/
public function getTask()
{
return $this->task;
}
}
I found my answer in here:
Best Practice for inserting objects with foreign keys in Symfony2
Answered by: Tuan nguyen
In ORM you have to set Foreign key by an object which your entity
associated with. You could use EntityManager#getReference to get a
reference to category entity without loading it from DB. Like this
$category = $entityManager->getReference('YourProject\Entity\Category', $categoryId);
$product = new Product();
$product->setCategory($category);
Similar questions:
Doctrine: How to insert foreign key value
Following function you should have already in your Slider entity (or
similar).
public function addImage(Image $image) {
$image->setSlider($this); // This is the line you're probably looking for
$this->images[] = $image;
return $this; }
What it does is if you persist the entity it writes the ID of the Slider (sid) into your Image.
Doctrine 2 entity
association does not set value for foreign key
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.
How can I do this?
My Entities:
Product entity
/**
* #ORM\Entity
* #ORM\Table(name="products")
*/
class Product
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #ORM\Column(type="string", length=512)
* #var string
*/
private $name;
/**
* (??)
* #var ArrayCollection
*/
private $images;
}
Article entity
/**
* #ORM\Entity
* #ORM\Table(name="articles")
*/
class Article
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #ORM\Column(type="string", length=512)
* #var string
*/
private $title;
/**
* (??)
* #var ArrayCollection
*/
private $images;
}
Image entity
/**
* #ORM\Entity
* #ORM\Table(name="images")
*/
class Image
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #ORM\Column(type="string", length=512)
* #var string
*/
private $name;
/**
* #ORM\Column(type="string", length=1024)
* #var string
*/
private $path;
}
I don't know how to create a link tables with additional fields like the picture. Which association should I use? How to manage these relations in entities?
Usually when you need a many to many approach Doctrine let you define such behaviour with a simple annotation.
#ORM\ManyToMany(targetEntity="full_qualified_namespace")
#ORM\JoinTable(
name="game_schemas_players",
joinColumns={#ORM\JoinColumn(name="this_field_name", referencedColumnName="id")},
inverseJoinColumns={#ORM\JoinColumn(name="that_field_anem", referencedColumnName="id")}
)
This will instruct Doctrine to create a relation the current entity and the target entity.
But that's not your case. For what I can see from your model you need to add some field on the 'middle' entity.
Here what you may want to do:
class Product
{
[...]
/**
* #var ArrayCollection | ProductImage[]
*
* #ORM\OneToMany(targetEntity="ProductImage", mappedBy="product")
*/
private $productImages;
}
class Image
{
[...]
/**
* #var ArrayCollection | ProductImage[]
*
* #ORM\OneToMany(targetEntity="ProductImage", mappedBy="image")
*/
private $productImages;
}
as you can see as defined here either Product and Image have a oneToMany relation with a middle entity which will be named ProductImage**.
The last step will be to implement such entity:
class ProductImage
{
[...]
/**
* #var Image
*
* #ORM\ManyToOne(targetEntity="Image", mappedBy="image")
* #ORM\JoinColumn(name="image_id", referencedColumnName="id")
*/
private $image;
/**
* #var Product
*
* #ORM\ManyToOne(targetEntity="Product", mappedBy="product")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
/**
* #ORM\Column(type="string", length=1024)
* #var string
*/
private $position;
}
The owning side of the relation both for Product and Image is still ProductImage.
As a side note in your entity is common practice to implement an add method in this fashion:
public function __contructor(){
$this->productImages = new ArrayCollection();
}
/**
* Add ProductImage
*
* #param ProductImage $productImage
* #return $this
*/
public function addDocument(ProductImage $productImage)
{
$productImage->addProductImage($productImage);
$this->documents->add($document);
return $this;
}
and then you can use such method with the following approach:
$product = new Product();
$image = new Image();
$productImage = new ProductImage($product,$image);
$product->addProductImage($productImage);
Don't forget to provide the usual setter method as Doctrine need them to initialize the entity.
Hope it helps, Regards.
So I have 3 entities with their properties.
A USER which has MANY USERSKILLS with each ONE having a single corresponding SKILL
Here are the entities:
/**
* #ORM\Table(name="skills")
* #ORM\Entity()
*/
class Skill
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=30, unique=true)
*/
private $name;
/**
* #ORM\Column(name="active", type="boolean")
* #var bool
*/
private $active = false;
/**
* #ORM\OneToMany(targetEntity="UserSkill", mappedBy="skill")
*/
private $userSkills;
}
/**
* #ORM\Table(name="user_skill")
* #ORM\Entity()
*/
class UserSkill
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="User", inversedBy="skills")
* #var User
*/
private $user;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Skill", inversedBy="userSkills")
* #var Skill
*/
private $skill;
/**
* #ORM\Column(type="integer")
* #var int
*/
private $level = 0;
}
/**
* StartupDriven\UserBundle\Entity\User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="StartupDriven\UserBundle\Entity\UserRepository")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\OneToMany(targetEntity="UserSkill", mappedBy="user")
* #var ArrayCollection
*/
private $skills;
}
They are created using 2 symfony form objects which works fantastically.
The problem is when I go to persist these objects in the controller.... I get an error
$form->handleRequest($request);
if ($form->isValid()) {
/**
* #var User $user
*/
$user = $form->getData();
/**
* #var UserSkill $userSkill
*/
foreach ($user->getSkills() as $userSkill) {
// No updating skills... Only new ones.
if (!$userSkill->getId()) {
// Check skill name match.
if ($matched_skill = $em->getRepository('StartupDrivenUserBundle:Skill')->findOneBy(array('name' => $userSkill->getName()))) {
$userSkill->setSkill($matched_skill);
}
else {
// No match. Create new generic skill.
$em->persist($userSkill->getSkill());
// THE ERROR HAPPENS ON THE FOLLOWING LINE!
$em->flush();
}
// Set the User
$userSkill->setUser($user);
// Persist the user skill.
$em->persist($userSkill);
$em->flush();
}
}
$em->persist($user);
$em->flush();
Error Message:
A new entity was found through the relationship 'StartupDriven\UserBundle\Entity\User#skills' that was not configured to cascade persist operations for entity: StartupDriven\UserBundle\Entity\UserSkill#0000000053e8628e00007f7f2fd56e1f. 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"}). If you cannot find out which entity causes the problem implement 'StartupDriven\UserBundle\Entity\UserSkill#__toString()' to get a clue.
I have tried every combination of persist & flush that I can think of. I have tried the cascade option above (which gives a different error that the "user id which is required by the primary key rules is not set")... I am completely lost. It seems like such a simple relationship... Where am I going wrong?
I am looking for some help as to why my OneToMany relationship in doctrine only returns one value. My data model has three tables. Users, Authorization, and Applications. The Authorization table is the glue that maps users to applications and also contains an accesslevel field to indicate their level of access for that application.
I have a user that has three entries in authorization for three different applications, but for some reason, doctrine is only loading 1 of those authorization records. I've included my full data model below. The attribute in question is "authorization" in the Webusers table.
Anyone know what I am doing wrong?
class Webusers {
/**
* #var integer
*
* #ORM\Column(name="userid", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $userid;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Applications", mappedBy="userid")
*/
private $applications;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="Authorization", mappedBy="user")
*/
private $authorization;
/**
* Constructor
*/
public function __construct() {
$this->applications = new \Doctrine\Common\Collections\ArrayCollection();
$this->authorization = new \Doctrine\Common\Collections\ArrayCollection();
}
class Authorization {
/**
* #var integer
*
* #ORM\Column(name="accesslevel", type="integer", nullable=false)
*/
private $accesslevel;
/**
* #var integer
*
* #ORM\Column(name="applicationid", type="integer", nullable=false)
* $ORM\Id
*/
private $applicationid;
/**
* #var integer
*
* #ORM\Column(name="userid", type="integer", nullable=false)
* #ORM\Id
*/
private $userid;
/**
* #var \Webusers
*
* #ORM\ManyToOne(targetEntity="Webusers")
* #ORM\JoinColumn(name="userid", referencedColumnName="userid")
*/
private $user;
}
class Applications {
/**
* #var integer
*
* #ORM\Column(name="applicationid", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $applicationid;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=50, nullable=false)
*/
private $name;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Webusers", inversedBy="applicationid")
* #ORM\JoinTable(name="authorization",
* joinColumns={
* #ORM\JoinColumn(name="applicationid", referencedColumnName="applicationid")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="userid", referencedColumnName="userid")
* }
* )
*/
private $userid;
/**
* Constructor
*/
public function __construct()
{
$this->userid = new \Doctrine\Common\Collections\ArrayCollection();
}
}
I was able to fix this by making sure I specified both the webusers and application relationship as ManyToOne AND giving them the #ORM\Id property since they make up a composite primary key.
I think the main reason this was failing was because I had not associated authorization to applications.
The key points of my new model are as follows:
class Webusers {
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\OneToMany(targetEntity="Authorization", mappedBy="user")
*/
private $authorization;
}
class Authorization {
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Applications")
* #ORM\JoinColumn(name="applicationid", referencedColumnName="applicationid")
*/
private $application;
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Webusers")
* #ORM\JoinColumn(name="userid", referencedColumnName="userid")
*/
private $user;
}
class Applications {
/**
* #ORM\OneToMany(targetEntity="Authorization", mappedBy="application")
*/
private $authorization;
}
Entity User
class User {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
...
/**
* #ORM\ManyToMany(targetEntity="Controleitor\Model\Entity\Account", mappedBy="user")
*/
protected $userAccount;
public function __construct() {
$this->userAccount = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getUserAccount() {
return $this->userAccount;
}
...
}
Entity Account
class Account{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
*
* #ORM\ManyToMany(targetEntity="JasUser\Model\Entity\User", inversedBy="userAccount")
* #ORM\JoinTable(name="user_has_account",
* joinColumns={#ORM\JoinColumn(name="idAccount", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="idUser", referencedColumnName="id")}
* )
*/
private $user;
public function __construct(array $options = null) {
$this->user = new \Doctrine\Common\Collections\ArrayCollection();
}
...
}
Test:
$user = $this->entityManager->getRepository('User')->findOneById($id);
$user->getUserAccount()->add($account);
$this->entityManager->persist($account);
$this->entityManager->flush();
$id = $entity->getId();
It gets the user, the userAccount and also inserts the new record account in the db, but it doesn't adds the record in the user_has_account table for the manytomany association between user and account with the add method as I was expecting..
Looks like you've got owning/inverse sides of your relation backwards.
The owning side has to use the inversedBy attribute of the OneToOne, ManyToOne, or ManyToMany mapping declaration. The inversedBy attribute contains the name of the association-field on the inverse-side.
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)
This can be somewhat annoying.
The preferred solution is to only touch the Collections internally. So instead of having your calling code do $user->getUserAccount()->add($account), implement it like this:
<?php
class Account {
// ...
public function addUser($user){
$this->users->add($user);
}
}
class User {
// ...
public function addAccount($account){
// relation must be added from the owning side
$account->addUser($this);
}
}