Laravel one of many relationship with argument - laravel

coming from this relationship from the docs:
https://laravel.com/docs/9.x/eloquent-relationships#advanced-has-one-of-many-relationships
/**
* Get the current pricing for the product.
*/
public function currentPricing()
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function ($query) {
$query->where('published_at', '<', now());
});
}
how can I make such an relation with a specific date?
The relation down below will work
/**
* Get pricing for the product of one specific date.
*/
public function priceOfDay(Carbon $date)
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function ($query) {
$query->where('published_at', '<', $date());
});
}
but how can I use it with Eloquent? How can I pass the date to this:
Product::with('priceOfDay')->get();
update
I now use the one to many relation with a closure
->with(['prices' => function ($query) use ($month) {
$query->where('published_at', '<', $month)
->orderByDesc('published_at')
->orderByDesc('id')
->first();
}])
it works with the little drawback of having a collection instead of an object as relation, but it fills my needs for the moment.
It would be nice if there was something like
->with(['relation', $param])
update 2
since there seems to bo no direct solution here the workarround i came up with:
->first() does not work in the query, you will end up getting all prices, so I finished with an each()
->with(['prices' => function ($query) use ($month) {
$query->where('published_at', '<', $month)
->orderByDesc('published_at')
->orderByDesc('id');
}])
->get()
->each(function ($product) {
$product->price = $product->prices->first()->price;
})

Related

Why might the Elequent ORM relationship feel empty?

Here is my method available in the controller
public function getProductListAll(){
$products = Product::with('property')
->whereStatus(1)
->where('show', 1)
->where('code', '!=', 1)
->where('is_roketable',true)
->with(['prices_roket' => function ($q) {
return $q->whereNull('user_id');
}]);
$products = $products->whereIn('product_type', ['education', 'consultancy', 'startup']);
$data['products'] = $products->select([
'id', 'name', 'image', 'commission', 'price',
'buy_now', 'type', 'short_desc', 'pricing_desc',
'offer_status', 'period_status', 'period', 'period_frequency',
'endpoint', 'product_type', 'unique_id', 'discount_show_status',
'annual_payment', 'pre_register', 'privacy_status', 'privacy',
'sg_project', 'last_pre_register_date', 'product_desc',
'product_consulting', 'product_status',
])->get();
return response()->json($data);
}
and here is the relationship in my model.
return $this->hasMany(ProductPrice::class)->select('price','created_at','user_id','offer_id')
->orderBy('price')
->where(function ($q) {
$q
->where(function ($q2) {
$q2->whereNull('offer_id');
})
->orWhere(function ($q2) {
$q2->whereNotNull('offer_id');
$q2->where('user_id', auth()->user() ? auth()->user()->id : 0);
});
});
I tried to change my relationship and rewrite it, but when I write select, I cannot access any data. When I remove select, they all come. I can't select at prices relationship.
You can not use ->select() inside your model relationship.
Instead what you can do to limit the selection is by adding it into ->with()
For example
$products = Product::query()
->with(['property' => function ($q) {
$q->select(['price','property.created_at','user_id','offer_id']);
}])
->where( ... )
// no need to add property 'price' inside this select
->select(['id', 'name', 'image', 'commission' ... ])
->get();

Laravel: Selective drop down lists

