Doctrine ORM - Same entity, different table for different ownership - php

I have an entity that in it's lifetime moves to different tables. Like so:
A village has many units
An army movement on the map contains many units
After an attack, the log file contains many units (with extra amount_killed column)
So anyway, this Unit is what I consider my entity, that can either be attached to a Village, Movement or Log via a table and foreign key.
I would think in Doctrine there is a way to say, this is an entity and it can as a foreign key to multiple tables. The thing is, I want to pluck Units from my Village and put them into a Movement but they're not defined as the same item so it will break.
My code for current usage will look something like this for transferring an entity to another table:
$villageUnits = $village->getVillageUnits();
$movementUnits = new ArrayCollection();
foreach ($villageUnits as $villageUnit) {
$movementUnit = new MovementUnit();
$movementUnit
->setLevel($villageUnit->getLevel());
->setAmount($villageUnit->getAmount());
->setUnitType($villageUnit->getUnitType());
$movementUnits->add($movementUnit);
}
At the moment, they're all seperate entities defined with Doctrine as follows:
TABLES
village_units
---------
id, village_id, unit_type_id, amount, level
movement_units
---------
id, movement_id, unit_type_id, amount, level
log_units
---------
id, log_id, unit_type_id, amount, amount_killed, level
ENTITIES
/**
* MovementUnit
*
* #ORM\Table(name="movement_units")
* #ORM\Entity
*/
class MovementUnit
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\Id
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="amount", type="integer", nullable=false)
*/
private $amount;
/**
* #var integer
*
* #ORM\Column(name="level", type="integer", nullable=false)
*/
private $level;
/**
* #var Movement
*
* #ORM\ManyToOne(targetEntity="Movement")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="movement_id", referencedColumnName="id")
* })
*/
private $movement;
/**
* #var UnitType
*
* #ORM\OneToOne(targetEntity="UnitType")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="unit_type_id", referencedColumnName="id")
* })
*/
private $unitType;
}
/**
* VillageUnit
*
* #ORM\Table(name="village_units")
* #ORM\Entity
*/
class VillageUnit
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\Id
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="amount", type="integer", nullable=false)
*/
private $amount;
/**
* #var integer
*
* #ORM\Column(name="level", type="integer", nullable=false)
*/
private $level;
/**
* #var Village
*
* #ORM\ManyToOne(targetEntity="Village")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="village_id", referencedColumnName="id")
* })
*/
private $village;
/**
* #var UnitType
*
* #ORM\OneToOne(targetEntity="UnitType")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="unit_type_id", referencedColumnName="id")
* })
*/
private $unitType;
}
/**
* LogUnit
*
* #ORM\Table(name="log_units")
* #ORM\Entity
*/
class LogUnit
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\Id
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="amount", type="integer", nullable=false)
*/
private $amount;
/**
* #var integer
*
* #ORM\Column(name="dead", type="integer", nullable=false)
*/
private $dead;
/**
* #var integer
*
* #ORM\Column(name="level", type="integer", nullable=false)
*/
private $level;
/**
* #var LogArmy
*
* #ORM\ManyToOne(targetEntity="LogArmy")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="log_army_id", referencedColumnName="id")
* })
*/
private $logArmy;
/**
* #var UnitType
*
* #ORM\OneToOne(targetEntity="UnitType")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="unit_type_id", referencedColumnName="id")
* })
*/
private $unitType;
}

I had the exact same problem and solved it with the answer of Robin in this question: Doctrine 2 Inheritance Mapping with Association
Just change the inheritanceType from "SINGLE_TABLE" to "JOINED" and your setup is working. If not, try using doctrine-module orm:schema-tool:create it will create the tables for you.

Related

Table join with entity (Symfony)

