Convert Raw SQL Query to Laravel Eloquent left outer join - mysql

can anyone help me turn the query in to Eloquent?
Model Orders::
Model TaskCard::
select
ifnull(data, o.created_at) as dataZlecenia,o.id, o.nazwa
from
orders o
left outer join
(select max(created_at) as data,order_id from task_cards
group by order_id) as a
on a.order_id=o.id
order by dataZlecenia desc

Seems like you have wrong namespace as well, try using App\ModelName instead of App\Models\ModelName. And use english for naming functions/classes :)
Check if your foreign keys are used correctly. If you want it to work just out of the box, orders table should have client_id as foreign key and id as primary key, while clients table has to have primary key id. Then this solution will work:
public function order(){
return $this->hasOne('App\Order');
}
via https://laravel.com/docs/5.8/eloquent-relationships
EDIT:
for better understanding :) in create_orders_table migration:
$table->bigIncrements('id');
$table->unsignedBigInteger('client_id');
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
Moreover I can't see a reason why your client can have just one order. I'd use in Client model:
public function orders(){
return $this->hasMany('App\Order');
}
And in Order model:
public function client(){
return $this->belongsTo('App\Client');
}
This way your client will be able to have multiple orders and an order will have just one assigned client.

Related

Couldn't use the whereNotExists clause correctly in Laravel 8 Eloquent

I have 2 tables, one with different users, and the second table is an invoice table called "factures" and has a foreign key of userid, I called it client_id, which I am trying to get is the number of clients created_by a certain administrator and who have no invoices yet, here is what I tried:
$clients = User::select('id')
->where([['created_by',$membre_id],['role','Client']])
->orWhere([['updated_by',$membre_id],['role','Client']])
->whereNotExists(function($query)
{
$query->select(DB::raw('client_id'))
->from('factures')
->where('created_by',$member_id);
})->get();
but this query gives me all clients created_by $member_id without exception.
What is wrong with my query?
Did you try the following:
$clients = User::select('id')
->where(function($query) use($member_id){
$query->where([['created_by',$membre_id],['role','Client']])
->orWhere([['updated_by',$membre_id],['role','Client']])
})
->whereNotExists(function($query) use($member_id){
$query->select(DB::raw('client_id'))
->from('factures')
->where('created_by',$member_id);
})
->get();
}
This answer applied the OR condition only between the first two conditions (created_by and updated_by) and its result is AND with the third condition.

NodeJS + MySQL, UPDATE with a JOIN, ambiguous column name

First of all, I understand why I'm getting this error message, and I know of a way to solve it, but I'm hoping for something more efficient than what I have in mind. Here is basically what I have:
UPDATE customer c
JOIN customer d ON c.customer_id = d.parent_customer_id
SET ?
WHERE d.customer_type = "Big Cheese";
So, the data being fed in to the "?" parameter looks like this:
{"customer_id": 10, "customer_name": "Cheese-It", ... }
The problem is, since I'm joining on a table that is basically itself, all of the columns have the same name. The only way I know how to fix this is edit the JSON and prefix all of the fields with the alias it needs:
{"c.customer_id": 10, "c.customer_name": "Cheese-It", ... }
I was hoping for a more elegant way of going about this. Is there a way to refactor my SQL so that it knows which table alias I want to update? Any ideas?
A subquery will do what you are wanting, but it's actually less efficient, as subqueries inside the WHERE clause are generally performance killers. I feel like you have to be parsing the JSON into SQL, so I would simply add the alias at that point.
Anyway, for reference, here's how you can refactor the SQL to not need an alias:
UPDATE customer
SET ?
WHERE customer_id IN (
SELECT c.customer_id
FROM customer c
JOIN customer d ON c.customer_id = d.parent_customer_id
WHERE d.customer_type = 'Big Cheese'
);
NOTE: this is untested
EDIT:
On second thought, an EXISTS clause would be slightly better for performance:
UPDATE customer c
SET ?
WHERE EXISTS (
SELECT 1
FROM customer d
WHERE d.parent_customer_id = c.customer_id
AND d.customer_type = 'Big Cheese'
);
Either way should work. As long as you don't have a JOIN in the update, there is only one table the SET columns can reference, so you will avoid the ambiguous column name error.
I know this is an older question, but I found a better solution that doesn't have the performance hit. You can add the alias to your property names in the object you're updating.
Here is the helper function to translate the standard property names with an alias.
const allowUpdate = ['name']
function addUpdateAlias(updated, alias) {
let validUpdate = {}
for (let p in updated) {
if (allowUpdate.indexOf(p) > -1) {
validUpdate[`${alias}.${p}`] = updated[p]
}
}
return validUpdate;
}
Now wrap the object you want to update with the function above and the alias is applied in the update!
Your parameters would then be: [addUpdateAlias(customer, 'c')] to pass into your original query.

Join Tables with sum in sql query

