Laravel Eager Loading and dynamically bind model relationship - laravel-5

Laravel version: 5.3
In my User model class i have a method like :
public function hasOneRelation($model)
{
return $this->hasOne($model);
}
And then i call this method like below code:
User::hasOneRelation('App\Client')->first();
i got the correct value but is there any N+1 problem.....

There is no way to eager load this dynamic relationship, so if you attempt to use this relationship in a loop, yes, you will end up with an N+1 problem.

You shouldn't have any N+1 issue here because you won't be calling that as a property of a model. That and just stay away from calling that in a loop on your model. You get the N+1 issue if you were to do something like:
//User Model
public function contract()
{
return $this->hasOne(Contract::class);
}
//Controller
public function index()
{
$users = User::all();
return view('users.index', compact('users'));
}
//View
<ul>
#foreach($users as $user)
<li>{{$user->contract->type}}</li>
#endforeach
</ul>
Loading the relation on each model within a loop instead of lazy loading it like:
//Controller
public function index()
{
$users = User::with('contract')->get();
return view('users.index', compact('users'));
}

Yes you just face N+1 problem. Read laravel official doc https://laravel.com/docs/5.3/eloquent-relationships#eager-loading

Related

Sort inside the with in laravel

$posts = PostUrl::with(['post' => function ($q) {
$q->orderBy('created_at', 'DESC');
}])
->where('category','like' ,'%'.$cat.'%')
->paginate(8);
How can I sort the result as per the created_at of the post inside?
When try to ->orderBy('post.created_at','DESC') it show column not found.
Add PostUrl models
public function post(){
return $this->hasMany('App\Post', 'postid')->orderBy('created_at', 'DESC');
}
Controller :
$posts = PostUrl::with(['post'])->where('category','like' ,'%'.$cat.'%')->paginate(8);
Make sure you have established correctly in your PostUrl model the relationship with your Post model (if it is so called). You should have a function similar to the following in your model in order to with() method works correctly in the controller:
public function post(){
return $this->hasMany('App\Post', 'postid');
}
This way there should be no problem for the orderBy clause to work properly.

Laravel get value from 2 column with relationship one to one

I have 2 tables and have relationship one to one. Second table use a FK from first one, and I want to display a list with all values .
public function index(Request $request)
{
$listOfPersons = new Person();
$listOfRegisters = new Register();
$listOfRegisters->listOfPersons()->associate();
return $listOfRegisters;
}
In Register Model
public function people(){
return $this->hasOne(Person::class);
}
In Person Model
public function register(){
return $this->hasOne(Register::class);
}
If you just want a list with all pairs of values, it should be enough with this code:
public function index(Request $request)
{
$registers = Register::all();
$list = [];
foreach($registers as $register){
array_push($list,['register'=> $register, 'person'=>$register->people]);
}
return $list;
}
But remember you can just have the list of registers and access the person via the relationship. Moreover, you should change the hasOne relationship to belongsTo in register.
I hope that helps.
I think you have to use leftjoin. (not foreach and php loop)
Because:
The alternative of handling this inside your PHP code with a foreach
loop is unattractive for several reasons. First, you would probably
need to bring in all information from both tables, which is wasteful
from both a memory and network usage point of view. Then, even after
you have brought in the data, you would be relying on PHP to perform
the join. PHP wasn't really designed for in house database operations,
and cannot use something like an index to speed up the process.
So you can write your query like:
User::leftJoin('register', 'register.user_id', '=', 'id');
However, I prefer to add a scope in my model for this situation
<?php
class User extends Authenticatable
{
public function scopeRegister($builder)
{
$query = $query->leftJoin('register', 'register.user_id', '=', 'id');
return $query;
}
and in my controller
public function index(Request $request)
{
$records = User::register()->get();
}

How can I make pagination of a findorfail method in laravel

I have this function in my controller which is the output of many animals and I would like to paginate it. How am I to do that
controller
public function show($id)
{
$farms = User::with(['animals'])->findOrFail($id);
return view('slaughter.show',compact('farms'));
}
Is there any other way of doing that because I have tried to add the paginate method at the end and I am getting an error
paginate() is a method on the Query Builder instance while findOrFail() returns an Eloquent model, and in this case a single instance, as it is for a single user by the given $id.
You can maybe query the animals through their own model, and paginate it, like this:
public function show($id)
{
$farms = Animal::where('user_id', $id)->paginate(10);
return view('slaughter.show',compact('farms'));
}
I would do this to get a paginated collection of animals for the User. Dependency Injection in the show method as well assuming you also require some user data on the slaughter page.
public function show(User $user)
{
$farms = Animals::where('user_id', $user->id)->paginate();
return view('slaughter.show',compact('farms', 'user'));
}
Your route will need updating, something like this...
Route::get('animals/{user}', 'AnimalsController#show')

Get indirect relationship from collection

I have:
User->hasMany('Order')
...
Order->hasMany('Product')
...
$users=User::with('orders','orders.product')->...
how can I retrieve all products bought from users in $users taking advantage of eager loading instead of doing other queries?
You could use a hasManyThrough relationship on your User model.
https://laravel.com/docs/5.4/eloquent-relationships#has-many-through
public function products()
{
return $this->hasManyThrough('App\Product', 'App\Order');
}
or, using a collection, as your already eager loading.
$products = User::with('orders','orders.product')->flatMap(function(User $user) {
return $user->orders->flatMap(function(Order $order) {
return $order->products;
});
});

Laravel 5.2 How to delete parent record from belongsTo relationship?

My Pricing model
protected $fillable = [
'zone_id',
'is_short_time',
'is_service_feeder',
'route_id',
'value',
];
public function route()
{
return $this->belongsTo(Route::class);
}
My Route Model
protected $fillable = [
'zone_id',
'from_city_id',
'to_city_id',
'is_in_the_city',
];
public function pricing(){
return $this->hasOne(Pricing::class);
}
Here is my controller
public function feeder_destroy($zoneid, $pricingfeederid)
{
$pricing_feeder = Pricing::find($pricingfeederid)->where('zone_id', $zoneid)->where('is_service_feeder', 1);
$pricing_feeder->route()->delete();
$pricing_feeder->delete();
}
The error says
Call to undefined method Illuminate\Database\Query\Builder::route()
I want to delete pricing record and route record too.
What wrong with my code? How should it's look like?
Any help appreciated.
Your controller should
$pricing_feeder = Pricing::find($pricingfeederid)->where('zone_id', $zoneid)->where('is_service_feeder', 1)->first();
Dont forget first() at the end.
Then use like so $pricing_feeder->route->delete();
Try this... $pricing_feeder->route->delete(); Removing () from route()
Your error is on the relation, not the Parent.
not the fanciest but you can delete the route that comes with pricing by adding this method to your route model
public function delete()
{
// delete all related pricing
$this->pricing()->delete();
// delete the route as well
return parent::delete();
}
then just call $pricing_feeder->route->delete();
Ok so I have this model relationships
In Post model
public function user(){
return $this->belongsTo(User::class);
}
In User model
public function post(){
return $this->hasMany(Post::class);
}
and the following code deleted all the posts and the user
$user = \App\User::findOrFail(1);
$user->post[1]->delete();
$user->delete();
Your code does not work, becouse you need to add a first() method call:
$pricing_feeder = Pricing::find($pricingfeederid)->where('zone_id', $zoneid)->where('is_service_feeder', 1)->first();
Than I'd check the returned object if is null. If is not, than you can delete the related relationship and the model as well.
Also, when calling the delete() methods use the relationships as follows:
$pricing_feeder->route->delete();

Resources