Полиморфные связи. Маленькое дополнение

    В декабре на Хабре была хорошая статья, о полиморфных связях в Рельсах. Вот она. Перед тем как читать дальше, пожалуйста ознакомьтесь с ней.
    Однако, у автора статьи осталось несколько неотвеченных вопросов. Вот на них то мы сегодня и найдем ответы.


    Во первых, в тексте статьи присутствует вот такой код.

    1. module PostsHelper
    2. def posts_smth_path(post)
    3. case post.content.class.to_s.downcase
    4. when "topic" : posts_topic_path(post)
    5. when "link" : posts_link_path(post)
    6. when "podcast" : posts_podcast_path(post)
    7. end
    8. end
    9. def posts_smths_path(post)
    10. case post.content.class.to_s.downcase
    11. when "topic" : posts_topics_path
    12. when "link" : posts_links_path
    13. when "podcast" : posts_podcasts_path
    14. end
    15. end
    16. end


    Он конечно же работает, но… это ведь совершенно не стиль Rails. Всё должно быть проще. Так и есть. Рельсы предлагают нам отличный вариант — метод polymorphic_path (). А также new_polymorphic_path () и edit_polymorphic_path (). Суть метода, а в том, что он в зависимости от того, какие модели в него подаются, будет выдавать нужные нам урлы. Вот так.

    1. <% if can? :edit, @post %>
    2. <%= 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" %>
    3. <% end %>


    Здесь, в зависимости от того, какой именно content у @post (Topic, Link, Podcast), будет подставляться url для его редактирования. Понятно, что polymorphic_path () и new_polymorphic_path () работают аналогично.

    Во-вторых, автору по ходу статьи подсказали, что неплохо было бы использовать accepts_nested_attributes_for и даже потом сделали статью на эту тему, но вот как это сочитается с полиморфными связями, в статье не написано. А сочитается это очень хорошо, главное подход знать:)

    Давайте для примера расмотрим контроллер, который будет создавать Topic.

    1. class Posts::TopicsController < PostsController
    2. def index
    3. @posts = Post.topics.find(:all)
    4. end
    5. def new
    6. @topic = Topic.new
    7. @topic.build_post
    8. @selectable_categories = Category.all.collect{ |c| [c.title, c.id] }
    9. end
    10. def edit
    11. @topic = Topic.find(params[:id])
    12. @post = @topic.post
    13. @selectable_categories = Category.all.collect{ |c| [c.title, c.id] }
    14. end
    15. def create
    16. @topic = Topic.new(params[:topic])
    17. @topic.post.author = current_user
    18. if @topic.save
    19. redirect_to post_path(@topic.post)
    20. else
    21. @selectable_categories = Category.all.collect{ |c| [c.title, c.id] }
    22. render :action => "new"
    23. end
    24. end
    25. def update
    26. @topic = Topic.find(params[:id])
    27. if @topic.update_attributes(params[:topic])
    28. redirect_to post_path(@topic.post)
    29. else
    30. @selectable_categories = Category.all.collect{ |c| [c.title, c.id] }
    31. render :action => "edit"
    32. end
    33. end
    34. end



    Видим, что всё очень легко. И самое главное — это работает. Рабочий прмер всего этого можно посмотреть вот тут.

    P.S. Для всех, кто программирует на Рельсе, посмотрите мою вчерашнюю ссылку на Хабре. Очень интересные вещи о ActiveRecord в Rails 3.

    UPD. Забыл ещё одну маленькую деталь. Для удобства редактирования и легкой масштабируемости можно ещё соответствующим образом допились вьюху. Пример вот тут (тут я её не хочу выкладывать, потому что слишком много кода будет). По ссылке файл с вьюхой для Post, из которого в зависости от типа content дергается вьюха для Topic или Link.
    Поделиться публикацией

    Комментарии 6

      +2
      Очень отличное дополнение, спасибо! Занес в избранное
        +2
        Спасибо вам за первоначальную статью. После неё взглянул на полиморфные связи по-новому.
        +1
        polymorphic_path спасет мир :).
        Спасибо. Не знал.
          +1
          Вот и я тоже не знал, случайно наткнулся на неё на railsforum.com.
          А вообще такой метод легко можно написать самому вот так.
            +1
            Говорят, eval плохой и скверный, потому лучше использовать instance_eval
            Но работает и так и так, да
          0
          Спасибо, в избранное.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое