Implicit joins and Where in Doctrine - how? - php

I have a ManyToMany relation of Users and Roles. That is I have User table and entity class, Role table and entity and a joining table "user_role" width user_id and role_id columns.
Now, I recently tried to get users with their roles, by using joins, like this:
$qb = $this->createQueryBuilder('u')
->join('user_role', 'ur', Join::ON, "I didn't know what to put here, nothing worked ")
Anyway, thanks to this answer I added correct mapping (annotations) to my both entity classes and then removed my own join, letting Doctrine do the job:
$qb = $this->createQueryBuilder('u');
$q = $qb->getQuery();
$users = $q->getResult();
And this works. I have a list of all users, and then I can access their roles (thanks to User->getRoles() method).
However, now I want to list only users having certain roles, for example 'ROLE_ADMIN' and I have no idea how to do this:
$qb = $this->createQueryBuilder('u')
>where('what_to_put_here = :roles')
->setParameter('roles', 'what_to_put_here')
By the way, the SQL code generated by Doctrine looks like this:
SELECT u0_.id AS id_0, u0_.username AS username_1, u0_.personal_name AS personal_name_2, u0_.password AS password_3, u0_.email AS email_4, u0_.is_active AS is_active_5 FROM user u0_
So there is no JOIN. From the Doctrine docs I know this is called "lazy load" - the roles of certain user will be fetched on demand.
But then, how can I do something like this:
SELECT * FROM `user`
JOIN user_role on user_role.user_id = user.id
JOIN role on role.id = user_role.role_id
WHERE role.role_name = 'ROLE_ADMIN'
?

Use this:
$qb = $this->createQueryBuilder('u')
->innerJoin('u.roles', 'r')
->where('r.roleName = :roleNameParameter')
->setParameter('roleNameParameter', 'ROLE_ADMIN');
I'm assuming you mapped the column role_name as property roleName in the entity.

Related

Transpose a raw SQL query with doctrine

I'm pretty new to Doctrine ORM and I'm trying to transform a raw SQL query with doctrine to get an array of Entities.
Basically I want to get one of more course entities where a user is indirectly registered. The course id is in a traineeship table. The traineeship id and the user id are in a registration table.
Here's my SQL query :
SELECT * FROM course c
LEFT JOIN traineeship t
ON c.id = t.courseId
LEFT JOIN registration r
ON t.id = r.traineeshipId
WHERE r.userId = 2681;
Here's what I try to do with doctrine :
return $this->createQueryBuilder('c')
->andWhere('t.course = c')
->leftJoin('c.traineeships', 't')
->andWhere('r.traineeship = t')
->leftJoin('t.registrations', 'r')
->andWhere('r.id = :user')
->setParameter('user', $user)
->getQuery()
->execute();
With my raw SQL query, I get two expected results with a given id. With the generated query by doctrine, I get only one result. So I guess my doctrine use is bad.
(Doctrine relations :
Course OneToMany Traineeship
Traineeship OneToMany Registration
Registration ManyToOne User)
From the raw SQL I would expect the following query:
return $this->createQueryBuilder('c')
->leftJoin('c.traineeships', 't')
->leftJoin('t.registrations', 'r')
->andWhere('r.user = :user')
->setParameter('user', $user)
->getQuery()
->getResult();
Please note that I'm using ->andWhere('r.user = :user') instead of ->andWhere('r.id = :user') as I assume that registration.id holds the id of the registration but not the id of the user. In my query I furthermore assume that registration has an attribute user which holds a reference to the user.
The query should return an array of course entities.

laravel query builder: join dependent on primary content

I'm using luman and Database Query Builder to fetch full user info from database.
First, Please Take a lock at my database structure:
I have a table called users and a series of other tables that are related to user groups (Ex: secretaries, patients, doctors and admins) which stores additional information about the users.
Also To determine user access, I have a level column on user table which can have one of this value as enum: 'admin', 'doctor', 'secretary', 'patient'.
So, I want to get this information using one query by join and select.
My training code is something like this:
$userInfo = User::where("userID", $userID)
->limit(1)
->join('[GROUP_TABLE_NAME]', function ($join) {
$join->on('user.userID', '=', '[GROUP_TABLE_NAME]' .'.'.
'[GROUP_NAME]' . 'ID');
})
->get();
The GROUP_NAME comes from level column on user table and the GROUP_TABLE_NAME can be built based on the GROUP_NAME value(Ex: ['secretary' => 'secretaries' , 'patient' => 'patients' , ...]).
Any idea to handle the join structure using laravel query builder?
First you should be aware of the fact that this code architecture is not convenient and not easy to understand for other developers.
SQL
You can achieve your goal by using union and join.
Just convert this query for laravel builder or use it directly with DB::statement: select users.*, infos.info from users left join (( select secretaries.* from secretaries ) UNION (select doctors.* from doctors)) infos ON users.id = infos.user_id where users.id=?.
But
The easiest way to do it is to fetch info in two queries, both indexed and fast: user from users by primary key and then info by indexed field user_id in it. Create Doctorinfo, Admininfo models and correspondent migrations. So user class can be smth like this:
public function getInfo() {
switch($this->level) {
'doctor':
return $this->doctorinfo;
...
}
}
private function doctorinfo() {
$this->hasOne('App\Doctorinfo');
}
Builder
You can use left join to join all sub tables as well. The following builder selects info column.
User::where("userID", $userID)->limit(1)
->leftJoin('patients', 'users.id', '=', 'patients.user_id')
->leftJoin('doctors', 'users.id', '=', 'doctors.user_id')
->leftJoin('admins', 'users.id', '=', 'admins.user_id')
->select('users.*', DB::raw('IF(users.level="admin", admins.info, (IF users.level="doctors", doctors.info, patients.info))'))

