get included reply in a recursive relationship in laravel 5 - php

I have a table named replies contains all replies of a topic in a Forum laravel application.
**replies Table**
---------------
reply_id
content
topic_id
reply_by
embed_reply
created_at
updated_at
replies can have another reply Herself.for that there is embed_reply column that hold reply_id of included reply.
Now I want details of included reply would have existed on the parent Reply on fetching.
for that i add this Method to reply Model :
public function included_reply ()
{
return $this->with('replies', function ($query) {
$query->where('reply_id',$this->embed_reply);
});
}
And for fetching replies of a specific Topic, I wrote this :
$topic = Topic::whereTopicId($id)
->with([
'replies' => function ($query) {
$query->orderBy('created_at', 'desc');
}
, 'replies.included_reply'
])
->first();
all of this return bellow Error:
BadMethodCallException in Builder.php line 2071:
Call to undefined method Illuminate\Database\Query\Builder::replies()
and I do not know how to do that.
what is solution?

I got my answer in:
laracasts.com/discuss
This is relationship that I must be added:
public function embed()
{
return $this->belongsTo(Reply::class, 'embed_reply');
}
and this for fetching that :
$reply = Reply::find($reply_id);
$embed = $reply->embed;

Related

Laravel get topic that has last post

I'm trying to do that , get list of topics order by who has new post so I create a relation in topic model like that
public function latest_post()
{
return $this->hasOne(Post::class)->latest();
}
then I used the query like that
Topic::where('locale',$this->locale)->with('latest_post')->paginate(15)->sortByDesc('latest_post.created_at');
but it's giving me an error
Collection::render does not exist
so I change the sort to orderBy like that
Topic::where('locale',$this->locale)->with('latest_post')->orderBy('latest_post','DESC')->paginate(15);
but this also gives me another error
Unknown column 'latest_post' in 'order clause'
how can solve this issue?
Hmmm. Try this
$topic = Post::orderBy('created_at','desc')->first()->join('topics', 'topics.id', '=', 'posts.topic_id')->first();
Or in your post model:
public function Topic()
{
return $this->belongsTo(Topic::class, 'topic_id');
}
To get the last active topic:
$topic = Post::orderBy('created_at','desc')->first()->Topic;

How to constraint scope of hasMany relationship in a query in Laravel

I am working on a laravel based project comprising of Authors, Posts and Comments as models.
The relationship between the models is as follows:
Author -> hasMany Post
Post -> hasMany Comment.
The problem is as follows:
Authors made multiple posts, and all of them have multiple comments. I want to get the count of the Authors with comments from internal members only on their latest post.
My Models are shown below-
Author.php
public function post() {
return $this->hasMany('App\Post');
}
public function latestPost() {
return $this->hasOne('App\Post', 'latest_post_id', 'id')-
>orderBy('created_at', 'desc');
}
Post.php
public function comment() {
return $this->hasMany('App\Comment', 'post_id', 'id');
}
The query I am using to get the data are as follow:
$author_count = Author::whereHas('latestPost.comment', function ($query) {
$query->createdByInternal();
})->count();
When I execute this query, it considers all the Post models instead of only the latest Post and gives the authors who have a comment by an Internal member on any of their post.
However, on eager loading the latestPost query using "with" function, I get only the comments on the latest post.
$author = Author::with('latestPost.comment')->get();
Thanks in advance.
If you want to count authors who have posts with no created_by_internal
= null comments, use whereDoesntHave():
Author::whereDoesntHave('latestPost.comment', function ($query) {
$query->notCreatedByInternal();
})
->count();
In the relationship definition put foreign key before the id:
public function latestPost()
{
return $this->hasOne('App\Post', 'latest_post_id', 'id')->latest();
}
Also, change to scope to:
public scopeNotCreatedByInternal($query)
{
return $query->whereNull('created_by_internal');
}

Laravel get one value from eager load

