Order by on relationship field - php

How can I order a result set by something on its relationship?
I am trying to get the Eloquent equivalent of this:
SELECT * FROM users INNER JOIN roles ON users.role_id = roles.id ORDER BY roles.label DESC
Here is what I am trying (based on the documentation):
$order = 'desc';
$users = User::with(['role' => function ($q) use ($order) {
$q->orderBy('label', $order);
}])->paginate(10);
but it doesn't order them properly. What am I doing wrong?
EDIT: Not sure if showing the model relationship is relevant but here it is:
public function role()
{
return $this->belongsTo(Role::class);
}

You should do it with join
User::with('role')->join('role', 'role.id', '=', 'users.id')->orderBy('role.label', $order)->paginate(10);

Related

How to write sub queries in laravel 8?

SELECT
posts.id,
(select count(*) from post_likes where post_id = 13 and user_id = 12) as post_like
FROM
posts
LIMIT 5
How to write this query in Laravel query builder?
If your ORM models are defined (and you have both Post and PostLike models), create a relationship in your Post.php model (if not already), like:
public function likes(){
return $this->hasMany(PostLike::class);
}
Then if you only need the count, try something like:
$userId = 12;
$postList = Post::query()
->whereId(13)
->withCount(['likes', 'likes AS post_like' => function ($query) use($userId) {
$query->where('user_id', '=', $userId);
}])
->limit(5)
->get();
// Then do something with result.
foreach ($postList as $post) {
$count = $post['post_like'];
}
Note that above we use post_like alias, and limit to user_id, just to much OP requirements; Else we could simply set likes_count to the number of relations, like:
->withCount('likes')
But you could use relationship for subquery with the whereHas(...) eloquent method, like:
Post::query()->whereHas('likes', function($query){
$query->where(...your statements for sub query go there);
}, '>', 4)->limit(5)->get(); //Select where more than 4 relation found with given parameters
For more see: https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence

Laravel 5.8 Eloquent query without using DB::raw() -- get rows based on latest value of many to many relationship

My goal is to select all issues that have been marked as false-positive. The issues are connected to Status via a ManyToMany relationship with pivot table IssuesStatus. The current status of an issue is determined by the value of the status column on the Status table.
I've come up with a solution that works but seems somehow suspect. I'm looking for a way to rewrite the query using Eloquent query builder without relying on the DB::raw() method.
public function getFalsePositivesAttribute() {
return Issue::where(DB::raw(
'( select `status` '.
'from `issues-status` '.
'left join `status` on `status`.id = `issues-status`.status_id '.
'where `issues-status`.issue_id = `issues`.id '.
'order by `issues-status`.id desc limit 1 )'
), '=', 'false-positive')->get();
}
Example of desired SQL query:
SELECT
`Issues`.id
FROM
`issues` AS `Issues`
LEFT JOIN
`issues-status` `IssueStatus` on `Issues`.id = `IssueStatus`.issue_id
LEFT JOIN
`status` as `StatusTable` on `IssueStatus`.status_id = `StatusTable`.id
WHERE
`Issues`.report_id = 2
AND
(
SELECT
`status`
FROM
`issues-status` `IssueStatus`
LEFT JOIN
`status` `StatusTable` on `StatusTable`.id = `IssueStatus`.status_id
WHERE
`IssueStatus`.issue_id = `Issues`.id
ORDER BY
`IssueStatus`.id desc
LIMIT 1
) = 'false-positive'
GROUP BY
`Issues`.id
Models:
class Issue extends Model {
...
public function status() {
return $this->belongsToMany( Status::class, 'issues-status')
->withTimestamps()
->withPivot('note');
}
...
}
class Status extends Model {
...
public function issues() {
return $this->hasMany(Issue::class);
}
...
}
Tables:
Issues:
id - identity
Status
id - identity
status - string
IssueStatus
id - identity
issue_id - relation to Issues
status_id - relation to Status
created_at - timestamp
note - text
If i understood correctly, you want to fetch the issue where the latest status is equals to something.
Add a latestStatus function to your Issues model:
public function latestStatus()
{
return $this->hasMany(IssueStatus::class)->latest();
}
Now, swap your status function with this one:
public function status() {
return $this->belongsToMany( Status::class, 'issues-status')
->withTimestamps()
->withPivot('note');
}
Then:
//Get all issues which has the desired status
Issue::whereHas('status', function($query){
$query->where('id', $desiredStatusId);
})
->get()
->reject(function($issue){
//filter the collection by the latest issue status,
// and reject those who does not have the desired latest status;
return $issue->latestStatus()->first()->status_id != $desiredStatusId;
});
Hope it helps.

Issue with group by laravel and sql