Laravel 5 - how to perform left join on same table

In my application, there's two tables at the moment: users and employees. The latter is simply a profile table for the boilerplate users table that ships with Laravel.
I have two foreign keys inside of employees, one is user_id that points to id on users table. So basically, the profile data of the registered user. The second foreign key in employees is manager_id. That is the id of the person who is the manager of an employee. Some users are managers and have a blank manager_id field.
I want to retrieve the row from my Employee model where the user_id matches the manager_idand then send them all to my view, where they will see the first_name and last_name of the manager. At the moment I see the manager ID instead of their name.
Sample LeftJoin =>
DB::connection('testing')->table('table1 as t1')
->select('t1.column, t2.column')
->leftJoin('table2 as t2','t2.client_id','=','t1.id')
->get();
Try some thing like this:
$users = DB::table('users t1')
->join('users t2', 't1.id', '=', 't2.user_id')
->select('your column list')
->get();
// here we are creating 2 aliases for users table which make it self join
In your Employees model add the following method:
public function manager()
{
return $this->hasOne('App\User', 'id', 'manager_id');
}
You can then get the name of the Manager like this:
$employee = \App\Employee::find(1);
$manager = $employee->manager()->first();
$manager_name = $manager->name;

Find auctions for a user in eloquent

I'm working with L5 and elequent
My table structure is..
users
id
other field
auctions
id
other field
lots
id
auction_id
other field
lot_user
lot_id
user_id
I want to find auctions for a user.
How can i do this?
$user = User::find($id);
$auctions = $user->auctions();
I have got an idea to do this with eloquent..
$auctions = $user->lots()
->join('auctions','auctions.id','=','lots.id')
->select(['auctions.*'])
->get();
I'm not sure Eloquent is going to be very efficient here, but you could do something like :
In your User(s) class, you need to define a many-many relationship like :
public function lots()
{
return $this->belongsToMany('App\Lot');
}
In your Lot(s) class, you need to define the inverse of a one-to-many relationship like:
public function auctions()
{
return $this->belongsTo('App\Auction')
}
Then, to get Lots for a user, you'd do something like :
$user->lots();
To get auctions, you'd need to loop over lots and call $lot->auctions() for each one, and then filter by id to get the unique auctions.
This is a case where it would probably be easier to use the DB facade to built a query instead of just trying to use Eloquent.
About DB facade.
Raw query will looks like this:
SELECT * FROM auctions AS a
INNER JOIN lots AS l ON (l.auction_id = a.id)
INNER JOIN lot_user AS lu ON (lu.lot_id = l.id AND lu.user_id = $findUserId)
GROUP BY a.id
And using query-builder you can do it like this:
DB::table('auctions')
->join('lots', 'lots.auction_id', '=', 'auctions.id')
->join('lot_user', function ($join) {
$join->on('lot_user.lot_id', '=', 'lots.id')
->where('lot_user.user_id', '=', $findUserId);
})
->groupBy('auctions.id')
->get();

How to get list depending on condition over the relation in Laravel

I have a User and Role entity, with a Many-to-Many relation. In my role table I have a column named visible that stores a boolean value. If a user has a role (at least one amongst others) which is not visible I want to exclude him from the result set.
I can get the related roles in my query and just iterate through them and find out that way, but what I really would like to do is just have my query only return the users with all roles that are visible instead of filtering the query afterwards.
Something like :
public function scopeVisible($query)
{
$query->whereHas('roles', function($q){
// and here i want to find that thing out
})
}
Lets first identify the tables we're working with
user
id - integer
name - string
role
id - integer
visible - bool in the form of SMALLINT 0 or 1
user_role
user_id - integer
role_id - integer
We'll solve it in plain old SQL first. Like this:
SELECT * FROM user
INNER JOIN user_role ON user_role.user_id = user.id
INNER JOIN role ON role.id = user_role.role_id
GROUP BY user.id
HAVING COUNT(user_role.user_id) = SUM(role.visible);
The key here is the HAVING statement. We do a count on the amount of roles a user has and then we do a sum on the visible column. If all roles are visible then the amount of relations a user has to roles will equal the amount of visible roles.
Now to convert this into Laravel speak. In order to do this we'll have to use Laravel's query builder.
$visibleUsers = DB::table('user')
->join('user_role', 'user_role.user_id', '=', 'user.id')
->join('role', 'role.id', '=', 'user_role.role_id')
->groupBy('user.id')
->havingRaw('COUNT(`user_role`.`user_id`) = SUM(`role`.`visible`)')
->get();
I'm not familiar with whereHas but I would try something like this
public function scopeVisible($query)
{
return $query->whereHas('roles', function($q) {
$q->where('visible', '=', true);
});
}
And then use it: $usersVisible = User::visible()->get();

Categories