How to write a natural join on eloquent? - laravel

I'm trying to write a natural joint on eloquent but without success, please could you help me?
So my sql request is:
SELECT * FROM `case_managers` NATURAL JOIN risks
I have did
Case_managers::join('risks')->get()
Thank you :)

You can use 'with' instead of join like
Case_managers::with('risk')->get();
where risk is your model name related to risks table.

Create a relationship in Case_managers model.
public function risks()
{
return $this->hasOne('App\Models\Risk','foreign_key','local_key');
//if there is going to be multiple entries,change `hasOne` as `hasMany`
}
You can access risks as
$res = Case_managers::find($id);
echo $res->risks->risk_field; //risk_field is the column name you want from risks table
//if `hasMany` is the relationship,use `foreach`
foreach($res->risks as $risk)
{
echo $risk->risk_field;
}

Related

Laravel relation with custom join condition

I received quite strange database to work with and I'm trying to create relation on model Invoice like below:
public function order()
{
return $this->hasOneThrough(Order::class, Shipment::class,
'id',
'id',
DB::raw('-order_id'),
Shipment::COL_ORDER_ID
);
}
but it gives me the error array_key_exists(): Argument #1 ($key) must be a valid array offset type.
Of course the problem is in the DB::raw part... As you may have guessed I'm trying to create join condition like -Invoice.order_id = Shipment.id or ABS(Invoice.order_id) = Shpment.id will be fine too because only order_id on the Invoice needs to be converted into positive number.
Any ideas to get it to work with minimal additional code as possible? Any help will be appreciated :)

How can i convert this sql query in a eloquent laravel command?

in sql query this commando do exactly i wanted:
SELECT
v.id,
(
SELECT sv.status_id
FROM status_viagem sv
WHERE sv.viagem_id = v.id
ORDER BY sv.created_at DESC LIMIT 1 ) AS status_id
FROM viagens v
Here is the sql results:
But i have no idea how can i do this using Laravel eloquent
Basically, a viagem entry can has a lot of status, but i need to get each viagem and their last status entry from status_viagem table (the pivot table)
by the way viagem/viagens means travel.
My class mapping:
class Viagem extends Model
{
...
public function status()
{
return $this->belongsToMany('App\Status')->withTimestamps();
}
...
}
class Status extends Model
{
public function viagens()
{
return $this->belongsToMany('App\Viagem')->withTimestamps();
}
}
The belongsToMany at both classes gets me a many-to-many:
can someone help me? thanks
---------- Temporary Solution -------
Thanks for all help guys. In fact i can't find a nice solution using only eloquent.
Step 1/3 - To bypass this situation i first execute the above sql to grab only the viagens under the desired status_id (last status_viagem entry):
$viagens_ids = DB::select(
"SELECT viagem_id FROM (
SELECT
v.id AS viagem_id,
(
SELECT sv.status_id
FROM status_viagem sv
WHERE sv.viagem_id = v.id
ORDER BY sv.id DESC LIMIT 1 ) AS status_id
FROM viagens v
) AS tt
WHERE tt.status_id = {$status->id}"
);
Step 2/3 - then i used the array_map to organize my viagens ids
$a = array_map(
function($obj) { return $obj->viagem_id; },
$viagens_ids
);
Step 3/3 - And at last i used elequent whereIn to fetch my viagens:
Viagem::with( 'status')->whereIn('id', $a)->get();
In fact i have solved the problem by a-old-sashion-way but i not happy with it because i wish i learn how to do it using eloquent. what bad to me.
There are many ways to query in laravel. I have created a test project for you to try. The gist are:
1. Eloquent ORM
Eloquent ORM is Laravel's magic which have some limitations in eager loading - which i just come across while contemplating your question for hours. It wont play nicely with first(), last(), and some more functions in the constrained eager loading closure.
In your case, our almost there can be fixed:
App\Models\Viagem::with(['status' => function($query){
return $query->orderBy('pivot_created_at', 'desc');
}])
->get()
It will return entire field for Viagem and Status including its pivot table (the status_viagem).
However, if you wanted to retrieve only viagem.id and status_viagem.status_id, you can map() it as such:
App\Models\Viagem::with(['status' => function($query){
return $query->orderBy('pivot_created_at', 'desc');
}])
->get()
->map(function($data){
$o = new stdClass();
$o->id = $data->id;
$o->status_id = $data->status->first()->id;
return $o;
});
Please take note that the statement above require sql query to be ran twice. Eager loading basically works by querying all the Viagem first then queries the Status and map them in memory based on the foreign keys. You can observe that replacing get with toSql will only give you the first query. Please enable Query Logging to see the second query.
2. Query Builder
Embarking from Ryan Adhitama Putra answer, you could do something like:
App\Models\Viagem::join('status_viagem', 'viagens.id', '=', 'status_viagem.viagem_id')
->orderBy('status_viagem.created_at', 'desc')
->groupBy(['status_viagem.status_id', 'viagens.id'])
->select(['viagens.id', 'status_viagem.status_id'])
->get();
This query builder approach guaranteed to be ran only once, you can replace the get() with toSql() to see the resulting query.
3. Raw Queries
Throwing DB::raw() can help sometime, but i really did not want to mention it.
I am not sure what viagens and viagem represent, but I think one of the relationships has to be belongsToMany() and the other hasMany().
then after you set relationships correctly, you can use Eloquent like this :
$status_id = Viagem::with('status')->orderBy('created_at', 'desc')->first()->pluck('status_id');
Try this.
$status_id = Viagem::join('status','viagem.id','status_viagem.viagem_id')
->select('viagem.id','status_viagem.status_id')
->get();

