I want to do a one to many / many to one relationship between two DAO.
After annoting properties, I have an unexpected and unlimited object in the result.
/**
* TicketSponsorDAO
*
* #ORM\Table(name="ticket_sponsor")
* #ORM\Entity
*/
class TicketSponsorDAO {
/**
* #var int
*
* #ORM\Column(name="ticket_id", type="integer")
*/
private $ticketId;
/**
* #ORM\ManyToOne(targetEntity="TicketDAO", inversedBy="sponsors")
* #ORM\JoinColumn(name="ticket_id", referencedColumnName="id")
*/
private $ticket;
...
}
And
/**
* TicketDAO
*
* #ORM\Table(name="ticket")
* #ORM\Entity
*/
class TicketDAO
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="TicketSponsorDAO", mappedBy="ticket")
*/
private $sponsors;
public function __construct() {
$this->sponsors = new ArrayCollection();
}
...
}
When I execute:
$sponsorEm = $em->getRepository(TicketDAO::class);
$spo = $sponsorEm->find("2");
var_dump($spo);
I have good properties about the ticket DAO, but the relation doesn't work and I have an unlimited object which is returned.
So in the MySQL database, I have the foreign key, the FK index and the primary key which are here.
The var_dump:
I follow this documentation: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional
Use symfony dumper as you are having a circular reference.
dump($spo);
Hi var_dump() will return your the type of objects and classes.
you should try to fetch the propertes like this
$spo->getSponsers();
it will return you an array or may be an collection;
For me it looks like one ticket have many sponsors and in this case I see only one problem. There is no auto_increment id in the TicketSponsorDao table, but there is a ticket_id column and I don't understand the purpose of that.
/**
* TicketSponsorDAO
*
* #ORM\Table(name="ticket_sponsor")
* #ORM\Entity
*/
class TicketSponsorDAO {
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
}
There is a recursion between the objets it's logic with OneToMany/ManyToOne relationship because the object of One is referred in the object of Many, ...
The var_dump doesn't manage it correctly.
To display the object I use dump from Symfony and it's good!
We can add a __toString with return serialize($this) into DAO class if the dump is displayed in browser.
Otherwise, we have an error:
... ENTITY could not be converted to string ...
Related
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
How can I determine (within Doctrine filter) if entity is directly hydrated or was only joined or is fetched from other's entity relation?
I know how can it be filtered globally, this works (filter is enabled in Symfony's kernel.request listener):
Entities
/**
* Period
*
* #ORM\Table(name="period")
* #ORM\Entity(repositoryClass="Acme\DemoBundle\Entity\PeriodRepository")
*/
class Period {
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Task", inversedBy="periods")
*/
private $task;
}
/**
* Task
*
* #ORM\Table(name = "task")
* #ORM\Entity(repositoryClass="Acme\DemoBundle\Entity\TaskRepository")
*/
class Task
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="type", type="integer")
*/
private $type;
/**
* #ORM\OneToMany(targetEntity="Period", mappedBy="task")
*/
private $periods;
}
Filter
namespace Acme\DemoBundle\Doctrine\Filter;
use Acme\DemoBundle\Entity\Task;
use Doctrine\ORM\Query\Filter\SQLFilter;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
* DisableTasksFilter excludes tasks from Doctrine results.
*/
class DisableTasksFilter extends SQLFilter
{
const NAME = 'acme_disable_tasks';
/**
* #inheritdoc
*/
public function addFilterConstraint(ClassMetadata $entity, $alias)
{
// Ignore tasks with type = 1
if ($entity->getReflectionClass()->name === Task::class) {
return "$alias.type != 1";
}
return '';
}
}
BUT... (THE PROBLEM)
Filter should work only for direct fetching Task entities and SHOULD NOT filter Task relations in Period entities, nor joined entities in queries (it could break some logic).
In our app there are few places where Tasks are fetched:
TaskManager methods
TaskRepository used in TaskManager and directly
User->tasks relation (via helper methods which filter collection with ArrayCollection::filter())
Period->task relation
It would be much easier if it could be possible to filter it with globally registered filter instead of adding conditions to each place where Tasks are fetched.
Is it even possible?
I am having an issue with Doctrine 2 and can not find a solution for it. It seems that when persisting an entity (and flushing it). When trying to search for it in DB using the assigned id while within the same PHP process doctrine is giving me the cached version of the entity instead of building a proper one with all correct relations. My problem with that is that my entity has some Many to many or many to one connections and if i get the the cached version all those properties come back as null instead of an instance of \Doctrine\Common\Collections\Collection.
Is there any way I can force doctrine to generate or update my entity to assign all those connections, or at least force it to serch for that entity insead of giving it to me from cache?
As requested examples:
class A extends AbstractDoctrineEntity {
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var \Core\Entity\B
* #ORM\ManyToOne(targetEntity="Core\Entity\B")
*/
protected $relationB;
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToMany(targetEntity="Core\Entity\C", inversedBy="relationA", fetch="EXTRA_LAZY")
*
*/
protected $relationC;
//getters and setters
}
class B extends AbstractDoctrineEntity {
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var \Core\Entity\A
* #ORM\OneToMany(targetEntity="Core\Entity\A")
*/
protected $relationA;
//getters and setters
}
class B extends AbstractDoctrineEntity {
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var \Core\Entity\A
* #ORM\ManyToMany(targetEntity="Core\Entity\A", mappedBy="realtionC", cascade={"persist", "remove")
*/
protected $relationA;
//getters and setters
}
// Service
class SomeService implements ServiceLocatorAwareInterface, MultiRepositoryServiceInterface {
use ServiceLocatorAwareTrait, MultiRepositoryServiceTrait;
public function createA(\Core\Entity\B $b) {
$a = new \Core\Entity\A();
$a->setB($b);
$this->getRepository('a')->persist($a); // gets entity manager persists entity $a and flushes
return $a;
/**
* returns an object
* a = {
* id = 2,
* relationB = Core\Entity\B,
* ralationC = null,
*/
}
}
public function getA($id){
$a = $this->getRepository('a')->persist($a); // retrieves object manager and queries by id;
return $a;
//**
* returns an object
* a = {
* id = 2,
* relationB = Core\Entity\B,
* ralationC = \Doctrine\Common\Collections\Collection, //which is an empty collection prototype and exactly what i need to retrieve in the first case when persisting the entity.
* }
*/
}
}
EDIT:
Found a solution, this may not be the nicest way to do this, so if someone knows a better way please update, but for now it works:
-----
public function createA(\Core\Entity\B $b) {
$a = new \Core\Entity\A();
$a->setB($b);
$this->getRepository('a')->persist($a); // gets entity manager persists entity $a and flushes
$this->getRepository('a')->detatch($a); // removes entity from management
return $this->getRepository('a')->find($a->getId());
/**
* returns an object
* a = {
* id = 2,
* relationB = Core\Entity\B,
* ralationC = \Doctrine\Common\Collections\Collection,
*/
}
}
----
This has a downside of re-querying after detachment and has a negative performance impact. If someone knows of a better solution it would be much appreciated.
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.
}