Doctrine ORM does not persist relations - php

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

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

Files storage with doctrine and symfony 3?

I need help for create my entities whit doctrine 2 in my symfony3 app :
I would like my users can posts articles which content is:
title
author
either one unique image (upload file)
or one unique movie ($url)
What do you recommend ?
Should I build my article entity like this ?
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="author", type="integer")
*/
private $author;
/**
* #var ?
*
* #ORM\Column(name="image", type="?")
*/
private $image;
/**
* #var string
*
* #ORM\Column(name="url_movie", type="string")
*/
private $url_movie;
/**
* #var integer
*
* #ORM\Column(name="media", type="integer")
*/
private $media;
}
(in controller : if $media = 1 => this is an image, else this is a video)
Or use something like Relation One-To-One with a new entity "media" for example ?
What is the best way for my case ?
Yes, it's normal. Sadly, the discriminator column is meant to be used by Doctrine, database side, therefore it's not accessible in your entity. There's two possible way to achieve what you want:
The first, using the children class name:
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
//...
/**
* Get article type.
*
* #return string
*/
public function getType()
{
// This will return "movie" or "image"
return strtolower(substr(strrchr(get_class($this), "\\"), 1));
}
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
//getter setter
}
The second, by declaring manually the type in your class:
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*/
protected $type;
//...
/**
* Get article type.
*
* #return string
*/
public function getType()
{
return $this->type;
}
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
public function __construct()
{
$this->type = 'movie';
}
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
public function __construct()
{
$this->type = 'image';
}
//getter setter
}
Personnaly, I have a preference for the first solution. I find it cleaner, and more evolutive (this code will adapt if you have to add a third article type).
Of course, you can also use instanceof to determine which subclass is the Article entity you're manipulating.
I think using a Media entity and handling the media type in the Media entity is the best way.
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ...
/**
* #var Media
*
* #OneToOne(targetEntity="Media")
* #ORM\JoinColumn(name="media_id", referencedColumnName="id")
*/
private $media;
}
class Media
{
const TYPE_IMAGE = 'image';
const TYPE_MOVIE = 'movie';
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $url;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $type;
}
An other way to do it could be to use entity inheritance to have differents entities for images and movies - if you need to.
Ok I did a Single Table Inheritance on my article class :
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
//...
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
//getter setter
}
That works great !

Can't Create association on Symfony2

I'm trying to create a "OneToMany" bidirectional association in my project but when I execute "doctrine:schema:update" nothing happens.
If I create this association directly from Sequel Pro and run the update schema command, that changes dissapear... :/
The relations is:
- One "id" from Customers Table with many "customer_id" form Control table.
Here is the Customers code:
<?php
namespace Ourentec\CustomersBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Customers
*
* #ORM\Table()
* #ORM\Entity
*/
class Customers
{
/* #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=100)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="lastname", type="string", length=100)
*/
private $lastname;
/**
* #var string
*
* #ORM\Column(name="address", type="text")
*/
private $address;
/**
* #var string
*
* #ORM\Column(name="phone", type="string", length=100)
*/
private $phone;
/**
* #var string
*
* #ORM\Column(name="pass", type="string", length=100)
*/
private $pass;
/**
* #var string
*
* #ORM\Column(name="tasks", type="text")
*/
private $tasks;
/**
* #var string
*
* #ORM\Column(name="status", type="string", length=100)
*/
private $status;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=100)
*/
private $email;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var string
*
* #ORM\Column(name="location", type="string", length=100)
*/
private $location;
/**
* #ORM\OneToMany(targetEntity="Control", mappedBy="customers")
*/
private $customer_id;
public function __construct()
{
$this->customer_id = new ArrayCollection();
}
And the Control code:
<?php
namespace Ourentec\CustomersBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Control
*
* #ORM\Table()
* #ORM\Entity
*/
class Control
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="customer_id", type="integer")
*
* #ORM\ManyToOne(targetEntity="Customers", inversedBy="control")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
*/
private $customerId;
/**
* #var integer
*
* #ORM\Column(name="user_id", type="integer")
*/
private $userId;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var integer
*
* #ORM\Column(name="seen", type="smallint")
*/
private $seen;
I followed the documentation from this 2 websites
http://symfony.com/doc/current/book/doctrine.html
http://librosweb.es/libro/symfony_2_x/capitulo_8/relaciones_y_asociaciones_de_entidades.html
But I don't know why it does not work..
Any idea will be appreciated :)
Mapping are not correct, I will try to explain how it works.
In Customers entity (you should rename it to Customer, entites names are singular)
/**
* #ORM\OneToMany(targetEntity="Control", mappedBy="customer")
*/
private $controls;
Mapped by option defines field name in the other entity.
/**
* #var integer
*
* #ORM\Column(name="customer_id", type="integer")
*
* #ORM\ManyToOne(targetEntity="Customers", inversedBy="controls")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
*/
private $customer;
Same thing with inversedBy.
In Customers entity you also need to init controls var as an ArrayCollection:
public function __construct()
{
$this->controls = new ArrayCollection();
}
With these mappings schema should be updated correctly.
For more info, check doctrine docs.

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.

Doctrine ORM - Same entity, different table for different ownership

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.

Categories