get posts where relationship count is equel to post's attribute - laravel

I have a model "Post" which has a field "daily" in the database.
$table->integer('daily'); // could be anything
The Model "Post" has a relationship to the model "Comments".
public function comments() {
return $this->hasMany(Comment::class);
}
public function completed() {
return $this->hasMany(Comment::class)->completed();
}
Comments model
public function scopeCompleted($query) {
return $query->....?
}
I want to get all user's posts where it's comments count are equal to the post's 'daily' field. For example: if the post's daily field is '5' and there are 5 comments to this posts, I want to see this post in the returned lists, otherwise don't return it.
I know I can get all of them and do it with loop, but I wanna know if there is any way to do this with eloquent only.

Try this
$user=User::with('posts','posts.comments')
->whereHas('posts.comments',function ($query){
//if any additional filters
},'=',DB::raw('posts.daily'))
->get();
or if not needed additional query then
$user=User::with('posts','posts.comments')
->whereHas('posts.comments',null,'=',DB::raw('posts.daily'))
->get();

Got it working like this
public function scopeCompleted($query) {
return $query->has('comments', '=', DB::raw('posts.comments'));
}

Related

Route model binding select specific columns with relation

Route:
Route::get('/posts/{post}', [PostController::class, 'show']);
Controller:
public function show(Post $post){
$postData = $post->load(['author' => function($query){
$query->select('post_id', 'name');
}])
->get(['title', 'desc', 'created_date'])
->toArray();
}
This returns all the posts in the database, While I only want to get the selected post passed to the show function.
So if I visit /posts/3, It should show data related to the post with id = 3, not all posts.
The result should be an array with only the selected post.
When you are inside the Controller you already have the Post you want. If you call get you are getting all posts with a new query.
To select specific columns from a relationship, you can do it like this:
public function show(Post $post)
{
$post->load('author:id,name,age'); // load columns id, name, age from author
}
Notice that including the id is required.
To specify columns for the Post, there's no way to do it per route, the only way is to override how Laravel resolves the implicit binding. For that, you can add this in your Post model class:
public function resolveRouteBinding($value, $field = null)
{
return $this->whereKey($value)->select(['id', 'body', 'author_id'])->firstOrFail();
}
Notice that including the author_id is required to load the Author afterwards inside the Controller method. Also keep in mind this will affect all routes where you implicitly load a Post.
Please don't use get() function on the model.
public function show(Post $post)
{
$post->load(['author' => function ($query) {
$query->select(['post_id', 'name']);
]);
dd($post->toArray());
}
Btw, the author shouldn't have a post_id, because author can have multiple posts. you should update the database structure or you are doing wrong in the controller. (my bad, Thanks #techno)

How to retrieve multiple relations with multiple tables in laravel eloquent

I'm using Laravel 5.8 to build a babysitting site. I have 4 tables with different relationships as below:
please see this image
The relationships are:
Babysitter->hasMany(session)
Sessions->hasOne(Review)
Sessions->hasOne(Kids)
Sessions->hasOne(Babysitter)
Sessions->hasOne(Parent)
I want to achieve 2 things:
First one
I want to show this result when listing all babysitters. I'm showing this information for each babysitter:
plsease see this image
See here what I couldn't achieve
plsease see this image
This is my code
Sitters::where('Status', 'active')->where('Verified', 1)->get();
Second one
Also, I've tried to show kids name with parent review as shown here:
plsease see this image
This is what i'm using
Sessions::select('Reviews.*', 'Sessions.Parent_id')->join('Reviews', 'Reviews.Session_id', '=', 'Sessions.id')->with('owner')->where('Trainer_id', session('user')->Id)->where('Status', '=', 'complete')->with('owner')->orderBy('Sessions.id', 'DESC')->get();
Here is Session.php Model
public function owner(){
return $this->belongsTo('App\Models\Parents', 'Parent_id');
}
As discussed change the relations:
Babysitter->hasMany(sesstion)
Sessions->hasOne(Review)
Sessions->belongsTo(Kids)
Sessions->belongsTo(Babysitter)
Sessions->belongsTo(Parent)
First one
in Babysitter.php declare the following attributes
class Babysitter extends Model
{
public function reviews()
{
$this->hasManyThrough(Review::class, Session::class);
}
public function getAverageReviewAttribute()
{
return $this->reviews()->avg('Rating');
}
}
Then you just need to call it on the model instance.
$babysitter = Babysitter::first();
return $babysitter->average_review;
Second one
Just use the relation
$babysitter = BabySitter::with(['sessions' => public function ($session) {
$session->with(['review','parent','kids']);
})->where('trainer_id', '=', session('user')->Id) //did not understand this condition
->first();
This assumes you have parent, kids and review relation declared on Session::class. (change the names if needed)
After a few days of searching & testing, this is what worked for me:
Inside (Sitters) Model, put this relation
public function sessions()
{
return $this->hasMany(Sessions::class, 'sitter_id')
->withCount('reviews')
->withCount(['reviews as review_avg' => function($query){
$query->select(DB::raw('AVG(Rating)'));
}]);
}
Also, inside (Sessions) Model, put this relation
public function reviews()
{
return $this->hasOne(Reviews::class, 'Session_id');
}
Now you query like this
return $sitters = Sitters::with('sessions')->get();
I hope this can help someone :)

How to attach extra data on eagerloaded relationship Laravel

I have this function that eagerloads the posts,
public function getUserPosts()
{
return User::with('posts')->where("organization_id", $this->getOrganization()->id)->get()->toArray();
}
This returns fine the user with posts, my question is can I add some extra data on eagerload something like
public function getUserPosts()
{
return User::with('posts', function(){
//say I fetch an array of posts here,
// I want to attach this array of posts with the eagerloaded posts
})->where("organization_id", $this->getOrganization()->id)->get()->toArray();
}
return User::with(['posts', function($query){
//here you have the query to the relationship, and can do normal query stuff with it
//like this:
$query->with('comments')->select('id', 'title')->where('created_at','>',Carbon::now());
}])->where("organization_id", $this->getOrganization()->id)->get()->toArray();

Laravel oneToMany accessor usage in eloquent and datatables

On my User model I have the following:
public function isOnline()
{
return $this->hasMany('App\Accounting', 'userid')->select('rtype')->latest('ts');
}
The accounting table has activity records and I'd like this to return the latest value for field 'rtype' for a userid when used.
In my controller I am doing the following:
$builder = App\User::query()
->select(...fields I want...)
->with('isOnline')
->ofType($realm);
return $datatables->eloquent($builder)
->addColumn('info', function ($user) {
return $user->isOnline;
}
})
However I don't get the value of 'rtype' for the users in the table and no errors.
It looks like you're not defining your relationship correctly. Your isOnline method creates a HasMany relation but runs the select method and then the latest method on it, which will end up returning a Builder object.
The correct approach is to only return the HasMany object from your method and it will be treated as a relation.
public function accounts()
{
return $this->hasMany('App\Accounting', 'userid');
}
Then if you want an isOnline helper method in your App\User class you can add one like this:
public function isOnline()
{
// This gives you a collection of \App\Accounting objects
$usersAccounts = $this->accounts;
// Do something with the user's accounts, e.g. grab the last "account"
$lastAccount = $usersAccounts->last();
if ($lastAccount) {
// If we found an account, return the rtype column
return $lastAccount->rtype;
}
// Return something else
return false;
}
Then in your controller you can eager load the relationship:
$users = User::with('accounts')->get(['field_one', 'field_two]);
Then you can do whatever you want with each App\User object, such as calling the isOnline method.
Edit
After some further digging, it seems to be the select on your relationship that is causing the problem. I did a similar thing in one of my own projects and found that no results were returned for my relation. Adding latest seemed to work alright though.
So you should remove the select part at very least in your relation definition. When you only want to retrieve certain fields when eager loading your relation you should be able to specify them when using with like this:
// Should bring back Accounting instances ONLY with rtype field present
User::with('accounts:rtype');
This is the case for Laravel 5.5 at least, I am not sure about previous versions. See here for more information, under the heading labelled Eager Loading Specific Columns
Thanks Jonathon
USER MODEL
public function accounting()
{
return $this->hasMany('App\Accounting', 'userid', 'userid');
}
public function isOnline()
{
$rtype = $this->accounting()
->latest('ts')
->limit(1)
->pluck('rtype')
->first();
if ($rtype == 'Alive') {
return true;
}
return false;
}
CONTROLLER
$builder = App\User::with('accounting:rtype')->ofType($filterRealm);
return $datatables->eloquent($builder)
->addColumn('info', function (App\User $user) {
/*
THIS HAS BEEN SUCCINCTLY TRIMMED TO BE AS RELEVANT AS POSSIBLE.
ARRAY IS USED AS OTHER VALUES ARE ADDED, JUST NOT SHOWN HERE
*/
$info[];
if ($user->isOnline()) {
$info[] = 'Online';
} else {
$info[] = 'Offline';
}
return implode(' ', $info);
})->make();

How to count average value for each user?

I have two tables: users, comments.
Each user has some comments.
I do request like as:
$users = User:with('comments')->get();
How can I count the average value in field comments.rate where users.id = comments.user_id
In result I should get collection with all rows wityh user information and field avg_rate
I tried to use ->avg(), but it returns only one row, not for each
I have own solution, but I have desire to move this code in model:
{{$users->reviewsAverage()->first()->avg("rate")}}
Model:
public function reviewsAverage()
{
return $this->hasMany('App\Review', 'user_id', 'id'); //->first()->avg('rate');
}
Have two functions one for the relationship and he other for the average function
public function comments()
{
return $this->hasManyThrough('App\Comment', 'id');
}
public function averageRating()
{
return $this->comments()->selectRaw('avg(rate) as average_rate, comment_id')
->groupBy('comment_id');
}
Then go ahead and query like this
foreach($users as $user){
$rate = $user->averageRate();
}

Resources