Laravel : Get all models that their last relationship have some condition - laravel

I have two models Post and Comment, i'd like to get all posts that their last comment is active:
// Model Post
public function comments()
{
return $this->hasMany('comments');
}
//Model Comment
public function post()
{
return $this->belongsTo('post');
}
i tried this solution :
public function lastComment()
{
return $this->hasOne('comment')->latest()
}
and in my controller :
$postsWithLastActiveComment = Post::whereHas('lastComment', function($q){
$q->where('active',1);
})->all();
but in this solution if the last comment is not active Previous comment will be taken

I am not sure if there's another simpler way of doing this, but maybe you can try it with a sub-query?
$lastComment = Comment::select('active')
->whereColumn('post_id', 'posts.id')
->latest()
->limit(1)
->getQuery();
$posts = Post::select('posts.*')
->selectSub($lastComment, 'last_comment_is_active')
->having('last_comment_is_active', 1)
->get();

->latest() only orders the posts by created_at so to get only the latest comment you need ->latest()->first()

I think the code below should work !
public function comments()
{
return $this->hasMany('comments');
}
public function lastComment()
{
return $this->comments()->latest()->first();
}

Shouldn't this
$postsWithLastActiveComment = Post::whereHas('lastComment', function($q){
$q->where('active',1);
})->all();
be
$postsWithLastActiveComment = Post::whereHas('lastComment', function($q){
$q->where('active',1);
})->get();

According to your question, the Post model has many Comments. And you want to get the comment from the post where active is one and must be the lastest id.
Get the last comment like the following
public function lastComment()
{
return $this->hasOne('comment')->latest()->take(1);
}
Get all posts which had the lastComment like the following
$latestCommentPosts = Post::whereHas('lastComment')->get()
And filter the latestCommentPosts like the following
$latestCommentPosts->where('active', 1)->get()
Or, you can archive by one query like the following as well.
Post::whereHas('comments', function($q) {
$q->where('active', 1);
})->get()
Like that, you got all the latest comment with active is 1.

Related

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

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'));
}

How to get latest eloquent relationship by column name in Laravel

I am building a small application on Laravel 5.6 where I am having two models Project and Status. In this I am having a relation as such:
In Project Model I am having:
public function statusUpdate()
{
return $this->hasMany('App\Status','project_id','id');
}
and to retrieve latest status I have:
public function latestStatus()
{
return $this->hasOne('App\Status','project_id','id')->latest();
}
In status I have columns: date, status, sub_status, comments.
I want to retrieve Status where I am having latest status by date mentioned in the column
I tried doing this in my model:
public function latestStatus()
{
return $this->hasOne('App\Status','project_id','id')->latest('date');
}
But this thing is not working out, help me out in this. Thanks
edit
I am using this relation in eager loading something like this:
Project::when( $request->name , function( $q) use( $request ) {
$q->where('name', 'like', '%' . $request->name .'%');
})->with('latestStatus')
->orderBy($request->sort_by_col, $request->order_by)
->paginate(30);
You can use orderBy in the relationship.
public function latestStatus()
{
return $this->hasOne('App\Status','project_id','id')->orderBy('date', 'desc');
}
Try it out.
You got your models wrong. This is what should be in the Project model
public function statuses() //plural because a project has many statuses
{
return $this->hasMany('App\Status','id','project_id');
}
If you want the latest status, call this in your controller:
Project::where('name', 'like', "%{$request->name}%")->with('statuses', function($q) {
return $q->orderBy('date', $request->order_by);
})->paginate(30);
If you want the latest project where the status has changed, first the Status model:
public function project()
{
return $this->hasOne('App\Project','project_id','id');
}
And in your controller:
$project = Status::latest()->first()->project;
Add first to the end of the query.
public function latestStatus()
{
return $this->hasOne('App\Status','project_id','id')->latest()->first();
}
This is how it works
The statusUpdate method builds the query and does the setup for has many relationship
The latest method adds the order by clause
The first methods adds the limit 1 clause, then executes the query and returns the first result

Return relationship only if user is logged in?

