I've got a problem with Doctrine ORM 2.5. The issue happens when I try to load instances of an entity that has an optional ManyToOne reference to itself using part of its primary key as a part of the foreign key.
The entity looks like the following:
class Category
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Client")
* #ORM\JoinColumn(name="client_id", referencedColumnName="client_id"))
* #var Client
*/
protected $client;
/**
* #ORM\Id
* #ORM\Column(type="string")
* #var string
*/
protected $category_id;
/**
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="parent_id", referencedColumnName="category_id", nullable=true),
* #ORM\JoinColumn(name="client_id", referencedColumnName="client_id", nullable=false)
* })
* #var Category
*/
protected $parent;
/**
* #ORM\Column(type="translated")
* #SAP\Mapping(fieldName="name")
* #var string
*/
protected $name;
}
As you can see, every child of a category must have the same client_id as its parent.
The problem seems to be that Doctrine expects the parent to exists of the foreigen key isnt empfy ( = at least one field has a non-null value). Thats obviously always true since the FK contains a part of the PK.
Is there a way to tell Doctrine that it shouldn't try to load the parent if parent_id is null?
The only alternatives I see are changing the schema by either using an artifical auto generated pk or adding a new column for the parent_client_id and make sure it equals the client_id in PHP-land.
Is there a better way?
It seems like this is a bug as reported here: https://github.com/doctrine/orm/issues/4384
From the comments on that issue setting fetch="EAGER" resolves the doctrine Missing value for primary key error. Not really a great solution, but it may work for you depending on your use case.
Related
So I have a following single inheritance table defined:
/**
* #Entity
* #Table(name="listKeys")
* #InheritanceType("SINGLE_TABLE");
* #DiscriminatorColumn(name="parent", type="string")
* #DiscriminatorMap({"Type1" = "Something1", "Type2" = "Something2"})
*/
abstract class ListKey
{
/**
* #OneToMany(targetEntity="KeyListValue", mappedBy="listKey", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private Collection $listValues;
/**
* #ManyToOne(targetEntity="KeyListValue")
* #JoinColumn(name="defaultKeyListValueId")
*/
private ?KeyListValue $defaultKeyListValue = null;
}
With the list options defined as:
/**
* #Entity
* #Table(name="keyListValues")
*/
class KeyListValue
{
/**
* #Column(type="string")
*/
private $label;
/**
* #ManyToOne(targetEntity="ListKey", inversedBy="listValues", cascade={"persist"})
* #JoinColumn(name="caKeyId")
*/
private ListKey $listKey;
}
Before adding list values to a key, persisting and flushing worked ok, so the key would get inserted first after the commit order calculation. But once I added a new column default key list value, the commit order changes, and key list values try to get inserted first so the transaction fails.
Workaround is that I use flush() after creating new key and the adding the list values but this is problematic since ListKey also has association with some other entities which are not persisted yet at the time of flush so cascades would have to be declared and I don't want that. Any suggestion on how I can redefine this relationship better or a better workaround so the commit order would first insert keys?
Zend Framework 3, Doctrine 2. I created some mapping of 2 tables MySQL
/**
* #ORM\Entity
* #ORM\Table(name="object")
*/
class Object
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(name="id")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="..\Host", inversedBy="object")
* #ORM\JoinColumn(name="ip", referencedColumnName="IP")
*/
protected $host;
}
/**
* #ORM\Entity
* #ORM\Table(name="host")
*/
class Host
{
/**
* #ORM\Id
* #ORM\Column(name="IP")
*/
protected $ip;
/**
* #ORM\OneToOne(targetEntity="..\Object", mappedBy="host")
* #ORM\JoinColumn(name="IP", referencedColumnName="ip")
*/
protected $object;
}
I have this error:
Error "Missing value for primary key id on ..\Object".
Why? It seems to be done according to the example -> 5.3. One-To-One, Bidirectional from the site Doctrine
The parameters you pass to JoinColumn are incorrect. You can check the doc for JoinColumn here.
Try like this:
// For Object class:
// #ORM\JoinColumn(name="host_ip", referencedColumnName="ip")
// For Host class
// #ORM\JoinColumn(name="object_id", referencedColumnName="id")
In addition to this: mappedBy is not supported in OneToOne annotations. You can use targetEntity and inversedBy but not mappedBy.
On the other hand, making the ip your primary key seems wrong. You should have an auto-generated primary id for Host and use that to connect the entities. You can still set you ip field as unique, so you won't have duplicates. Plus you don't have any other fields, you can even just put the ip within Object class.
Also, I don't recommend using Object as your class name. It is too general.
I currently have to Entities in my application:
Page
Block
A Page can have many Blocks, which are shared across many Pages, so it is quite obvious that the relation is a ManyToMany. However, I need to be able to add the same Block twice (or more) to the same Page. Doctrine creates the "page_block" join table automatically, but with page_id and block_id both as Primary Keys, therefore adding a duplicate throws an error.
Is it possible, without adding an additional Entity, to tell doctrine to allow duplicates on the Page--Block relation ?
Well, I'm not sure about that behavior in doctrine, but if that is the case, then you can do something that I almost always do. Represent the ManyToMany relation as two OneToMany-ManyToOne. You must create your own PageBlock entity and configure it's foreign keys.
class Page{
/**
* #var array
*
* #ORM\OneToMany(targetEntity="PageBlock", mappedBy="page", cascade={"all"})
*/
private $pageBlocks;
}
class Block{
/**
* #var array
*
* #ORM\OneToMany(targetEntity="PageBlock", mappedBy="block", cascade={"all"})
*/
private $pageBlocks;
}
class PageBlock{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \stdClass
*
* #ORM\ManyToOne(targetEntity="Page", inversedBy="pageBlocks")
* #ORM\JoinColumn(name="id_page", referencedColumnName="id")
*/
private $page;
/**
* #var \stdClass
*
* #ORM\ManyToOne(targetEntity="Block", inversedBy="pageBlocks")
* #ORM\JoinColumn(name="id_block", referencedColumnName="id")
*/
private $block;
}
As you can see the primary key remains as ID, so problem resolved. I say almost always do because this is how I do it if I need an extra attribute in the relation(almost always it happens). I suspect that could be a way of do it with the ManyToMany annotation, but there is no difference with this approach.
Hope this help you.
When searching I came up with many results of people having similar problems but they were always related to association errors. I'm trying add a simple text field to a table in a database and, for the life of me, I can't figure out what's different about this time - when I've done it with no problems many times before.
I've added a 'record_checksum' field to 4 different entities, but I will use just one, here to simplify the example. (The same error happens for all 4).
Here is an example of my Entity\Cloud.php file, with the 'record_checksum' field added at the bottom:
use Doctrine\ORM\Mapping as ORM;
namespace Entity;
/**
* Entity\Cloud
*
* #orm:Table(name="cloud")
* #orm:Entity
* #orm:HasLifecycleCallbacks
*/
class Cloud
{
/**
* #var integer $id
*
* #orm:Column(name="id", type="integer", length="13")
* #orm:Id
* #orm:GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var float $position_x
*
* #orm:Column(name="position_x", type="float", length=9)
*/
private $position_x;
/**
* #var float $position_y
*
* #orm:Column(name="position_y", type="float", length=9)
*/
private $position_y;
/**
* #var string $commit_group
*
* #orm:Column(name="commit_group", type="string", length=32, nullable=true)
*/
private $commit_group;
/**
* #var string $commit_key
*
* #orm:Column(name="commit_key", type="string", length=13, nullable=true)
*/
private $commit_key;
/**
* #var string $record_checksum
*
* #orm:Column(name="record_checksum", type="string", length=32, nullable=true)
*/
private $record_checksum;
The rest of the class is getter/setter methods, so I will leave it out. To see the entire Entity file, I put it up on pastebin ( http://pastebin.com/9LheZ6A1 ). The 'commit_key' I'd just added a few weeks ago, with no problems.
Now I update the schema:
$ doctrine orm:schema-tool:update --dump-sql
ALTER TABLE cloud ADD record_checksum VARCHAR(32) DEFAULT NULL;
$ doctrine orm:schema-tool:update --force
Updating database schema...
Database schema updated successfully!
I verified this field now exists in the DB table.
However, when I run a simple DQL query like this:
$dql = "SELECT c.id AS id, c.commit_key AS key, c.record_checksum AS checksum ".
"FROM Entity\\Cloud c WHERE c.commit_group = :commit_group";
I get the error:
[Semantical Error] line 0, col 42 near 'record_checksum': Error: Class Entity\Cloud has no field or association named record_checksum,
I've banged my head on the wall over this for a while now. I'm sure I'm overlooking something really stupid. Any help is greatly appreciated!
-Nick
Try to:
Clear any cache that may contain the config or PHP code.
Rename a field in case the first solution didn't work.
Go to the Entity in question and check the variable name. If it's something like TESTVar, and you're trying to use testVar in the query, it's going to give you the error that the variable doesn't exist in the Entity.
Change the query to use TESTVar instead of testVar, and it will work.
OK, if anyone can help me with this that would be great, because it appears to be intractable.
I have 2 entities set up in a new zf-boilerplate project as below. I am trying to follow the tutorial on Zendcasts.com - One-to-Many with Doctrine 2, but can't get doctrine to recognise the associations I have mapped. If I run orm:schema-tool:create --dump-sql, it dumps the generated Sql, but NOT the ALTER TABLE statements at the end which should would create the Foreign Key Mapping, I can't get that to work properly.
I've tried everything I can think of, the JOIN statement I need to run obviously doesn't work either, but I figure if I can just get Doctrine to recognise the ALTER statement I can carry it from there.
Any ideas would be great, let me know if you need more info. I thought at first maybe the .ini file might be set up wrong, but I think this is more something to do with the relationship annotation?
Library/Photo/Entity/Gallery.php
<?php
namespace Photo\Entity;
/**
* #Entity(repositoryClass="Photo\Entity\Repository\MyGallery")
* #Table(name="gallery")
*/
class Gallery {
/**
* #Id #GeneratedValue
* #Column(type="smallint",nullable=false)
* #var integer
* #OneToMany(targetEntity="Photo", mappedBy="galleryID")
*/
protected $id;
/**
* #Column(type="string", length=200)
* #var string
*/
protected $gallery;
Library/Photo/Entity/Photo.php
<?php
namespace Photo\Entity;
/**
* #Entity(repositoryClass="Photo\Entity\Repository\MyPhoto")
* #Table(name="photo")
*/
class Photo {
/**
* #Id #GeneratedValue
* #Column(type="smallint",nullable=false)
* #var integer
*/
protected $id;
/**
* #Column(type="smallint",nullable=false)
* #var integer
* #ManyToOne(targetEntity="Gallery")
* #JoinColumns({
* #JoinColumn(name="gallery_id", referencedColumnName="id")
* })
*/
protected $galleryID;
Hmm... I see.. Check you column names, gallery_id vs galleryID looks suspicious.
If it is gallery_id, then you have to change the $galleryID annotation to #Column(type="smallint", nullable=false, name="gallery_id")
Generally, everywhere in the object model you should use the object field names, for example mappedBy="galleryID", but the column itself should be mapped with the appropriate DB name, like I mentioned #Column(name="gallery_id"), or for example #JoinColumns({#JoinColumn(name="gallery_id" referencedColumnName="id")})