I'm trying to launch a query in Symfony2 (I'm quite new), where I need to join two different entities, in different bundles:
Candc/ComercioBundle/Entity/Venta/ItemVentaCarta And
Candc/ProductoBundle/Entity/Producto.
They have a relation manytoone-onetomany.
Class Producto/////
/**
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\OneToMany(targetEntity="Candc\ComercioBundle\Venta\ItemVentaCarta", mappedBy="Producto")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
And:
Class ItemVentaCarta//////
/**
* catalog card wich is referenced.
* #ORM\ManyToOne(targetEntity="\Candc\ProductoBundle\Entity\Producto", inversedBy="ItemVentaCarta")
* #ORM\JoinColumn(name="carta_id", referencedColumnName="id", nullable=false)
*/
private $carta;
This is the query I'm launching:
public function findLastProducts(){
//this is what I need to do in SQL language :
$consulta = 'SELECT * FROM c_venta_item
LEFT JOIN c_venta_item_carta
ON c_venta_item.id=c_venta_item_carta.id
LEFT JOIN usuario ON c_venta_item.user_id = usuario.id
LEFT JOIN producto ON c_venta_item_carta.carta_id = producto.id';
return $this->getEntityManager()
->createQuery("SELECT ivc
FROM \Candc\ComercioBundle\Entity\Venta\ItemVentaCarta ivc
LEFT JOIN ivc.producto p
WHERE ivc.carta = p.id")
->getResult();
}
I'm in Symfony 2.7.7 and the exception I get is that one:
[Semantical Error] line 0, col 105 near 'p
WHERE': Error: Class Candc\ComercioBundle\Entity\Venta\ItemVentaCarta has no association named producto
(I tried both producto and Producto, to avoid Typo errors)
Also searched in the forum, founded many post related, but cant solve it.
I cleared cache, and also tried a schema update, but I get a message that says:
"there's nothing to update buddy, your db is already sync with the entity metadata"
Hi DQL is bound to the object properties, not the mapped entity name, please update your code by doing the following:
Class ItemVentaCarta//////
/**
* catalog card wich is referenced.
* #ORM\ManyToOne(targetEntity="\Candc\ProductoBundle\Entity\Producto", inversedBy="ItemVentaCarta")
* #ORM\JoinColumn(name="carta_id", referencedColumnName="id", nullable=false)
*/
private $producto; // previously $carta
Also, it looks like your query needs changing to:
SELECT ivc
FROM \Candc\ComercioBundle\Entity\Venta\ItemVentaCarta ivc
LEFT JOIN ivc.producto p ON ivc.carta = p.id
Related
Given this code, Doctrine launchs as many querys as rows I have in table2
$qb = $this->getModelManager()->createQuery($er->getClassName(), 't1')->getQueryBuilder();
$qb->select('t1, t2, t3')
->innerJoin('table1.table2', 't2');
->innerJoin('table2.table3', 't3')
->where('t3.id = :foo')
->setParameter('foo', $foo);
each query is like this one:
SELECT t0.id AS id_1,
t0.name AS name_2,
t0.slug AS slug_3,
t0.description AS description_4,
t0.visible AS visible_5
FROM table2 t0
WHERE t0.id = ?
and those are the entities:
TABLE1: The main entity, witch is related to table 2 by manyToOne if I make the innerJoin with table 2, Doctrine act as expected (1 query)
/**
* #ORM\Entity(repositoryClass = "Table1Repo")
* #ORM\Table(name="table1")
*/
class table1 extends BaseTable1 implements table1Interface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
* #Gedmo\Versioned
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="table2", inversedBy="tables1")
* #ORM\JoinColumn(name="table2_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $table2;
}
TABLE2, related with table one by OneToMany, and with table 3 by ManyToMany.
/**
* #ORM\Entity(repositoryClass="table2Repository")
* #ORM\Table(name="table2")
*/
class table2 extends Basetable2
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="table3", inversedBy="table2s")
* #ORM\JoinTable(name="table3_table2")
*/
protected $table3;
/**
* #ORM\OneToMany(targetEntity="table1", mappedBy="table2")
* #Accessor(getter="getTables1")
*/
protected $tables1;
}
TABLE3: oly related with table 2 by a ManyToMany relation. When I make the innerJoin with table 2, Doctrine still acts as expected, making only one query
/**
* #ORM\Entity(repositoryClass = "table3Repo")
* #ORM\Table(name="table3")
* #Gedmo\Loggable
*/
class table3 extends Basetable3
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="table2", mappedBy="tables3")
* #ORM\JoinTable(name="table3_table2")
*/
protected $tables2;
}
So, as I add both innerJoins to the query Builder, Doctrine makes only one query, but is when I add the WHERE clause, when Doctrine makes the 279 querys, one per row in table2, witch is related with table1 by oneToMany and with table3 by ManyToMany.
Other relevant point is that the querybuilder is beeing executed under SonataAdmin query_builder fiel option.
I can't find why I'm getting this behaviour, any clue?
When you run a query, you always need to have a root entity which joins to others. In your case, that is table1. After fetching, based on query and entity metadata, Doctrine will attempt to create an object for each instance of root, but unless told otherwise, it will stop there. In fact, for each sub-objects (e.g. table2), it will create dummy "proxy" objects which are barely shallow representations, and are to be resolved from DB whenever to try to dereference them. The process of converting DB results to objects is known as object hydration.
In order to do perform hydration of sub-objects, you need to "select" sub-entity as well:
$qb->select('t1', 't2', 't3')
->innerJoin('t1.table2', 't2');
->innerJoin('t2.table3', 't3')
->where('t3.id = :foo')
->setParameter('foo', $foo);
Pay attention not to go crazy with fetching everything, as it takes longer and consumes reasonably more RAM. Fine-tune your query, until you reach what you want (logic and performance-wise).
Hope this helps...
I'm trying to figure out how to join two tables, while querying the second table. I thought it was as simple as:
// Within ServerServiceRepository
return $this->createQueryBuilder('ss')
->join(ServiceType::class, 't')
->where('t.serviceTypeName = :name')
->getQuery()
->execute(['name' => $name]);
But turns out, not so much...
The issue is the query is NOT joining the keys (service_type_id on both tables). What's going on here? I have all the OneToMany relationships setup correctly:
/**
* ServerService
*
* #ORM\Table(name="server_services")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ServerServiceRepository")
*/
class ServerService extends AbstractEntity
{
/**
* #var ServiceType
*
* #ORM\Column(name="service_type_id", type="integer", nullable=false)
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Supportal\ServiceType", fetch="LAZY", inversedBy="serviceTypeId")
* #ORM\JoinColumn(name="service_type_id", referencedColumnName="service_type_id")
*/
private $serviceType;
// [...]
}
/**
* ServiceType
*
* #ORM\Table(name="service_types")
* #ORM\Entity
*/
class ServiceType extends \AppBundle\Entity\AbstractEntity
{
/**
* #var integer
*
* #ORM\Column(name="service_type_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\OneToMany(targetEntity="AppBundle\Entity\ServerService", fetch="EXTRA_LAZY", mappedBy="serviceType")
*/
private $serviceTypeId;
/**
* #var string
*
* #ORM\Column(name="service_type_name", type="string", length=255, nullable=true)
*/
private $serviceTypeName;
// [...]
}
I've added / removed the OneToMany relationship from ServiceType to no change. This is really a unidirectional relationship. Per Doctrine's own docs (Chapter 5), ServerType does not require a relationship mapping.
The SQL query is generating a JOIN that's missing the actual keys:
INNER JOIN service_types s1_ ON (s1_.service_type_name = ?)
What am I missing here on Doctrine to get this working right? I've looked at the tutorials. Symofny's example is almost exact what I'm after, except I need to select by "category name" not Product id: https://symfony.com/doc/2.8/doctrine/associations.html
I've got to be missing something so super simple. But I can't for the life of me peg it...
Edit:
I've removed the OneToMany from ServiceType in my code. It's optional. Not needed for this anyway. This is a unidirectional relationship.
I've tried this:
return $this->createQueryBuilder('ss')
->join('ss.serviceType', 't')
->where('t.serviceTypeName = :name')
->getQuery()
->execute(['name' => $name]);
Resulting in this error:
[Semantical Error] line 0, col 85 near 't WHERE t.serviceTypeName': Error: Class AppBundle\Entity\ServerService has no association named serviceType
Solution
The solution was removing the #ORM Column definition. Looks like it's a conflict in the relationship definitions.
First of all, change the ManyToOne docblock definition to:
/**
* #var ServiceType
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Supportal\ServiceType", fetch="LAZY", inversedBy="serverService")
* #ORM\JoinColumn(name="service_type_id", referencedColumnName="service_type_id")
*/
private $serviceType;
Also change the OneToMany, remove line #ORM\OneToMany(targetEntity="AppBundle\Entity\ServerService", fetch="EXTRA_LAZY", mappedBy="serviceType")
Create new field serverService for the OneToMany relation:
/**
*
* #ORM\OneToMany(targetEntity="ServerService", mappedBy="serviceType")
*/
private $serverService;
You should join on the relation field, in this case serviceType. The way you defined the join is like selecting both tables.
Change to:
return $this->createQueryBuilder('ss')
->join('ss.serviceType', 't')
->where('t.serviceTypeName = :name')
->getQuery()
->execute(['name' => $name]);
Since you are applying a condition on the joined result here, using a LEFT JOIN or simply JOIN is the same.
References:
How to Work with Doctrine Associations / Relations
how to do left join in doctrine
Left join ON condition AND other condition syntax in Doctrine
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'm trying to join the Entities PhpbbTopics and PhpbbForums on the field forumId. These are PhpBB tables and therefore do not have a foreign key on a database level. However, when joining the two Entities using DQL I get the following exception:
[Semantical Error] line 0, col 78 near 'f': Error: Class
Project\MyBundle\Entity\PhpbbTopics has no association named forumId
The code that is being executed in my Controller is:
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT f.forumName
FROM ProjectMyBundle:PhpbbTopics t JOIN t.forumId f'
);
The two entites are:
**
* PhpbbTopics
*
* #ORM\Table(name="phpbb_topics", indexes={#ORM\Index(name="forum_id", columns={"forum_id"}), #ORM\Index(name="forum_id_type", columns={"forum_id", "topic_type"}), #ORM\Index(name="last_post_time", columns={"topic_last_post_time"}), #ORM\Index(name="topic_approved", columns={"topic_approved"}), #ORM\Index(name="forum_appr_last", columns={"forum_id", "topic_approved", "topic_last_post_id"}), #ORM\Index(name="fid_time_moved", columns={"forum_id", "topic_last_post_time", "topic_moved_id"})})
* #ORM\Entity
*/
class PhpbbTopics
{
/**
* #var \Project\MyBundle\Entity\PhpbbForums
*
* #ORM\ManyToOne(targetEntity="Project\MyBundle\Entity\PhpbbForums")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="forum_id", referencedColumnName="forum_id")
* })
*/
private $forumId;
}
And:
/**
* PhpbbForums
*
* #ORM\Table(name="phpbb_forums", indexes={#ORM\Index(name="left_right_id", columns={"left_id", "right_id"}), #ORM\Index(name="forum_lastpost_id", columns={"forum_last_post_id"})})
* #ORM\Entity
*/
class PhpbbForums
{
/**
* #var integer
*
* #ORM\Column(name="forum_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $forumId;
/**
* #var string
*
* #ORM\Column(name="forum_name", type="string", length=255, nullable=false)
*/
private $forumName;
}
Both of these entities have more fields than I have shown. These fields are not used in my query. Every field also has a getter and setter, which are not shown. If you feel the need to see more of the entities, please leave a comment.
I have tried the following solutions but they did not solve my issue:
Doctrine Class “....” has no association named “…”
Error: Class …\Entity.. has no association named
Error: Class … has no field or association named
EDIT:
I found that the file PhpbbTopics.om.xml at src\Project\MyBundle\Resources\config\doctrine did not contain the relationship to PhpbbForums. I have replaced the line:
<field name="forumId" type="integer" column="forum_id" nullable="false"/>
With:
<many-to-one field="forumId" target-entity="PhpbbForums">
<join-columns>
<join-column name="forum_id" referenced-column-name="forum_id"/>
</join-columns>
</many-to-one>
This did not solve or change the issue.
I have solved my issue by changing the syntax of the join. The syntax I am using right now explicitly states which fields of which entities should be joined together. The new query is:
$query = $em->createQuery(
'SELECT f.forumName
FROM ProjectMyBundle:PhpbbTopics t JOIN ProjectMyBundle:PhpbbForums f WITH f.forumId = t.forumId'
);
By using this query, I am able to remove the ManyToOne relationship that I have defined in PhpbbTopics.php and PhpbbTopics.om.xml. Without the declared relationship, my entity matches my database table closer as the table phpbb_topics does not have a foreign key to phpbb_forums.
I have some entities with a one-to-many / many-to-one relationship -
Production class -
/**
* #OneToMany(targetEntity="ProductionsKeywords", mappedBy="production")
*/
protected $productionKeywords;
ProductionsKeywords class -
/**
* #ManyToOne(targetEntity="Production", inversedBy="productionKeywords")
* #JoinColumn(name="production_id", referencedColumnName="id", nullable=false)
* #Id
*/
protected $production;
/**
* #ManyToOne(targetEntity="Keyword", inversedBy="keywordProductions")
* #JoinColumn(name="keyword_id", referencedColumnName="id", nullable=false)
* #Id
*/
protected $keyword;
Keyword class -
/**
* #OneToMany(targetEntity="ProductionsKeywords", mappedBy="keyword")
*/
protected $keywordProductions;
If I write a DQL query like
$query = $this->entityManager->createQuery("SELECT p FROM \Entity\Production p");
The productions, productionKeywords and keywords all load fine, however if I try to fetch join the productionKeywords and keywords like
$query = $this->entityManager->createQuery("SELECT p, pk, k FROM \EntityProduction p
LEFT JOIN p.productionKeywords pk
LEFT JOIN pk.keyword k
");
then the entities are not loaded.
Not sure what I'm doing wrong as I have the same relationship setup with some other entities and it works fine with them.
OK, so it seems that if you have a 'pure' join entity (like the ProductionsKeywords class above) then it can't be used in a fetch query. I got around the issue by using a timestamp column in the productions_keywords table as another property in the ProductionsKeywords class and then the fetch joins began to work.