Recipe 3.16. Modeling a Threaded Forum with acts_as_nested_setProblemYou want to create a simple threaded discussion forum that stores all its posts in a single table. All posts should be visible in a single view, organized by topic thread. SolutionCreate a posts table with following Active Record migration. Make sure to insert an initial parent topic into the posts table, as this migration does: db/migrate/001_create_posts.rb :
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do t
t.column :parent_id, :integer
t.column :lft, :integer
t.column :rgt, :integer
t.column :subject, :string
t.column :body, :text
end
Post.create :subject => "What's on your mind?"
end
def self.down
drop_table :posts
end
end
Then specify that the Post model is to contain data organized as a nested set by calling acts_as_nested_set in the Post class definition. app/models/post.rb : class Post < ActiveRecord::Base acts_as_nested_set end
app/controllers/posts_controller.rb :
class PostsController < ApplicationController
def index
list
render :action => 'list'
end
def list
@posts = Post.find(:all,:order=>"lft")
end
def view
@post = Post.find(params[:post])
@parent = Post.find(@post.parent_id)
end
def new
parent_id = params[:parent] 1
@parent = Post.find(parent_id)
@post = Post.new
end
def reply
parent = Post.find(params["parent"])
@post = Post.create(params[:post])
parent.add_child(@post)
if @post.save
flash[:notice] = 'Post was successfully created.'
else
flash[:notice] = 'Oops, there was a problem!'
end
redirect_to :action => 'list'
end
end
The
new.
app/views/posts/new.rhtml : <h1>New post</h1> <p>In response to:; <b><%= @parent.subject %></b></p> <% form_tag :action => 'reply', :parent => @parent.id do %> <%= error_messages_for 'post' %> <p><label for="post_subject">Subject:</label>; <%= text_field 'post', 'subject', :size => 40 %></p> <p><label for="post_body">Body:</label>; <%= text_area 'post', 'body', :rows => 4 %></p> <%= submit_tag "Reply" %> <% end %> <%= link_to 'Back', :action => 'list' %> Define a Posts helper method named get_indentation that determines the indentation level of each post. This helper is used in the forum's thread view. app/helpers/posts_helper.rb :
module PostsHelper
def get_indentation(post, n=0)
$n = n
if post.send(post.parent_column) == nil
return $n
else
parent = Post.find(post.send(post.parent_column))
get_indentation(parent, $n += 1)
end
end
end
Now, display the threaded form in the list.rhtml view with: app/views/posts/list.rhtml :
<h1>Threaded Forum</h1>
<% for post in @posts %>
<% get_indentation(post).times do %>_ <% end %>
<%= post.subject %>
<i>[
<% unless post.send(post.parent_column) == nil %>
<%= link_to "view", :action => "view", :post => post.id %>
<% end %>
<%= link_to "reply", :action => "new", :parent => post.id %>
]</i>
<% end %>
Finally, add a view.rhtml template for showing the details of a single post: app/views/posts/view.rhtml : <h1>View Post</h1> <p>In response to: <b><%= @parent.subject %></b></p> <p><strong>Subject:</strong> <%= @post.subject %></p> <p><strong>Body: </strong> <%= @post.body %></p> <%= link_to 'Back', :action => 'list' %> Discussion
acts_as_nested_set
is a Rails implementation of a nested set model of trees in SQL.
acts_as_nested_set
is similar to
acts_as_tree
, except that the underlying data model stores more information about the
An interesting part of the solution is the use of the helper method
get_indentation
. This is a recursive function that walks up the tree to count the number of parents for each node in the forum. The number of
Two links are placed next to each post. You can view the post, which displays its body, or you can reply to the post. Replying to a post adds a new post to the set of posts directly underneath that parent post.
In the list view and the
get_indentation
helper, the
parent_column
method is called on the
post
object. That call returns
parent_id
by default, and in
post.send(post.parent_column)
This notation allows you to change the
class Post < ActiveRecord::Base acts_as_nested_set :parent_column => "topic_id" end Figure 3-9 shows the list view of the solution's forum. Figure 3-9. A threaded forum made using acts_as_nested_set
See Also
|