Laravel eloquent sort by role name on relationship model - php

I'm stuck with a problem where I have to sort / order a collection of models by their relationship's data.
I've got it setup like this:
Models:
User, Team, TeamUser, Role
The TeamUser model is a pivot model / table (containing user_id and team_id.
If it's worth mentioning I am also using spatie/laravel-permissions for the roles.
How would I go forth when I want to sort the users in a team by their role.name?
I'm talking about the users() relation in the Team model (see further down for code sample).
Some users have the role team-leader and most have the role team-seller. I've tried doing a ordinary ..->sortBy('role.name') but that doesn't seem to work. Thanks in advance if anyone could help me out.
User.php
/**
* Team relation
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function team()
{
return $this->belongsToMany('App\Team', 'team_users', 'user_id', 'team_id');
}
Team.php
/**
* User relation
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users()
{
return $this->belongsToMany('App\User', 'team_users', 'team_id', 'user_id')->withTimestamps();
}

if you want to order the result based on nested relation column, you must use a chain of joins:
$values = Team::query()
->leftJoin('users', 'users.team_id', '=', 'teams.id')
->leftJoin('model_has_roles', function ($join) {
$join->on('model_has_roles.model_id', '=', 'users.id')
->where('model_has_roles.model_type', '=', 'app\Models\User');
})
->leftJoin('roles', 'roles.id', '=', 'model_has_roles.role_id')
->orderBy('roles.name')
->get();
i have tried it, it work just fine.
please note that if you want to order by multiple columns you could add 'orderBy' clause as much as you want:
->orderBy('roles.name', 'DESC')->orderby('teams.name', 'ASC') //... ext

Related

Laravel - Four levels deep relationship

I am trying to map a relationship between the User and Order Model within Order Model.
An order has one project. The project has one team and the team has one owner/user
The relationship is four levels deep like following:
order > project > team > user
My database tables are as follows:
App\Order
id,
project_id
App\Project
id,
name,
team_id
App\Team
id
name
user_id
App\User
id
name
My Order Model:
class Order extends Model{
function customer(){
// trying to map the relationship here
}
}
I would like to access the customer like following:
$order = Order::find(1);
echo $order->customer->name;
Could you please tell me how to map the above-mentioned relationship?
You can get it by including joins in your query like:
$data = Order::select('orders.*')
->join('projects', 'orders.project_id', '=', 'products.id')
->join('teams', 'projects.projects_id', '=', 'teams.id')
->join('users', 'teams.teams_id', '=', 'users.id')
->get();
For Eloquent you can use this package.
You can define deeper relationships :
Order → belongsTo → Project → belongsTo → Team → belongsTo → User :
class Order extends Model
{
use \Znck\Eloquent\Traits\BelongsToThrough;
public function customer()
{
return $this->belongsToThrough('App\User', ['App\Team', 'App\Project']);
}
}
Another way would be to define a belongsTo in your model by adding joins to directly reach users table
class Order extends Model
{
public function customer()
{
return $this->belongsTo(Project::class, 'project_id')
->join('team as t', 't.id', '=', 'projects.team_id')
->join('users as u', 'u.id', '=', 't.user_id')
->select(['u.id as uid','u.name','projects.id']);
}
}
And then you can directly get the customer information from order object
$order = Order::find(1);
$order->customer->name;
OR
Via eager loding
$orders = Order::with('customer')->get();

Query a model using a relationship

I have a model called unit that has this relationship
/**
* Get the users associated with the unit
*/
public function users()
{
return $this->hasMany('App\Models\User\UserData');
}
In the UserData model there is a column called user_id which I am trying to put in my condition in my query. I am trying to do a query like this
Unit::where('user_id', Auth::id())->first()
but there is no user_id column in the Unit table, only though the users relationship
Ended up doing this
Unit::whereHas('users', function($q) {
$q->where('user_id', Auth::id());
})->first();

Property [users] does not exist on this collection instance

This error has been posted here several times but I'm encountering something a little different. I have two Tables named users and user_type. And users uses a foreign key from user_type. I have to fetch all users and their types, I'm using Laravel's Eloquent ORM to define relationships, this is a one to one relation.
Users Model:
/**
* Get the user type that user has.
*/
public function users(){
return $this->belongsTo('App\Models\UserType', 'ut_id', 'id');
}
UserType Model:
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'ut_id';
/**
* Get the user associated with the user type.
*/
public function users(){
return $this->hasOne('App\Models\Users', 'user_type_id', $this->primaryKey);
}
Fetching Controller:
$users = Users::all()->users;
According to Laravel ORM one-to-one I can access this method as a property, but it's showing me the defined error. I've also tried to access it as a method but it's saying:
Method Illuminate\Database\Eloquent\Collection::users does not exist.
I've also tried to fetch them by join() but it's returning only a few users, I don't know why:
$users = Users::where('id', '>', '0')
->join('user_type', 'user_type.ut_id', '=', 'users.id')
->select([
'user_type.ut_name',
'users.*'
])->get();
Can someone tell me what I'm doing wrong?
P.s: I just want to show all the users with their respective types
You had missed the exact foreign key between your users table and usertypes table.
First, you defined the that the foreign key of your user table is 'ut_id' base of what you had in your belongsTo relationship. On this one
/**
* Get the user type that user has.
*/
public function users(){
return $this->belongsTo('App\Models\UserType', 'ut_id', 'id');
}
Second is that, in your user type model, you used a foreign key to user table named 'user_type_id', which is at first you named it as 'ut_id' in your users table. On this one
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'ut_id';
/**
* Get the user associated with the user type.
*/
public function users(){
return $this->hasOne('App\Models\Users', 'user_type_id', $primaryKey);
}
You have to match this foreign keys you used to solve your problem.
Now, to fetch your all user with their types, your query should look like this.
$users = Users::with('users')->get();
assuming that your user table has this relationship
public function users(){
return $this->belongsTo('App\Models\UserType', 'ut_id', 'id');
}
and your user types model has this relationshio
public function users(){
return $this->hasOne('App\Models\Users', 'ut_id', $this->primaryKey);
}
in User model
public function type(){
return $this->hasOne(UserType::class, 'id');
}
in UserType Model
public function users(){
return $this->belongsToMany(User::class, 'id');
}
Your relations seem to be wrong.
Users links to UserType with id to ut_id, but userType links to User with id to user_type_id
I'm pretty sure that it should be this for userTypes
/**
* Get the user associated with the user type.
*/
public function users(){
return $this->hasMany('App\Models\Users', 'id', 'user_type_id');
}
and then this for Users
public function userTypes(){
return $this->belongsTo('App\Models\UserType', 'user_type_id', 'id');
}
Then you can eager load for all the results you want...
$users = Users::where('id', '>', '0')
->with('userTypes')
->get();

Transform Raw Union Query into Eloquent Relationship

i've a question about Laravel Relationships.
I've a raw union query and i want to translate into a eloquent relationship.
First of all... i have 4 tables involved:
roles
id|name
permissions
id|name|code|description
permission_role
role_id|permission_id
users
id|...........|role_id
permission_user
user_id|permission_id
Inside my User model, i've this method:
/**
* #TODO: Transform this into a eloquent relationship
*
* #return Collection
*/
public function permissions()
{
$query = sprintf('
(
SELECT permissions.*
FROM permissions
INNER JOIN permission_role ON permission_role.permission_id = permissions.id
WHERE permission_role.role_id = %s
) UNION
(
SELECT permissions.*
FROM permissions
INNER JOIN permission_user ON permission_user.permission_id = permissions.id
WHERE permission_user.user_id = %s
)', $this->role_id, $this->id);
return Permission::hydrate(DB::select($query));
}
The point is, i want to fetch all permissions by the role that the user is associated, and the separated permissions associated to the user.
Can i transform this in some eloquent relationship like hasMany, belongsToMany, etc... ?
The "merge" function in Laravel collection might be able to help you.
The big differnt is that I close off the query with ->get() in advance, and I use merge() instead of union()
// In Controller
public function GetUsersWithPermission()
{
$permissionByRole = User::with('permission_role.permission')->get();
$permissionByUser = User::with('permission_user.permission')->get();
$result = $permissionByRole->merge($permissionByUser);
}
// User Model : get PermissionRole By User
public function permission_role() {
return $this->hasOne('App/Model/permission_role', 'role_id', 'role_id'); }
public function permission_user() {
return $this->hasOne('App/Model/permission_user', 'user_id', 'id'); }
// permission_role Model : get Permissions By Role
public function permission(){
return $this->hasMany('App/Model/Permissions', 'id', 'permission_id'); }
// permission_user Model : get Permissions By User
public function permission(){
return $this->hasMany('App/Model/Permissions', 'id', 'permission_id'); }
Note: I don't have your data so I can't proof it work, but it least it work on my data so should worth your try. and it return all data like: all user details, and permissions so you can use select() function to get Specific Columns.

Laravel Eloquent Fetch all rows and their respective pivot table rows

I have a table of orders, which contain line items, which are stored in a pivot table.
Once all of the line items have been successfully processed, the order will be marked as "processed" and needs to be displayed on the page.
I therefore want to fetch all orders which have been marked as "processed" as well as the line items which are included in their respective orders.
My query looks like this:
$orders = DB::table('order_product')
->join('products', 'order_product.product_id', '=', 'products.product_id')
->join('orders', 'order_product.order_id', '=', 'orders.order_id')
->join('customers', 'orders.customer_id', '=', 'customers.customer_id')
->where('order_product.variation_status', '=', 'dispatch')
->where('orders.store', '!=', 'null')
->groupBy('order_product.order_id')
->get();
return response()->json($orders);
My thinking is to fetch all the pivot table items which are processed and then group the result by order_id but this unfortunately doesn't work.
I receive the following output:
Unfortunately the variation property only contains one line item from the pivot table, instead of two.
Can somebody help as to what I might be doing wrong?
UPDATE
Here are my models
Order.php
/**
* The products that belong to the Order.
*/
public function products()
{
return $this->belongsToMany('App\Product','order_product','order_id','product_id')
->withPivot(['qty', 'variation', 'variation_status'])
->withTimeStamps();
}
/**
* The customer that belongs to the Order.
*/
public function customer()
{
return $this->belongsTo('App\Customer', 'customer_id');
}
Product.php
/**
* The orders that belong to the product.
*/
public function orders()
{
return $this->belongsToMany('App\Order')
->withPivot(['qty', 'variation_status'])
->withTimeStamps();
}
I can't guarantee that this is correct right away because I normally don't work with ->withPivot and have no test environment running yet so this is from the top of my head at the moment. But this might give you an insight of how to handle this usecase.
Let's start with order as a base
Order::get();
Now let's extend this functionality to retrieve an order with a customer and products
Order::with('customer', 'products')->get();
What we've to do now is resolving your where conditions in the above eloquent query:
->where('order_product.variation_status', '=', 'dispatch')
->where('orders.store', '!=', 'null')
What you could do is the following:
Order::with(['products' => function($query){
$query->where('variation_status', 'dispatch');
}, 'customer'])
->where('store','!=','NULL')
->get();

Categories