Symfony 2 relations issue - php

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.

Related

Doctrine Entity Relationship

Am having a bit of a challenge creating entity relationship between a product its Category and associated color(s) with the following Entities (I omitted the getters and setters though):
#Product
/**
* Product
*
* #ORM\Table(name="product")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
*/
class Product
{
/**
* #var int
*
* #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;
/**
* #var float
*
* #ORM\Column(name="price", type="float")
*/
private $price;
/**
* #var int
*
* #ORM\Column(name="category", type="integer")
*
* Many Products have one category
*
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false)
*/
protected $category;
/**
* #var int
*
* Many Products have one color
*
* #ORM\ManyToOne(targetEntity="Color", inversedBy="products")
* #ORM\JoinColumn(name="color_id", referencedColumnName="id")
*
* #ORM\Column(name="color", type="integer")
*/
private $color;
}
#Category
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
*/
class Category
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=170, unique=true)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="desc", type="string", length=170, nullable=true)
*/
private $description;
/**
* One Category has many products assigned to it
*
* #ORM\OneToMany(targetEntity="Product", mappedBy="category", cascade={"persist"})
*/
private $products;
/**
* Class Constructor
*
* #param None
* #return void
**/
public function __construct()
{
$this->products = new ArrayCollection();
}
}
#Color
class Color{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="color", type="string", length=191, unique=true)
*/
private $color;
/**
* #var string
*
* #ORM\Column(name="code", type="string", length=191, unique=true)
*/
private $hexcode;
/**
* One Color has many products assigned to it
*
* #ORM\OneToMany(targetEntity="Product", mappedBy="color", cascade={"persist"})
*/
private $products;
/**
* Class Constructor
*
* #param none
* #return void
**/
public function _construct(){
$this->products = new ArrayCollection();
}
}
When I run:
* php bin/console doctrine:schema:validate:
I get the error messages:
* The association AppBundle\Entity\Category#products refers to the owning
side field AppBundle\Entity\Product#category which is not defined as
association, but as field.
* The association AppBundle\Entity\Category#products refers to the owning
side field AppBundle\Entity\Product#category which does not exist.
and:
* The association AppBundle\Entity\Color#products refers to the owning side
field AppBundle\Entity\Product#color which is not defined as association,
but as field.
* The association AppBundle\Entity\Color#products refers to the owning side
field AppBundle\Entity\Product#color which does not exist.
I noticed that whenever I comment out the lines:
** #ORM\Column(name="category", type="integer")
** #ORM\Column(name="color", type="integer")
The above errors vanish but I get a new message and error saying:
** [Mapping] OK - The mapping files are correct.
** [Database] FAIL - The database schema is not in sync with the current
mapping file.
What could i be doing wrong, am new to the doctrine concept, and i have followed the documentations. Any help would be appreciated...
For the Doctrine ORM the relations are not Integers but Entity objects.
Remove The #ORM\Column annotations from all fields with relations (in each entity).
Then update your database schema in development environment with:
php bin/console doctrine:schema:update --force
Then generate a doctrine migration file to execute on your production server
php bin/console doctrine:migrations:generate
Update the generetad migration file to your needs
And execute him by this way
php bin/console doctrine:migrations:execute timestampOfTheMigrateFile

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 !

Symfony ManyToOne Form add, delete in DB

I have entity developer and comment and relationship Many comment to One developer. And I need form when I see all comment for developer and edit - add, delete in DB . What are the solutions to this problem
entity Comment:
class Comments
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Developer", inversedBy="comments")
* #ORM\JoinColumn(name="talent_id", nullable = true, referencedColumnName="id")
* */
protected $talent;
/**
* #var string
*
* #ORM\Column(name="added_by", type="string", length=10, nullable=true)
*/
private $added_by;
/**
* #var string
*
* #ORM\Column(name="comment", type="string", length=10, nullable=true)
*/
private $comment;
entity Developer:
class Developer extends CustomUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/////
/**
* #ORM\OneToMany(targetEntity="Comments", mappedBy="talent", cascade={"persist", "remove"})
*/
protected $comments;
Maybe need form in form but how to do this?
You are looking for field type collection.
Example usage of collection type
class Comments
{
....
/**
*
*#ORM\ManyToOne(targetEntity="Developer", inversedBy="developer_to_comments")
* #ORM\JoinColumn(name="developer_to_comments_id", referencedColumnName="id", nullable=false)
*
*/
private $comments_to_developer;
...
}
And class Developer
class Developer extends CustomUser
{
....
/**
*
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="Comments", mappedBy="comments_to_developer", cascade={"remove"})
*/
private $developer_to_comments;
public function __construct()
{
$this->developer_to_comments = new ArrayCollection();
}
....
}
And don't forget use Doctrine\Common\Collections\ArrayCollection

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

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