I'm new to Symfony and am having trouble getting my entities set up. I want to be able to access the tag names from my Acasset entity.
Here are the relevant entities:
class Actag
{
/**
* #var string
*
* #ORM\Column(name="tag_name", type="string", length=200, nullable=true)
*/
private $tagName;
/**
* #var integer
*
* #ORM\Column(name="tag_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $tagId;
/**
* Acassettag
*
* #ORM\Table(name="acAssetTag", indexes={#ORM\Index(name="IDX_7C4A2A745DA1941", columns={"asset_id"}), #ORM\Index(name="IDX_7C4A2A74BAD26311", columns={"tag_id"})})
* #ORM\Entity
*/
class Acassettag
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \AdminBundle\Entity\Acasset
*
* #ORM\ManyToOne(targetEntity="AdminBundle\Entity\Acasset", inversedBy="asset", cascade="PERSIST")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="asset_id", referencedColumnName="asset_id")
* })
*/
private $asset;
/**
* #var \AdminBundle\Entity\Actag
*
* #ORM\ManyToOne(targetEntity="AdminBundle\Entity\Actag")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="tag_id", referencedColumnName="tag_id")
* })
*/
private $tag;
/**
* Acasset
*
* #ORM\Table(name="acAsset", indexes={#ORM\Index(name="IDX_3B81679E68BA92E1", columns={"asset_type"}), #ORM\Index(name="IDX_3B81679E12469DE2", columns={"category_id"})})
* #ORM\Entity(repositoryClass="AdminBundle\Repository\AcAssetRepository")
*/
class Acasset
{
/**
* #var string
*
* #ORM\Column(name="asset_name", type="string", length=100, nullable=false)
*/
private $assetName;
/**
* #var integer
*
* #ORM\Column(name="asset_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $assetId;
/**
* #var \AdminBundle\Entity\Acassettype
*
* #ORM\OneToOne(targetEntity="AdminBundle\Entity\Acassettype", mappedBy="asset", fetch="EAGER", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="asset_type_id", referencedColumnName="asset_type_id")
* })
*/
private $assetType;
/**
* #var \AdminBundle\Entity\Actag
*
* #ORM\ManyToOne(targetEntity="AdminBundle\Entity\Actag")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="tag_id", referencedColumnName="tag_id")
* })
*/
private $assetTags;
/**
* Set tag
*
* #param \AdminBundle\Entity\Actag $tag
*
* #return Acassettag
*/
public function setAssetTags(\AdminBundle\Entity\Actag $tag = null)
{
$this->tag = $tag;
return $this;
}
/**
* Get tag
*
* #return \AdminBundle\Entity\Actag
*/
public function getAssetTags()
{
return $this->tag;
}
So in my Acasset entity I have created $assetTags but I am getting an error: Invalid column name 'tag_id'.
But $tag in the Acasettag entity works, which is set up the same way. What am I missing, still struggling a little with this part of Symfony.
I didnt't include all the getters and setters in this post, just the one I created for this.
Why does your Actag-Entity hat also a tagId field? You don't need a defined Id-Column on the Owning-Side. You have to define a OneToMany.
Many Acassettag are owned by one Actag (because of the ManyToOne-Definition in Acassettag
So every Acassettag will need the field actag_id with the Id of the owning Actag which will be autogenerated by your ManyToOne-Definition
When you create a new Acassettag Entity and want to "connect" it with a existing Actag you need Acassettag
public setActag(Actag $actag)
{
$this->tag = $actag;
return $this;
}

Doctrine cascade removing fails with OneToMany and OneToOne

I've been struggling for the past few days on a simple case of cascade removing using Doctrine.
Doctrine and Symfony are up to date.
I have two entities Serie and Asset that are linked to each other by two relationships OneToOne and OneToMany.
The schema is exactly like this :
A Serie has many Assets. (content).
A Serie can have an Asset. (a preview, this field is nullable).
However, no matter how I try to write and rewrite the annotations, I ALWAYS end up with this error:
An exception occurred while executing 'DELETE FROM serie WHERE id = ?' with params [1]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or
update a parent row: a foreign key constraint fails
(galanthis.asset, CONSTRAINT FK_2AF5A5CAA3A9334 FOREIGN KEY
(serie) REFERENCES serie (id))
Of course, the problem disappear if I delete the "preview" field and its annotations in the following code:
/**
* Serie
*
* #ORM\Table(name="serie")
* #ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
* #ORM\HasLifecycleCallbacks
*/
class Serie
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=96)
*/
private $title;
/**
* #var integer
*
* #Gedmo\SortablePosition
* #ORM\Column(name="position", type="integer", nullable=true)
*/
private $position;
/**
* #var \Portfolio
*
* #ORM\ManyToOne(targetEntity="Portfolio", inversedBy="series")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="portfolio", referencedColumnName="id")
* })
*/
private $portfolio;
/**
* #var \Asset
*
* #ORM\OneToOne(targetEntity="Asset")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="preview", referencedColumnName="id", nullable=true, onDelete="SET NULL")
* })
*/
private $preview;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="Asset", mappedBy="serie", cascade={"remove"})
**/
private $assets;
Here's the code for the Asset entity:
/**
* Asset
*
* #ORM\Table(name="asset")
* #ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="asset", type="string")
* #ORM\DiscriminatorMap({"asset" = "Asset", "video" = "Video","image" = "Image"})
*
*/
class Asset
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=128)
*/
protected $path;
/**
* #var string
*
* #ORM\Column(name="filename", type="string", length=64)
*/
protected $filename;
/**
* #var integer
*
* #ORM\Column(name="position", type="integer", nullable=true)
* #Gedmo\SortablePosition
*/
protected $position;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255, nullable=true)
*/
protected $description;
/**
* #var string
*
* #ORM\Column(name="mime", type="string", length=16, nullable=true)
*/
protected $mime;
/**
* #var \Serie
*
* #ORM\ManyToOne(targetEntity="Serie", inversedBy="assets")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="serie", referencedColumnName="id")
* })
*/
protected $serie;
/**
* #var UploadedFile
*/
protected $file;
/**
* #var string
*/
protected $extension;
It's driving me crazy, it's just some simple relationships... Is there a mistake I'm not seeing anymore, or do i need to use a workaround?
My guess is to set the cascade={"remove"} on the ManyToOne relationship in the Asset entity and not the other way around. That way, it tells Doctrine what to do when you delete a serie that is linked to many assets.

