Laravel Get data One to Many Relationship with conditions - php

how to display data one too many relationships with conditions into view.
I have a blog post that has a comment, but this comment has a condition that published or not.
here my Post Model
...
public function comments()
{
return $this->hasMany(Comment::class);
}
...
on my code above I can simply use $post->comments to show all the comments.
like I said before, I need only show comments with published is true
but how to add conditions?...

You can achieve this by getting the post with it's published comments;
$post = Post::where('id', $id)->with('comments', function ($q) {
$q->where('published', true);
})->first();
Then when in the view you call $post->comments, you will only get the published one.
Alternatively, if you really want to you can update your Model to have a published comments relationship, but this is less advised.
public function publishedComments()
{
return $this->hasMany(Comment::class)->where('published', true);
}

You can get only published comments by using
$this->post->comments()->where('published', true)->get()
Check the Documentation
Of course, since all relationships also serve as query builders, you can add further constraints to which comments are retrieved by calling the comments method and continuing to chain conditions onto the query: $comment = App\Post::find(1)->comments()->where('title', 'foo')->first();

Related

Calling Laravel relationship with aggregate

If I have a Laravel 5.5 model called User that hasMany Posts and each Post hasMany Hits, is there an aggregate function I can call to get the total number of Hits for a User across all Posts, where the Hit was created in the last week?
It seems like there may be a clever way to do it besides doing something like
$hits = $user->posts()->hits()
and then looping over those hits to check created date.
In this case it seems like raw sql would be better, but I figured there may be an Eloquent way to handle a situation like this.
I think the right solution is just to use a HasManyThrough relationship to grab all the Hit rows, joined through the posts table.
So it'd look like this on the User model (roughly):
return $this->hasManyThrough(
Hit::class,
Post::class
// if you have non-standard key names you can specify them here-- see docs
);
Then when you have your User model you can just call $user->hits to get a collection of all the associated hits through all the user's Posts
You can add the code below to your Post model.
protected static function boot()
{
parent::boot();
static::addGlobalScope('hitCount', function ($builder) {
$builder->withCount('hits');
});
}
It automatically provides a field hits_count whenever you fetch a post.
$post = Post::first();
$hits = $post->hits_count; //Count hits that belongs to this post
You can read the documentation here to customize it to your need.
Set HasManyThrough relation in the User model:
public function hits()
{
return $this->hasManyThrough('App\Models\Hits','App\Models\Posts','user_id','post_id','id');
}
then you can do this:
$reults = $user->hits()->where('hits_table_name.created_at', '>=', Carbon::today()->subWeek())->count();
HasManyThrough Link
Use DB::enableQueryLog(); and DB::getQueryLog(); to see if executed SQL Query is correct;

Trying to get data via related table in Laravel

What I'm trying to do is to show the posts that have been saved by the user in the profile. I will try to explain it as good as possible refering to my code. So:
public function userProfil($id)
I have the profile function which get the data from userprofile table. and inside I have the following code for saved data:
$authed = User::find($id);
$savedarticles = $authed->mysaves;
$allsavings = DB::select("Select * from article where id=$savedarticles->id");
But this code does not work like this anyway. I can do this instead:
$authed = User::find($id);
$savedarticles = $authed->mysaves;
But when I try to get articles from article table with the article_id of mysaves, it does not work such as this:
$allsaved= DB::table('article')->where('id', $savedarticles->article_id);
the error it gives is like:
Property [article_id] does not exist on this collection instance.
although savearticle table has article_id I can output it without the line above and in view I get them as:
#foreach($savedarticles as $savedarticle)
<p>{{$savedarticle}}</p>
#endforeach
it gives me everything that is in the savearticle table and I can get do savedarticle->article_id and get article_id but can't get it in controller.
I am using Laravel 5.4.
The error message Property [article_id] does not exist on this collection instance. means you are trying to get an attribute of a single instance but from a collection.
For example the collection could be like
[$article1, $article2, $article3]
therefore what you tried to do is something similar to
[$article1, $article2, $article3]->article_id
You are trying to get an attribute from a collection instead of a single instance.
As for your query, you can use where in sql statement to search for rows that match any item in an array
$allsaved= DB::table('article')->whereIn('id', $savedarticles->pluck('article_id')->all());
What I have understood is that A USER has many POSTS and a POST belong to an article.
If this is true then you have to do following.
1: In USER model define a relation to get all posts. like below.
public function posts() {
// Foreign key will be a key that is stored in posts table and represent the user MAY BE: user_id
$this->hasMany(Posts::class, 'foreign_key', 'local_key')
}
This will allow you to get all posts belong to a user.
2: In posts, model defines a user relation like below.
public function user() {
$this->belongsTo(User::class, 'foreign_key', 'local_key');
}
This will allow you to get a post User;
3: Now in your controller you will have something like this.
public function show($user_id) {
// find a user with posts as eager loading(to avoid query again)
$user = User::with(['posts'])->where('id', $user_id)->first();
// get all posts that belong to this user
$posts = $user->posts;
}
In controller show($user_id) method you will have a user data as well as user posts data. Now if you want to get a post relations then simply define as below. let say a post belongs to an article as well.
4: In posts, model defines a relation to get an article.
public function article() {
// This will allow you to get a post artcle
$this->belongsTo(Article::class, 'foreign_key', 'local_key');
}
Now you can get the article as well while finding a user. please see below. I am rewriting controller show action to give you a better understanding.
5: Get a user with user_id
public function show($user_id) {
// find a user with posts as eager loading(to avoid query again)
// eager loading for posts & post child, this will give you NOSQL at runtime and all data will come from one query.
$user = User::with(['posts', 'posts.article'])->where('id', $user_id)->first();
// get all posts that belong to this user
$posts = $user->posts;
foreach($posts as $post) {
$article = $post->article; // Child relation of post.
}
}
Hope you will understand the flow, you have to make sure models relation to work it perfectly. If you need further help please let me know.

