I have a Developers entity and table named CodeUserReference
class CodeUserReference
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*#ORM\ManyToOne(targetEntity="Artel\CustomerBundle\Entity\Developers", inversedBy="newreference")
*/
protected $alluser;
/**
* #ORM\Column(type="string", length=255)
*/
protected $codereference;
and Developers
class Developers extends SUser
{
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, unique=false, nullable=false)
* #Assert\Length(min=3, max=255)
* #Assert\NotBlank
*/
protected $email;
/**
* #ORM\ManyToMany(targetEntity="Artel\CustomerBundle\Entity\CodeUserReference", inversedBy="alluser")
*/
protected $newreference;
I have get query in my Action
public function profileGetAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$code_user_reference = $em->getRepository('ArtelCustomerBundle:CodeUserReference')->findOneByCodereference($request->query->get('reference'));
$user_by_email = $em->getRepository('ArtelCustomerBundle:Developers')->findOneByEmail($request->query->get('email'));
if (!empty($code_user_reference) && empty($user_by_email))
{
$id = $code_user_reference->getAlluser()->getId();
$user_by_reference = $em->getRepository('ArtelCustomerBundle:Developers')->findOneById($id);
$user_by_reference_json = $em->getRepository('ArtelCustomerBundle:Developers')->createQueryBuilder('d')
->where('d.id= :id')
->groupBy('d.id')
->setParameter('id', $code_user_reference->getAlluser()->getId())
->getQuery()->getArrayResult();
echo json_encode( array('user' => $user_by_reference_json));
die;
}
If(empty($code_user_reference) && !empty($user_by_email))
.......
I get Id in code_user_reference, then I find Developers objects with this ID, then I create a QueryBuilder and in this QueryBuilder I find the developers objects again, for JSON table. I find this very hard, who knows easier practics ?
There is certanly mismapping.
if you have #ORM\ManyToOne from one site, there should be #ORM\OneToMany from other.
Or, if you need many-to-many relationship, there should be #ORM\ManyToMany on both entities.
Also, relation should be mapped at one side. If you have (targetEntity="Artel\CustomerBundle\Entity\Developers", inversedBy="newreference") it means it should be mapped here
something like
#ORM\ManyToOne(targetEntity="Artel\CustomerBundle\Entity\Developers", inversedBy="newreference")
#ORM\JoinColumn(name="id_developer", referencedColumnName="id")
and at the other side must be mappedBy instead of inversedBy
* #ORM\OneToMany(targetEntity="Artel\CustomerBundle\Entity\CodeUserReference", mappedBy="alluser")
Related
Hello i am using Doctrine ORM, i have major problem. Overall i have 10 tables, each table has it's own address fields(street, city, state, zip). So basically i am trying to refactor db structure, and created one new address table with those fields, old tables will hold address_id as a reference, and i will remove address fields from each table, and migrate data.
Since it's very big project with large code base trying to determine whats the best approach to do this refactoring regarding entities.
Things to concern
find() method
findBy() method
persist
Query Builder
Here it's my userCreditCard entity, it has address object in it, and that object should be replaced with "address_id".
class UserCreditCard extends LoggableEntity implements UserCreditCardInterface
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var int|null
*
* #ORM\Column(name="user_id", type="integer", nullable=true)
*/
protected $userId;
/**
* #var string
*
* #ORM\Column(name="cc_number", type="string", length=255, nullable=false)
*/
protected $ccNumber;
/**
* #var string
*
* #ORM\Column(name="cc_name", type="string", length=255, nullable=false)
*/
protected $ccName = '';
/**
* #var \DateTime
*
* #ORM\Column(name="cc_date", type="date", nullable=false)
*/
protected $ccDate;
/**
* #var bool
*
* #ORM\Column(name="bill_addr_type", type="boolean", nullable=false)
*/
protected $billAddrType = false;
/**
* #var UserBillingAddress
* #ORM\Embedded(class = "\Cae\User\Dal\Entity\Embeddable\UserBillingAddress", columnPrefix=false)
*/
protected $address;
Here is address object.
/** #ORM\Embeddable */
class UserBillingAddress extends AbstractAddressEmbeddable
{
/**
* #var string
*
* #ORM\Column(name="bill_street_address", type="string", length=255, nullable=false)
*/
protected $address = '';
/**
* #var string
*
* #ORM\Column(name="bill_street_address2", type="string", length=255, nullable=false)
*/
protected $address2 = '';
/**
* #var string
*
* #ORM\Column(name="bill_city", type="string", length=255, nullable=false)
*/
protected $city = '';
My question would be since it's very large project is it possible to some how override with entity listeners or with magic setter so for example when someone do
$this->userCreditCardRepository->find($id);
pull address data from new Address table (i can't do joins since address table lives in another module, so i have to use public services).
Maybe to override find, findBy and other methods in AbstractRepository, but i am not sure what the best approach? Any idea is appreciated.
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
I have made a queryBuilder inside an entity repository to delete a link between two table.
I have this two entities
Domain :
/**
* #var int
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=64)
* #Assert\NotBlank
* #Assert\Length(max="64")
* #AppAssert\DomainName
*/
private $name;
// Some other fields
/**
* #var SshKey[]|ArrayCollection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\SshKey", inversedBy="domains")
* #ORM\JoinTable(name="domain_sshkey",
* joinColumns={#ORM\JoinColumn(referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="key_id", referencedColumnName="id")}
* )
*/
private $sshKeys;
And SshKeys :
/**
* #var int
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
*/
private $createdAt;
// Other fields
/**
* #var Domain[]|ArrayCollection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Domain", mappedBy="sshKeys")
*/
private $domains;
I am trying to delete links between this two tables when SshKeys id is in sshKeys field inside domain table.
So I made this query builder in my DomainRepository
public function deleteSshkeyDomainLink($invalidSshkey)
{
$qb = $this->createQueryBuilder('d');
$qb->delete()
->where($qb->expr()->in('ssh.id', ':ssh_keys_id'))
->setParameter('ssh_keys_id', $invalidSshkey)
->join('d.sshKeys', 'ssh')
;
return $qb->getQuery()->execute();
}
But this QB return this error
[Doctrine\ORM\Query\QueryException]
[Semantical Error] line 0, col 39 near 'ssh.id IN(:s': Error: 'ssh' is not defined.
[Doctrine\ORM\Query\QueryException]
DELETE AppBundle\Entity\Domain d WHERE ssh.id IN(:ssh_keys_id)
I don't understand why this is returning ssh is not defined because I have made a join with this alias.
This query builder should work ? I really don't know how too fix this.
Thanks for your help.
why do you want to delete Domain when you just need to delete sshKey from Domain (link between them)?
In Domain entity you can define method removeSshKey like this for example
public function removeSshKey(SshKey $key)
{
$this->sshKeys->removeElement($key);
return $this;
}
Then in controller where you want to delete the link between entities you should call it something like this
$domain = $this->getDoctrine()->getRepository('Domain')->find($domainId);
foreach ($domain->getSshKeys() as $sshKey)
{
if ($sshKey->getId() == $invalidSshKeyId)
{
$domain->removeSshKey($sshKey);
}
}
$em = $this->getDoctrine()->getManager();
$em->flush();
this should delete the link
I just installed the Translatable DoctrineExtensions from the StofDoctrineExtensionsBundle.
I have two entities "Space" and "Equipment" with many to one relation :
/**
* Space
* #ORM\Table(name="space")
* #ORM\Entity(repositoryClass="Project\ProjectBundle\Repository\PlaceRepository")
*/
class Space implements Translatable
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Project\ProjectBundle\Entity\Equipment", cascade={"persist"})
*/
private $equipments;
/**
* #var string
* #Gedmo\Translatable
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #Gedmo\Locale
*/
private $locale;
public function setTranslatableLocale($locale)
{
$this->locale = $locale;
}
}
And :
/**
* Equipment
* #ORM\Table("equipment")
* #ORM\Entity(repositoryClass="Project\ProjectBundle\Repository\EquipmentRepository")
*/
class Equipment implements Translatable
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #Gedmo\Translatable
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #Gedmo\Locale
*/
private $locale;
public function setTranslatableLocale($locale)
{
$this->locale = $locale;
}
}
In the spaceRepository, I have, in my get query function :
public function getSpace($id) {
$query->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
$query->setHint(\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE,$locale);
$db = $this
->createQueryBuilder('s')
->andWhere('s.id = :id')
->setParameter('id', $id)
->leftJoin('s.equipments', 'e')
->addSelect('e');
$query = $db->getQuery();
$query->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
$query->setHint(\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'en');
return $query->getSingleResult();
}
The TranslationWalker should get all the results in one single query but it doesn't work, and, finally, I have one query for each equipment (more than 150 queries).
How could I get all translated elements in one single query ?
Try to set the hydration mode manually as your issue is related to this problem.
Take a look at this answer on how to set the hydration mode. It worked for me.
In my case I simply added the following hints to a query
use Doctrine\ORM\Query;
use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
...
$query->setHydrationMode(TranslationWalker::HYDRATE_OBJECT_TRANSLATION);
$query->setHint(Query::HINT_REFRESH, true);
Hope that helps!
I have 2 classes:
Company:
class ComCompany
{
/**
* #var integer
*
* #ORM\Column(name="cmp_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $cmpId;
/**
* #var string
*
* #ORM\Column(name="cmp_name", type="string", length=100, nullable=true)
*/
private $cmpName;
/**
* #var integer
*
* #ORM\Column(name="cmp_code", type="integer", nullable=true)
*/
private $cmpCode;
/**
* #var \Catalog\WebBundle\Entity\ComCity
*
* #ORM\ManyToOne(targetEntity="Catalog\WebBundle\Entity\ComCity")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="cmp_city", referencedColumnName="cit_id")
* })
*/
private $cmpCity;
public function getCmpName()
{
return $this->cmpName;
}
public function getCmpCode()
{
return $this->cmpCode;
}
public function getCmpCity()
{
return $this->cmpCity;
}
}
And city
class ComCity
{
/**
* #var integer
*
* #ORM\Column(name="cit_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $citId;
/**
* #var string
*
* #ORM\Column(name="cit_name", type="string", length=255, nullable=true)
*/
private $citName;
public function getCitId()
{
return $this->citId;
}
public function getCitName()
{
return $this->citName;
}
}
This 2 tables have associations Company.comCity = City.citId
How to add getter method to ComCompany class to get City.citName ?
I have foreign keys and Entity is generated properly, but there not method for get citName from Company class
Just add the following code to your ComCompany class
public function getCityName()
{
return $this->cmpCity->getCitName();
}
You don't need no this getter method while you already have it in ComCity class. Because adding it (like answer suggested) is making duplicate code. You should use
$company->getCmpCity()->getCitName()
instead
And also: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
Both answers provided are absolutely correct. However, take into account that if you're using lazy loading an extra query will be triggered each time you call getCityName unless you use a JOIN in your DQL/Query Builder.
This can have terrible performance issues if you call getCityName in a loop so I thought it was worth mentioning it.