I have a Posts and a Comments table, where each post can have multiple comments.
I want to make a query that gets all posts, and also all of the logged in user's comments.
Here is what I have so far:
$posts = Post::select('posts.*')
->with(['comments' => function($query) {
if (Auth::check()) {
$query->where('user_id', Auth::user()->id);
}
}])
->get();
And my Post.php model class looks like this:
class Post extends Model
{
public function comments()
{
return $this->hasMany('App\Comment');
}
}
The query returns the correct results when the user is logged in.
But when the user is not logged in, it returns the comments of ALL users, instead of returning nothing (because the user is logged in and therefore they have no comments).
How can I fix this?
you can do a little trick in your post model:
class Post extends Model
{
public function comments()
{
if(Auth::check()) {
return $this->hasMany('App\Comment')->where('user_id', Auth::user()->id);
}else{
return $this->hasMany('App\Comment')->where('user_id',-1);
}
}
}
and then simply :
$posts = Post::select('posts.*')->with('comments')->get()
so if user is not logged in it will return all comments with user_id of "-1" which will be nothing
There are two ways I can think of to this.
First you can only load the comments if the user is logged in:
$posts = Post::select('posts.*');
if(Auth::check()) {
$posts->with(['comments' => function($query) {
$query->where('user_id', Auth::user()->id);
}]);
}
$posts = $posts->get();
Or you could load all comments but set the user_id to null if the user isn't logged in. Since every comment should have a user_id no comments will be returned.
$posts = Post::select('posts.*')
->with(['comments' => function($query) {
$query->where('user_id', Auth::check() ? Auth::id() : null);
}])
->get();
The code in the second one looks cleaner IMO, but the first one will prevent an unnecessary query from being executed.
You can separate it, more legible and practical:
$posts = Post::all();
At your Post model create a function that will return all user's comments:
public function userComments()
{
return $this->comments->where('user_id', Auth::user()->id);
}
And i guess at your view you have a foreach to iterate all posts, inside your foreach you load post's comments, so you can do that:
#foreach($posts as $post)
$post->userComments()
#endforeach

How to sort a query by the results of a model's method in Laravel

I have a model that has a "hasMany" relationship with a second model which records "votes" for the former. I then have a method which can be called upon to simply count the number of votes, as follows:
class Car extends Model
{
public function votes()
{
return $this->hasMany(Vote::class);
}
public function score()
{
return $this->votes->count();
}
}
In my controller, I want to run a query that returns all() but sorts by score()... something along the lines of:
$cars = Car::all()->sortBy('score');
But that doesn't work obviously... I feel like I'm on the right track, but don't quite know how to make this work. Would appreciate some help. Thanks.
Try this solution:
$cars = Car::with('votes')->get()->sortBy(function($car)
{
return $car->votes->count();
});
Hope it help you :)
the code should be like this :
$products = Shop\Product::join('shop_products_options as po', 'po.product_id', '=', 'products.id')
->orderBy('po.pinned', 'desc')
->select('products.*') // just to avoid fetching anything from joined table
->with('options') // if you need options data anyway
->paginate(5);

Laravel query with hasmany relationship

I'm new to laravel so forgive me for the possible noobish question. Also, I did look into all the other 'similar' questions but either they're don't reproduce the right solution or i'm honestly having a hard time wrapping my head around it.
The scenario:
I have a Post model and a Subject model. This is what they look like at the moment.
in Post.php
public function subjects() {
return $this->belongsToMany('Subject', 'posts_subjects');
}
in Subject.php
public function posts() {
return $this->belongsToMany('Post', 'posts_subjects');
}
Now, what i need to achieve is:
In case an query parameter is passed to the request(ie q=Food) i want to return only the posts that contain the subject food in their subjects relationship and none of the others. if nothing is passed, then i just show everything...
This is what my PostsController's index action looks like atm.
public function index() {
$q = Input::get('q');
$posts = Post::all();
return View::make('posts.index', ['posts' => $posts]);
}
How would i go about doing that? would greatly appreciate some help.
Thanks a lot
PS. I'm able to get all the posts with this code.
This is untested code, but it probably will work for you, if you have only one Subject returned by that query:
public function index()
{
if($q = Input::get('q'))
{
if($subject = Subject::with('posts')->where('name', $q)->first())
{
$posts = $subject->posts;
}
else
{
$posts = array();
}
}
else
{
$posts = Post::all();
}
return View::make('posts.index', ['posts' => $posts]);
}

Resources