I'm building a repository method to look if a fos_group is assigned to any fos_user before allowing group to be deleted. Basically this is the raw query I'm trying to make using DQL:
SELECT
COUNT(*)
FROM
fos_user u
LEFT JOIN fos_user_user_group fug ON (fug.user_id = u.id)
WHERE
fug.group_id = :group_id
This is how I setup the relationship between Users and Groups:
/**
* #ORM\ManyToMany(targetEntity="Group")
* #ORM\JoinTable(name="fos_user_user_group",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
And this is what I have done til now:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('COUNT(gr.id)')
->from("UserBundle:Group", "gr");
return $qb->getQuery()->getSingleScalarResult();
But I'm stuck since I do not know how to add the join sentence with not mapped table/entity (fos_user_user_group), can anyone give me some push?
You have mapped your property there you are in right way just join the property $groups in user entity it will join your junction table fos_user_user_group and the use what ever you where clause is
$qb
->select('COUNT(g.id)')
->from('UserBundle:User', 'u')
->join('u.groups g')
->where('g.id = :val')
->setParameter('val',$your_group_id);
Related
I have native sql query with left join when have on with or condition, how to represent it in query builder ?
$query = " SELECT te.id
FROM task_executions AS te
INNER JOIN tasks AS t ON t.id = te.task_id
LEFT JOIN cost_objects AS co ON co.id = t.cost_object_id
LEFT JOIN cost_object_managers AS com ON com.cost_object_id = co.id OR com.cost_object_id = co.parent_id
and I need represent it in query builder. But in User entity I have ManyToMany relation, without separate table and when I try left join WITH condition this is not same what I need. I need change relation for ON
LEFT JOIN cost_object_managers AS com ON com.cost_object_id = co.id OR com.cost_object_id = co.parent_id
User entity
class User
{
...
/**
* #ORM\ManyToMany(targetEntity="CostObject", mappedBy="users")
*/
private $costObjects;
}
CostObject entity
class CostObject
{
/**
* #var CostObject
*
* #ORM\ManyToOne(targetEntity="CostObject", inversedBy="children")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
* })
*/
private $parent;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="User", inversedBy="costObjects")
* #ORM\JoinTable(name="cost_object_managers",
* joinColumns={#ORM\JoinColumn(name="cost_object_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
private $users;
and my query builder without condition
$qb->select('te')
->from('AppBundle:TaskExecution', 'te')
->innerJoin('te.task', 't')
->leftJoin('t.costObject', 'co')
->leftJoin('co.users', 'com')
this is $query->getSQL()
SELECT some_name FROM task_executions t0_ INNER JOIN tasks t1_ ON t0_.task_id = t1_.id LEFT JOIN cost_objects c2_ ON t1_.cost_object_id = c2_.id LEFT JOIN cost_object_managers c4_ ON c2_.id = c4_.cost_object_id LEFT JOIN users u3_ ON u3_.id = c4_.user_id ORDER BY t0_.execution_start DESC
In this example I see ON relation condition LEFT JOIN users u3_ ON u3_.id = c4_.user_id. And need change it like in native sql
Now I have
$qb->select('te')
->from('AppBundle:TaskExecution', 'te')
->innerJoin('te.task', 't')
->leftJoin('t.costObject', 'co')
->leftJoin(
'co.users',
'com',
Join::ON,
$qb->expr()->orX(
'co = com.costObjects',
'co.parent = com.costObjects'
)
)
but got error
[Syntax Error] line 0, col 112: Error: Expected end of string, got 'ON'
if I used WITH condition, in my sql represent I still have relation by id, I don't need that
->leftJoin(
'co.users',
'com',
Join::WITH,
$qb->expr()->orX(
'co MEMBER OF com.costObjects',
'co.parent MEMBER OF com.costObjects'
)
)
LEFT JOIN users u3_ ON u3_.id = c4_.user_id AND (EXISTS (SELECT 1 FROM cost_object_managers c5_ INNER JOIN cost_objects c6_ ON c5_.cost_object_id = c6_.id WHERE c5_.user_id = u3_.id AND c6_.id IN (c2_.id)) OR EXISTS (SELECT 1 FROM cost_object_managers c5_ INNER JOIN cost_objects c6_ ON c5_.cost_object_id = c6_.id WHERE c5_.user_id = u3_.id AND c6_.id IN (c2_.parent_id)))
I mean users u3_ ON u3_.id = c4_.user_id AND but in native query we have only LEFT JOIN cost_object_managers AS com ON com.cost_object_id = co.id OR com.cost_object_id = co.parent_id
How it's reproduce in Query Builder with ON condition type?
If you have the query and it works for you, you don't need to do all the work to transform it to DQL or the QueryBuilder programmatic syntax. You can just use Doctrine's Native Query and then - if needed - map the result to your object. Just create a custom repository and in it a new method that roughly looks like this:
public function findTaskExecutionBy...()
{
$query = $this->entityManager->createNativeQuery('SELECT te.id FROM ...');
return $query->getSingleScalarResult(); // If it's just one id you expect
}
You can also use $query->getResult() if you expect multiple id's to be returned. Or use the ResultSetMapping if you want the whole Task-object:
$rsm = new ResultSetMappingBuilder($this->entityManager);
$rsm->addRootEntityFromClassMetadata('App\Entity\Task', 'te');
$query = $this->entityManager->createNativeQuery(
'SELECT te.* FROM ...',
You can also check the Doctrine documentation for a more detailed explanation and some more examples: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html
$rsm
);
I have Many to Many relationship between Institutes and Courses. I want to build query that returns only the institutes list whom some courses has been assigned. I have wrote queries in this situation for one to many. but for not many to many. here is the relationships,
class Institutes {
/**
* #ORM\ManyToMany(targetEntity="Courses", inversedBy="institutes")
* #ORM\JoinTable(name="institute_courses",
* joinColumns={#ORM\JoinColumn(name="institute_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="course_id", referencedColumnName="id")}
* )
*/
protected $courses;
}
class Courses {
/**
* #ORM\ManyToMany(targetEntity="Institutes", mappedBy="courses")
*/
protected $institutes;
}
here is the query that i have written, but didn't work properly.
$repository->createQueryBuilder('s')
->leftJoin('CoursesBundle:Courses','c', 'ON c.institutes = s.courses')
->where('s.active = :active')
->andWhere('s.verified = :active')
->setParameter('active', true)
->orderBy('s.name', 'ASC');
This should do the trick:
$repository->createQueryBuilder('i')
->innerJoin('i.courses','c')
->where('i.active = TRUE')
->andWhere('i.verified = TRUE')
->orderBy('i.name', 'ASC');
You can use a JOIN as you would with other kinds of associations. The following query will find all courses which have been assigned at least to one institute:
SELECT c FROM SomeBundle:Courses c JOIN c.institutes i
You can filter the results further by adding a join condition:
SELECT c FROM SomeBundle:Courses c JOIN c.institutes i WITH i.something = :someParam
I have an entity Category, and this category has a recursive relationship with itself where each category can be a parent to several other categories. The relationship looks like this:
/**
* #var parent
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumn(referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
*/
private $children;
I need to make a query builder query that selects all categories that are either children(have a parent) Or the categories that have no parent and also have no children (in other words all categories except the parents that have children) I can't seem to be able to do it. Please help.
You need next DQL query:
$query = 'SELECT c FROM AcmeBundle:Category c LEFT JOIN c.parent p LEFT JOIN c.children ch WHERE p IS NOT NULL OR (ch IS NULL AND p IS NULL)';
If you need QueryBuilder sequence for this query you can use next code:
$qb = $em->createQueryBuilder();
$query = $qb
->select('c')
->from('AcmeBundle:Category', 'c')
->leftJoin('c.parent', 'p')
->leftJoin('c.children', 'ch')
->where($qb->expr()->orX(
$qb->expr()->isNotNull('p'),
$qb->expr()->andX(
$qb->expr()->isNull('ch'),
$qb->expr()->isNull('p'),
)
))
->getQuery();
I have a User entity and Role entity. A user can have many roles and a role can have many users. The parts that matter (I think) are the following:
In User.php
/**
* #ORM\ManyToMany(targetEntity="Role",inversedBy="users")
* #ORM\JoinTable(name="user_role",
* joinColumns={#ORM\JoinColumn(name="userid", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="roleid", referencedColumnName="id")}
* )
*/
protected $roles;
public function __construct(){
$this->roles = new ArrayCollection();
}
In Role.php:
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="roles")
* #ORM\JoinTable(name="user_role",
* joinColumns={#ORM\JoinColumn(name="roleid", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="userid", referencedColumnName="id")}
* )
*/
protected $users;
public function __construct(){
$this->users = new ArrayCollection();
}
Trying the following query gives me a User that gets it's id and name field replaced with Role id and name
$result = $this->getEntityManager()->createQuery('SELECT
u.id, u.username, u.name, u.email, r.id, r.name
FROM mytestBundle:User u
LEFT JOIN u.roles r
WHERE u.id = :id')
->setParameter('id', $id)
->getArrayResult();
The following gives me an error: Error: Cannot select entity through identification variables without choosing at least one root entity alias.
$result = $this->getEntityManager()->createQuery('SELECT
u.id, u.name, r
//... rest is same
The following gives me the result as I expect but includes too many user fields. One user watching the profile of another user can't see the user's email, password, salt, ... but is allowed to see the user's roles:
$result = $this->getEntityManager()->createQuery('SELECT
u, r
So the question is: How do I select only certain user fields and have doctrine nest Roles in a user record without it throwing an error?
Posted too early, sorry.
This errors is quite common and many SO answers do not apply to my situation but this one does.
Using partial solved my problem:
$result = $this->getEntityManager()->createQuery('SELECT
partial u.{id, name}, r
I have an Entity Video related with a Entity Category and I need to run this SQL with Doctrine QueryBuilder, with this I can get the most used categories in all videos (1000+):
SELECT c.*
FROM Video v
INNER JOIN video_category vc ON vc.video_id = v.id
INNER JOIN Category c ON vc.category_id = c.id
GROUP BY c.id
HAVING COUNT(v.id) > 1000
ORDER BY c.name ASC;
My querybuilder:
$queryBuilder = $this->getEntityManager()
->createQueryBuilder()
->select('c')
->from('AcmeVideoBundle:Video', 'v')
// Can Doctrine join itself silently with relational info in the Entities?
->join('AcmeCategoryBundle:Category', 'c', Expr\Join::WITH, 'v.id = c.id')
->groupBy('c.id')
->having('COUNT(v.id) > 1000')
->orderBy('c.name', 'ASC')
->getQuery();
But the SQL query output by queryBuilder is this:
SELECT c0_.id AS id0, c0_.NAME AS name1
FROM Video v1_
INNER JOIN Category c0_ ON (v1_.id = c0_.id)
GROUP BY c0_.id
HAVING COUNT(v1_.id) > 1000
ORDER BY c0_.NAME ASC
Without the relational table (video_category)
The Entities mapping:
/**
* Video
*
* #ORM\Table
* #ORM\Entity(repositoryClass="Acme\VideoBundle\Entity\VideoRepository")
*/
class Video
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="Acme\CategoryBundle\Entity\Category", cascade={"persist"})
*/
private $category;
// More fields, getters and setters etc...
}
/**
* Category
*
* #ORM\Table
* #ORM\Entity(repositoryClass="Acme\CategoryBundle\Entity\CategoryRepository")
*/
class Category
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
// More fields, getters and setters etc...
}
How can I use the relation table to run the original SQL query with doctrine Querybuilder? I missed something?
INFO: When I findBy{field}, persist, flush, clear on all entities works fine, the Doctrine relations are ok, I have a Video, Category and video_category tables fine, the original SQL query works perfect.
// Can Doctrine join itself silently with relational info in the Entities?
->join('AcmeCategoryBundle:Category', 'c', Expr\Join::WITH, 'v.id = c.id')
Yes! In fact that is one of the major reasons for using an ORM such as Doctrine 2.
Try:
->leftJoin('v.category','c')
The manual goes into more details though oddly enough is does not seem to have a join example. Hence the common confusion.
http://docs.doctrine-project.org/en/latest/reference/query-builder.html
And you may not be aware of this but the United Nations has passed a resolution outlawing the use of abbreviations for aliases. Just to be safe, try:
$queryBuilder = $this->getEntityManager()
->createQueryBuilder()
->addSelect('category')
->from('AcmeVideoBundle:Video', 'video')
->leftJoin('video.category', 'category')
->groupBy('category.id')
->having('COUNT(video.id) > 1000')
->orderBy('category.name', 'ASC')
->getQuery();
Ok, solved, the problem was that the Entities isn't fully mapped for ManyToMany bidirectional relationship.
The Entities are now:
class Video
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Acme\CategoryBundle\Entity\Category", inversedBy="video") // Note the inversedBy key
*/
private $category;
}
class Category
{
/**
* #var Video
*
* #ORM\ManyToMany(targetEntity="Acme\VideoBundle\Entity\Video", mappedBy="category") // Again the inversed
*/
private $video; // New field for bidirectional ManyToMany
}
And the final QueryBuilder working (now with full alias version :P):
$queryBuilder = $this->getEntityManager()
->createQueryBuilder()
->select('category')
->from('AcmeCategoryBundle:Category', 'category')
->join('category.video', 'video')
->groupBy('category.id')
->having('COUNT(video.id) > 1000')
->orderBy('category.name', 'ASC')
->getQuery();
Best regards