开发者

Calculating rowspan in an HTML table of nested DataMapper resources

I have a legacy database that sits behind a really crappy PHP app. The web app is causing all sorts of problems with the database, and I just need a quick-and-dirty way of representing what is stored in the database in a way that my client can see how bad the problem is, So I built a Sinatra + DataMapper application, but I'm having trouble calculating some values in the view.

In the end, I want a table like this:

+++++++++++++++++++++++++++++++++++++++++++++
+ Profile + Registration + Signup + Payment +
+         +              +        +++++++++++
+         +              +        + Payment +
+         +              ++++++++++++++++++++
+         +              + Signup + Payment +
+         +++++++++++++++++++++++++++++++++++
+         + Registration + Signup + Payment +
+         +              ++++++++++++++++++++
+         +              + Signup +         + <- Signup w/ no payments
+++++++++++++++++++++++++++++++++++++++++++++
+ Profile + Registration + Signup + Payment +
+         +++++++++++++++++++++++++++++++++++
+         + Registration +        +         + <- Registration w/ no Signups
+++++++++++++++++++++++++++++++++++++++++++++
+ Profile +              +        +         + <- Profile w/ no Registrations
+++++++++++++++++++++++++++++++++++++++++++++

My first issue is that this requires a ton of SQL queries [basically (n+1)*(n+1)*(n+1)*(n+1)], but AFAIK there is no way to make DataMapper do eager loading of nested models, nor anyway to cache the data model in a way that makes reads more efficient. (I'm not writing anything back to the DB, and a snapshot of the database is fine - don't really care about live updates). This is tolerable for now because this is a 1-off report.

My Second issue is calculating the rowspan for the given columns. At first I was just using the number of payments for the current scope (profile.registrations.signups.payments.size, registration.signups.payments.size, etc.) but as you can see in the first example above, there is actually one less payment than what would be required to have an accurate rowspan for the profile.

The only theoretical solution that I've been able to come up with is to basically loop over each child object at every step along the way to find out exactly what children objects are available beyond that step, but that seems really inelegant and would require an exponential increase in the SQL being run.

Is there some other way that I can approach this? I'm not married to DataMapper or HAML, I just want to get this done as efficiently as possible.

I'm pasting my current HAML and data models below for reference.

index.haml (it's nasty, I know)

#content
  %p #{@profiles.size} Profiles
  %table.display{:border=>1, :cellpadding=>2,:width=>'100%'}
    %thead
      %th Prof ID
      %th Profile
      %th Reg's
      %th Reg ID
      %th Registration
      %th Signups
      %th Event ID
      %th Event
      %th Event Date
      %th Payments
      %th Pmt ID
      %th Amt
      %th Pmt Date
    %tbody
      - @profiles.each do |profile|
        - profile_rowspan = [profile.registrations.signups.payments.size, profile.registrations.signups.size, profile.registrations.size, 1].detect { |i| i > 0 }
        %tr{:valign=>'top'}
          %td{:rowspan => profile_rowspan, :class => "profile"} #{profile.id}
          %td{:rowspan => profile_rowspan} #{profile.firstname} #{profile.lastname}
          %td{:ro开发者_如何学Pythonwspan => profile_rowspan} #{profile.registrations.size}
          - if profile.registrations.size == 0
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
            %td{:rowspan => profile_rowspan}
          - profile.registrations.each_with_index do |registration, reg_index|
            - if reg_index != 0
              != "</tr><tr valign=\"top\">"
            - reg_rowspan = [registration.signups.payments.size, registration.signups.size, 1].detect { |i| i > 0 }
            %td{:rowspan => reg_rowspan, :class => "registration"} #{registration.id}
            %td{:rowspan => reg_rowspan} #{registration.firstname} #{registration.lastname}
            %td{:rowspan => reg_rowspan} #{registration.signups.size}
            - if registration.signups.size == 0
              %td{:rowspan => reg_rowspan}
              %td{:rowspan => reg_rowspan}
              %td{:rowspan => reg_rowspan}
              %td{:rowspan => reg_rowspan}
              %td{:rowspan => reg_rowspan}
              %td{:rowspan => reg_rowspan}
              %td{:rowspan => reg_rowspan}
            - registration.signups.each_with_index do |signup, signup_index|
              - if signup_index != 0
                != "</tr><tr valign=\"top\">"
              - signup_rowspan = [signup.payments.size, 1].detect { |i| i > 0 }
              %td{:rowspan => signup_rowspan, :class => "signup"} #{signup.event.id}
              %td{:rowspan => signup_rowspan} #{signup.event.event_title}
              %td{:rowspan => signup_rowspan} #{signup.event.event_start}
              %td{:rowspan => signup_rowspan} #{signup.payments.size}
              - if signup.payments.size == 0
                %td{:rowspan => signup_rowspan}
                %td{:rowspan => signup_rowspan}
                %td{:rowspan => signup_rowspan}
              - signup.payments.each_with_index do |payment, payment_index|
                - if payment_index != 0
                  != "</tr><tr valign=\"top\">"
                %td{:class => "payment"} #{payment.id}
                %td #{payment.total}
                %td #{payment.payment_date}

profile.rb

class Profile
  has n, :registrations
end

registration.rb

class Registration
  belongs_to :profile
  has n, :signups
  has n, :payments
end

signup.rb

class Signup
  belongs_to :registration
  belongs_to :event
  has n, :payments
end

payment.rb

class Payment
  belongs_to :registration
  belongs_to :signup
end

event.rb

class Event
  has n, :signups
  has n, :payments, :through => :signups
end


If you are using MySQL, it can return results of a query as HTML. It'd be worth looking to see what it can do as a starting point.

Then, you could use Nokogiri to find empty cells and manipulate the HTML however you want.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