代码之家  ›  专栏  ›  技术社区  ›  Matt Rogish

在Rails中对复杂的多AR对象表进行排序/分页/筛选

  •  0
  • Matt Rogish  · 技术社区  · 15 年前

    我有一个从多ActiveRecord对象数组中提取的复杂表。此列表是特定用户所有“喜爱”项目(歌曲、消息、博客帖子等)的组合显示。这些项中的每一项都是一个成熟的AR对象。

    我的目标是为用户提供一个简化的搜索、排序和分页界面。用户不需要知道歌曲有歌手,消息有作者——对于最终用户,表中的两个条目都将显示为“user”。因此,搜索框将只是一个下拉列表,询问他们搜索哪个(用户名、创建地址等)。在内部,我需要将其转换为适当的对象搜索,合并结果并显示。

    我可以单独进行分页(mislav将分页)、排序和过滤,但将它们组合在一起会遇到一些问题。

    例如,如果我对项目的组合列表进行分页,分页插件可以很好地处理它。由于分页是在应用程序和数据库中进行的,因此这是没有效率的,但我们假设预期的用例将表明绝大多数用户的偏好项将少于30个,并且所有其他行为、服务器功能等表明这不会成为瓶颈。

    但是,如果我想对列表进行排序,我不能通过分页插件对其进行排序,因为它依赖于结果集是从单个SQL查询派生的假设,并且字段名在整个过程中是一致的。因此,我必须通过ruby对合并的数组进行排序,例如。

    @items.sort_by{ |i| i.whatever }
    

    过滤器也会出现同样的问题。如果用户过滤“父项”(消息的线程,歌曲的相册),我必须将其转换为适当的集合对象方法。也很恶心。

    这不是确切的设置,但已经足够接近了。请注意,这是一个遗留应用程序,因此更改它非常困难,尽管并非不可能。当然,也可以做一些枯燥的工作,但是不要关注下面代码的风格或优雅。但是,解决方案的风格/优雅很重要!:D

    class User < ActiveRecord::Base
      ...
      has_and_belongs_to_many :favorite_messages, :class_name => "Message"
      has_and_belongs_to_many :favorite_songs,    :class_name => "Song"
      has_many                :authored_messages, :class_name => "Message"
      has_many                :sung_songs,        :class_name => "Song"
    end
    
    class Message < ActiveRecord::Base
      has_and_belongs_to_many :favorite_messages  
      belongs_to              :author, :class_name => "User"
      belongs_to              :thread
    end
    
    class Song < ActiveRecord::Base
      has_and_belongs_to_many :favorite_songs  
      belongs_to              :singer, :class_name => "User"
      belongs_to              :album
    end
    

    控制器:

    def show
      u = User.find 123
    
      @items = Array.new
      @items << u.favorite_messages
      @items << u.favorite_songs
      # etc. etc. 
    
      @items.flatten!
    
      @items = @items.sort_by{ |i| i.created_at }
    
      @items = @items.paginate :page => params[:page], :per_page => 20
    end
    
    def search
    
      # Assume user is searching for username like 'Bob'
    
      u = User.find 123
    
      @items = Array.new
      @items << u.favorite_messages.find( :all, :conditions => "LOWER( author ) LIKE LOWER('%bob%')" )
      @items << u.favorite_songs.find( :all, :conditions => "LOWER( singer ) LIKE ... " )
      # etc. etc. 
    
      @items.flatten!
    
      @items = @items.sort_by{ |i| determine appropriate sorting based on user selection }
    
      @items = @items.paginate :page => params[:page], :per_page => 20
    end
    

    视图:

       #index.html.erb
       ...
       <table>
         <tr>
           <th>Title (sort ASC/DESC links)</th>
           <th>Created By (sort ASC/DESC links))</th>
           <th>Collection Title (sort ASC/DESC links)</th>
           <th>Created At (sort ASC/DESC links)</th> 
         </tr>
         <% @items.each |item| do %>
           <%= render { :partial => "message", :locals => item } if item.is_a? Message %>
           <%= render { :partial => "song",    :locals => item } if item.is_a? Song %>
         <%end%>
       ...
       </table>
    
       #message.html.erb
       # shorthand, not real ruby
       print out message title, author name, thread title, message created at
    
       #song.html.erb
       # shorthand
       print out song title, singer name, album title, song created at
    
    3 回复  |  直到 15 年前
        1
  •  3
  •   Sohan    15 年前

    Thinking sphinx是一个rails插件,可以帮助您搜索多个模型/字段,并且内置分页/排序功能。更多 thinking-sphinx 地点。

        2
  •  0
  •   Matt Rogish    15 年前

        3
  •  0
  •   Tim Snowhite    14 年前

    您还可以将#collection#title和#title方法添加到每种类型的 item ,并通过键入进行排序。