Laravel display latest comment of a post - laravel

I have a table of threads, and on the index page I want to display the latest comment of the thread. So far I have selected the latest comment and try to display it on the page, but it only displays the latest comment overall, not the latest for the specific post
So my ThreadsController looks like this right now, selecting all the comments and displaying the latest one first.
public function index()
{
$threads = Thread::latest()->paginate(10);
$latestComment = Comment::latest()->first();
return view('threads.index', compact('threads', 'latestComment'));
}
Thread model
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
Comment Model
public function user() {
return $this->belongsTo(User::class);
}
public function thread()
{
return $this->belongsTo(Thread::class);
}
public function commentable() {
return $this->morphTo();
}
So how do I select the latest comment from the specific thread and display it on the index?
Edit:
Controller:
public function index()
{
$threads = Thread::latest()->with('comments')->paginate(10);
return view('threads.index', compact('threads'));
}
Index blade:
#foreach($threads as $thread)
{{ $thread->latestComment->user->name }}
#endforeach
Comments table migration
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->string('body');
$table->unsignedBigInteger('commentable_id');
$table->string('commentable_type');
$table->timestamps();
});
}

You have defined your relationship between a Thread and Comment correctly, but you're not making use of that relationship in the index function of your ThreadsController. You're just grabbing the latest 10 Threads and the most recent Comment, but not the comment related to your Thread.
What you're wanting is to get all your Threads and pass them to your view with their latest Comment.
You can use eager loading to attach your Comment relationships to your models.
Thread::latest()->with('comments')->paginate(10);
That will grab your 10 latest Threads along with their Comments. So in your view you could do something like:
#foreach ($threads as $thread)
{{ $thread->comments()->latest('id')->first()->comment }}
#endforeach
Whilst this works, it's a bit verbose. So what you can do is piggy back off your comments function to return just the most recent Comment as a relationship.
class Thread extends Model
{
use HasFactory;
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
public function latestComment()
{
return $this->morphOne(Comment::class, 'commentable')->latest('id');
}
}
This gives you shorter method for accessing the latest Comment for a Thread.
So going back to your scenario;
In your ThreadsController.php
public function index()
{
$threads = Thread::latest()->with('latestComment')->paginate(10);
return view('threads.index', compact('threads'));
}
Then in your index.blade.php file
#foreach ($threads as $thread)
{{-- Access whatever properties you have on your comment --}}
{{ $thread->latestComment->id }}
#endforeach

You can create a relation between tread and comment.
And then in blade you cam do something like this:
$thread->comments->latest()->first()

Comment::all()->orderBy('id', 'DESC')->get()

Related

Logic for Up- and Down Voting System to Avoid Multiple Voting of an Item by a User in PHP Laravel

I am thinking about what is the most resource efficient logic for an up and down voting system. A user can vote a post and its comments up- and down. For this I have implemented a polymorphic relationship between the models vote, post and comment.
My problem is: I want to list all posts with the title and the votes on a post overview page. The up and down vote buttons can be used. As soon as a (logged in) user votes for a post, it should be increased or decrease by 1. The button will be highlighted so that the user can see how he has already voted for this post. If he clicks the same button again, the vote should be undone.
My approach
$posts = Post::with('votes')->get();
Gives me posts with the relation votes and the corresponding votes. Now I could iterate the vote collection in the blade for each individual post and catch via the user id whether he has already voted.
Question(s)
My goal is actually a list only with the posts where I add fields in the backend like hasVoted, voteDirection. But only for the logged in user. 1. that I don't have to do a nested iteration and 2. that I don't have unnecessary overhead (votes from other users).
// Vote Model
class Vote extends Model
{
public function voteable()
{
return $this->morphTo();
}
public function user()
{
return $this->belongsTo(User::class);
}
}
// Post Model
class Post extends Model
{
public function publisher()
{
return $this->belongsTo(User::class, 'publisher_id', 'id');
}
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
public function votes()
{
return $this->morphMany(Vote::class, 'voteable');
}
}
// User Model
class User extends Authenticatable
{
public function comments()
{
return $this->hasMany(Comment::class);
}
public function votes()
{
return $this->hasMany(Vote::class);
}
}
// Comment Model
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
public function user()
{
return $this->belongsTo(User::class);
}
public function votes()
{
return $this->morphMany(Vote::class, 'voteable');
}
}
// Vote migration
Schema::create('votes', function (Blueprint $table) {
$table->id();
$table->morphs('voteable');
$table->integer('user_id')->nullable();
$table->integer('vote')->default(1);
$table->timestamps();
});
// Post migration
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->integer('publisher_id'); // user
$table->string('slug');
$table->string('title');
$table->string('votes')->default(json_encode(['up' => 0, 'down' => 0]));
$table->timestamps();
});
I would approach this issue simply with a component of 2 radio buttons. Then via names attach the values and then there is no way that one user has voted 2 times on the same post. I hope this helps you.

