Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

Heh. I don’t actually know if "fat model" is a term outside the Perl lists/sites I frequent. I don’t have links right now but if some turn up, I’ll /msg you and update. I do have some demo/discussion for consideration.

The idea is simple: business logic is consumed by everything that consumes the model (DB code) so it needs to be in the same place to avoid duplication. It’s an extension of the MVC debate which makes the Controllers *very* thin, doing as little as possible and containing no business logic outside of authorization style stuff, the View being quite dumb and not doing much either, and the Model being "fat."

In a web app, the Controllers and Views might end up doing things with data; simplistic example: summing. In the fat Model this would be in the schema code.

This example, being simplistic, will only involve information that is available to the DB/schema already. It could involve external info/models/services. This is where things can get really "fat" if needed. You’d want the external stuff abstracted well so that you could mock it or run safe dev versions easily.

Example - checkout_total for a cart full of items

Cart has items, items have a cart, and items have a cost.

Obviously the information to get a sum of the items in the cart is available and can be put together a couple different ways. But it’s a multi-step process that is going to be needed over and over, so rather than putting it into DBIC syntax (which can be hairy or alterantively verbose for this kind of thing), or raw SQL, or letting an iterator do sums in a view, or some combination of all of those depending on the context, we’ll put it in the model.

Since it’s an ORM, we already know what calling it should look like: $cart->checkout_total. There is no limit to how many of this kind of helper we can write and what it can do is only limited by judgement.

Let your mind race ahead to write out checkout_total. What does it look like? Here’s the idiomatic implementation with DBIC–

sub checkout_total { +shift->items->get_column("cost")->sum; }

Already, I hope, you can see that once you get your chops down, DBIC makes some easy but verbose things even easier and brief. Spelled out–

# Inside My::Schema::Cart or an auxillary class only # loaded into the space on demand. sub checkout_total { # My::Schema::Cart object. my $self = shift; # items is a has_many relationship. my $items_rs = $self->items; # ->items_rs to avoid contex ambiguity +. # Get a DBIx::Class::ResultSetColumn object for cost. my $cost_col = $items_rs->get_column("cost"); # Perform SUM. my $sum = $cost_col->sum; return $sum; }

After you get your legs, the first version becomes more legible than the second. And this is really a baby example. You can continue this sort of thing, riding the relationships, to achieve really powerful queries that remain readable as they become more complex. The business logic can become self-documenting and every piece of every query can be individually testable which will not be the case with SQL libraries or hand written DBI stuff.

And now the full, working, demo code–

# This is not exactly how it would be set up for real. # Load order and @INC stuff requires some tweaks to # have in one file. BEGIN { package My::Schema::Item; use strict; use warnings; use parent "DBIx::Class::Core"; __PACKAGE__->table("item"); __PACKAGE__->add_columns( "id", { data_type => "integer", is_auto_increment => 1, is_nul +lable => 0 }, "cart", { data_type => "integer", is_nullable => 0 }, "cost", ); __PACKAGE__->set_primary_key("id"); # See below. # __PACKAGE__->belongs_to(cart => "My::Schema::Cart"); package My::Schema::Cart; use strict; use warnings; use parent "DBIx::Class::Core"; __PACKAGE__->table("cart"); __PACKAGE__->add_columns( "id", { data_type => "integer", is_auto_increment => 1, is_nul +lable => 0 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->has_many( items => "My::Schema::Item", { "foreign.cart" => "self.id" } ); sub checkout_total { +shift->items->get_column("cost")->sum; } # Would actually be above. My::Schema::Item->belongs_to(cart => "My::Schema::Cart"); # This would really be lib/My/Schema.pm package My::Schema; use strict; use warnings; use parent "DBIx::Class::Schema"; __PACKAGE__->load_classes(qw( Item Cart )); } use strict; use warnings; my $schema = My::Schema->connect("dbi:SQLite::memory:"); $schema->deploy; my $cart_id = 1024; $schema->populate( Cart => [ ["id"],[$cart_id] ] ); my $cost = sub { sprintf "%.2f", rand(100) }; for ( 1 .. 6 ) { $schema->populate( Item => [ [qw( id cart cost )], [ $_, $cart_id, $cost->() ] ] ); } for my $cart ( $schema->resultset("Cart")->all ) { printf("cart:%d --> \$%6.2f\n", $cart->id, $cart->checkout_total); for my $item ( $cart->items ) { printf(" item:%d --> \$%5.2f\n", $item->id, $item->cost); } } __DATA__ cart:1024 ---> $222.16 item:1 --> $46.46 item:2 --> $ 5.56 item:3 --> $84.50 item:4 --> $ 6.72 item:5 --> $15.50 item:6 --> $63.42

That example is fun and actually useful but a little weak because it’s based on a Result. Resultset stuff is where it gets really cool. There is a really good walk-through of that here: dbix-masterclass.xul, though no one seems to support XUL at this time so you might just have to read the plain text source of the slides.

The caveat with DBIC is that it’s not easy. The learning curve is pretty steep. I think it amply repays the investment.


In reply to Re^3: Maybe database tables aren't such great "objects," after all ... by Your Mother
in thread Maybe database tables aren't such great "objects," after all ... by sundialsvc4

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Domain Nodelet?
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this?Last hourOther CB clients
    Other Users?
    Others about the Monastery: (3)
    As of 2024-09-18 05:47 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?
      The PerlMonks site front end has:





      Results (23 votes). Check out past polls.

      Notices?
      erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.