I've got this query in Laravel that's returning all forums with some extra information:
return Forum::with(['messages.user' => function($query){
$query->select('id', 'name');
}])->withCount('messages')->paginate(10);
but now it eager loads all related messages as well but I only need the author from a message. How could I get this result?
Assuming the table you have for your Message model is messages and that it has the columns forum_id and user_id, and the table for your User model is users you could just define a belongsToMany and get the information that way:
public function users()
{
return $this->belongsToMany(User::class, 'messages')->distinct();
}
then:
return Forum::with(['users' => function($query){
$query->select('users.id', 'users.name');
}])->withCount('messages')->paginate(10);
Hope this helps!
Without eager loading,
//for a particular forum
//Get a forum entity
$forum = Forum::find($id);
// Get its messages as a Collection
$forumMessages = $forum->messages;
// Iterate over each message on the Collection to find its author. This will look for id in the User model based on the 'author_id' stored by you in the message table.The collection $forumMessages will now have the author's name. This is just to give you an idea. Structure accordingly to your needs.
$forumMessages->each(function ($message){
$message->author = User::find($message->author_id)->name;
});
Try using
return Forum::with([
'messages' => function ($messages) {
return $messages->with([
'user' => function ($user) {
return $user->select('id', 'name');
}
])->select('id', 'author'); // you get author from messages
}
])->withCount('messages')->paginate(10);
This also eager loads but you only get id and author. id is needed to make the relationship with user.

laravel eloquent sort by relationship

I have 3 models
User
Channel
Reply
model relations
user have belongsToMany('App\Channel');
channel have hasMany('App\Reply', 'channel_id', 'id')->oldest();
let's say i have 2 channels
- channel-1
- channel-2
channel-2 has latest replies than channel-1
now, i want to order the user's channel by its channel's current reply.
just like some chat application.
how can i order the user's channel just like this?
channel-2
channel-1
i already tried some codes. but nothing happen
// User Model
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies'])
->orderBy('replies.created_at'); // error
}
// also
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies' => function($qry) {
$qry->latest();
}]);
}
// but i did not get the expected result
EDIT
also, i tried this. yes i did get the expected result but it would not load all channel if there's no reply.
public function channels()
{
return $this->belongsToMany('App\Channel')
->withPivot('is_approved')
->join('replies', 'replies.channel_id', '=', 'channels.id')
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'ASC');
}
EDIT:
According to my knowledge, eager load with method run 2nd query. That's why you can't achieve what you want with eager loading with method.
I think use join method in combination with relationship method is the solution. The following solution is fully tested and work well.
// In User Model
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved');
}
public function sortedChannels($orderBy)
{
return $this->channels()
->join('replies', 'replies.channel_id', '=', 'channel.id')
->orderBy('replies.created_at', $orderBy)
->get();
}
Then you can call $user->sortedChannels('desc') to get the list of channels order by replies created_at attribute.
For condition like channels (which may or may not have replies), just use leftJoin method.
public function sortedChannels($orderBy)
{
return $this->channels()
->leftJoin('replies', 'channel.id', '=', 'replies.channel_id')
->orderBy('replies.created_at', $orderBy)
->get();
}
Edit:
If you want to add groupBy method to the query, you have to pay special attention to your orderBy clause. Because in Sql nature, Group By clause run first before Order By clause. See detail this problem at this stackoverflow question.
So if you add groupBy method, you have to use orderByRaw method and should be implemented like the following.
return $this->channels()
->leftJoin('replies', 'channels.id', '=', 'replies.channel_id')
->groupBy(['channels.id'])
->orderByRaw('max(replies.created_at) desc')
->get();
Inside your channel class you need to create this hasOne relation (you channel hasMany replies, but it hasOne latest reply):
public function latestReply()
{
return $this->hasOne(\App\Reply)->latest();
}
You can now get all channels ordered by latest reply like this:
Channel::with('latestReply')->get()->sortByDesc('latestReply.created_at');
To get all channels from the user ordered by latest reply you would need that method:
public function getChannelsOrderdByLatestReply()
{
return $this->channels()->with('latestReply')->get()->sortByDesc('latestReply.created_at');
}
where channels() is given by:
public function channels()
{
return $this->belongsToMany('App\Channel');
}
Firstly, you don't have to specify the name of the pivot table if you follow Laravel's naming convention so your code looks a bit cleaner:
public function channels()
{
return $this->belongsToMany('App\Channel') ...
Secondly, you'd have to call join explicitly to achieve the result in one query:
public function channels()
{
return $this->belongsToMany(Channel::class) // a bit more clean
->withPivot('is_approved')
->leftJoin('replies', 'replies.channel_id', '=', 'channels.id') // channels.id
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'desc');
}
If you have a hasOne() relationship, you can sort all the records by doing:
$results = Channel::with('reply')
->join('replies', 'channels.replay_id', '=', 'replies.id')
->orderBy('replies.created_at', 'desc')
->paginate(10);
This sorts all the channels records by the newest replies (assuming you have only one reply per channel.) This is not your case, but someone may be looking for something like this (as I was.)