Using Laravel Eloquent to count how many times something exists in an efficient manner

I have a table called rentals, within each row are columns state,city,zipcode which all house ids to another table with that info. There are about 3400 rentals. I am pulling each column to display the states,city and zipcode distinctly. I need to show how many rentals are in each one. I am doing this now via ajax, the person starts typing in what they want to see and it auto completes it with the count, but its slow because of the way im doing it.
$rentals_count = Rentals::where('published',1)->get();
foreach($states as $state) {
echo $state.”-“.$rentals_count->where(‘state’,$state->id)->count();
}
Above is roughly what im doing with pieces removed because they are not related to this question. Is there a better way to do this? It lags a bit so the auto complete seems broken to a new user.
Have you considered Eager loading your eloquent query? Eager loading is used to reduce query operations. When querying, you may specify which relationships should be eager loaded using the with method:
$rental_counts = Rentals::where('published',1)->with('your_relation')->get();
You can read more about that in Laravel Documentation
$rentals = Rentals::wherePublished(true)->withCount('state')->get();
When you loop through $rentals, the result will be in $rental->state_count
Setup a relation 'state' on rentals then call it like this
$rentals_count = Rentals::where('published',1)->with('state')->get()->groupBy('state');
$rentals_count->map(function($v, $k){
echo $v[0]->state->name .' - '. $v->count();
});
Meanwhile in Rentals Model
public function state(){
return $this->hasOne(State::class, 'state'); //state being your foreign key on rentals table. The primary key has to be id on your states table
}

Select one column with where clause Eloquent

Im using Eloquent. But I'm having trouble understanding Eloquent syntax. I have been searching, and trying this cheat sheet: http://cheats.jesse-obrien.ca, but no luck.
How do i perform this SQL query?
SELECT user_id FROM notes WHERE note_id = 1
Thanks!
If you want a single record then use
Note::where('note_id','1')->first(['user_id']);
and for more than one record use
Note::where('note_id','1')->get(['user_id']);
If 'note_id' is the primary key on your model, you can simply use:
Note::find(1)->user_id
Otherwise, you can use any number of syntaxes:
Note::where('note_id', 1)->first()->user_id;
Note::select('user_id')->where('note_id', 1)->first();
Note::whereNoteId(1)->first();
// or get() will give you multiple results if there are multiple
Also note, in any of these examples, you can also just assign the entire object to a variable and just grab the user_id attribute when needed later.
$note = Note::find(1);
// $user_id = $note->user_id;

Fetching related objects

In Symfony 2 book there is an example of how to do that for ONE $product: http://symfony.com/doc/2.0/book/doctrine.html#fetching-related-objects
It is quite simple:
public function showAction($id)
{
$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Product')
->find($id);
$categoryName = $product->getCategory()->getName();
// ...
}
But what if i want to fetch ALL products with category info joined automatically to each project?
Thank you!
This will do the trick:
$products = $this->getDoctrine()->getRepository('AcmeStoreBundle:Product')->findAll();
However, each time you do a getCategory on a product a sql query will be triggered which could result in performance issues.
What you really want to do is to make yourself a ProductManager service and write an explicit query joining product and category. So only one sql query will be generated. The Doctrine 2 manual has plenty of examples.
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.1/reference/query-builder.html
You can either iterate over the products and get the categories from there. Adjusting the fetch mode on the relations might help in reducing the amount of queries performed.
However, you might also just write a custom DQL query to get what you need. It might look like something like this:
SELECT p, c
FROM AcmeStoreBundle:Product p
INNER JOIN p.category c

Resources