SitePoint Sponsor

User Tag List

Results 1 to 8 of 8
  1. #1
    SitePoint Member
    Join Date
    Mar 2008
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Help modelling data structure relationship

    Hi all, thanks in advance for any help

    I am writing a database interface, very basic stuff really. But I'm essentially a newbie and I've realised it's not easy to poorly design your application with RoR, where I would of probably used some funky looking logic with SQL with PHP for example. I'm a bit lost with how to structure it in Rails.

    Basicly, it is a database for a business that has to store information aboout
    Councils, Project Builders and fees respectively.
    For each council there is static fees, but for each Project Builder there is fees that are unique to each Council.

    I want the data to be displayed by, Council displaying all the static council fees. (Easy part, got that working fine.)
    And for each Project Builder to display a list of Councils and their respective fees (which are unique to that council for that builder. Quite baffled here.)

    I was thinking, to have a table for each Project Builder, with Council as the unique ID. But I don't know how to let the user create a table with RoR, and then be able to populate it with rows.

    Thanks for any advice, this has been driving me crazy for days.

  2. #2
    SitePoint Guru
    Join Date
    Aug 2005
    Posts
    986
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If I'm understanding it correcly you would make a new table that stores three columns:

    council_id
    project_builder_id
    amount

    So for each combination of a council and a project builder you store a row with the respective fee in this table. You have to create a new model for this table, but I have no idea what would be a good name for it. Fee maybe?

    Code ruby:
    class Fee < ActiveRecord::Base
      belongs_to :council
      belongs_to :project_builder
    end

    Now put these lines in your ProjectBuilder & Council models:

    Code ruby:
    class ProjectBuilder < ActiveRecord::Base
      has_many :fees
    end
     
    class Council < ActiveRecord::Base
      has_many :fees
    end

    Use it like this:

    Code ruby:
    <% for project_builder in @project_builders %>
      <h2><%= project_builder.name %></h2>
      <ul>
      <% for fee in project_builder.fees %>
        <li><%= fee.amount %></li>
      <% end %>
    <% end %>

  3. #3
    SitePoint Member
    Join Date
    Mar 2008
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks so much, that sounds perfect, I had it way wrong and have been stumbling around with many-to-many relationship.

    Just one question, if for example, I wanted to list (and let the user work with) all the project builder fees, for a particular council. How could I do that?
    (i would like to list project builders, and then let them select a council after that.. )

    Thanks again!

  4. #4
    SitePoint Guru
    Join Date
    Aug 2005
    Posts
    986
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Listing all fees for a particular council is very easy:

    Code ruby:
    # in council controller
    def show
      @council = Council.find(params[:id])
    end
     
    # view for this action
    <h1><%= @council.name %></h1>
    <ul>
    <% for fee in @council.fees %>
    <li><%= fee.project_builder.name %>: <%= fee.amount %></li>
    <% end %>
    </ul>

    So this lists the fees for all project builders on the page of a council, like this:

    Code:
    {council name}
    
    * {project builder 1 name}: {project builder 1 fee}
    * {project builder 2 name}: {project builder 2 fee}
    * {project builder 3 name}: {project builder 3 fee}
    * ...

  5. #5
    SitePoint Member
    Join Date
    Mar 2008
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks so much, it was what I needed

    Just one q. when using:
    Code Ruby:
    form_for(@task) do |f|

    at the moment, in the Show for each Project Builder, I'm trying to let the user add fees for each Project Builder (naming them isn't important, just their amounts).

    I have tried:
    Code Ruby:
    <% form_for(@project_builders.fee) do |f| %>
      <p>
        <b>Name</b><br />
        <%= f.text_field :name  %>
      </p>
      <p>
        <b>Amount</b><br />
        <%= f.text_field :amount %>
      </p>
      <p>
        <%= f.submit "Create" %>
      </p>
    <% end %>
    </p>

    But I get a a nil exception error, and when I do "project_builder.fees" .. (which makes more sense anyway). I get an "array_path error"..
    Am I missing something in the controller that's not letting me work with it?

  6. #6
    SitePoint Guru
    Join Date
    Aug 2005
    Posts
    986
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You probably wanted @project_builder instead of @project_builders in the show page for one project builder. Second, @project_builder.fees is an array containing all fees for that project builder. You have to create a loop and display a form for each fee:

    Code ruby:
    <% for fee in @project_builder.fees %>
     your form code here, using fee instead of @project_builders.fee
    <% end %>

  7. #7
    SitePoint Member
    Join Date
    Mar 2008
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks so much for your help so far Fenrir2.

    Everything is working good, and I decided to let the user edit project builder specific fees and council specific fees respectively in-line as they create or update a Project Builder or Council (just made sense).

    And I found a fantastic solution for complex forms like this @ http://railscasts.com/episodes/75

    But I have one more problem to overcome (learning from what you taught me with dealing with this kind of relationship, I was able to list fees BY councils FOR builders, and vica versa, for editing and when creating. )

    However, when someone creates a new Council, and then they go to edit an existing Project Builder, the new council is not present for them to edit. I know why this is though.

    Code Ruby:
    # _fields.html.erb
    <% for council in @council %>
      <%=h council.name %>
    <% end %>  
    <div id="fees">
      <%= render :partial => 'fee', :collection => @project_builder.fees %>
    </div>
    Formatting and styling used to align council names and builder listings was omitted for brevity (and it was an ugly solution anyway.)

    Code Ruby:
    # _fee.html.erb
    <div class="fee">
      <% fields_for_fee(fee) do |fee_form| %>
      <tr>
        <td><%= fee_form.text_field :plumbing_fee %></td>
        # etc.. I decided to add static columns for each particular fee
      </tr>
      <% end %>
    </div>

    Code Ruby:
    # project_builder.rb
    after_update :save_fees
     
    def new_fee_attributes=(fee_attributes)
        fee_attributes.each do |attributes|
          fee.build(attributes)
        end
    end
     
    def existing_fee_attributes=(fee_attributes)
        fees.reject(&:new_record?).each do |fee|
          attributes = fee_attributes[fee.id.to_s]
          if attributes
            fee.attributes = attributes
          else
            fees.delete(fee)
          end
        end
    end
     
    def save_fees
        fees.each do |fee|
          fee.save(false)
        end
    end

    Code Ruby:
    # project_builders_controller.rb
    def new
      @project_builder = ProjectBuilder.new
      @project_builder.fees.build
    end
     
    def update
      params[:project_builder][:existing_fee_attributes] ||= {}
      ...
    end

    Problem lies in the way I retrieve fees for existing project builders, and the best solution I came up with was to create the entries (to the Fees table) as each new council is created so I don't get nil exception errors (I hate them lol). I considered using insert_SQL in the Council model but I thought there must be a better way with the ever so dynamic Rails. Any suggestions?

    Thanks so much for all your help to date, mate

    P.S.
    Code Ruby:
    # project_builders_helper.rb
    def fields_for_fee(fee, &block)
      prefix = fee.new_record? ? 'new' : 'existing'
      fields_for("project_builder[#{prefix}_fee_attributes][]", fee, &block)
    end

  8. #8
    SitePoint Guru
    Join Date
    Aug 2005
    Posts
    986
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Maybe you should loop over all councils instead of over @project_builder.fees here:

    Code ruby:
    <&#37;= render :partial => 'fee', :collection => @project_builder.fees %>

    You can then use @project_builder.fees.find_or_initialize_by_council(council). This method returns a Fee;

    - a new one if there is no fee in the database yet
    - the old one if there is one in the database

    There's documentation about this under "Dynamic attribute-based finders" http://api.rubyonrails.org/classes/A...cord/Base.html


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
  •