I have 2 entities — NewsItem and Category. It is unidirectional association between this entities: 1 NewsItem has 1 Category, 1 Category has many NewsItem's.
I am trying to create association mapping like in THIS example. What I've tried? My code:
class NewsItem {
// other fields
/**
* #var int
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="News\Entity\Category")
*/
protected $category_id;
// other fiels, getters, setters etc
}
After that, I deleted tables in the database manually and run command orm:schema-tool:update --force in command line. It says that some queries are executed without errors — it's ok. But when I open table Category in HeidiSQL there are no FOREIGN KEYS there. That means that tables are not linked.
What I did wrong?
You can watch full code of this News\Entity\NewsItem entity here: click me. News\Entity\Category entity is here: click me.
you should remove * #ORM\Column(type="integer") as it is conflicting with the many-to-one relation.
Even if it is not the cause of the bug, you should also rename protected $category_id; to protected $category;
Also, your two entities are under the same namespace so it's not necessary to add the related entity's full path. targetEntity="Category" is enough.
You have incorrect mapping information for the Category entity.
Your NewsItem.php file should look like this:
namespace Your\Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinColumn;
/**
* #ORM\Table(name="news_item")
* #ORM\Entity
*/
class NewsItem {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Category
*
* #ORM\ManyToOne(targetEntity="Category", inversedBy="news_items")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
// Rest of code omitted.
}
And your Category.php should look like this:
namespace Your\Bundle\Entity;
/**
* #ORM\Table(name="category")
* #ORM\Entity
*/
class Category {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="NewsItem", mappedBy="category")
*/
private $news_items;
public function __construct(){
$this->news_items = new ArrayCollection();
}
// Rest of code omitted.
}
Related
I am new to Symfony and trying to setup entities and relationships. Even though I seemed to have correctly annotated the primary keys.
When running
php bin/console doctrine:schema:validate
I got an error as follows:
The referenced column name 'brandId' has to be a primary key column on the target entity class 'AppBundle\Entity\Brands'.
The entities look like ( only relevant portions):
Thanks
Brands
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="brands")
*/
class Brands
{
/**
* #ORM\Id
* #ORM\Column(type="smallint",length=3,unique=true,options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $brandId;
/**
* one brands has many models
* #ORM\OneToMany(targetEntity="Models", mappedBy="brandId")
* #ORM\JoinColumn(name="brandId", referencedColumnName="brandId")
*/
private $models;
public function __construct()
{
$this->models = new ArrayCollection();
}
Models:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ModelsRepository")
* #ORM\Table(name="models")
*/
class Models
{
/**
* #ORM\Column(type="smallint",length=4,unique=true,options={"unsigned":true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $modelId;
/**
* #ORM\Column(type="string", length=25)
*/
private $model;
/**
* Many models for one brand
* #ORM\ManyToOne(targetEntity="Brands",inversedBy="models")
* #ORM\JoinColumn(name="brandId", referencedColumnName="brandId")
*/
private $brandId;
A valid and clean entity mapping should be something like:
Brand.php
/**
* #ORM\Entity
* #ORM\Table(name="brands")
*/
class Brand
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* one brands has many models
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Model", mappedBy="brand")
*/
private $models;
public function __construct()
{
$this->models = new ArrayCollection();
}
}
Model.php
class Model
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #ORM\Column(type="string", length=25)
*/
private $name;
/**
* Many models for one brand
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Brand",inversedBy="models")
*/
private $brand;
}
for clean and easy usage:
$brand->getId(); //get id of brand
$brand->getModels(); //get array of Model object, ArrayCollection
$model->getBrand()->getId(); // Get id of related brand of some model
$model->getBrand()->getName(); //get the name of other propery of related brand
In your Brands model, this annotation should not be present.
* #ORM\JoinColumn(name="brandId", referencedColumnName="brandId")
JoinColumn only applies to ManyToOne and OneToOne fields. This is because in a one to many relationship, there will not be a join column in the table that contains the data for the owning ("one") side of the relationship.
JoinColumn is for defining a column on the "many" side that identifies which record on the "one" side owns it, so including it in the Models model is okay.
Wrong answer removed...
Bonus tips: Entity classes or better their instances should represent single datasets of a table. So their names should be singular ;)
By chosing not prefixed names for your primary key columns, you can spare some code, because Doctrine can work its magic then.
Also you should not prefix one column with the table name, if you do not prefix all of them.
This all ended up being an issue with Doctrine's naming strategy, which I did not know about. See this article for details
That strategy was set as default, while I was trying to adapt mysql's underscore naming strategy to PHP's camel case naming strategy. This was causing Symfony not to find the primary and thus the error.
Once I learned about that and with the valuable input I've received with this post, I have refactored the entities as below:
Thank you all.
Brands
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="brands")
*/
class Brands
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="smallint",length=3,unique=true,options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* one brands has many models
*
* #ORM\OneToMany(targetEntity="Models", mappedBy="brandId")
*/
private $models;
public function __construct()
{
$this->models = new ArrayCollection();
}
/**
* #ORM\Column(type="string", length=25)
*/
private $brand;
.......
}
Models
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ModelsRepository")
* #ORM\Table(name="models")
*/
class Models
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="smallint",length=4,unique=true,options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=25)
*/
private $model;
/**
* Many models for one brand
* #ORM\ManyToOne(targetEntity="Brands",inversedBy="models")
* #ORM\JoinColumn(name="brand_id", referencedColumnName="id")
*/
private $brandId;
/**
* Many models for one segment
* #ORM\ManyToOne(targetEntity="Segments", inversedBy="models")
* #ORM\JoinColumn(name="segment_id", referencedColumnName="id")
*/
private $segmentId;
.....
}
I have to rewrite application from zf1 to sf2.
But I need to keep old database schema.
And I have problem with many to many relations.
There are 2 entities: Exceptions, Regions and it was too Exceptionregions, but I deleted it.
There are 3 tables in database - exceptions, regions and exceptionregions, which is hash table.
Below I attach screen with relations:
My code:
1. Exception entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Exceptions
*
* #ORM\Table(name="Exceptions")
* #ORM\Entity
*/
class Exceptions
{
/**
* #var integer
*
* #ORM\Column(name="ExceptionID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $exceptionid;
/**
* Many exceptions have many regions.
* #ORM\ManyToMany(targetEntity="Regions", inversedBy="exceptions")
* #ORM\JoinTable(name="exceptionregions"),
* joinColumns={#ORM\JoinColumn(name="ExceptionID", referencedColumnName="ExceptionID")},
* inverseJoinColumns={#ORM\JoinColumn(name="RegionID", referencedColumnName="RegionID")}
* )
*/
private $regions;
public function __construct()
{
$this->regions = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add region
*
* #param AppBundle\Entity\Regions $region
*/
public function addRegion(\AppBundle\Entity\Regions $regions)
{
$this->regions[] = $regions;
}
/**
* Get regions
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getRegions()
{
return $this->regions;
}
...
}
Region entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Regions
*
* #ORM\Table(name="Regions")
* #ORM\Entity
*/
class Regions
{
/**
* #var string
*
* #ORM\Column(name="RegionName", type="string", length=45, nullable=false)
*/
private $regionname;
/**
* #var integer
*
* #ORM\Column(name="RegionID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $regionid;
/**
* #ORM\ManyToMany(targetEntity="Exceptions", mappedBy="regions")
*/
private $exceptions;
...
}
And I got this error:
The column id must be mapped to a field in class AppBundle\Entity\Exceptions since it is referenced by a join column of another class.
Of course entity Exceptions is connected with few entities, not only regions.
I got stuck with this issue, I can't resolve this problem and continue my project.
Anybody has any idea how to repair this or any advice? What am I doing wrong?
I'd be grateful for any comment.
I found a solution for this problem.
Maybe someone will benefit from this too.
So, the working code:
/**
* Many exceptions have many regions.
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Regions", inversedBy="exceptions")
* #ORM\JoinTable(name="exceptionregions",
* joinColumns={
* #ORM\JoinColumn(name="ExceptionID", referencedColumnName="ExceptionID")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="RegionID", referencedColumnName="RegionID")
* })
*/
private $regions;
#Alvin, thank you for your commitment.
Have you tried to make the relation bi-directional like so:
class Exceptions{
...
/**
* Many exceptions have many regions.
* #ORM\ManyToMany(targetEntity="Regions", inversedBy="exceptions")
* #ORM\JoinTable(name="regions_exceptions")
*/
private $regions;
class Regions{
...
/**
* #ORM\ManyToMany(targetEntity="Exceptions", mappedBy="regions")
*/
private $exceptions;
Not sure if that will work, but can you try it.
Documentation here:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-many-bidirectional
** EDIT #2 **
Can you try this change:
class Exceptions
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(name="ExceptionID", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $exceptionid;
Also, if that doesn't work, try:
php bin/console doctrine:schema:update --force
Following the Symfony 2.8 book examples for the most part, as a total beginner I'm having trouble fetching data from the database with a ManyToOne relationship. I have a database table categories and one deploys.
deploys has a category_id which refers to categories.id. So one category can have many deploys. The database has data, when I custom query in pma I get results.
My Question:
After executing my controller code I get a Collection (see image), but not filled with deploy elements as it seems. What am I doing wrong? How can I display the deploy elements with a certain category_id
In my Controller I do:
$category = $this->getDoctrine()->getRepository('AppBundle:Category')->find(1);
$deploys = $category->getDeploys();
dump($deploys);
die();
This will display:
I have a Deploy Entity: (part of it, it has been generated by app/console)
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="deploys")
*/
class Deploy {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="deploys")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
// etc
And a Category Entity:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="categories")
*/
class Category {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\Column(type="integer", length=10)
*/
private $weight;
/**
* #ORM\OneToMany(targetEntity="Deploy", mappedBy="category")
*/
private $deploys;
/**
* Constructor
*/
public function __construct() {
$this->deploys = new ArrayCollection();
}
// etc
This is lazy loading feature. They will be loaded form DB once you'll try to access them.
Try to simply access some elements of this collection (e.g. iterate over $deploys) and you will see it will work just fine.
Note that on your screen there is attribute initialized: false. That means that this collection was not accessed yet, so Doctrine didn't have to load it.
Doctrine fails with a simple bi-directional many-to-one relationship between FoodDes (many) and FoodGroup (one). Both entities are shown here:
/**
* #ORM\Entity
* #ORM\Table(name="FOOD_DES")
*/
class FoodDes
{
public function __construct()
{
$this->foodGroup = new ArrayCollection();
}
/**
* #ORM\Id
* #ORM\Column(name="NDB_No", type="string", length=10)
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="FoodGroup", inversedBy="fdGroupCode")
* #ORM\JoinColumn(name="FdGrp_Cd", referencedColumnName="FdGrp_CD")
*/
protected $foodGroup;
}
>
/**
* #ORM\Entity
* #ORM\Table(name="FD_GROUP")
*/
class FoodGroup
{
/**
* #ORM\Id();
* #ORM\GeneratedValue(strategy="NONE");
* #ORM\OneToMany(targetEntity="FoodDes", mappedBy="foodGroup")
*/
protected $fdGroupCode;
When I run doctrine orm:schema-tool:create, it fails with error:
No identifier/primary key specified for Entity
'Acme\Entities\FoodGroup'. Every Entity must have an
identifier/primary key.
However, I labeled $fdGroupCode as my only identifier.
Next approach
I've also tried creating a new primary key $id on the FoodGroup entity and removing the primary key label from $fdGroupCode on FoodGroup. Below is the new FoodGroup entity.
/**
* #ORM\Entity
* #ORM\Table(name="FD_GROUP")
*/
class FoodGroup
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer", nullable=false)
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="FoodDes", mappedBy="foodGroup")
*/
protected $fdGroupCode;
When I run doctrine orm:schema-tool:create again, it results with a new error:
[Doctrine\ORM\ORMException]
Column name FdGrp_CD referenced for relation from
Acme\Entities\FoodDes towards Acme\Entities\FoodGroup does not exist.
This error doesn't make any sense. Of course it wouldn't exist. I am running it against an empty database!
These error occur running from the command line, but they also occur when querying the entities against a database. Can somebody please help me?
I'd rather give you working example of OneToMany from one of my projects, so you can see the difference and format code in proper way. If it does not work, then try to get a new Symfony dist and start over.
<?php
// SomeBundle/Entity/Shop/Product.php
namespace SomeBundle\Entity\Shop;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="shop_products")
*/
class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="bigint")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="ProductItem", mappedBy="product")
*/
protected $productItem;
}
Related entity:
<?php
// SomeBundle/Entity/Shop/ProductItem.php
namespace SomeBundle\Entity\Shop;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="shop_products_items")
*/
class ProductItem
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="bigint")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="productItem")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
protected $product;
}
For reasons why your code does not work could be many (namespaces, folder structure, column names, etc.). This example works and tested. Give it a try : )
I am new to Symfony2 and Doctrine.
I have two classes: User and Book. Each user can like very much, quite, or dislike every book in the database.
In order to describe this kind of association, I defined three different ManyToMany relationships between the two classes.
Here is an extract of my code:
Here is class User
<?php
namespace MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="MyBundle\Entity\UserRepository")
*/
class User
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=64, unique=true)
*/
private $username;
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="user_likemuch")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $books_likemuch;
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="user_likequite")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $books_likequite;
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="user_dislike")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $books_dislike;
public function __construct()
{
$this->books_likemuch = new ArrayCollection();
$this->books_likequite = new ArrayCollection();
$this->books_dislike = new ArrayCollection();
}
}
And here is class Book:
<?php
namespace MyBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Book
*
* #ORM\Table(name="book")
* #ORM\Entity(repositoryClass="MyBundle\Entity\BookRepository")
*/
class Book
{
/**
* #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=64)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="books_likemuch")
*/
protected $user_likemuch;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="books_likequite")
*/
protected $user_likequite;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="books_dislike")
*/
protected $user_dislike;
public function __construct() {
$this->user_likemuch = new ArrayCollection();
$this->user_likequite = new ArrayCollection();
$this->user_dislike = new ArrayCollection();
}
}
However, when I try to update the schema of the database through
php app/console doctrine:schema:update --force
I get the following error:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'XXX/myproject/app/data/users.user_book' already exists.
Actually, I do not even understand if I an describing the relationship in a wrong way, or if doctrine cannot manage multiple relationships between two classes.
Does anyone have any suggestion?
Thanks in advance!
Doctrine can indeed manage multiple relationships. The classic example is between Users and Roles. The problem here is that you're trying to create multiple ManyToMany relationships between the same two entities. What Doctrine will do to manage these relationships is create a "join" table using the convention table1_table2, or in your case "user_book". So the reason you are getting the Doctrine error is ostensibly because it's attempting to create the same table 3 times.
I think what you want to do is have 3 separate ManyToMany "join" tables, one for likemuch, likequite, and dislike. So try specifying the join table for each association and see if that helps. Ex:
/**
* #ORM\ManyToMany(targetEntity="Book", inversedBy="user_likemuch")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
* #ORM\JoinTable(name="user_book_likemuch")
*/
protected $books_likemuch;
See the section on bi-directional association mapping for ManyToMany relationships: Doctrine Association Mapping