Laravel 8 - Get Visitors User Details

hoping something can help. I've been struggling with this one for a while and not sure if its something obvious that I am missing.
I have a user profile setup, where when someone views it, it stores the user id of that person in a table. I would like to create a "Who's visited me" where it will show the user details of that person who has visited their profile.
So far everything is working but I cannot get the Who's visited me working to show the details.
Here is what I have so far
User Model
public function profile()
{
return $this->hasOne(Profile::class);
}
public function profileViews(){
return $this->hasMany(ProfileView::class, 'profile_id');
}
ProfileView Model
protected $fillable = [
'profile_id',
'visitor_id',
];
public function users()
{
return $this->belongsTo(User::class);
}
Profile Controller
public function profile(User $user)
{
$profile_id = $user->id;
ProfileView::updateOrCreate(['visitor_id' => Auth::user()->id, 'profile_id' => $profile_id, 'updated_at' => now()]);
return view('members.users.profile', compact('user' ));
}
Just incase you need it, my migration for the Profile Visitors Tables
Profile View Table
public function up()
{
Schema::create('profile_views', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('visitor_id');
$table->unsignedBigInteger('profile_id');
$table->timestamps();
$table->foreign('visitor_id')
->references('id')
->on('users')
->onDelete('cascade');
});
}
Here is what I have in the Who has visited me (this is where I am struggling so was playing about
#foreach(Auth::user()->profileViews as $view)
{<li>{{ $view->user->name }} </li>}
#endforeach
So we only have two models here, User and Profile, with a many-to-many relationship. ProfileView is actually just a pivot between the two, so a class definition isn't needed. But assuming the timestamp is something you'd like to get access to, you'll need to make allowances for that.
I'd suggest something like this:
class User extends Model {
public function profile() {
return $this->hasOne(Profile::class);
}
}
class Profile extends Illuminate\Database\Eloquent\Model {
public function user() {
return $this->belongsTo(User::class);
}
public function views() {
return $this->hasMany(User::class, 'profile_views', 'profile_id', 'visitor_id')
->withPivot('created_at');
}
}
Now, to add a profile view you can edit your controller method like this (I assume the $user is the user whose profile is being viewed.) Rather than creating an instance of the pivot, you attach the relationship. I've detached previous relationships, assuming you only want to keep the most recent one.
public function profile(User $user)
{
$user->profile->views()->detach(Auth::id());
$user->profile->views()->attach(Auth::id());
return view('members.users.profile', compact('user'));
}
And to retrieve the information:
<ul>
#foreach(Auth::user()->profile->views as $view)
<li>{{ $view->name }} # {{ $view->pivot->created_at }} </li>
#endforeach
</ul>
This is all a bit verbose due to your naming not matching Laravel conventions, and your choice to store profile_id in the pivot table instead of user_id but should do what you need.

View is not returning attributes stored in database

I am attempting to return which user posted a comment, along with the time they posted the comment.
I have a model for comments
use Illuminate\Database\Eloquent\Model;
class comments extends Model
{
protected $guarded = [];
public function adjustments()
{
return $this->belongsToMany(User::class, 'adjustments')
->withTimestamps();
}
}
A pivot table which tracks which users posted which comments
public function up()
{
Schema::create('adjustments', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id')->index();
$table->unsignedInteger('comments_id')->index();
$table->timestamps();
});
}
An empty adjustments model
use Illuminate\Database\Eloquent\Model;
class adjustment extends Model
{
//
}
In php artisan tinker when $comment = App\comment::first(); and $user = App\user::first(); I'm able to successfully attach a user_id to a comment_id using $comment->adjustments()->attach($user->id) and calling App\adjustments::all(); will correctly return
=> Illuminate\Database\Eloquent\Collection {#2935
all: [
App\adjustment {#2941
id: 1,
user_id: 1,
comments_id: 1,
created_at: "2019-11-16 17:05:10",
updated_at: "2019-11-16 17:05:10",
},
],
}
When I'm trying to show the adjustments in my view, I get an empty list.
#foreach ($comment->adjustments as $user)
<li>{{$user->name}} on {{$user->pivot->updated_at}}</li>
#endforeach
In my products controller (a user makes comments on products) I have the following code in my show function
public function show(products $product, comments $comment, User $user)
{
return view ('products.show', compact('product'), compact('comment'));
}
Here you don't need a pivot table. Becasue here you has one to many relation. User can create many comment. & one comment is belongs one user.In user model add this
public function comments()
{
return $this->hasMany(Comment::class);
}
& comments table u have a foreign key user_id.
in comment model
public function user()
{
return $this->belongsTo(User::class,'user_id','id')
}
public function show(products $product, comments $comment, User $user)
{
$comments=Comment::with('user')->all();
return view ('products.show', compact(['product','comments']));
}
#foreach ($comments as $comment)
<li>{{$comment->user->name}} on {{$comment->updated_at}}</li>
#endforeach
Then you can acces all comments table data with a user