It is showing Sum(with table name) is not valid. Kindly help:
Modws.DisplayDataGrid(dgvClosingBalance,
"Select
Invoice.Customer, Invoice.Sum(Total),
RptTempTable.Sum(INVOICETOTAL), RptTempTable.Sum(CNTOTAL),
RptTempTable.Sum(DEBITTOTAL), RptTempTable.Sum(RECEIPTTOTAL)
From Invoice
inner join RptTempTable on Invoice.Customer = RptTempTable.Customer")
RptTempTable.Sum(INVOICETOTAL) should be Sum(RptTempTable.INVOICETOTAL)
The same goes for the other calls to sum()
The table prefix belongs to the column name not the function call.
MySQL will accept this invalid SQL and will return "inderminate" (aka "random") values instead.
To understand the implications of MySQL's "loose" (aka "sloppy") group by implementation you might want to read these articles:
http://www.mysqlperformanceblog.com/2006/09/06/wrong-group-by-makes-your-queries-fragile/
http://rpbouman.blogspot.de/2007/05/debunking-group-by-myths.html

laravel 4 - join latest result in group by query

This query gives a pagination of all 'albums' with a picture and description for each. Now I am trying to get always the latest picture of each album.
I have tried to add a second orderBy('pics.created_at') , but that did not work. I think I need some kind of subquery but don't know how.
$query = AlbumPic::select(DB::raw('COUNT(pics.id) as picscount,
pics.url,
pics.user_id,
pics.created_at,
albums.id as album_id,
albums.title,
albums.text,
users.username'))
->join('albums','albums.id','=','album_pic.album_id')
->join('pics','pics.id','=','album_pic.pic_id')
->join('users','users.id','=','pics.user_id');
if(!is_null($user_id))
$query->where('album_pic.user_id',$user_id);
$albums = $query->groupBy('albums.id')
->orderBy('albums.created_at','desc')
->paginate(20);
edit
I made a mistake. I don't have created_at and updated_at in the album_pic table .
So my 'Album' - model/relations are now like this:
public function pics()
{
return $this->belongsToMany('Pic');
}
public function latestPic()
{
return $this->belongsToMany('Pic')->latest('pics.created_at');
}
And the query now looks like this:
$q = Album::with('pics')->with('latestPic.users');
if(!is_null($user_id))
$q->where('albums.user_id',$user_id);
$albums = $q->orderBy('albums.created_at','desc')
->paginate(20);
This works. Only thing I would like to improve is the way, the pictures per album are counted. Now I get all with with('pics') and then do a count($album->pics) in the view. If there is a way to not load everything, but only count the pictures, it would be nice.
You need to get the MAX(created_at) inside a subquery; see MySQL select MAX(datetime) not returning max value for example.
Really, though, if you're doing this in Laravel, it would be better to set these all up as relations and leverage the power of Eloquent. Then, you can define a relationship for pictures that uses ->latest() to return the most recent. See laravel eloquent query group by last id for an example (which uses one table, but the principle is the same for multiple tables).
Here's how you could set this up using Eloquent relations:
User model (User.php)
class User extends Eloquent {
public function albums()
{
return $this->hasMany('Album');
}
}
Album model (Album.php)
class Album extends Eloquent {
public function pics()
{
return $this->belongsToMany('Pic');
}
public function latestPic()
{
return $this->belongsToMany('Pic')->latest('album_pic.created_at');
}
}
Because you have a many-to-many relationship between albums and pics, in the latestPic() relation, you must specify the album_pic.created_at field for latest()—since we are actually interested in the order of entries in the pivot table, rather than in the pics table.
Finally, link this all together. For example, for a user with id of 1:
$albums = User::find(1)->albums()->with('pics')->with('latestPic')->paginate(20);
foreach($albums as $album) {
echo('<br>Album:');
var_dump($album->title);
echo('All pics:');
foreach($album->pics as $pic) {
var_dump($pic->url);
}
echo('Latest pic:');
$latestPic = $album->latestPic->first();
if ($latestPic) {
var_dump($latestPic->url);
}
}
Note that we are eager loading the pics and latestPic to reduce the number on calls to the database. Also note that accessing the $latestPic->url is wrapped in an if statement, otherwise albums that do not have any photos will throw an error since $album->latestPic would return null.
As #cedie correctly noted, Laravel doesn't handle pagination all that efficiently when using a groupBy statement, but that shouldn't be a problem in this case. The underlying queries do not use groupBy, so you should be save to use ->paginate(20).
Try using this in your select query:
max(pics.created_at) as created_at
instead of this:
pics.created_at
So your code should look like this:
AlbumPic::select(DB::raw('COUNT(pics.id) as picscount,
pics.url,
pics.user_id,
max(pics.created_at) as created_at,
albums.id as album_id,
albums.title,
albums.text,
users.username'))
Perhaps ypu can figure out how to adapt this for your purposes...
SELECT ap.*
, p.*
FROM album_pic ap
JOIN pics p
ON p.id = ap.pic_id
JOIN
( SELECT ap.*
, MAX(p.created_at) max_created_at
FROM album_pics ap
JOIN p.*
ON p.id = ap.pic_id
) x
ON x.album_id = ap.album_id
AND x.max_created_at = p.created_at;

Symfony2 & Doctrine: Order entites by foreign attribute

I would like to use a select to get an array of entities with Doctrines QueryBuilder. But I need an ORDER BY which uses a foreign attribute (attribute in a table related with a foreign key). What I would like to write intuitively is something like this:
$repo = $this->getDoctrine()->getRepository('MyBundle:relation_table');
$query = $repo->createQueryBuilder('r')
->orderBy('r.fevent.date', 'DESC')
->getQuery();
Which, not surprisingly, doesn't work. In SQL my SELECT looks like this:
SELECT r.* FROM relation_table AS r
INNER JOIN events AS e
ON e.IDevent = r.Fevent
ORDER BY e.date
But I also need Doctrine to give me the entity-object back. I think of two possible solutions:
Use the QueryBuilder to create an INNER JOIN, or
Create a free SQL Statement, same as above, and tell Doctrine somehow to create an entity object with its results.
Any hints? Thanks.
You need to join the entity you want to order with:
$query = $repo->createQueryBuilder('r')
->join('r.fevent', 'f')
->orderBy('f.date', 'DESC')
->getQuery()
;
Joining both tables in your Doctrine Query (the first of your proposed solutions) is pretty straigtforward. Take a look here: http://symfony.com/doc/2.0/book/doctrine.html#joining-to-related-records
A similar question was answered here: Doctrine 2: How to search for an entity by its association's value?