Selecting required fields using relations in Laravel - php

I have a model Meetings like this:
public function meeting_comments(){
return $this->hasMany('App\MeetingsComments', 'meeting_id', 'id');
}
public function meeting_users() {
return $this->hasMany('App\UserMeetingDetails', 'meeting_id', 'id');
}
The Controller is like this:
$res = Meetings::with('meeting_comments', 'meeting_users')
->select('')->get()->toArray();
I only need comments from meeting_comments and user_id from meeting_users.
What do I put in select to only get the required fields from meeting_comments and meeting_users ??

You do it through a closure in the with call:
$res = Meetings::with(['meeting_comments' => function($query) {
$query->select('comments', 'meeting_id');
}, 'meeting_users' => function($query) {
$query->select('user_id', 'meeting_id');
}])
->get()->toArray();
I'm taking this from memory, so the syntax may be slightly incorrect, but it should work. :)

Related

How Can I get Latest 5 Commentors user information By Laravel Relational Eloquent eager loading

I'm in a situation where I need to display the last 5 unique commenters information at the top of the comment list as follows screenshot.
comment image
To do this. I did as follows:
Post Model
public function comments()
{
return $this->hasMany(Comment::class);
}
public function commenter_avatars(){
return $this->comments()->distinct('user_id')
->select('id','post_id','user_id','parent_id')
->whereNull('parent_id')
->with('user')->limit(5);
}
My Controller method as follows
public function index() {
$feeds = auth()->user()
->posts()
->with(['user:id,first_name,last_name,username,avatar', 'media', 'commenter_avatars'])
->orderBy('id', 'desc')
->paginate(10);
return PostResource::collection($feeds);
}
I tried to use groupBy and Distinct.. But did't work as expected.
Did I miss something? or Have there any more best way to solve this?
Thank you in advance!
Noted: I am using latest Laravel (8.48ˆ)
I don't know about your joining of post, user and comments table. But i guess, you can do something similar to following.
At first get latest 5 unique user id of one post:
$userIds = Comments::where("post_id", $post_id)->distinct("user_id")->orderBy("id")
->limit(5)->pluck('user_id');
Then, fetch those user information
$users = Users::whereIn("id", $userIds )->get();
Then, you can return those users
UPDATE
You may use map() to fetch and reorder output. Following is an idea for you:
In Controller:
public function index(Request $request) {
$skipNumber = $request->input("skip"); // this is need for offsetting purpose
$userIds = [];
$feeds = Posts::with("comments")->where("comments.user_id", Auth::id())
->skip($skipNumber)->take(10)->orderBy('comments.id', 'desc')
->map(function ($item) use($userIds){
$users = [];
$count = 0;
foreach($item["comments"] as $comment) {
if(!in_array($comment["user_id"], $userIds) && $count < 5){
$count++;
$userIds.push($comment["user_id"])
$user = User::where("id", $comment["user_id"])->first();
$users.push($user);
}
if($count == 5) break;
}
$data = [
"post" => $item,
"latest_users" => $users
];
return $data;
})->get();
return PostResource::collection($feeds);
}
My code syntax may be slightly wrong. Hopefully you will get the idea.
I have solved this issue by using eloquent-eager-limit
https://github.com/staudenmeir/eloquent-eager-limit

Laravel Eloquent: How to add where condition from the with function model relation

Anyone can help me? How to add where condition from the with function model relation.
I tried ->where('driver.group_id',$group_id) but it does not work. please look my code below.
public function getDriversDailyEarnings()
{
$group_id = Auth::user()->group->id;
$today = Carbon::today();
$data = DailyEarnings::with(['payout_cost',
'status:id,name',
'driver' => function ($query) {
$query->with('user');
}])
->where('driver.group_id',$group_id)
->whereDate('created_at', $today)
->get();
return response()->json($data, 200);
}
You can try this. Haven't tested it yet.
DailyEarnings::with([
'payout_cost',
'status:id,name'
'driver' => function($query) {
$query->where('group_id', auth()->user()->group->id)->with('user');
}
])
->whereHas('driver', function($query) {
$query->where('group_id', auth()->user()->group->id);
})
->whereDate('created_at', now()->format('Y-m-d'))
->get();
To pass other clauses to a relation, just use it inside an array, where the key and the name of the relation and the value is a function that receives an instance of the query, something like this:
public function getDriversDailyEarnings()
{
$data = DailyEarnings::with([
'payout_cost' => function ($myWithQuery) {
$myWithQuery->where('column-of-relation-here', 'value-here')
->orWhere('name', 'LIKE', '%Steve%');;
}
])->get();
return response()->json($data, 200);
}
Perhaps something like this:
// ->where('driver.group_id',$group_id)
->whereHas('driver', fn($qry) => $qry->where('group_id', $group_id)) // if group_id a field on driver