So my drop down list works that I HAVE to select both options for it to display a list of hotels based on the criteria selected (Distance and price), but I want to make sure that the user can find all hotels based on just Price, for example. When I do this, no hotels appear and my drop down list doesn't quite work. So how would I exactly implement code that will let the user select all the hotels based on just the ONE criteria.
SearchController.php
public function index(Request $request)
{
$distances = DB::table('posts')->select('distance')->distinct()->get()->pluck('distance');
$prices = DB::table('posts')->select('price')->distinct()->get()->pluck('price');
$post = Post::query();
if ($request->has('price')) {
$post->where('price', $request->price);
}
if ($request->has('distance')) {
$post->where('distance', $request->distance);
}
return view('Pages.search', [
'distances' => $distances,
'prices' => $prices,
'posts' => $post->get(),
]);
}
Have you tried using when?
public function index(Request $request)
{
// Unnecessary select * changed to select only the columns you need.
$distances = DB::table('posts')->select('distance')->distinct()->get(['distance'])->pluck('distance');
$prices = DB::table('posts')->select('price')->distinct()->get(['price'])->pluck('price');
$post = Post::when($request->has('price'), function ($query) use ($request) {
$query->where('price', $request->price);
})
->when($request->has('distance'), function ($query) use ($request) {{
$query->where('distance', $request->distance);
})
->get();
/* php >= 7.4
$post = Post::when($request->has('price'), fn($query) => $query->where('price', $request->price))
->when($request->has('distance'), fn($query) => $query->where('distance', $request->distance))
->get();
*/
return view('Pages.search', compact('distances', 'prices', 'post');
}
So instead of the use of 'has' i simply implemented the 'filled' and this ensured that my filtering worked.

How to pass the current/main model data to the eagerloading constraint for use as a variable

$studentIds = ExamRoutineDetail::where('routine_id', $routineId)
->distinct()
->pluck('student_id')
->all();
$data = Student::whereIn('id', $studentIds)
->where('school_id', $schoolId)
->where('grade_id', $gradeId)
->with([
'routine_detail' => function ($query) use ($routineId) {
$query->where('routine_id', $routineId);
},
'routine_detail.routine',
'routine_detail.routine.routine_info' => function ($query) use ($routineId) {
$query->where('routine_id', $routineId);
},
'routine_detail.routine.routine_info.student_marks' => function ($query) use ($studentIds) {
$query->where('student_id', $studentIds);
},
])
->get();
I want to use the student's id in the final where condition in the last layer of the nested eagerloading i.e. routine_detail.routine.routine_info.student_marks.
so something like this
'routine_detail.routine.routine_info.student_marks' => function ($query) use ($studentIds) {
$query->where('student_id', $id);
},
where id would be equal to the id of the student in context, from the main Student:: .... model.

Eloquent - How should I make a condition in a join table

I have a tournament table.
Each tournament hasMany Championships.
I want to get the tournament that match the championshipID = 333.
So, I do it :
$tournament = Tournament::with([
'championships' => function ($query) use ($request) {
$query->where('id', '=', 333);
},
'championships.settings',
'championships.category',
'championships.tree.user1',
'championships.tree.user2',
'championships.tree.user3',
'championships.tree.user4',
'championships.tree.user5'
])->first();
Example of 1 of my relations:
public function settings()
{
return $this->hasOne(ChampionshipSettings::class);
}
Tell me if you need all, to post it.
But as I put 1 eager loading relationship, I get all my tournaments instead of getting just one.
What Am I missing???
I think you're looking for whereHas() which will allow you to filter a model based on a related model's constraints. You should also use a subquery for the nested constraints if you're getting the related model more than once to avoid query duplication like:
$tournament = Tournament::whereHas('championships', function($query) use ($championshipId) {
return $query->where('id', $championshipId);
})
->with(['championships' => function ($query) use ($request) {
$query->where('id', '=', 333)
->with([
'settings',
'category',
'tree' => function($query) {
return $query->with('user1', 'user2', 'user3', 'user4', 'user5');
}]);
}])
->first();

Laravel - condition on nested models

I'm writing a code to get nested objects in Laravel. I was wondering if it is possible to write conditions in hasMany or belongsTo.
Here is what I'm doing, that makes the question clear:
$posts = Post::where(
array(
'status' => 'active'
)
)
->orderBy('id', 'asc')
->with(['postResponsibilities' => function($query){
$query->where('status', 'active');
}])
->with(['postRequirements' => function($query){
$query->where('status', 'active');
}])
->with(['postSalaries' => function($query){
$query->where('status', 'active');
}])
->skip($limit * ($page - 1))->take($limit)->get();
So, I have to put nested queries to get only those records whose status is active.
In the Post model, I've written:
public function postRequirements(){
return $this->hasMany('App\Models\PostRequirement', 'post_id');
}
public function postResponsibilities(){
return $this->hasMany('App\Models\PostResponsibility', 'post_id');
}
public function postSalaries(){
return $this->hasMany('App\Models\PostSalary', 'post_id');
}
Is there a way such that I can define status condition inside the nested models?
So that I can write:
$posts = Post::where(
array(
'status' => 'active'
)
)
->orderBy('id', 'asc')
->with('postResponsibilities')
->with('postRequirements')
->with('postSalaries')
->skip($limit * ($page - 1))->take($limit)->get();
I hope the question is clear, thanks
What you can do is apply those conditions inside the relationship methods you put on the Post model, for example:
class Post
{
public function postRequirements() {
return $this->hasMany('App\Models\PostRequirement', 'post_id')
->where('status', 'active');
}
}
Yes it is possible to eager load multiple relationships.
See: https://laravel.com/docs/master/eloquent-relationships#eager-loading
In your case it would be something like:
Post::with('postResponsibilities', 'postRequirements', 'postSalaries')->where()....

Resources