Laravel ordering results of a left join - laravel

I am trying to replicate the below SQL in Laravels Eloquent query builder.
select a.name, b.note from projects a
left join (select note, project_id from projectnotes order by created_at desc) as b on (b.project_id = a.id)
where projectphase_id = 10 group by a.id;
So far I have:
$projects = Project::leftjoin('projectnotes', function($join)
{
$join->on('projectnotes.project_id', '=', 'projects.id');
})
->where('projectphase_id', '=', '10')
->select(array('projects.*', 'projectnotes.note as note'))
->groupBy('projects.id')
->get()
which works for everything except getting the most recent projectnotes, it just returns the first one entered into the projectnotes table for each project.
I need to get the order by 'created_at' desc into the left join but I don't know how to achieve this.
Any help would be much appreciated.

Your subquery is unnecessary and is just making the entire thing inefficient. To be sure though, make sure this query returns the same results...
SELECT
projects.name,
notes.note
FROM
projects
LEFT JOIN
projectnotes notes on projects.id = notes.project_id
WHERE
projects.projectphase_id = 10
ORDER BY
notes.created_at desc
If it does, that query translated to the query builder looks like this...
$projects = DB::table('projects')
->select('projects.name', 'notes.note')
->join('projectnotes as notes', 'projects.id', '=', 'notes.project_id', 'left')
->where('projects.projectphase_id', '=', '10')
->orderBy('notes.created_at', 'desc')
->get();

Related

SELECT COUNT in WHERE CLAUSE in Eloquent

I know that something similar was here some time ago,but it wasn't the same case and I can't just figure it out on myself.
I need to transform raw SQL query to Eloquent.
This query contains SELECT COUNT in WHERE clause, for simplicity I have this (may has not much sense) query :
SELECT u.column1, u.column2, u.column3, s.column1 FROM users u
LEFT JOIN salary s ON u.id = s.user_id
WHERE
(
SELECT count(cars_id) FROM cars WHERE cars.user_id = u.id
) = 0
AND u.city IN ("London","Paris")
I tried:
$columns = [
'users.column1',
'users.column2',
'users.column3',
'salary.column1'
];
$q = User::select($columns)
->leftJoin('salary', 'salary.user_id', '=', 'users.id')
->whereRaw(" (SELECT COUNT(cars_id) FROM cars WHERE cars.user_id = u.id) = 0 ")
->whereRaw("u.city IN ('London','Paris')")
->get();
But it doesn't return same results as raw SQL (SQL had 161 rows and Eloquent 154 rows).
Maybe you know how to transform this kind of query correctly to Eloquent?
Thanks
Based on your query I believe this should do it:
DB::table('users as u')
->select([ 'u.column1', 'u.column2', 'u.column3', 's.column1'])
->leftJoin('salary as s', 'u.id', '=', 's.user_id')
->leftJoin('cars as c', 'c.user_id', '=', 'u.id')
->whereIn('u.city', ['London', 'Paris'])
->havingRaw('count(c.id) = 0')
->get();
Please let me know.

Convert SQL to Laravel Eloquent Statement