Firstly I have problem which count products which are sold every day. In sql I have query
select product_name, sum(quantity) as quantity from invoice_product
join invoices on invoices.id = invoice_product.invoice_id
join products on products.id = invoice_product.product_id
where invoices.issued_at = '2019-05-16'
and products.`made_by_us` = 1
group by product_name
It show me interesting for me information but I used product_name to make group by but I should use product_id - I need show name too but I don't know how to do it.
Secondly I want to use it in Laravel so maybe someone know which is it possible to do it in Eloquent?
Thank you in advance :)
I would go with withCount() combined with select(DB::raw()), like this:
$products = Product::withCount(['invoices as quantity' => function ($query) {
$query->select(DB::raw('sum(quantity)'));
}])->get();
Then, you can access each quantity sum like this:
$quantity = $products->first()->quantity;
You would need to update your model relationships to achieve that.
Models:
InvoiceProduct Model
class InvoiceProduct extends Model
{
protected $table = 'invoice_product';
protected $guarded = [
'id',
];
}
public function invoice()
{
return $this->belongsTo('App\Invoice'); // Assuming `Invoice` Model is directly in app folder
}
public function product()
{
return $this->belongsTo('App\Product'); // Assuming `Product` Model is directly in app folder
}
Controller:
$full_query = InvoiceProduct::whereHas('invoice', function ($query) {
return $query->where('issued_at', '2019-05-16');
})->whereHas('product', function ($query) {
return $query->where('made_by_us', 1);
});
$product_names = $full_query->get(['product_name']);
$total_quantities = $full_query->sum('quantity');

Eloquent Users in Same Group

I have the usual users, groups and group_user tables. I know the raw SQL that I want:
SELECT group_user.group_id, users.* FROM users
INNER JOIN group_user ON users.id = group_user.user_id
WHERE group_user.group_id IN
(SELECT group_id FROM group_user WHERE user_id=?)
ORDER BY group_user.group_id;
where ? is replaced current user's id.
but, I want to use Eloquent (outside of laravel) for this. I have tried using a User model with a groups method
public function groups() {
return $this->belongsToMany('\Smawt\User\Group');
}
and a Membership model with a users method
public function users($group_id) {
return $this->where('group_id', '=', $group_id)->get();
}
and then I loop through the groups and then loop through all its members. Finally, I append all the data to get one $users object at the end, to pass through to my view.
$thisMembership = new Membership;
$myGroups = $app->auth->groups;
$users = [];
foreach ($myGroups as $myGroup) {
foreach ($thisMembership->users($myGroup->id) as $myUser) {
$thisUser = $app->user->where('id', '=', $myUser->user_id)->first();
$thisUser->group_id = $myGroup->id;
array_push($users, $thisUser);
}
}
Then in my view I loop through my $users as normal. Although this method works, it will not be very efficient as I am unable to work out how to use Eager Loading with it.
Is there a simpler more 'Eloquent' way of getting an object of users who are in the same group as the current user? I don't want just want a list, or an array, as I want to use the other methods defined in my user model.
I have been able to construct the following Eloquent query, although I am not sure this is the 'best' way:
$users = User::join('group_user', 'users.id', '=', 'group_user.user_id')
->whereIn('group_user.group_id', function($query) {
$query->select('group_id')
->from('group_user')
->where('group_user.user_id', '=', $_SESSION['user_id']);
})->orderBy('group_id', 'asc')
->get();
The Eloquent way for the relationship and use of it:
Tables: users, groups
Models: User Group
Pivot Table: group_user (id, user_id, group_id)
In User Model:
public function groups()
{
// pivot table name and related field
// names are optional here in this case
return $this->belongsToMany('Group');
}
In Group Model:
public function users()
{
// pivot table name and related field
// names are optional here in this case
return $this->belongsToMany('User');
}
Use Case (Example):
$usersWithGroup = User::with('groups')->find(1); // or get()
$groupWithUsers = Group::with('users')->find(1); // or get()
For more information check Eloquent section on documentation.
Update:
If user belongsto any group
$usersWithGroup = User::has('groups')->with('groups')->find(1);
Also using if a user belongs to specific group:
$someGroup = 'general';
$usersWithGroup = User::whereHas('groups', function($q) use($someGroup) {
$q->where('group_name', $someGroup);
})
->with('groups')->find(1);

Laravel Eloquent Inner Join

This is the query I currently using for Inner Join in my laravel app:
public function getReservationListAPI()
{
$id = JWTAuth::parseToken()->authenticate()->id;
$result = DB::table('pporders AS ord')
->join('products AS pd', 'ord.product_id', '=', 'pd.id')
->select('ord.*')
->where('pd.user_id',$id)
->get();
dd($result);
}
How can I wrote this query in Eloquent form? Thanks!!
EDIT
Relationship:
Product hasMany Order
Order belongsTo Product
User hasMany Product
Product belongsTo User
It depends on your relation, but something like this:
Pporders::with(['product' => function($q) use($id) {
$q->where('user_id', $id);
}])->get();
But it won't make the same sql query what you described, but gives the same result.

Categories