how to fetch commented name on posts in laravel 5.4

i followed the convention to avail the required data with compact method to view
Schema User Table
id,name,email,password
Schema post Table
id,user_id,title,body,photo
Schema comment Table
id,post_id,user_id,comments,photo
in user model
public function post()
{
return $this->hasMany(post::class);
}
public function comments()
{
return $this->hasMany(comment::class);
}
in post model
Public function User()
{
return $this->belongsTo('App\User');
}
public function comments()
{
return $this->hasMany(comment::class);
}
in comment model
public function posts()
{
return $this->belongsTo('App\post');
}
in controller
$list = User::with('post','comments')->paginate(1);
return view('dynamic',compact('list'));
in view
#foreach($lists->comments as $comments)
{{$comments->name}}
#endforeach
post showing successfully according to users
and comments showing successfully according to posts
but commented name not showing up // please help
how to make relationship for fetch comments with commented user name
please help thanks in advance
you have to change your code as below
#foreach($lists->comments as $comments)
{{$comments->posts->user->name}}
#endforeach
or
you need to add relationship to your comment model as below
Public function User()
{
return $this->belongsTo('App\User');
}
after that you need to change your code as below
#foreach($lists->comments as $comments)
{{$comments->user->name}}
#endforeach
change code as below in your view:
#foreach($lists->comments as $comments)
{{$comments->User->name}}
#endforeach

Counting total posts by a user in the blade view

I have sent a collection of all posts in my blog to my index view and then used the following code to count the total posts made by each user.
<p class="joined-text">Posts: {{count(App\Posts::where('user_id', $post->user->id)->get())}}</p>
Is this bad practice to do this from within the blade view? If it is how would I achieve this?
Models
class Posts extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany(Comments::class, 'post_id');
}
}
class User extends Authenticatable
{
public function posts()
{
return $this->hasMany('\App\Posts::class');
}
public function comments()
{
return $this->hasMany(Comments::class);
}
}
Simple solution:
<p class="joined-text">Posts: {{ App\Posts::where('user_id', $post->user_id)->count() }}</p>
Updated
Complete and better solution:
Post.php:
public function user(){
return $this->belongsTo(App\User::class);
}
User.php:
public function posts(){
return $this->hasMany(App\Post::class);
}
public function getPostsCountAttribute(){
return $this->posts()->count();
}
blade:
<p class="joined-text">Posts: {{ $post->user->posts_count }}</p>
Yes it is bad.
You could use relationships if have the user_id field in posts table
class User extends Model
{
public function posts()
{
return $this->hasMany('App\Post');
}
}
In controller
return view('sth')->with(['posts'=>$user->posts]);
Then in view
$posts->count();
Or just getting counts if you don't need posts
$postCount = $user->posts()->count();

Resources