I've been working on a few tables where through a rather complex relationship (that I'm trying to clean up, but I still need reports made from the data through my Laravel).
At the moment, I can pull the data using the following SQL query to my MySQL database:
SELECT
customers.id,
customers.customer_name,
SUM(shipments.balance) AS shipmentBalance
FROM customers
LEFT JOIN shipments
ON customers.id = shipments.bill_to
AND balance > (SELECT IFNULL(SUM(payments_distributions.amount),0)
FROM payments_distributions
WHERE payments_distributions.shipment_id = pro_number)
GROUP BY customers.id, customers.customer_name
ORDER BY shipmentBalance DESC
LIMIT 5;
I'm just not sure how to rewrite it properly into the whereRaw or DB::raw statements that Laravel Eloquent requires, as my previous attempts have failed.
Update
Here is the closest solution I have tried:
DB::table('customers')
->select('customers', DB::raw('SUM(shipments.balance) AS shipmentBalance'))
->leftJoin(
DB::raw('
(select shipments
ON customers.id = shipments.bill_to
AND balance > (SELECT IFNULL(SUM(payments_distributions.amount),0)
FROM payments_distributions
WHERE payments_distributions.shipment_id = pro_number)'))
->groupBy('customers.id')
->orderByRaw('shipmentBalance DESC')
->limit(5)
->get();
Update 2
Edit for Dom:
Using everything as it stands with your answer, I get the following response:
SQLSTATE[42S22]: Column not found: 1054 Unknown column '' in 'on clause' (SQL: select customers.id, customers.customer_name,SUM(s.balance) AS shipmentBalance from `customers` left join `shipments` as `s` on `customers`.`id` = `s`.`bill_to` and s.balance > (SELECT IFNULL(SUM(payments_distributions.amount),0) FROM payments_distributions WHERE payments_distributions.shipment_id = s.pro_number) = `` group by `customers`.`id`, `customers`.`customer_name` order by SUM(s.balance) DESC limit 5)
But if I remove this section, it brings up the page and the customers (though in the wrong order as I have removed one of the necessary components:
$join->on(DB::raw('s.balance >
(SELECT IFNULL(SUM(payments_distributions.amount),0)
FROM payments_distributions
WHERE payments_distributions.shipment_id = s.pro_number)
'));
Is there anything I can provide you with to get this specific statement to work with your entire answer?
Use this:
DB::table('customers')
->select('customers.id', 'customers.customer_name', DB::raw('SUM(shipments.balance) AS shipmentBalance'))
->leftJoin('shipments', function($join) {
$join->on('customers.id', 'shipments.bill_to')
->where('balance', '>', function($query) {
$query->selectRaw('IFNULL(SUM(payments_distributions.amount),0)')
->from('payments_distributions')
->where('payments_distributions.shipment_id', DB::raw('pro_number'));
});
})
->groupBy('customers.id', 'customers.customer_name')
->orderByDesc('shipmentBalance')
->limit(5)
->get();
Without the Models containing relationships or being able to test on this specific project, this is the most eloquent way I can think of performing your task.
The benefit of starting with the Customer model is you will have a laravel collection and can paginate as needed. Also review the eloquent docs, they help you understand all the different options. Hope his helps.
P.S. Start by using your model in your controller or wherever you are placing this query with:
use App\Customer
The query
$theQuery = Customer::select(DB::raw('customers.id, customers.customer_name,SUM(s.balance) AS shipmentBalance'))
->leftJoin('shipments as s', function($join)
{
$join->on('customers.id', '=', 's.bill_to');
$join->on(DB::raw('s.balance >
(SELECT IFNULL(SUM(payments_distributions.amount),0)
FROM payments_distributions
WHERE payments_distributions.shipment_id = s.pro_number)
'));
})
->groupBy('customers.id', 'customers.customer_name')
->orderByRaw('SUM(s.balance) DESC')
->limit(5)
->get();

I want to pass this query to Eloquent in laravel

I'm trying to make a query with eloquent, I try to get all the users who have one or more ads, taking into account that it is a one to many relationship (users to ads).
This query in general does what I want, but I do not know if it is well done and also how to pass it to Eloquent through the User model.
SELECT users.id, COUNT( anuncio.id ) AS 'total' FROM users
INNER JOIN anuncio ON users.id = anuncio.usuario_id WHERE
(SELECT COUNT( * ) FROM anuncio WHERE anuncio.usuario_id = users.id) >0
GROUP BY users.id ORDER BY total DESC
I have tried several ways that only return Builder to me.
For example:
$listas = new User;
$listas = $listas->join('anuncio','users.id','=','anuncio.usuario_id');
$listas = $listas->select(array('users.*', DB::raw('COUNT(anuncio.id) AS total')));
$listas = $listas->where(function($query) use ($listas){
$query->select(DB::raw('COUNT(anuncio.usuario_id)'))
->where('anuncio.usuario_id','=','users.id')
->groupBy('anuncio.usuario_id');
},'>','0');
$listas = $listas->orderBy('total','DESC')->paginate(48);
By any suggestion I will be very grateful.
Try with this
$listas = User::join('anuncio','users.id','=', 'anuncio.usuario_id')
->select('users.id',DB::raw("count(anuncio.id) as total"))
->groupby('users.id')
->having('total', '>', '0')
->orderby('total', 'desc')
->get();
Just use left join for this.
$users = DB::table('users')
->leftJoin('anuncio', 'users.id', '=', 'anuncio.usuario_id')
->get();
So Left Join will only select those result which has any match in anuncio table.

Big raw query to Eloquent

I have the following query:
select users.email, count(login_history.id) actions from users
left join group_memberships ON group_memberships.user_id = users.id
left join groups ON groups.id = group_memberships.group_id
left join organisation ON organisation.group_id = groups.parent_id
right join login_history ON login_history.user_id = users.id
where group_memberships.group_id = 564 AND ((MONTH(login_history.created_at) = MONTH(CURDATE()) AND YEAR(login_history.created_at) = YEAR(CURDATE())))
group by login_history.user_id
As I'd like to use scopes I need this query in Eloquent. I could use selectRaw and whereRaw and that should work, however would it be better to use Eloquent: Relationships for the Users model and this query? Is it even possible?
Any input much appreciated.
All of these db operations are well documented here. Except for maybe the left joins.
You do that like this (with a callback function):
->leftJoin('category_lang', function( $join ) {
$join->on( 'category_lang.category_id', '=', 'do_hta_dirs.hd_pag_ID' );
$join->on( 'category_lang.lang_id', '=', 'do_hta_dirs.hd_dID' );
$join->on( 'category_lang.active', '=', DB::raw( '1' ) );
})

Query builder isn't execute where = on another column

Sorry, my question title isn't very clear but I couldn't figure a way to word this question.
I have the following query builder code.
return self::select(DB::raw('sum(user_points.points) AS points, users.id AS user_id, users.username, users.avatar, users.firstname, users.lastname'))
->join('users', 'users.id', '=', 'user_points.user_id')
$query->where('user_points.artist_id', 0)->orWhere('user_points.artist_id', 'users.favourite_artist_id');
})
->where('user_points.created_at', '>=', $startDate)
->where('user_points.created_at', '<=', $endDate)
->groupBy('users.id')
->orderBy('points', 'DESC')
->orderBy('user_id', 'ASC')
->simplePaginate(100);
It runs ok but is ignore the inner where query, specifically it's ignoring part of this;
$query->where('user_points.artist_id', 0)->orWhere('user_points.artist_id', 'users.favourite_artist_id');
})
It's matching 'user_points.artist_id = 0', but it's not matching the 'user_points.artist_id = users.favourite_artist_id', presumable it's got something to do with the way it's handling the bindings? But I can't seem to find a way to get it to work.
The complete query should end up like this;
SELECT SUM(user_points.points) AS points, users.id AS user_id, users.username, users.avatar, users.firstname, users.lastname
FROM user_points
INNER JOIN users ON users.id = user_points.user_id
WHERE (user_points.artist_id = 0 OR user_points.artist_id = users.favourite_artist_id)
AND user_points.created_at >= '$startDate' AND user_points.created_at <= '$endDate'
GROUP BY user_id
ORDER BY points DESC, user_id ASC
I updated the query builder code to this.
return self::select(DB::raw('sum(user_points.points) AS points, users.id AS user_id, users.username, users.avatar, users.firstname, users.lastname'))
->join('users', 'users.id', '=', 'user_points.user_id')
->where(function($query) {
$query->Where('user_points.artist_id', 0)->orWhere(DB::raw('user_points.artist_id = users.favourite_artist_id'));
})
->where('user_points.created_at', '>=', $startDate)
->where('user_points.created_at', '<=', $endDate)
->groupBy('users.id')
->orderBy('points', 'DESC')
->orderBy('user_id', 'ASC')
->simplePaginate(100);
That didn't work as the final query ended up looking like this.
select sum(user_points.points) AS points, users.id AS user_id, users.username, users.avatar, users.firstname, users.lastname
from `user_points` inner join `users` on `users`.`id` = `user_points`.`user_id` where (`user_points`.`artist_id` = ? or user_points.artist_id = users.favourite_artist_id is null)
and `user_points`.`created_at` >= ?
and `user_points`.`created_at` <= ?
group by `users`.`id`
order by `points` desc, `user_id` asc
You need to add the second where (or) as a raw query. The where method will add the second column as a value so you are actually trying the following:
user_points.artist_id = 'users.favourite_artist_id'
Try the following:
whereRaw("user_points.artist_id = users.favourite_artist_id")

Resources