Symfony 2 relations issue

I nead help.
I have 3 entities. Book, Category And BookCategory - book can have multiple categories so i used another table.
I can easily acces Book and Category useing BookCategory table but i dont know how to do this by Book->BookCategory->Category.
class Category
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="text")
*/
private $name;
/**
* #var Category
* #ORM\ManyToOne(targetEntity="Category", inversedBy="Category")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
private $parent;
class BookCategory
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Book
* #ORM\ManyToOne(targetEntity="Book", inversedBy="BookCategory")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $bookId;
/**
* #var Category
* #ORM\ManyToOne(targetEntity="Category", inversedBy="BookCategory")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $categoryId;
/**
* #var integer
*
* #ORM\Column(name="priority", type="integer")
*/
private $priority;
class Book
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="text")
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="author", type="text")
*/
private $author;
/**
* #var float
*
* #ORM\Column(name="price", type="float")
*/
private $price;
How i need to config my entities or how to make my DQL to achive wanted results?
With your code, you only established the relationship from BookCategory to Book. As you said, that enables you to get the Book associated to one BookCategory.
To go the other way and get all BookCategory that belong to one book, you also need to specify this relationship. What you want is a OneToMany relationship from Book to BookCategory.
<?php
//...
use Doctrine\ORM\Mapping\OneToMany;
class Book
{
//...
/**
* #OneToMany(targetEntity="BookCategory", mappedBy="bookId")
*/
private $bookCategories;
//...
}
class BookCategory
{
//...
/**
* #var Book
* #ORM\ManyToOne(targetEntity="Book", inversedBy="bookCategories")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $bookId;
//...
}
After adding the necessary getters and setters, getBookCategories() will give you an Array with all BookCategory that belong to the Book.
For more details, have a look at the official Symfony2 documentation:
http://symfony.com/doc/current/book/doctrine.html#relationship-mapping-metadata
Edit:
Included use statement. Corrected inversedBy property for bookId.

The column id must be mapped to a field

Just changed some setting to a doctrine entity configuration and got the following execption
The column id must be mapped to a field in class VSmart\OrmBundle\Entity\Ob
ject since it is referenced by a join column of another class.
I would expect that the column id is not mapped to a field in Object. Though, see here the code of that particular mapping:
/**
* #var integer
*
* #ORM\Column(name="Id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
So, what is the exception really trying to tell me?
Update
This is the other end of the relation.
Object definition:
/**
* #var integer
*
* #ORM\Column(name="Id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Address", type="string", length=45, nullable=true)
*/
private $address;
/**
* #var string
*
* #ORM\Column(name="Name", type="string", length=45, nullable=true)
*/
private $name;
/**
* #var \Entis
*
* #ORM\ManyToOne(targetEntity="Entis",inversedBy="objects")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="EntisId", referencedColumnName="Id")
* })
*/
private $entis;
/**
* #var \Objecttype
*
* #ORM\ManyToOne(targetEntity="ObjectType")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="ObjectType", referencedColumnName="Id")
* })
*/
private $objectType;
/**
* #var \Unit
*
* #ORM\ManyToOne(targetEntity="Unit")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="UnitId", referencedColumnName="Id")
* })
*/
private $unit;
/**
* #var \Dimension
*
* #ORM\ManyToOne(targetEntity="Dimension")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="DimensionId", referencedColumnName="Id")
* })
*/
private $dimension;
/**
* #ORM\ManyToMany(targetEntity="Tag") */
private $tags;
/**
* #ORM\OneToMany(targetEntity="Measurement", mappedBy="object")
*/
private $measurements;

Doctrine ORM does not persist relations

I have a problem persisting relations with doctrine.
This is the process I'm doing:
Create Material object & fill with data
Create PossibleMaterialConfiguration Object & fill with data
Assign PossibleMaterialConfiguration Object to Material Object with
$material->addPossibleMaterialConfiguration($possibleMaterialConfiguration);
Result:
PossibleMaterialConfiguration.material_id is empty!
But when I do it the other way round and assign Material Object to PossibleMaterialConfiguration it does work!
I'm driving crazy! What the hell is going on here? Thanks in advance.
My two entities:
Material.php
/**
* Material
*
* #ORM\Table()
* #ORM\Entity
*/
class Material
{
/**
* #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)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="PossibleMaterialConfiguration", mappedBy="material", cascade={"persist"})
*/
private $possibleMaterialConfigurations;
PossibleMaterialConfiguration.php
/**
* PossibleMaterialConfiguration
*
* #ORM\Table()
* #ORM\Entity
*/
class PossibleMaterialConfiguration
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Material", inversedBy="possibleMaterialConfigurations")
* #ORM\JoinColumn(name="material_id", referencedColumnName="id")
*/
private $material;
/**
* #var float
*
* #ORM\Column(name="thickness", type="decimal")
*/
private $thickness;
/**
* #var float
*
* #ORM\Column(name="lengthMin", type="decimal")
*/
private $lengthMin;
/**
* #var float
*
* #ORM\Column(name="lengthMax", type="decimal")
*/
private $lengthMax;
/**
* #var float
*
* #ORM\Column(name="widthMin", type="decimal")
*/
private $widthMin;
/**
* #var float
*
* #ORM\Column(name="widthMax", type="decimal")
*/
private $widthMax;
Take a look at the article about owing side in doctrine :
http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html
Basically, doctrine won't check the side where is the "mappedBy" annotation.
If you want to do :
$material->addPossibleMaterialConfiguration($possibleMaterialConfiguration);
Then your addPossibleMaterialConfiguration function should be :
function addPossibleMaterialConfiguration($assoc) {
$this->possibleMaterialConfigurations[] = $assoc;
$assoc->setMaterial($this);
}

Categories