How can I call comments + user information that is specified to the specific post the comment was created under. For example:
/articles/8
new comment created with user_id = 3
Page will show Username + comment + created_at
This is my current code:
Post Show Page
<%= #post.title %>
<%= render '/comments/form' %>
<% #post.user.comments.reverse.each do |comment| %>
<li>
<%= comment.user.email %>
<%= comment.comment %>
</li>
<% end %>
I grab user information associated with the comment but the problem is, it's listing all the comments. How do I make only comments with article_id of 8 for example appear.
Post Controller
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save!
flash[:notice] = "Successfully created..."
redirect_to posts_path
else
flash[:danger] = "failed to add a post"
render 'new'
end
end
def show
#comment = Comment.new
#comments = Comment.find(params[:id])
end
Comment Controller
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
if #comment.save
flash[:notice] = "Successfully created..."
redirect_to post_path(#post)
else
flash[:alert] = "failed"
redirect_to root_path
end
end
Routes
resources :sessions, only: [:new, :create, :destroy]
resources :users
resources :posts do
resources :comments
end
Schema of the comments
create_table "comments", force: :cascade do |t|
t.text "comment"
t.integer "user_id"
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I am assuming that your model looks like
class Comments < ApplicationRecord
belongs_to :user
belongs_to :post
end
class User < ApplicationRecord
has_many :posts
has_many :comments
end
class Post < ApplicationRecord
belongs_to :user
has_many :comments
end
We want to get all the comments for a post so we we can do something like this
# Note that I took the user from this line
<% #post.comments.reverse.each do |comment| %>
<li>
<%= comment.user.email %>
<%= comment.comment %>
</li>
<% end %>
I hope that this should work.
Related
hey everyone so I'm working on a small blog and there is a new section a want to add. An "editor's choice" section so basically i want to add a small div that contains 3 randomly pick posts from the blog. The problem is I do not know how to show only three randomely select titles from all the posts. I tried #posts.sample(3) and that shows the entire record of the Post. I just want to show the title only.
I want to show the editors choice on the posts index view
Schema
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id"
t.string "nickname"
end
Posts_controller
class PostsController < ApplicationController
before_action :current, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all
end
def show
end
def new
#post = Post.new
end
def create
#post = Post.new(posts_params)
#post.user = current_user
if #post.save
redirect_to posts_path
else
render :new
end
end
def edit
end
def update
#post.user = current_user
#post.update(posts_params)
#post.save
redirect_to posts_path
end
def destroy
if #post.destroy
redirect_to posts_path
end
end
private
def posts_params
params.require(:post).permit(:title, :content, :user_id, :image, :nickname)
end
def current
#post = Post.find_by(id: params[:id])
end
end
post_index.html.erb
<%= render "layouts/blog_header" %>
<% #posts.each do |post| %>
<div class="posts">
<div>
<% unless !post.image.attached? %>
<%= image_tag post.image, class: "post-index-image" %><br>
<% end %>
</div>
<p class="post-index-title"><%= link_to post.title, post_path(post.id), class: "post-index-title" %></p>
<p>Published by: <strong><%= post.user.nickname %></strong></p>
</div>
<% end %>
<%= #posts.sample(3) %>
Thanks!
I would start with something like this when you are using PostgreSQL:
#posts.order('RANDOM()').limit(3)
or when you are on MySQL:
#posts.order('RAND()').limit(3)
I think it should do what you want:
#posts.sample(3).pluck(:title)
I'd go with
#posts.pluck(:title).sample(3)
More efficient than
#posts.sample(3).pluck(:title)
Thanks for visiting
Whenever I click on Add a friend I get "friend added" but upon checking my console it says Friendship id: 10, user_id: 2, friend_id: 0, created_at: "2015-09-11 05:05:27", updated_at: "2015-09-11 05:05:27"> meaning it's adding but not associating with a friend_id. Can someone help me figure out why it's not saving friend_id whenever I click add a friend. Thank you for all your help.
Schema.rb
create_table "friendships", force: true do |t|
t.integer "user_id"
t.integer "friend_id"
t.datetime "created_at"
t.datetime "updated_at"
end
Routes.rb
root "users#index"
resources :friendships, only: [:create, :destroy]
resources :sessions, only: [:new, :create, :destroy]
resources :users
Users/show
<% #users.each do |user| %>
<% if user.user_name != current_user.user_name %>
<% if #friendshiplink.nil? %>
<%= user.user_name %>
<%= link_to "Add Friend", friendships_path(friend_id: user), method: :post %>
<% else %>
<%= link_to(
("Unfollow"),
"/friendships/#{ #friendshiplink.id }",
method: :delete) %>
<% end %>
<% end %>
<% end %>
User Model
has_many :friendships
has_many :friends, through: :friendships
has_many :inverse_friendships, class_name: "Friendship", foreign_key: "friend_id"
has_many :inverse_friends, through: :inverse_friendships, source: :user
Friendship Model
belongs_to :user
belongs_to :friend, class_name: "User"
User Controller
def show
#users = User.all
#user = User.friendly.find(params[:id])
current_user
if #current_user
#followerlink = Follower.where(leader_id: #user.id,
follower_id: #current_user.id).first
#friendshiplink = Friendship.where(user_id: #user.id,
friend_id: #current_user.id).first
end
end
Friendship Controller
def create
#friendship = current_user.friendships.build(friend_id: params[:friend_id])
if #friendship.save
flash[:notice] = "Added friend."
redirect_to root_url
else
flash[:error] = "Unable to add friend."
redirect_to root_url
end
end
def destroy
#friendship = current_user.friendships.find(params[:id])
#friendship.destroy
flash[:notice] = "Removed friendship."
redirect_to current_user
end
def friendship_params
params.require(:friendship).permit(:friend_id, :user_id)
end
Thanks again for all the help. If there are any question or I'm missing a part of my code which may be needed, Please ask.
This may not be the issue but give it a try:
<%= link_to "Add Friend", friendships_path(friend_id: user.id), method: :post %>
OR
<%= link_to "Add Friend", { action: :create, controller: 'friendship',:friend_id => user.id }, method:"post" %>
Also to make sure of error please add your params from your console
UPDATE: do it in simplest way
def create
#friendship = current_user.friendships.build
#friendship.friend_id = params[:friend_id]
#friendship.user_id = current_user.id
if #friendship.save
building my first blog, head is spinning.
I am trying to show comments in my dashboard view. I am able to pull in the comments and the ID but what I would really like to do is also put the title of the post the comment lives and link to it. From my very amateur understanding I think I am not making the connection from comments to post.
Here is what I got...
Post model has_many :comments
Comments model belongs_to :posts
routes
resources: posts do
resources: comments
end
Dashboard Controller:
class DashboardController < ApplicationController
before_filter :authorize, only: [:index]
def index
#posts = Post.all
#comments = Comment.all
end
end
Comments Controller:
class CommentsController < ApplicationController
before_filter :authorize, only: [:destroy, :show]
def create
#post = Post.find(params[:post_id])
#comment =
#post.comments.create(comments_params)
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def comments_params
params.require(:comment).permit(:commenter, :body)
end
end
Posts Controller:
class PostsController < ApplicationController
before_filter :authorize, only: [:edit, :update, :destroy, :create, :new]
def index
#posts = Post.where(:state => "published").order("created_at DESC")
end
def new
#post = Post.new
end
def show
#post = Post.find(params[:id])
redirect_to_good_slug(#post) and return if bad_slug?(#post)
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to dashboard_path
else
render 'new'
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(post_params))
redirect_to dashboard_path
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to dashboard_path
end
private
def post_params
params.require(:post).permit(:title, :text, :author, :short, :photo, :state)
end
end
Dashboard View:
</div>
<div class="fluid dash">
<div class="gridContainer clearfix">
<div class="fluid dash_post_list">
<h3>Manage Posts</h3>
<% #posts.each do |post| %>
<div class="fluid list-items">
<div class="fluid list_each">
| <%= post.id %> | <%= link_to post.title, post %>, <%= post.created_at.strftime('%b, %d') %> - <%= post.state %>
</div>
<div class="fluid crud">
<i class="fa fa-pencil-square-o"></i><%= link_to 'Edit', edit_post_path(post) %>
<i class="fa fa-times-circle"></i><%= link_to 'Delete', post_path(post),
method: :delete, data: { confirm: 'Are you sure?' } %>
</div>
</div>
<% end %>
</div>
<div class="fluid dash_right">
<div class="fluid create_new">
<h3>Create New</h3>
<%= link_to 'New Post', new_post_path %>
</div>
<div class="fluid alerts">
<h3>Alerts!</h3>
<% #comments.each do |comment| %>
<%= comment.post_id %>
<% end %>
</div>
</div>
</div>
</div>
DB Schema
create_table "comments", force: true do |t|
t.string "commenter"
t.text "body"
t.integer "post_id"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "flag", default: false
end
create_table "posts", force: true do |t|
t.string "title"
t.text "text"
t.datetime "created_at"
t.datetime "updated_at"
t.text "short"
t.text "author"
t.string "photo_file_name"
t.string "photo_content_type"
t.integer "photo_file_size"
t.datetime "photo_updated_at"
t.string "state"
end
The end goal is, when a user makes a new comment, add it to alerts in the dashboard where I can then take a link from the dashboard to the post and then approve or delete it.
Thanks you for any help you can give me.
So from you question, My understanding is that you cannot create a link to the post?
Forgetting ajax for now. This is how you like it to the post show page.
Dashboard:
-#comments.each do |comment|
.comment
=link_to comment.body, comment.post
and in the post show page
-#comment.each do |comment|
.comment
=link_to 'Approve', approve_comment_path(comment)
=link_to 'Delete', comment_path(comment), method: :delete
You will need a custom action to set it to approve or you can reuse the update action.
I've setup two models that are commentable through same comments table:
My comments schema:
create_table "comments", force: true do |t|
t.text "body"
t.integer "commentable_id"
t.string "commentable_type"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
My comment model:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :user
acts_as_votable
end
My movie model
class Movie < ActiveRecord::Base
belongs_to :user
has_many :comments, as: :commentable
end
My book model:
class Book < ActiveRecord::Base
belongs_to :user
has_many :comments, as: :commentable
end
My comments controller:
def index
#commentable = find_commentable
#comments = #commentable.comments
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
flash[:notice] = "Successfully created comment."
redirect_to #commentable
else
render :action => 'new'
end
end
def upvote_movie
#movie = Movie.find(params[:movie_id])
#comment = #movie.comments.find(params[:id])
#comment.liked_by current_user
respond_to do |format|
format.html {redirect_to :back}
end
end
def upvote_book
#book = Book.find(params[:book_id])
#comment = #book.comments.find(params[:id])
#comment.liked_by current_user
respond_to do |format|
format.html {redirect_to :back}
end
end
private
def find_commentable
params[:commentable_type].constantize.find(params[:commentable_id])
end
end
How can I add threading(reply to comments) to what I already have?
Here is a blog that talks about threading:http://www.davychiu.com/blog/threaded-comments-in-ruby-on-rails.html
I'm just not sure how to put the two together.
Here is what I have in my movie show view:
<%= render partial: "comments/form", locals: { commentable: #movie } %>
<% #comments.each do |comment| %>
<hr>
<p>
<strong><%= link_to comment.user.username, user_path(comment.user), :class => "user" %>
</strong> <a><%= "(#{time_ago_in_words(comment.created_at)} ago)" %></a>
</p>
<p>
<%= simple_format(auto_link(comment.body, :html => { :target => '_blank' } )) %>
<% end %>
Here is what my comment form looks like:
<%= form_for [commentable, Comment.new] do |f| %>
<%= hidden_field_tag :commentable_type, commentable.class.to_s %>
<%= hidden_field_tag :commentable_id, commentable.id %>
<p>
<%= f.text_area :body %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
I scanned the article you mentioned and found the solution there is quite limited.
The basic idea in the article is to set a comment itself as commentable. So a nested comment is actually NOT a comment of the post, but of the parent comment.
The drawbacks are apparent and unacceptable:
It's hard to get other things right. For example, posts.comments.size is no long correct.
You'll have hard dependency on this structure. If in one day you don't want to display comments in thread but plainly, you...will kick a stone.
If you want to do it on current comment system, it's hard.
Actually a simple solution could solve the problem:
Add an extra field reply_to to comment model, referring to other comment's id.
When adding comment, add a reply_to id if it replied to one.
When showing, show a list of all comments with reply_to null.
Then for each comment, show nested comments has its id. And do it recursively.
If you want to limit the nested level, you can add an extra nested_level field, getting in from the front-end. If nest limit is 3, no comments is allowed to reply a comment with nest level of 3.
add: demo helper to render recursively
def render_replied_comments(comment)
if comment.has_reply
comments.replies.each do |reply|
render partial: 'comment', locals: {comment: reply}
render_replied_comment(reply)
end
end
end
# View
#post.top_level_comments.each do |comment|
render partial: 'comment', locals: {comment: comment}
end
You would add a parent_id to the comments model that is a self-referencing relationship. So parent comments would have a parent_id of nil and all child comments would have a parent_id of that parent comment. You are essentially constructing a tree.
The Ancestory Gem is ideal for this or roll your own, good learning experience.
Within my rails app, I currently have comments setup to work with my posts model, which is functioning properly. How do I add comments to my books model?
Here is what I have so far:
Here is what I have in my schema for the comments:
create_table "comments", force: true do |t|
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "post_id"
t.integer "book_id"
end
In my user model:
class User < ActiveRecord::Base
has_many :comments
acts_as_voter
end
In my post model:
class Post < ActiveRecord::Base
has_many :comments
end
In my book model:
class Book < ActiveRecord::Base
has_many :comments
end
In my comment model:
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :book
belongs_to :user
acts_as_votable
end
In my comments controller:
class CommentsController < ApplicationController
def create
post.comments.create(new_comment_params) do |comment|
comment.user = current_user
end
respond_to do |format|
format.html {redirect_to post_path(post)}
end
end
def upvote
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.liked_by current_user
respond_to do |format|
format.html {redirect_to #post}
end
end
private
def new_comment_params
params.require(:comment).permit(:body)
end
def post
#post = Post.find(params[:post_id])
end
end
In my routes file:
resources :posts do
resources :comments do
member do
put "like", to: "comments#upvote"
end
end
end
In my view:
<% #post.comments.each do |comment| %>
<%= comment.body %>
<% if user_signed_in? && (current_user != comment.user) && !(current_user.voted_for? comment) %>
<%= link_to “up vote”, like_post_comment_path(#post, comment), method: :put %>
<%= comment.votes.size %>
<% else %>
<%= comment.votes.size %></a>
<% end %>
<% end %>
<br />
<%= form_for([#post, #post.comments.build]) do |f| %>
<p><%= f.text_area :body, :cols => "80", :rows => "10" %></p>
<p><%= f.submit “comment” %></p>
<% end %>
What do I add to my comments controller to get comments working on both posts and books? What do I add to my routes file?
Thanks in advance for any help.
You don't want to specify each type of object that can hold Comment objects. That creates a headache of if-elsif-else blocks all over the place. Instead, you want things to be Commentable, and they all will have .comments on them.
This is called a polymorphic association in Active Record. So you would have your models something like:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
class Book < ActiveRecord::Base
has_many :comments, as: :commentable
end
And modify your database accordingly, it's all in the linked article. Now when you build a Comment object for a form, it will have pre-populated a commentable_id and commentable_type, which you can toss in hidden fields. Now it doesn't matter what the Comment is associated with, you always treat it the same.
I'd leave User as a separate association, since it's not really the same idea.