Filtering in Laravel using regex

I'm trying to filter products based on query string. My goal is to get products from a collection if it's given, otherwise get every product. Could someone help me what's wrong with the following code?
$products = \App\Product::where([
'collection' => (request()->has('collection')) ? request('collection') : '[a-z]+',
'type' => (request()->has('type')) ? request('type') : '[a-z]+'
])->get();
PS.: I've also tried with 'regex:/[a-z]+', it's not working...
$products = \App\Product::where(['collection' => (request()->has('collection')) ? request('collection') : 'regex:/[a-z]+'])->get();
What you can do is use when eloquent clause, so your where clause for collections will be triggered only when the request('collection') exists, same logis applie to type as well.
$products = \App\Product::
when(request()->has('collection'), function ($q) {
return $q->where('collection', request('collection'));
});
->when(request()->has('type'), function ($q) {
return $q->where('type', request('type'));
})
->get();
Or another way if you have your request values assigned to a variable something like:
$collection = request('collection');
$type= request('type');
$products = \App\Product::
when(!empty($collection), function ($q) use ($collection) {
return $q->where('collection', $collection);
});
->when(!empty($type), function ($q) use ($type) {
return $q->where('type', $type);
})
->get();

How to attach conditionals to eloquent relation query?

I am trying to create the filters and I just want to attach the query to relation by using some conditionals. I don't want to put those conditional in the first block of the code. So how can I get the query instance so I can attach the new queries to the existing relation?
$query = Category::query();
$query->where('category_type', 'xyz')
->with(['products' => function ($query) {
$query->where('condition1', 'value')
->where('condition2', 'value');
}]);
if (isset($queryParams['param1'])) {
$query->with(['products' => function ($query) use ($queryParams) {
$query->getQuery()->where('type', $queryParams['param1']);
}]);
}
Currently, it just overwrites the first query relation condition.
This should work :
$query = Category::query();
$query->where('category_type', 'xyz')
->with(['products' => function ($query) use ($queryParams) {
$query->where('condition1', 'value')
->where('condition2', 'value');
if (isset($queryParams['param1'])) {
$query->where('type', $queryParams['param1']);
}
}]);

reduce database query to one and avoid Call to a member function load() on null error

I have this function:
public function show($id)
{
if (count($post = Post::find($id))) {
$post = $post->load(['comments' => function ($q) {
$q->latest();
$q->with(['author' => function ($q) {
$q->select('id', 'username');
}]);
}, 'user' => function ($q) {
$q->select('id', 'username');
}]);
$this->authorize('seePost', $post);
return view('post.show', ['post' => $post]);
} else {
dd('no post');
}
}
I added the if statement as if I try to open a route to a non existent post id I get the error Call to a member function load() on null.
However now I have two queries, one looks for the Post in the DB and if it finds one then I have to load the relations with the second one. What can I do to go back to just one query with all the relations loaded and avoid the error? Any clue?
You can use Constraining Eager Loads do it like this:
https://laravel.com/docs/5.8/eloquent-relationships#constraining-eager-loads
$post = Post::with(["comments" => function ($query) {
// Order by created_at, query comment author & select id, username
$query->latest()->with(["author" => function ($q) {
$q->select("id", "username");
}]);
}, "user" => function ($query) {
// Query post author & select id,username
$query->select("id", "username");
}])
// Fetch post or throw a 404 if post is missing
->findOrFail($id);
// You can also return an empty post instance like this if post is missing
// ->findOrNew([]);
// Or return the post or null if post is missing
// ->find($id);
// Authorize
$this->authorize('seePost', $post);
return view("post.show", ["post" => $post]);
Laravel has an Eager Loading feature that would be helpfull in your case. Eager Loading allows you to autoload relations along with the same query that you use to retrieve your main model info. https://laravel.com/docs/5.8/eloquent-relationships#eager-loading
You could a below codes.
Easiest way is :
$post = Post::with('comments.author', 'user')
->find($id);
Or fine tune query with callback :
$post = Post::with(['comments' => function ($q) {
// if you use comments select, then you need to specify foreign key too
$q->select('id', 'author_id', 'details') // comment fields
->latest(); // Use chaining method
// OR use $q = $q->latest();
},
'comments.author' => function ($q) {
$q->select('id', 'username'); // author fields
},
'user' => function ($) {
$q->select('id', 'username'); // user fields
}])
->find($id);
In some cases you might need some modifications, bu in overall that should avoid you N+1 queries problem.

Categories