Fetching and filtering relations in Laravel Eloquent

I have the following models in Eloquent: groups, threads, comments and users. I want to find all comments in a specific group from a specific user.
This is my current approach:
$group->threads->each(function ($thread) use ($user_id)
{
$user_comments = $thread->comments->filter(function ($comment) use ($user_id)
{
return $comment->owner_id == $id;
});
});
This looks ugly as hell, is probably slow as hell, and I just want to get rid of it. What is the fastest and most elegant way in Eloquent to get to my result set?
If a group hasMany threads, and a thread hasMany comments, you can add another relationship to group: group hasMany comments through threads.
On the group:
public function comments() {
return $this->hasManyThrough('Comment', 'Thread');
}
Now, you can get the comments in a group by $group->comments;
From here, you can tack on the requirement for the user:
$user_comments = $group->comments()->where('owner_id', $user_id)->get();
If you want, you can extract the where out into a scope on the Comment.
patricus solution pointed me in the right direction. I cross posted my question to the laracasts Forum and got a lot of help from Jarek Tkaczyk who also frequently visits this site.
hasManyThrough() for the Group model is the way to go:
public function comments()
{
return $this->hasManyThrough('ThreadComment', 'Thread');
}
There a couple of caveats, though:
Use the relation object instead of a collection ($group->comments(), NOT $group->comments)
If you use Laravel’s soft deletes you can’t just change the get() to a delete(), because you’ll get an ambiguity error for the column updated_at. You can’t prefix it either, it’s just how Eloquent works.
If you want to delete all comments from a specific user in a specific group you’ll have to do it a little bit differently:
$commentsToDelete = $group->comments()
->where('threads_comments.owner_id', $id)
->select('threads_comments.id')
->lists('id');
ThreadComment::whereIn('id', $commentsToDelete)->delete();
You basically fetch all relevant IDs in the first query and then batch delete them in a second one.

Laravel use link table on model

So I have the tables tags, posts and a link table for these
Now I want to get all the tags from the current post.
Now I want to get all the tags related to this post.
I made a model "Tag" (with no functions yet, just extending Eloquent)
How can I use this model to get all the tag names/titles based on the current post id, or do I need a seperate model for the Linking table(Which seems incorrect to me)?
I'm kinda lost in it right now, probably due to too much searching.
Anyone can help me out here?
SOLVED
$post = Post::where('id', $id)->first();
$tags= $post->tags;
Tags function in the Post model:
public function tags()
{
return $this->belongsToMany('Tag');
}
Add the following function to your Post model
public function tags()
{
return $this->belongsToMany('Tag');
}
Now you can call $post->tags()->getResults() to get all tags of a post.
Corresponding docs: http://laravel.com/docs/4.2/eloquent#many-to-many

count() returning soft deleted items in laravel

I have a model Comments that uses soft deleting: it has a one-to-many relationship with my Post model.
My site will have a native mobile app associated with it and I need to send a count of the comments to it when I send the information about a post and for some reason it is returning the count WITH the soft deleted items.
I've got the Post array working and sending the comment count using
protected $appends = array('score','commentcount', 'ups', 'downs');
and
public function getCommentcountAttribute()
{
return DB::table('comments')
->where('post_id',$this->id)
->where('deleted_at','=',NULL)
->count();
}
in my post model. I've also tried
public function getCommentcountAttribute()
{
return $this->comments()->count();
}
and
public function getCommentcountAttribute()
{
return $this->comments()->whereNull('deleted_at')->count();
// also: return $this->comments()->where('deleted_at',NULL)->count();
}
also when defining the relationship I've tried adding ->whereNUll('deleted_at') to both the ->hasMany('Comment') and the ->belongsTo('Post') with no luck.
I've checked the database and ran the SQL I'm expecting Fluent and Eloquent to be generating which is
SELECT * FROM `comments` WHERE post_id=31 and deleted_at=null
(31 being the post I'm using to test). Nothing is working. Let me know if you guys need to see anymore specific functions as I'd rather not post my entire models.
I was able to make it work with ->whereRaw('deleted_at = ?',array(NULL)). That seems pretty hacky to me though. I'd gladly accept a better answer.
You have to enable Soft Deleting in your model.
class Comment extends Eloquent {
protected $softDelete = true;
}
That's it.
And you don't need to include the following where clauses in your queries:
return DB::table('comments')
->where('post_id',$this->id)
//->where('deleted_at','=',NULL) // no needed, Laravel by default will include this condition
->count();
public function getCommentcountAttribute()
{
// remove ->whereNull('deleted_at')
return $this->comments()->count();
}
Change your code to:
return \App\Comments::count();
Soft delete works only on models, not queries:
class Comment extends Eloquent
{
protected $softDelete = true;
}
Whilst this is an old post the following should hopefully be helpful with others if they come across this.
For laravel V5 and above.
Add use SoftDeletes; to your model.
If you are trying to get the count that includes soft deletes use the following:
Model::withTrashed()->'yourquery'
If you do not want soft deleted records included then you can follow the normal convection.
Model::select()->get();

Categories