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.
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 use Symfony 3. I've got a parent class Licence which has some fields:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class Licence
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\Column(
* type="string",
* unique=true,
* length=30,
* nullable=false
* )
*/
protected $licenceKey;
}
And I have a child class which extends this one. Surely, it has the same field but I need them to be stored in database in a specific place:
So I wrote the following:
namespace AppBundle\Entity\LicenceCase\FirstLicenceCase;
use AppBundle\Entity\Licence as BaseLicence;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="lic_first")
* #ORM\InheritanceType("SINGLE_TABLE")
*/
class FirstLicenceCase extends BaseLicence
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
public function getLicenceKey()
{
return $this->licenceKey;
}
public function setLicenceKey($licenceKey)
{
$this->licenceKey = $licenceKey;
}
}
I need to get the unique columns for any specific LicenceCase. I am going to create new class for every LicenceCase with different fields and methods. But with this code I don't get a proper columns in my database. How can I solve this problem?
The solution will be to specify Inheritance tree where I need to specify all children. e.g in parent class to write the following annotations.
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="integer")
* #DiscriminatorMap({"1" = "LicenceCase1", "2" = "LicenceCase2", "3" = "LicenceCase3"})
*/
abstract class Licence
{
// ...
}
This works for me.
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 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.
}
I'm making entities with Symfony2 and Doctrine2. I made some entities that represent a many-to-many relation between two of my entities.
An example of one of these entities :
/**
* #ORM\Entity
*/
class Contact_Conference_Invitation
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Aurae\UserBundle\Entity\Contact")
*/
private $contact;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Aurae\ConferenceBundle\Entity\Conference")
*/
private $conference;
/**
* #var datetime dateInvitation
*
* #ORM\Column(name="dateInvitation", type="datetime")
*/
private $dateInvitation;
//Getters and setters
}
I have tried updating my sql schema, but the tables corresponding to these entities do not appear. Is there somewhere I have to declare them (config or such)? If not, what's wrong?
Thanks a lot
Edit : I had forgotten the namespace for these class, and that's why they were omitted by Doctrine. Another case closed :) thanks for the answers!
Assumptions ...
No, you don't need to declare them anywhere else than in your Entity directory.
What's the error message you got?
I guess you added
use Doctrine\ORM\Mapping as ORM;
on the top of your classes to let them be mapped.
I tried ...
I tried to generate your entities by adding a simple Contact & Conference entities and it's working fine.
Here are the code snippets:
Contact_Conference_Invitation.php
namespace Ahsio\StackBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Contact_Conference_Invitation
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Ahsio\StackBundle\Entity\Contact")
*/
private $contact;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Ahsio\StackBundle\Entity\Conference")
*/
private $conference;
/**
* #var datetime dateInvitation
*
* #ORM\Column(name="dateInvitation", type="datetime")
*/
private $dateInvitation;
//Getters and setters
}
Contact.php
namespace Ahsio\StackBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Contact
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #param $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
Conference.php
namespace Ahsio\StackBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Conference
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #param $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
Here are the generated tables:
NB: I used a specific namespace for the entities generation to work fine, you need to change them.
Also don't forget to check that you have automapping enabled.
In your config.yml you should have
doctrine:
orm:
auto_mapping: true
Came across this question because my entities weren't generated as well, this was my issue, it could save some time to people struggling with the same issue.
I don't see the definition of your ManyToMany relation in the sample of code you provided.
As an example, here's a ManyToMany relationship I implemented for a project
Entity Project.php
/**
* #var Provider[]
*
* #ORM\ManyToMany(targetEntity="Provider", mappedBy="projects")
*/
protected $providers = null;
Entity Provider.php
/**
* #var Project[]
*
* #ORM\ManyToMany(targetEntity="Project", inversedBy="providers")
* #ORM\JoinTable(name="PROVIDER_PROJECT")
*/
protected $projects = null;
As you can see, here you define the join table for your ManyToMany relationship.
Of course those entities are specific for my particular project but you get the idea and you can adapt it easily for your needs.