В декабре на Хабре была хорошая статья, о полиморфных связях в Рельсах. Вот она. Перед тем как читать дальше, пожалуйста ознакомьтесь с ней.
Однако, у автора статьи осталось несколько неотвеченных вопросов. Вот на них то мы сегодня и найдем ответы.
Во первых, в тексте статьи присутствует вот такой код.
Он конечно же работает, но… это ведь совершенно не стиль Rails. Всё должно быть проще. Так и есть. Рельсы предлагают нам отличный вариант — метод polymorphic_path (). А также new_polymorphic_path () и edit_polymorphic_path (). Суть метода, а в том, что он в зависимости от того, какие модели в него подаются, будет выдавать нужные нам урлы. Вот так.
Здесь, в зависимости от того, какой именно content у @post (Topic, Link, Podcast), будет подставляться url для его редактирования. Понятно, что polymorphic_path () и new_polymorphic_path () работают аналогично.
Во-вторых, автору по ходу статьи подсказали, что неплохо было бы использовать accepts_nested_attributes_for и даже потом сделали статью на эту тему, но вот как это сочитается с полиморфными связями, в статье не написано. А сочитается это очень хорошо, главное подход знать:)
Давайте для примера расмотрим контроллер, который будет создавать Topic.
Видим, что всё очень легко. И самое главное — это работает. Рабочий прмер всего этого можно посмотреть вот тут.
P.S. Для всех, кто программирует на Рельсе, посмотрите мою вчерашнюю ссылку на Хабре. Очень интересные вещи о ActiveRecord в Rails 3.
UPD. Забыл ещё одну маленькую деталь. Для удобства редактирования и легкой масштабируемости можно ещё соответствующим образом допились вьюху. Пример вот тут (тут я её не хочу выкладывать, потому что слишком много кода будет). По ссылке файл с вьюхой для Post, из которого в зависости от типа content дергается вьюха для Topic или Link.
Однако, у автора статьи осталось несколько неотвеченных вопросов. Вот на них то мы сегодня и найдем ответы.
Во первых, в тексте статьи присутствует вот такой код.
- module PostsHelper
- def posts_smth_path(post)
- case post.content.class.to_s.downcase
- when "topic" : posts_topic_path(post)
- when "link" : posts_link_path(post)
- when "podcast" : posts_podcast_path(post)
- end
- end
- def posts_smths_path(post)
- case post.content.class.to_s.downcase
- when "topic" : posts_topics_path
- when "link" : posts_links_path
- when "podcast" : posts_podcasts_path
- end
- end
- end
Он конечно же работает, но… это ведь совершенно не стиль Rails. Всё должно быть проще. Так и есть. Рельсы предлагают нам отличный вариант — метод polymorphic_path (). А также new_polymorphic_path () и edit_polymorphic_path (). Суть метода, а в том, что он в зависимости от того, какие модели в него подаются, будет выдавать нужные нам урлы. Вот так.
- <% if can? :edit, @post %>
- <%= link_to "Edit this #{@post.content.class.to_s.downcase}", edit_polymorphic_path([:posts, @post.content]), :class => "b-post-edit_link g-link-no-visited" %>
- <% end %>
Здесь, в зависимости от того, какой именно content у @post (Topic, Link, Podcast), будет подставляться url для его редактирования. Понятно, что polymorphic_path () и new_polymorphic_path () работают аналогично.
Во-вторых, автору по ходу статьи подсказали, что неплохо было бы использовать accepts_nested_attributes_for и даже потом сделали статью на эту тему, но вот как это сочитается с полиморфными связями, в статье не написано. А сочитается это очень хорошо, главное подход знать:)
Давайте для примера расмотрим контроллер, который будет создавать Topic.
- class Posts::TopicsController < PostsController
- def index
- @posts = Post.topics.find(:all)
- end
- def new
- @topic = Topic.new
- @topic.build_post
- @selectable_categories = Category.all.collect{ |c| [c.title, c.id] }
- end
- def edit
- @topic = Topic.find(params[:id])
- @post = @topic.post
- @selectable_categories = Category.all.collect{ |c| [c.title, c.id] }
- end
- def create
- @topic = Topic.new(params[:topic])
- @topic.post.author = current_user
- if @topic.save
- redirect_to post_path(@topic.post)
- else
- @selectable_categories = Category.all.collect{ |c| [c.title, c.id] }
- render :action => "new"
- end
- end
- def update
- @topic = Topic.find(params[:id])
- if @topic.update_attributes(params[:topic])
- redirect_to post_path(@topic.post)
- else
- @selectable_categories = Category.all.collect{ |c| [c.title, c.id] }
- render :action => "edit"
- end
- end
- end
Видим, что всё очень легко. И самое главное — это работает. Рабочий прмер всего этого можно посмотреть вот тут.
P.S. Для всех, кто программирует на Рельсе, посмотрите мою вчерашнюю ссылку на Хабре. Очень интересные вещи о ActiveRecord в Rails 3.
UPD. Забыл ещё одну маленькую деталь. Для удобства редактирования и легкой масштабируемости можно ещё соответствующим образом допились вьюху. Пример вот тут (тут я её не хочу выкладывать, потому что слишком много кода будет). По ссылке файл с вьюхой для Post, из которого в зависости от типа content дергается вьюха для Topic или Link.