I want select a column (which has a foreign key constraint) without creating joins on tables. I have two tables named eventupdate and eventcategory. The event column is common in both tables. whenever I try the following code it gives an error.
Please give some suggestion. I don't want to create a join.
$qb2 = $this->em->createQueryBuilder();
$from = 'Entities\EventCategory cat';
$qb2->add('from',$from)
->select('cat.event')
->Where('cat.id=3);
$query=$qb2->getQuery();
There are two options that I can see:
HINT_INCLUDE_META_COLUMNS together with ArrayHydrator
$query = $queryBuilder->getQuery();
$query->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true);
var_dump($query->getArrayResult()); // Will return array with raw foreign key column name => value, e.g. user_id => 5
Create separate property in Entities\EventCategory which has the foreign key as primitive type
/**
* #var User
*
* #ManyToOne(targetEntity="User")
* #JoinColumn(name="user_id", referencedColumnName="user_id")
*/
private $user;
/**
* #var int
*
* #Column(name="user_id", type="integer", nullable=false)
*/
private $userId;
Related
I am using symfony 3.4.1, with doctrine/orm 2.5.13. I have 2 tables. store and store_product.
In Store entity I have:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Store\Product", mappedBy="store")
* #ORM\JoinColumn(name="store_id")
*/
private $products;
and in Store\Product entity I have composite index with (store_id,product_id). I have:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Store", inversedBy="products")
* #ORM\JoinColumn(name="store_id",referencedColumnName="id",nullable=false,onDelete="CASCADE")
* #ORM\Id()
* #ORM\GeneratedValue("NONE")
* #var $store \AppBundle\Entity\Store
*/
protected $store;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Product", inversedBy="stores")
* #ORM\JoinColumn(name="product_id",referencedColumnName="id",nullable=false,onDelete="CASCADE")
* #ORM\Id()
* #ORM\GeneratedValue("NONE")
* #var $product \AppBundle\Entity\Product
*/
protected $product;
Earlier I produced a native query using the guide. It was only fetching entries from store table and it worked perfectly.
Now I am trying to join store_product table and it is not going so well. I am using the following query which returns 1 result.
SELECT st.id, st.name, stp.store_id, stp.product_id, stp.price FROM store st LEFT JOIN store_product stp ON st.id = stp.store_id WHERE st.id=1 LIMIT 1;
returns something like:
id | name | store_id | product_id | price
----+------------+----------+------------+-------
1 | Store Name | 1 | 1234567890 | 129
I setup the result set mapping as follows:
$rsm = new ResultSetMapping();
$rsm->addEntityResult('AppBundle\Entity\Store', 'st');
$rsm->addFieldResult('st', 'id', 'id');
$rsm->addFieldResult('st', 'name', 'name');
$rsm->addJoinedEntityResult('AppBundle\Entity\Store\Product', 'stp',
'st', 'products');
$rsm->addFieldResult('stp','store_id', 'store');
$rsm->addFieldResult('stp','product_id','product');
$rsm->addFieldResult('stp','price','price');
I am getting error: Notice: Undefined index: store Can anybody see the reason of the error?
I managed to get this working at last after 3 months.
Apparently I made a mistake of trusting documentation when I added foreign keys with addFieldResult() for joined table.
I needed to remove addFieldResult() lines for store_id and product_id` and add them like:
$rsm->addMetaResult('stp', 'store_id', 'store_id', true);
$rsm->addMetaResult('stp', 'product_id', 'product_id', true);
It wasn't enough to just add them with addMetaResult() I had to set 3rd parameter true also.
Even though documentation says Consequently, associations that are fetch-joined do not require the foreign keys to be present in the SQL result set, only associations that are lazy.
What worked for me was using addJoinedEntityFromClassMetadata from the ResultSetMappingBuilder instead of addJoinedEntityResult. Using the example from the documentation:
/**
* #param string $class The class name of the joined entity.
* #param string $alias The unique alias to use for the joined entity.
* #param string $parentAlias The alias of the entity result that is the parent of this joined result.
* #param string $relation The association field that connects the parent entity result
* with the joined entity result.
*/
$rsm->addJoinedEntityFromClassMetadata(Address::class, 'a', 'u', 'address');
I recently worked out an issue with querying ManyToMany relationship join tables, the solution was same as this answer and was wondering how it works.
lets say i have a simple ManyToMany relationship between groups and team, there will be a groups_team tables that will automatically be created here
groups entity
/**
* Groups
*
* #ORM\Table(name="groups")
* #ORM\Entity(repositoryClass="AppBundle\Model\Repository\GroupsRepository")
*/
class Groups {
/**
* #ORM\ManyToMany(targetEntity="Team", inversedBy="group")
*/
protected $team;
public function __construct() {
$this->team = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="groupname", type="string", length=255)
*/
private $groupname;
//obligatory getters and setters :)
team entity
/**
* Team
*
* #ORM\Table(name="team")
* #ORM\Entity(repositoryClass="AppBundle\Model\Repository\TeamRepository")
*/
class Team {
/**
* #ORM\ManyToMany(targetEntity="Groups", mappedBy="team")
*/
protected $group;
public function __construct(){
$this->group = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="teamname", type="string", length=255)
*/
private $team;
//[setters and getters here]
in order to get all the teams in a group i would have to query the groups_team table.i would have directly queried the table in just mysql but in symfony i have to do this
$groups = $em->getRepository("AppBundle\Model\Entity\Groups")->findBy(array('tournament' => $tournament->getId()));
//get all teams with group id in groups_team table
foreach ($groups as $group) {
$teamsingroup = $em->getRepository("AppBundle\Model\Entity\Team")->createQueryBuilder('o')
->innerJoin('o.group', 't')
->where('t.id = :group_id')
->setParameter('group_id', $group->getId())
->getQuery()->getResult();
echo "</b>".$group->getGroupname()."</b></br>";
foreach ($teamsingroup as $teamingroup) {
echo $teamingroup->getTeam()."</br>";
}
}
Can someone explain to me how the innerJoin is working and what is the concept behind this, maybe a few documentation to learn about this. are there better way to do this with symfony and doctrine.
Using ManyToMany between 2 entities involves a third table generally called as a junction table in this type of relation when you build a DQL (doctrine query) doctrine automatically joins junction table depending on the nature of relation you have defined as annotation so considering your query
$teamsingroup = $em->getRepository("AppBundle\Model\Entity\Team")
->createQueryBuilder('o')
->innerJoin('o.group', 't')
You are joining Team entity with Group entity in innerJoin('o.group') part o is the alias for Team entity and o.group refers to property defined in Team entity named as group.
/**
* #ORM\ManyToMany(targetEntity="Groups", mappedBy="team")
*/
protected $group;
Which has a ManyToMany annotation defined for this type of relation doctrine joins your team table first with junction table and then joins your junction table with groups table and the resultant SQL will be something like
SELECT t.*
FROM teams t
INNER JOIN junction_table jt ON(t.id = jt.team_id)
INNER JOIN groups g ON(g.id = jt.group_id)
WHERE g.id = #group_id
Another thing related your way of getting team for each group you can minimize your code by excluding createQueryBuilder part within loop, once you have defined teams property as ArrayCollection i.e $this->team = new ArrayCollection(); on each group object you will get collections of teams associated to that particular group by calling getTeam() function on group object similar to below code.
foreach ($groups as $group) {
$teamsingroup = $group->getTeam();
echo "</b>".$group->getGroupname()."</b></br>";
foreach ($teamsingroup as $teamingroup) {
echo $teamingroup->getTeam()."</br>";
}
}
I guess it's literally select statement with INNER JOIN using key columns defined entity class as mappedBy or inversedBy.
Why don't you have a look of doctrine log and see what the native sql is composed?
How to get Doctrine to log queries in Symfony2 (stackoverflow)
http://vvv.tobiassjosten.net/symfony/logging-doctrine-queries-in-symfony2/ (some code examples)
I don't know your user story behind this, but I also heard that it is recommended to use one to many relationship instead of many to many, unless there is a strong reason to do so, as most of cases can be handled by one to many by reconsidering models.
I have a Person entity which has two relations (hometown and current) to Location table. Both of these fields can be null, otherwise they must exist in the Location table:
class Person {
.....
/**
* #var Location
* #ORM\OneToOne(targetEntity="Location")
* #ORM\JoinColumn(name="hometown_id", referencedColumnName="id",nullable=true)
**/
protected $hometown;
/**
* #var Location
* #ORM\OneToOne(targetEntity="Location")
* #ORM\JoinColumn(name="current_id", referencedColumnName="id", nullable=true)
**/
protected $current;
....
}
Now, I want to update my db schema, based on doctrine:schema:update --dump-sql output, but it creates problems:
CREATE UNIQUE INDEX UNIQ_8D93D6494341EE7D ON person (hometown_id);
CREATE UNIQUE INDEX UNIQ_8D93D649B8998A57 ON person (current_id);
I cannot define these indexes as there are more than one null row in the table.
Would you please help me?
A OneToOne relationship is unique as it would mean that only one person could be assigned to one location and one location to one person.
In your scenario you would want one person to have multiple locations and one location could have multiple person(s). This would be a ManyToMany relationship.
In Doctrine when you use a ManyToMany you will specify a JoinTable that Doctrine will manage (You don't have to create an entity for a JoinTable). The JoinTable breaks down the ManyToMany to something like a OneToMany such as one person to many location(s) as shown in example below. The JoinTable will store the values you want when they apply.
/**
* #ORM\ManyToMany(targetEntity="Location")
* #ORM\JoinTable(name="hometown_location",
* joinColumns={#ORM\JoinColumn(name="person_id", referencedColumnName="id", unique=true)},
* inverseJoinColumns={#ORM\JoinColumn(name="location_id", referencedColumnName="id")}
* )
**/
protected $hometown;
/**
* #ORM\ManyToMany(targetEntity="Location")
* #ORM\JoinTable(name="current_location",
* joinColumns={#ORM\JoinColumn(name="person_id", referencedColumnName="id", unique=true)},
* inverseJoinColumns={#ORM\JoinColumn(name="location_id", referencedColumnName="id")}
* )
**/
protected $current;
public function __construct() {
$this->hometown = new \Doctrine\Common\Collections\ArrayCollection();
$this->hometown = new \Doctrine\Common\Collections\ArrayCollection();
}
If there is no location to assign to hometown or current that is fine, no space is taken up.
When you do have a location to assign to either hometown or current it will have to be a valid location from the location table.
It looks like you are looking for FOREIGN KEY
http://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html
ALTER TABLE `Person` ADD INDEX ( `hometown_id` ) ;
ALTER TABLE `Person` ADD FOREIGN KEY ( `hometown_id` ) REFERENCES `Location` ( `id` )
ON DELETE RESTRICT ON UPDATE RESTRICT ;
ALTER TABLE `Person` ADD INDEX ( `current_id` ) ;
ALTER TABLE `Person` ADD FOREIGN KEY ( `current_id` ) REFERENCES `Location` ( `id` )
ON DELETE RESTRICT ON UPDATE RESTRICT ;
I have an Item entity that has a ManyToOne relationship to a Category entity. I want them to be joined by a field other than Category's id (in this case, a field called id2). My schema is listed below.
class Item {
/**
* #ORM\Id
* #ORM\Column(name = "id", type = "integer")
* #ORM\GeneratedValue(strategy = "AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity = "Category")
* #ORM\JoinColumn(name = "category_id", referencedColumnName = "id2")
*/
protected $category;
}
class Category {
/**
* #ORM\Id
* #ORM\Column(name = "id", type = "integer")
* #ORM\GeneratedValue(strategy = "AUTO")
*/
protected $id;
/**
* #ORM\Column(name = "id2", type = "string", length = "255", unique = "true")
*/
protected $id2;
When I try saving an Item I get this error:
Notice: Undefined index: id2 in vendor/doctrine/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 511
Sure enough, if I change id2 to id in the JoinColumn annotation, everything works fine, but I need the entities to be connected through id2. Is this possible?
Edit
What I want to achieve is impossible according to the official Doctrine 2 docs.
It is not possible to use join columns pointing to non-primary keys.
Doctrine will think these are the primary keys and create lazy-loading
proxies with the data, which can lead to unexpected results. Doctrine
can for performance reasons not validate the correctness of this
settings at runtime but only through the Validate Schema command.
source: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/limitations-and-known-issues.html#join-columns-with-non-primary-keys
I think Doctrine wants these to be primary keys, from the docs:
name: Column name that holds the foreign key identifier for this relation.
Another thing that jumps out at me from your code sample is category.id2 being type string, I would at least expect it to be an integer, but it may also need to be for #JoinColumn to work properly.
You may be able to get away with just #Index on category.id2 and leave it as a string though; worth a shot anyway.
Just to report. I was able to join non-PKs in Many2One (undirectional) relation, BUT my object can't be loaded the normal way. It must be loaded with DQL like:
SELECT d,u FROM DEntity d
JOIN d.userAccount u
this way I stopped getting error: Missing value for primary key id on ....
I need to map the same column to 2 differences tables (lets say normal and extended).
/**
* #var ItemValue
*
* #OneToOne(targetEntity="ItemValue")
* #JoinColumn(name="id_value", referencedColumnName="id_value")
*/
private $value;
/**
* #var ItemValueExtended
*
* #OneToOne(targetEntity="ItemValueExtended")
* #JoinColumn(name="id_value", referencedColumnName="id_value")
*/
private $valueExtended;
/**
* #var string $isExtended
*
* #Column(name="is_extended", type="string", nullable=false)
*/
private $isExtended = 'YES';
I have no problem with joining data based on the isExtended attribute using DQL:
"SELECT id,idv FROM ItemData id
JOIN id.value idv WHERE id.isExtended='NO'";
and
"SELECT id,idv FROM ItemData id
JOIN id.valueExtended idv WHERE id.isExtended='YES'";
but when ever I want to persist a new object, NULL is inserted in id_value column ?!!
$oValue = ItemValue();
.
.
$oData = new ItemData();
$oData->setValue($oValue);
.
.
.
$em->persist($oData);
$em->flush();
Any Idea ?
From Doctrine2 documentation:
In the case of bi-directional associations you have to update the
fields on both sides.
One possible solution would be:
$oData = new ItemData();
$oData->setValue($oValue);
$oValue->setData($oData);
but it's tedious. Another better one is set the cascade option on both sides of the one-to-one association:
#OneToOne(targetEntity="ItemValue"), cascade={"persist", "remove"})
This way your code will work. You can choose the appropriate cascade options looking at here.
In the case where both the parent and child entity are new entities (neither have been persisted) a PrePersist lifecycle event on the parent can help:
/**
* ....
*
* #ORM\HasLifecycleCallbacks
*/
class ParentEntity {...
/**
* #ORM\PrePersist()
*/
public function prePersist() {
foreach($this->getChildEntities() as $childEntity) {
$childEntity->setParent($this);
}
}
-
class ChildEntity {
....
This will automatically create the child -> parent relationship upon saving the parent.
In many instances Doctrine will then be able to work out the rest at the SQL level.