SitePoint Sponsor

User Tag List

Results 1 to 5 of 5
  1. #1
    SitePoint Enthusiast
    Join Date
    Jun 2007
    Location
    Miami, FL
    Posts
    92
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Setting an order to an "each" loop.

    Hi everyone,

    I'm trying to figure out how to set the order of items displayed from a loop.

    What I mean is, let's say I have a some items in the DB and I'm reading displaying them. I want them to display in ascending order by name. I would use:
    Code Ruby:
    @items = Mode.find(:all, :order = "name ASC")
     
    @items.each do |i|
      puts i
    end

    Now, what if I have a loop within a loop? This is the code I've got in my controller, then view:

    Code Ruby:
     
    CONTROLLER::
     
    def index
      @groups = Group.find(:all)
      @group = Group.find(params[:group_id])
      @group_owner = @group.user_id
      @group_members_pages, @group_members = paginate(:group_members, :conditions => ["group_id = ? AND user_id != #{@group_owner}", @group], :order => 'group_members.created_at ASC')
     
      respond_to do |format|
        format.html
        format.xml { render :xml => @group_members.to_xml }
      end
    end
     
     
    VIEW::
     
    <% @group_members.each do |member|
            member.user.entries.each do |entry| %>
     
    MY XHTML HERE FOR entry.user.name, entry.title, etc.
     
            <% end %>
    <% end %>

    It shows all the proper entries for the members of a specific group just fine (let's say, 3 members in the group, each with 3 entries, that shows all 9 entries.) The thing is I want to order them specifically. Right now they're displaying by their ascending created_at date on the item, and by ascending created_at date for the user itself. I want to display them by descending created_at date for the item, completely disregarding the user. So instead of this:

    User 1 item created 07/20/07
    User 1 item created 07/21/07
    User 1 item created 07/22/07

    User 2 item created 07/19/07
    User 2 item created 07/20/07
    User 2 item created 07/26/07

    User 3 item created 07/12/07
    User 3 item created 07/13/07
    User 3 item created 07/24/07

    I want it to look more like this:

    User 3 item created 07/12/07
    User 3 item created 07/13/07
    User 2 item created 07/19/07
    User 1 item created 07/20/07
    User 2 item created 07/20/07
    User 1 item created 07/21/07
    etc...

    By the way, if you notice the controller where @group_members is defined, it's set to order them by created_at ASC, which is why it's showing the entries by grouped name then created order. That's essentially the root of the problem.

    Thanks in advance.

  2. #2
    SitePoint Evangelist
    Join Date
    Feb 2006
    Location
    Worcs. UK
    Posts
    404
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think you need to look at using has_many and belongs_to relationships between your Group, GroupMember, and User models. If you set these up correctly your coding will be much simpler. Have a look at this page from api.rubyonrails.com

    http://api.rubyonrails.com/classes/A...ssMethods.html

    For example, if you add:
    Code:
    has_many :group_members
    to your Group model you will then have a group_members method automatically added to your Group objects. If you then did:
    Code:
    group = Group.find(params[:group_id])
    group.group_members would return a hash of all the GroupMember objects that had their group_id = group.id

    has_many will take conditions, so you could add code to exclude the group owner. It would be something like:
    Code:
    has_many :group_members, :conditions => ["user_id != ?", self.user_id]
    and you could also add an order clause.

    Also you should be able to add something like this to your Group model:
    Code:
    has_one :owner, :class_name => "User", :foreign_key => 'user_id'
    This would give you an owner method so that group.owner would return the user object that matches user_id.

    You could also create a has_many relationship in you GroupMember model like this:
    Code:
    has_many :entries, :through => :entries, :source => :user
    then you will have a group_member.entries method that will return the entries from the group member without you having to go via group_member.user

    You view code could then become something like:
    Code:
    for group_member in @group.group_members
      for entry in group_member.entries
         #You XML code
      end
    end
    You'd then use order options in your has_many relationships to control the order that the entries are listed.

  3. #3
    SitePoint Enthusiast
    Join Date
    Jun 2007
    Location
    Miami, FL
    Posts
    92
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Reggie,

    Thanks for your answer. I already have a has_many relationship set up for group members, but even when I order the results the way I want them, it still only orders the results for the users first, then the entries. So I still get the problem that if User1 has an older entry than User2, User1's still shows up first because the name order supersedes the entry order.

    It's because the loop is going through all the users, and the second loop through the entries, so when the first loop is iterating through a user the second one is grabbing the entries as it goes, so all the entries for the user being looped get pooled and placed right away, then when it finishes looping through that first user's entries it continues loop1 and grabs another user, etc.

  4. #4
    SitePoint Evangelist
    Join Date
    Feb 2006
    Location
    Worcs. UK
    Posts
    404
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Having slept on this, the main question I have is what your GroupMember class is doing. If all it is doing is tracking which users belongs to which groups, then you would be better using a has_and_belongs_to_many relationship. This will require that you replace you group_members table with groups_users join table. You will then get a group.users and user.groups methods.

    There is probably a way of creating a customised has_and_belongs_to_many to generate a group.articles method, which is really what you are after.

    However, I'd do it with a custom SQL call. Something like this in your Group model:
    Code:
    def articles
      sql = "SELECT articles.* 
                 FROM articles
              INNER JOIN (
                SELECT user_id
                  FROM groups_users
                  WHERE group_id = #{self.id}
              ) AS group_user
              ON article.user_id = group_user.user_id
              ORDER BY articles.created_at ASC"
      Article.find_by_sql(sql)
    end
    I've not tested that specific SQL code so it may well need tweaking. However, I have used this technique a number of times so know this approach works.

    Another advantage of using a join (either through a dirty find_by_sql or a neater ActiveRecord way) is that you gather all the data in one go. So you will not have to loop within a loop on you view to output the table. This will greatly improve the speed with which the page will be generated.

    Your controller would then simply need to define:
    Code:
    @group = Group.find(params[:group_id])
    And your View would have something like:
    Code:
    for article in @group.articles
      #Your XML code
    end

  5. #5
    SitePoint Enthusiast
    Join Date
    Jun 2007
    Location
    Miami, FL
    Posts
    92
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh, by the way, forgot to respond but I got it working thanks to your help. I was making a little mistake in associations but overall it's working perfectly now.

    Now I'm trying to do the same thing with tags using acts_as_taggable_on_steroids and getting a different but somewhat similar problem. I want to place all the tags made by all the users in the group, and while I get them to show up, they show up like this:

    Let's say User1 has the tags "black, red, blue", User2 tags "black orange green", User3 tags "orange, purple, red", and User4 tags "red, green, orange." The tags are showing up in the cloud as such:

    black, red, blue, black, orange, green, orange, purple red, red, green, orange. Their sizes/count are also relative to the user. I wanted to use the same method as entries to order/classify them but has_many :tags doesn't really look like a solution.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •