Doctrine - OneToMany relation, all result row doesn't fetch in object - php

I try to get all my objects DemandCab with their children object (DecisionCab).
My 2 entities
/**
* DemandCab.
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="DemandCabRepository")
*/
class DemandCab
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var DecisionCab
*
* #ORM\OneToMany(targetEntity="\My\CabBundle\Entity\DecisionCab", mappedBy="demandCab")
*/
private $decisionsCab;
/**
* #var \DateTime
*
* #ORM\Column(name="startDate", type="datetime")
*/
private $startDate;
/**
* #var \DateTime
*
* #ORM\Column(name="endDate", type="datetime", nullable=true)
*/
private $endDate;
/**
* #var int
*
* #ORM\Column(name="followup", type="integer", nullable=true)
*/
private $followup;
/**
* #var InfoCab
*
* #ORM\ManyToOne(targetEntity="\My\CabBundle\Entity\InfoCab", inversedBy="demandsCab")
*/
private $infoCab;
}
/**
* DecisionCab.
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="DecisionCabRepository")
*/
class DecisionCab
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var DemandCab
*
* #ORM\ManyToOne(targetEntity="\My\CabBundle\Entity\DemandCab", inversedBy="decisionsCab")
*/
private $demandCab;
/**
* #var bool
*
* #ORM\Column(name="decision", type="boolean", nullable=true)
*/
private $decision;
/**
* #var string
*
* #ORM\Column(name="motif", type="string", length=500, nullable=true)
*/
private $motif;
/**
* #var string
*
* #ORM\Column(name="role", type="string", length=255)
*/
private $role;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime", nullable=true)
*/
private $date;
/**
* #var DemandCab
*
* #ORM\ManyToOne(targetEntity="\My\CabBundle\Entity\DemandCab", inversedBy="decisionsCab")
*/
private $demandCab;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="\My\CabBundle\Entity\User", inversedBy="decisionsCab")
*/
private $user;
}
In my DemandCabRepository
public function findAllCompleted(){
$qb = $this->createQueryBuilder("dem");
$qb->select('dem, dec');
$qb->leftJoin("dem.decisionsCab", "dec");
$qb->andWhere("dem.completed = 1");
$qb->orderBy("dem.startDate", "DESC");
return $qb->getQuery()->getResult();
}
My DemandCab data
My DecisionCab data
When i dump result, only 2 decisions appear ...
... whereas when i use getArrayResult, i have my 4 decisions ...
The query is good but i dont understand why hydration remove DecisionCab object with attribute decision at 0 or 1 (only null are display).
I would like to understand why and is there a solution to get DemandCab object with all DecisionCab children object.
Thanks

I am able to reproduce your issue, but I am not sure if this is your case.
Anyway, my assumption is that you query the demand Entity joined with decision relation at least once with the help of a query builder. Maybe this is done in your action, in an event listener or somewhere else in your code.
So you may have something like:
$qb = $this->getDoctrine()
->getRepository(DemandCab::class)->createQueryBuilder("dem");
$qb->select('dem, dec');
$qb->leftJoin("dem.decisionsCab", "dec");
$qb->andWhere("dec.decision IS NULL");
$qb->orderBy("dem.startDate", "DESC");
$results = $qb->getQuery()->getResult(); // <-- the decisionsCab collection is hydrated but filtered
$qb2 = $this->getDoctrine()
->getRepository(DemandCab::class)->createQueryBuilder("dem");
$qb2->select('dem, dec');
$qb2->leftJoin("dem.decisionsCab", "dec");
$qb2->andWhere("dem.completed = 1");
$qb2->orderBy("dem.startDate", "DESC");
$q = $qb2->getQuery();
//$q->setHint(Query::HINT_REFRESH, true);
$results = $q->getResult();
The issue is in Doctrine\ORM\Internal\Hydration\ObjectHydrator, it has the property "initializedCollections" where already initialized collections are kept and the collections are hashed by the parent entity type and the entity itself. Unfortunately in the above case, the heydrator does not understand that the collection is filtered in the 1st query and uses it in the 2nd query in order to avoid rehydration.(github link)
The solution is to cause the query builder to refresh. Try the code:
$qb->orderBy("dem.startDate", "DESC");
$q = $qb->getQuery();
$q->setHint(Query::HINT_REFRESH, true); // <-- Tell the hydrator to refresh
return $q->getResult();

First you have initialize your class with the relation ManyToOne with an ArrayCollection.
And you don't need any of this 'DemandCabRepository'. All the work is done by Doctrine

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

Insert into a symfony 3 table

I need to make a simple insertion in the database,
Here is the code in the controller:
public function verifCreateOrder(Request $req){
$store = 1;
$provider =2;
$creation = $creation_start = $req->get('creation_start');
$delivery = $creation_start = $req->get('creation_start');
$em = $this->getDoctrine()->getManager();
$order = new OrderList();
$order->setNumStore($store);
$order->setNumProvider($provider);
$order->setCreation($creation);
$order->setDelivery($delivery);
$em->persist($order);
$em->flush();
die();
return $this->redirectToRoute('search.view');
}
The code of the OrderList entity :
class OrderList
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="Provider")
* #ORM\JoinColumn(name="num_provider", referencedColumnName="id")
*/
private $numProvider;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="Store")
* #ORM\JoinColumn(name="num_store", referencedColumnName="id")
*/
private $numStore;
/**
* #return int
*/
public function getNumStore()
{
return $this->numStore;
}
/**
* #param int $numStore
*/
public function setNumStore($numStore)
{
$this->numStore = $numStore;
}
/**
* #var \DateTime
*
* #ORM\Column(name="creation", type="date")
*/
private $creation;
/**
* #var \DateTime
*
* #ORM\Column(name="delivery", type="date")
*/
private $delivery;
/**
* #var \DateTime
*
* #ORM\Column(name="last_update", type="date", nullable=true)
*/
private $lastUpdate;
/**
* #var \DateTime
*
* #ORM\Column(name="emission", type="date", nullable=true)
*/
private $emission;
/**
* #var int
*
* #ORM\Column(name="reduction_1", type="integer", nullable=true)
*/
private $reduction1;
/**
* #var int
*
* #ORM\Column(name="reduction_2", type="integer", nullable=true)
*/
private $reduction2;
/**
* #var string
*
* #ORM\Column(name="comment", type="string", nullable=true, length=255)
*/
private $comment;
Here is the error returned by symfony:
Expected value of type "AppBundle\Entity\Provider" for association field "AppBundle\Entity\OrderList#$numProvider", got "integer" instead.
Please help me resolve this error.
You are mixing the Doctrine relation and database foreign key concepts (they are very close, but not the same). The problem is that you can't treat related entity identifier as related entity in Doctrine. You need to pass whole related entity and not just foreign key.
There are at least two solutions:
Instead of passing identifier of store (1) and identifier of provider (2), firstly retrieve this entities from the database ($em->find(...)).
You can trick doctrine with getReference method, that should return you a proxy of needed object with id ($em->getReference('Path\To\Store', 1 and $em->getReference('Path\To\Provider', 2).
If you want more information, you can check these docs: reference proxies and relations.
public function verifyCreateOrder(Request $req){
$storeId = 1;
$providerId = 2;
$creation = $creation_start = $req->get('creation_start');
$delivery = $creation_start = $req->get('creation_start');
$em = $this->getDoctrine()->getManager();
// getting references
$store = $em->getReference(Store::class, $storeId);
$provider = $em->getReference(Provider::class, $providerId);
$order = new OrderList();
$order->setNumStore($store);
$order->setNumProvider($provider);
$order->setCreation($creation);
$order->setDelivery($delivery);
$em->persist($order);
$em->flush();
die();
return $this->redirectToRoute('search.view');
}
I am also new to php but, you set $provider to be a integer = 2, and your relation expect object Provider $provider.

DQL query returns: StateFieldPathExpression or SingleValuedAssociationField expected

I have the following DQL query:
public function findByIdJoinedToCodeExample($pageId)
{
$query = $this->getEntityManager()
->createQuery('
SELECT c FROM acmeStyleGuideBundle:codeExample c
JOIN c.PageContent p
WHERE p.codeExample = :cex'
)
->setParameter('cex', $pageId);
try {
return $query->getResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
It is attempting to retreive data from an entity called codeExample which has a ManyToOne relationship with an entity called PageContent. The relationships seem to be set up correctly as the database is being correctly set up and the fixtures are being populated but when I try to run the above query I am faced with the following error:
An exception has been thrown during the rendering of a template ("[Semantical Error] line 0, col 130 near 'codeExample =': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected.") in acmeStyleGuideBundle:Page:pages.html.twig at line 16.
It is being called by the following controller:
// find the current pages code examples (if there are any)
public function findCodeExamplesAction($pageId =10)
{
$em = $this->getDoctrine()->getManager();
$codeExample = $this->getDoctrine()
->getRepository('acmeStyleGuideBundle:codeExample')
->findByIdJoinedToCodeExample($pageId);
return $this->render(
'acmeStyleGuideBundle:Page:codeExample.html.twig',
array(
'Code' => $codeExample
)
);
}
Note: $pageID = 10 is because it was telling me that pageID wasn't being populated. That seems to be a separate issue than this one so I set a default for that for now.
I've been looking at this for hours but I'm still learning Doctrine and Symfony and I just cannot figure this one out by myself.
Here are my entities for codeExample:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="textExample", type="text")
*/
private $textExample;
/**
* #var string
*
* #ORM\Column(name="codeExample", type="text")
*/
private $codeExample;
/**
* #var integer
*
* #ORM\Column(name="lang", type="integer")
*/
private $lang;
/**
* #ORM\ManyToMany(targetEntity="PageContent", mappedBy="codeExample")
*/
protected $PageContent;
/**
* Constructor
*/
public function __construct()
{
$this->PageContent = new \Doctrine\Common\Collections\ArrayCollection();
}
Here are my entities for PageContent:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection $pageSector_Id;
* #ORM\ManyToMany(targetEntity="pageSector", inversedBy="PageContent")
* #ORM\JoinTable(
* name="pageSector_PageContent",
* joinColumns={#ORM\JoinColumn(name="PageContent_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="pageSector_Id", referencedColumnName="id")}
* )
*/
protected $pageSector;
/**
* #var ArrayCollection $pageCategory_Id;
* #ORM\ManyToMany(targetEntity="pageCategory", inversedBy="PageContent")
* #ORM\JoinTable(
* name="pageCategory_PageContent",
* joinColumns={#ORM\JoinColumn(name="PageContent_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="pageCategory_Id", referencedColumnName="id")}
* )
*/
protected $pageCategory;
/**
* #ORM\ManyToOne(targetEntity="pageTypes", inversedBy="PageContent")
* #ORM\JoinColumn(name="pageTypesId", referencedColumnName="id")
*/
protected $pageTypes;
/**
* #var integer
*
* #ORM\Column(name="pageTypesId", type="integer")
*/
private $pageTypesId;
/**
* #ORM\OneToMany(targetEntity="PageContent", mappedBy="parentPage")
*/
private $childPages;
/** #ORM\ManyToOne(targetEntity="PageContent", inversedBy="childPages")
* #ORM\JoinColumn(name="parentPage_id", referencedColumnName="id")
**/
private $parentPage;
/**
* #var string
*
* #ORM\Column(name="pageName", type="string", length=255)
*/
private $pageName;
/**
* #var string
*
* #ORM\Column(name="pageUrl", type="string", length=255)
*/
private $pageUrl;
/**
* #var string
*
* #ORM\Column(name="richText", type="text")
*/
private $richText;
/**
* #var ArrayCollection $pageSector_Id;
* #ORM\ManyToMany(targetEntity="codeExample", inversedBy="PageContent")
* #ORM\JoinTable(
* name="codeExample_PageContent",
* joinColumns={#ORM\JoinColumn(name="PageContent_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="codeExample_Id", referencedColumnName="id")}
* )
*/
protected $codeExample;
Any advice you can give me would be amazing. I'm completely stuck here.
I managed to work it out myself:
$query = $this->getEntityManager()
->createQuery('
SELECT c FROM acmeStyleGuideBundle:codeExample c
JOIN c.PageContent p
WHERE p.codeExample = :cex'
)
->setParameter('cex', $pageId);
Should have been:
$query = $this->getEntityManager()
->createQuery('
SELECT c FROM acmeStyleGuideBundle:codeExample c
JOIN c.PageContent p
WHERE p.id = :cex'
)
->setParameter('cex', $pageId);
This seems odd to me as I thought that the point of objects is to NOT use the id but it worked so that's good enough for me. If there is a better way, please feel free to tell me.

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 joined inheritance, repository findall returns both parent and child objects

I have a problem with doctrine's InheritanceType JOINED, I have a parent entity 'template' and child entity 'implementation',
/**
* Netvlies\Bundle\MisBundle\Entity\ItemTemplate
*
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discriminator", type="string")
* #ORM\DiscriminatorMap({"template" = "ItemTemplate", "implementation" = "Item"})
*/
class ItemTemplate
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
...
/**
* Netvlies\Bundle\MisBundle\Entity\Item
*
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Item extends ItemTemplate
{
/**
* #var \DateTime $created
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
/**
* #var \DateTime $updated
*
* #ORM\Column(name="updated", type="datetime")
*/
private $updated;
...
I'm able to create and save both entities without problems. I can also retrieve Item entities using it's repositories findall() function just fine. But when i do the same with the parent entity I receive a resultset containing both parent and child classes.

Categories