Laravel hasMany relation count number of likes and comments on post

The code:
$posts = Jumpsite::find($jid)
->posts()
->with('comments')
->with('likes')
->with('number_of_comments')
->with('number_of_likes')
->where('reply_to', 0)
->orderBy('pid', 'DESC')
->paginate(10);
Each post has a comment and likes. I only display a few of the comments initially to avoid large loads. But I want to show how many the total comments and likes for each post. How do I do this?
Model code:
public function likes()
{
return $this->hasMany('Like', 'pid', 'pid');
}
public function comments()
{
return $this->hasMany('Post', 'reply_to', 'pid')->with('likes')->take(4);
}
public function number_of_likes()
{
return $this->hasMany('Like', 'pid', 'pid')->count();
}
Note:
This is an API. All will be returned as JSON.
update
The return
Post
author_id
message
Comments(recent 4)
user_id
message
post_date
Number_of_likes
Likes
user_id
Number_of_total_comments
Number_of_total_likes
update
How I return the data
$posts = $posts->toArray();
$posts = $posts['data'];
return Response::json(array(
'data' => $posts
));
Just by using that I get all the data i want in the json. But I also want to add the total counts.
update
protected $appends = array('total_likes');
public function getTotalLikesAttribute()
{
return $this->hasMany('Like')->whereUserId($this->uid)->wherePostId($this->pid)->count();
}
but getting the error:
Unknown column 'likes.post_id'
error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'likes.post_id' in 'where clause' (SQL: select count(*) as aggregate from `likes` where `likes`.`deleted_at` is null and `likes`.`post_id` = 4 and `pid` = 4 and `uid` = 1)
You can use that following code for counting relation model result.
$posts = App\Post::withCount('comments')->get(); foreach ($posts as $post) { echo $post->comments_count; }
And also set condition with count like this
$posts = Post::withCount(['votes', 'comments' => function ($query) { $query->where('content', 'like', 'foo%'); }])->get();
In your model place the following accessors:
Count total Likes:
public function getTotalLikesAttribute()
{
return $this->hasMany('Like')->whereUserId($this->author_id)->count();
}
Count total comments:
From your description, i can see, you have retrieving the number of posts as comments
public function getTotalCommentsAttribute()
{
return $this->hasMany('Post')->whereUserId($this->author_id)->count();
}
Now, from your controller:
$post = Jumpsite::find($jid);
// total comments
var_dump( $post->total_comments );
// total Likes
var_dump( $post->total_likes );
Count comments for all blog posts in laravel
Step 1: Place this code inside your ‘Post’ model.
// Get the comments for the blog post.
public function comments()
{
return $this->hasMany('App\Comment');
}
Step 2: Place this code inside your ‘PostController’ controller.
$posts= Post::all();
return view('home')->with('posts', $posts);
Step 3: Then count comments for all posts using the below code.
#foreach($posts as $row)
{{$row->comments->count()}}
#endforeach
See details here: http://www.pranms.com/count-comments-for-all-blog-posts-in-laravel/
Just single change to get results of the count
In relation
public function getTotalLikesAttribute(){
return $this->hasMany('Like')->where('author_id',$this->author_id)->count();
}
In the view
$post->getTotalLikesAttribute()

Categories