I thought if I don't want to override relation's on condition I just use WITH to add an additional condition. I'm not sure if my mapping is wrong, but DQL without join condition in WITH makes a CROSS Join
Here My Entities Relations (Symfony):
Movie:
/**
* #ORM\OneToMany(targetEntity="MovieInterest", mappedBy="movie")
*/
private $movieInterests;
MovieInterest:
/**
* #ORM\ManyToOne(targetEntity="Movie", inversedBy="movieInterests")
*/
private $movie;
Here my Query in MovieInterestRepository:
public function deleteByMovieId($id, $user)
{
$em = $this->getEntityManager();
$result = $em->createQuery(
"SELECT mi FROM CineupsWebAppBundle:MovieInterest mi
JOIN CineupsWebAppBundle:Movie m WITH m.id=mi.movie
WHERE m.id=".$id." AND mi.user=".$user->getId())
->getResult();
foreach ($result as $entity) {
$em->remove($entity);
}
$em->flush();
}
If I remove WITH m.id=mi.movie the Query gets too many results
Try to do it this way:
$result = $em->getRepository('CineupsWebAppBundle:MovieInterest')
->createQuery('mi')
->innerJoin('mi.Movie', 'm')
->andWhere('m.id = :id')
->andWhere('mi.user = :user')
->getQuery()
->setParameter(':id', $id)
->setParameter(':user', $user->getId())
->getResult();
This will also solve sqj-injection vulnerability at your original query
You need to add mapping information to your entity:
/**
* #ORM\ManyToOne(targetEntity="Movie", inversedBy="movieInterests")
* #ORM\JoinColumn(name="id", referencedColumnName="movie")
*/
private $movie;
I found the problem: I joined with table and not with relation. Here the correct code:
"SELECT mi FROM CineupsWebAppBundle:MovieInterest mi
JOIN mi.movie m
WHERE m.id=".$id." AND mi.user=".$user->getId())
Related
i want to count how many baby for a parent
class abonnementRepository extends \Doctrine\ORM\EntityRepository
{
public function SumEnfantDQL($id)
{
$entityManager = $this->getEntityManager();
$query = $entityManager->createQueryBuilder();
$query->select('sum(o.id) AS somme');
$query->from('AppBundle:enfant', 'o');
$query->join('AppBundle:User','p')->where('p.id = :id');
$rez = $query->getQuery()->getResult();
return $rez;
}
}
the entity enfant had matricul_prt and entity user had enfant_id and $id parameter is the parent id
i don't know how it work with join or innerJoin .So what i want to do is
SELECT SUM(*)
FROM enfant e
WHERE e.matricul_prt = $id;
Thank you so much
First of all, you should create a Repository class for AppBundle:enfant, this repo does not look like created fot 'enfant'.
Next the method should look like below, but only if there is valid association between 'enfant' and 'User'.
public function SumEnfant(int $id): int
{
return $this->createQueryBuilder('e') <- alias for 'enfant'
->select('sum(e.id)')
->join('e.user', 'u') <- join by entity property
->where('u.id = :id') <- condition for associated entity
->setParameter('id' , $id) <- parameter
->getQuery()
->getSingleScalarResult();
}
Try this, read doc once again and modify for your case.
$query = $this->createQueryBuilder('C')
->select('C.id, C.name, P.id, P.name')
->leftJoin('C.Product', 'P')
->getQuery()
->getResult();
Above query returns HYDRATE_ARRAY, and I want HYDRATE_OBJECT.
$query = $this->createQueryBuilder('C')
->select('partial C.{id, name}, partial P.{id, name}')
->leftJoin('C.Product', 'P')
->getQuery()
->getResult();
your can get result
foreach($query as $q) {
echo $q->getId().' -- '.$q->getProduct()->getId();
}
If you want to use any mysql function like group_concat and get ids
$query = $this->createQueryBuilder('C')
->select('C, GROUP_CONCAT(C.id) AS ids, partial P.{id, name}')
->leftJoin('C.Product', 'P')
->getQuery()
->getResult();
foreach($query as $q) {
echo $q[0]->getId().' -- '.$q[0]->getProduct()->getId().' -- '.$q['ids'];
}
Here you can get all field of category table, group ids of category table and product table filed
Thanks
Ashok Chitroda
You can do it.
/**
* return all devices enabled to array
*
* #return array
*/
private function getAllDevicesToArray()
{
// return to array not object
return $this->getDoctrine()
->getRepository('AdminBundle:Device')
->createQueryBuilder('d')
->where('d.enabled = :enabled')
->setParameter('enabled', 1)
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
}
I have this entities
class Business
{
...
/**
*
* #ORM\ManyToMany(targetEntity="Category", inversedBy="businesses")
*
*/
private $categories;
...
}
class Category
{
...
/**
*
* #ORM\ManyToMany(targetEntity="Business", mappedBy="categories")
*
*/
private $businesses;
...
}
And I try this query in BusinessRepository but does not work
$em = $this->getEntityManager();
$dql = "
SELECT a
FROM BusinessMainBundle:Business a
WHERE a.title LIKE :title AND :category IN (a.categories)";
$query = $em->createQuery($dql)
->setParameter("title", "%".$title."%")
->setParameter("category", $category);
If you can resolve this problem I would be thankful
You need to join categories. Your DQL should look like this:
$dql = "
SELECT a
FROM BusinessMainBundle:Business:Business a
LEFT JOIN a.categories c
WHERE a.title = :title AND c.id IN (:category)
";
Where
->setParameter("category", $category);
$category should be an array of values (in this example id of categories).
Im use many-to-many relationship;
User entity;
/**
* #ORM\ManyToMany(targetEntity="Conversation", inversedBy="users")
*/
protected $conversations;
Conversation entity;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="conversations")
* #ORM\JoinTable(name="user_conversation")
*/
protected $users;
When, I work this function;
$user->getConversations();
Symfony work this sql code in background;
SELECT
t0.id AS id1,
t0.conversationid AS conversationid2
FROM
Conversation t0
INNER JOIN user_conversation ON t0.id = user_conversation.conversation_id
WHERE
user_conversation.user_id = ?
And select all conversation. This will be performance problem. So, I work with repository class. But, I can't work many-to-many and limit function with together. What should I do? What I write to repository class?
If you want to optimize access to large collections in doctrine just use Criteria (That only works on OneToMany associations.)
Example:
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Criteria;
/**
* #ORM\Entity
* #ORM\Table
*/
class User
{
....
public function getLatestConversation()
{
$criteria = Criteria::create()
->setMaxResults(10);
return $this->conversations->matching($criteria);
}
}
For ManyToMany I think you must create a custom query:
public function getLatestConversations($user)
{
$qb = $this->createQueryBuilder("c");
$qb
->leftjoin("c.users", "u")
->where("u = :user")
->setParameter("user", $user)
->setMaxResults(2);
return $qb->getQuery()->getResult();
}
You can convert it to Arraycollection Object:
$input = $repo->getConversations(); //manytomany relation
$arr = new ArrayCollection();
foreach ($input as $e){
$arr->add($e);
}
And then use your Criteria on $arr
Try
$repository = $em->getRepository('YourBundle:Conversations');
$query = $repository->createQueryBuilder('C')
->join('C.Users', 'U')
->where('U.id = :uid')
->setMaxResults(20) //set your amount for limit
->setParameter('uid', $user_id)
->getQuery();
$result = $query->getResults();
I am using the FOS bundle and I want to retrieve all users with a given ROLE from the database.
What is the best way to do this?
Just add this in your UserRepository or replace $this->_entityName by YourUserBundle:User:
/**
* #param string $role
*
* #return array
*/
public function findByRole($role)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->where('u.roles LIKE :roles')
->setParameter('roles', '%"'.$role.'"%');
return $qb->getQuery()->getResult();
}
If you are using FOSUser Groups you should use:
/**
* #param string $role
*
* #return array
*/
public function findByRole($role)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->leftJoin('u.groups', 'g')
->where($qb->expr()->orX(
$qb->expr()->like('u.roles', ':roles'),
$qb->expr()->like('g.roles', ':roles')
))
->setParameter('roles', '%"'.$role.'"%');
return $qb->getQuery()->getResult();
}
Well, if there is no better solution, I think I will go to a DQL query:
$query = $this->getDoctrine()->getEntityManager()
->createQuery(
'SELECT u FROM MyBundle:User u WHERE u.roles LIKE :role'
)->setParameter('role', '%"ROLE_MY_ADMIN"%');
$users = $query->getResult();
If you have this requirement and your user list will be extensive, you will have problems with performance. I think you should not store the roles in a field as a serialized array. You should create an entity roles and many to many relationship with the users table.
As #Tirithen states, the problem is that you will not get the users that have an implicit role due to role hierarchy. But there is a way to work around that!
The Symfony security component provides a service that gives us all child roles for a specific parent roles. We can create a service that does almost the same thing, only it gives us all parent roles for a given child role.
Create a new service:
namespace Foo\BarBundle\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Symfony\Component\Security\Core\Role\Role;
/**
* ReversedRoleHierarchy defines a reversed role hierarchy.
*/
class ReversedRoleHierarchy extends RoleHierarchy
{
/**
* Constructor.
*
* #param array $hierarchy An array defining the hierarchy
*/
public function __construct(array $hierarchy)
{
// Reverse the role hierarchy.
$reversed = [];
foreach ($hierarchy as $main => $roles) {
foreach ($roles as $role) {
$reversed[$role][] = $main;
}
}
// Use the original algorithm to build the role map.
parent::__construct($reversed);
}
/**
* Helper function to get an array of strings
*
* #param array $roleNames An array of string role names
*
* #return array An array of string role names
*/
public function getParentRoles(array $roleNames)
{
$roles = [];
foreach ($roleNames as $roleName) {
$roles[] = new Role($roleName);
}
$results = [];
foreach ($this->getReachableRoles($roles) as $parent) {
$results[] = $parent->getRole();
}
return $results;
}
}
Define your service for instance in yaml and inject the role hierarchy into it:
# Provide a service that gives you all parent roles for a given role.
foo.bar.reversed_role_hierarchy:
class: Foo\BarBundle\Role\ReversedRoleHierarchy
arguments: ["%security.role_hierarchy.roles%"]
Now you are ready to use the class in your own service. By calling $injectedService->getParentRoles(['ROLE_YOUR_ROLE']); you will get an array containing all parent roles that will lead to the 'ROLE_YOUR_ROLE' permission. Query for users that have one or more of those roles... profit!
For instance, when you use MongoDB you can add a method to your user document repository:
/**
* Find all users with a specific role.
*/
public function fetchByRoles($roles = [])
{
return $this->createQueryBuilder('u')
->field('roles')->in($roles)
->sort('email', 'asc');
}
I'm not into Doctrine ORM but I'm sure it won't be so different.
You can use just this on your DQL:
SELECT u FROM YourFavouriteBundle:User u WHERE u.roles [NOT] LIKE '%ROLE_YOUR_ROLE%'
Of course with QueryBuilder it's more elegant:
// $role = 'ROLE_YOUR_ROLE';
$qb->where('u.roles [NOT] LIKE :role')
->setParameter('role', "%$role%");
Finally i solved it, following is an exact solution:
public function searchUsers($formData)
{
$em = $this->getEntityManager();
$usersRepository = $em->getRepository('ModelBundle:User');
$qb = $usersRepository->createQueryBuilder('r');
foreach ($formData as $field => $value) {
if($field == "roles"){
$qb->andWhere(":value_$field MEMBER OF r.roles")->setParameter("value_$field", $value);
}else{
$qb->andWhere("r.$field = :value_$field")->setParameter("value_$field", $value);
}
}
return $qb->getQuery()->getResult();
}
Cheers!
In case you need to filter users by role using a DQL filter in a YAML file (In EasyAdminBundle for instance)
entities:
Admin:
class: App\Entity\User
list:
dql_filter: "entity.roles LIKE '%%ROLE_ADMIN%%'"
Here I give an alternative solution :
I find users of roles for a given array
In controller I call the function like that
$users = $userRepository->findUsersOfRoles(['ROLE_ADMIN', 'ROLE_SUPER_USER']);
Then in my repository I make a loop to generate condition and set the parameters :
public function findUsersOfRoles($roles)
{
$condition = 'u.roles LIKE :roles0';
foreach ($roles as $key => $role){
if ($key !== 0){
$condition .= " OR u.roles LIKE :roles".$key;
}
}
$query = $this->createQueryBuilder('u')
->where($condition);
foreach ($roles as $key => $role){
$query ->setParameter('roles'.$key, '%"'.$role.'"%');
}
return $query->getQuery() ->getResult();
}