This is driving me crazy.
I have a normal Symfony2 Security System, with User and Role entities with a ManyToMany relation between them.
Lets suppose that i have 3 roles in the database, ROLE_1, ROLE_2 and ROLE_3.
How can i retrieve all Users that dont have ROLE_3 for example ?
I already tried something like:
$qb->innerJoin('u.roles', 'r , 'WITH', $qb->expr()->notIn('r.id', ':roles')))
->setParameter('roles', array(3));
My Question is, how can i Query in the JoinTable ?
This is more of a SQL question than it is a Symfony or Doctrine question. The first solution that comes to mind is a subquery, although that may or may not be the most efficient way to handle this problem. Something like this:
SELECT username
FROM users
WHERE users.id NOT IN (
SELECT users.id
FROM users
INNER JOIN user_roles
ON users.id = user_roles.user_id
INNER JOIN roles
ON user_roles.role_id = roles.id
WHERE roles.id NOT IN (1,2)
);
Where 1 and 2 are the IDs of the role you want to include in your results, and user_roles is the bridge table between users and roles.
Related
I have two tables namely users and activities which has many to many relationships in my Laravel app. So, I have a pivot table activity_users with user_id and activity_id. I know that I can simply fetch all the users enrolled in an activity with a many to many relationship in my model.
But I want to get only those users from the pivot table which are enrolled in only one activity. I have been trying to do this for many days and I am still stuck. I have created a work around to filter the values based on activities_count after fetching from the database, but I wanted a more efficient solution.
Table users
id
name
1
User A
2
User B
Table activities
id
name
1
Activity 1
2
Activity 2
Table activity_users
activity_id
user_id
1
1
1
2
2
1
From this sample data what I want is: if I query for unique users in Activity 1, it should return only User B (user_id = 2) as he/she is not enrolled in other activities. And if I query for unique users in Activity 2, it should return null because it has only one user i.e. User A and he/she is already enrolled in Activity 1.
Fetching distinct values doesn't work as it just removes duplicates. I have already set to not allow duplicate combination of values in pivot table.
How about use Having?
You can find user with only one Activity like:
SELECT user_id FROM activity_users
GROUP BY user_id
HAVING count(user_id) = 1
Like Honk der Hase said in comments
Providing some code would help, if only to show more information on your relationships between models. But at a guess I think something like this should work:
$users = User::with("activities")
->withCount("activities")
->having("activities_count", 1)
->whereHas("activities", fn ($q) => $q->where("activities.id", $activity_id));
We eager load the activities relationship and get the count as an alias, so we can catch only those with a single activity. Then we filter the relationship to search for the provided activity id.
This assumes you've set up your User and Activity models with a proper many-to-many relationship.
For completeness, the resulting typically (for Laravel) verbose SQL is:
SELECT `users`.*, (
SELECT COUNT(*)
FROM `activities`
INNER JOIN `activity_user` ON `activities`.`id` = `activity_user`.`activity_id`
WHERE `users`.`id` = `activity_user`.`user_id`
) AS `activities_count`
FROM `users` WHERE EXISTS (
SELECT * from `activities`
INNER JOIN `activity_user` ON `activities`.`id` = `activity_user`.`activity_id`
WHERE `users`.`id` = `activity_user`.`user_id`
AND `activities`.`id` = ?
)
HAVING `activities_count` = ?
I'm trying to build a basic query using symfony and doctrine. The query will return a User and all the jobs they are working on. From the two tables 'User' and UserDetails (Contains User_id and Job_id). User is mapped to userdetails correctly as one- many.
my query is
SELECT userdetails, u FROM TestBundle:User
join userdetails.u
As user is a field in userdetails, but userdetails isn't a member of users the following query doesn't work. Is there any way to write this so the result will look like User.userDetails.
Try something like
SELECT
u, ud
FROM
TestBundle:User u
JOIN //LEFT JOIN if you want also users without UserDetails
TestBundle:UserDetails ud
WITH
u.id = ud.user_id
of course your variables (like ud.user_id) could vary but we don't have enough informations to work on so we have to guess
I have a roles table which contains various user roles.
A pivot tables joins this to my users table.
users
id | name
roles
id | title
user_role
user_id | role_id
I would like to display all roles that exist and pre tick the ones that belong to a specific user.
How would I go about this, I take it I cannot do this in mySQL?
I was considering getting all roles with one query, then with another query get all roles that belong to a specific user.
Then loop through all roles and if there is a match with the specific users roles, output a checked box instead of an unchecked one.
Is there a better way?
Something like
SELECT R.id as role_id,
R.title as role_title,
UR.user_id as user_id,
U.name as user_name
FROM Roles R
LEFT JOIN user_role UR
ON UR.role_id = R.id
AND UR.user_id = :myuserid
LEFT JOIN users U
ON U.id = UR.user_id
should return a complete list of roles, with either a NULL or the user id in the user column to indicate any roles that the user has been granted (user identified by id as :myuserid)
I think normal SQL Query can do most of the work: Checkout this SQLFIddle for sample of what your query might look like
SELECT r.* FROM roles r LEFT JOIN user_role ur ON r.id=ur.role_id WHERE ur.user_id=3;
I have 5 tables:
user ( user id, user name, etc.. )
role ( role_id, role_name )
user_role ( user_id, roles_id )
form ( form_id, form_name, etc.. )
form_access ( form_id, role_id )
user contains all registered user data.
role contains all different types of roles.
user_role contains which user has which role (all assigned roles are stored)
form contains all form data.
form_access contains data like which user role has which form access(one form can be assigned to many user roles).
I wanted to write a SQL query in PHP to retrieve form name based on the user logged in and his role, e.g. if Admin logs in he should get all forms, if HR logs in he should get forms related to HR only.
I tried this query:
$query = "SELECT ur.role_id, f.form_name, f.form_desc
FROM user_role ur, froms f
WHERE users.id = '".$user."',
users.status ='A',
forms.form_id = form_access.form_id,
from_access.role_id = user_role.role_id,
user_role.role_id = '".$user."'";`
Some one help me out with the correct query?
Since you start from the user_id you'll want to select from user_role and JOIN form_access and form.
SELECT `form`.`form_name` AS `form_name`
FROM `user_role` AS `ur`
INNER JOIN `form_access` AS `fa` ON `fa`.`role_id` = `ur`.`role_id`
INNER JOIN `form` AS `f` ON `f`.`form_id` = `fa`.`form_id`
WHERE `ur`.`user_id` = '".$user."'
PS: Check the table and column names.
You have to use AND .
BUT this should be better with joins.
$query = "SELECT ur.role_id, f.form_name, f.form_desc
FROM from_access
INNER JOIN froms f ON forms.form_id = form_access.form_id
INNER JOIN user_role ur ON from_access.role_id = user_role.role_id
INNER JOIN users ON users.id = user_role.role_id
WHERE users.id = '".$user."'
AND users.status ='A' ";
Your query is entirely broken, the others may have provided you with solutions but I'm going to give you some advice.
You've written an entire query, tried it, and it failed. I write queries all day long but if I write a whole query in Notepad then execute it it's probably going to have some minor error in it somewhere too.
Start from the ground up. You're trying to get a list of forms the user has access to, so lets start with the forms_access table. So what's the most basic starting point? How about:
SELECT fa.role_id, fa.form_id
FROM forms_access fa
Ok, thats overly simplified but if that ran at least we know we're connected to the database.
So we can easily tell which form_ids each role has access to. Now we know our linking table to users is user_roles, so let's add that in:
SELECT ur.user_id, fa.form_id, fa.role_id
FROM forms_access fa
INNER JOIN user_roles ur ON fa.role_id = ur.role_id
So we've joined forms_access to user_roles on the foreign key role_id. Now we can see for every user_id, which role_id they have and which form_ids they can access.
So we're pretty much there, we just need the information from the forms table, so lets JOIN to that too:
SELECT ur.user_id, f.form_name, f.form_desc, fa.form_id, fa.role_id
FROM forms_access fa
INNER JOIN user_roles ur ON ur.role_id = fa.role_id
INNER JOIN forms f ON f.form_id = ur.form_id
Great! Now we have a list of each form_name/form_desc that each user_id can access.
Try the above step by step, if you skip to the end there could well be an error since I have not tested the code, and I don't know for sure that your table definitions match the question. If you do it step by step you only need to check the most recently added line to find the error.
I have just noticed in the question that you also need users.status = 'A', so in the same way as above you'll need to join to the users table on an appropriate foreign key, give it a go.
Now, once you've done all that you need to filter the results to a specific user_id - notice up till this point we haven't bothered with the WHERE clause.
Now don't go adding some variant of WHERE user_id = '$user' right away because then you've introduced 2 potential errors. Instead try adding WHERE user_id = 0 (or some known user_id). Does the query run and the results look correct? Great, now finally try adding in your php variable.
Say you have Users and Roles in a many-to-many relationship. Standard setup with following tables:
user role user_role
user_id role_id user_id
email name role_id
Preferably with as few queries as possible, what's a good way to load all users with all tags belonging to them? That is, I'd like to end up with users, each having an array of roles.
I'm currently thinking of using the following, which'll requires two queries.
Get all users with group concatenated role ids.
Get all role names in separate query
Loop through users, explode role ids and connect them with names from role query as we go.
Any good/better/more efficient/cleaner/quicker alternatives? Any good way to do this in a single query?
To return all roles for all users
<?php
$array=array();
$sql='SELECT user.email,role.name
FROM user
INNER JOIN user_role ON user_role.user_id=user.user_id
INNER JOIN role ON user_role.role_id=role.role_id';
$stmt = db::db()->query($sql);
while($row=$stmt->fetch(PDO::FETCH_ASSOC))
{
if (!isset($array[$row['email']])){$array[$row['email']]=array();}
array[$row['email']][]=$row['name'];
}
?>
To return all roles for a single user
<?php
$sql='SELECT user.email,role.name
FROM user
INNER JOIN user_role ON user_role.user_id=user.user_id
INNER JOIN role ON user_role.role_id=role.role_id
WHERE user.email=?';
$stmt = db::db()->prepare($sql);
$stmt->execute(array('anEmail#domain.com'));
$roles=$stmt->fetchAll(PDO::FETCH_ASSOC);
?>
The above assumes that all users have at least one role. If not, use outer joins.