[Catalyst] catalyst tutorial: MyAppDB/Book.pm vs. MyApp/Model/Book.pm

mla maurice.aubrey at gmail.com
Tue May 15 20:37:57 GMT 2007


Daniel Hulme wrote:
> On Tue, May 15, 2007 at 01:24:17PM +0100, Matt S Trout wrote:
>> On Tue, May 15, 2007 at 03:47:57AM -0700, mla wrote:
>>> I like how if you fetch columns that don't exist in the table
>>> the object becomes read-only by default.
>> That's neat, although I prefer tools that presume I know that I'm doing.
> 
> Why would you want to fetch columns that don't exist in the table? And
> if you did, why would you want the object to be read-only. I can easily
> think of cases where you'd want the object that comes out of the ORM to
> have fields that didn't come from the database, even more so than object
> inflation, and it seems likely that you'd want it to do something clever
> when it puts the object back, but I can't think of a useful situation
> for this case. Could someone supply me with one, please?

I think it relates to how AR approaches the OO-relational impedance 
mismatch.

   http://en.wikipedia.org/wiki/Object-Relational_impedance_mismatch

On the one hand, we want to fetch and store through
objects and have centralized validation, encapsulation, a single
interface to both raw and computed values, etc.

OTOH, wearing the relational hat, we want to do complex joins that
efficiently combine columns from multiple tables and views.

As I understand it (and I'm new to all this, so apologies if I'm off),
the ActiveRecord approach would return you ActiveRecord subclasses in
both of those cases. However, if it's a 1-1 column mapping, it treats it
as a modifiable object. If not, it's treated as read-only
but you still use the same interface to access the data.

So in the modifiable case, it would be something like this:

   my $user = Try::User->find(user_id => 3443)
     or croak "can't find user";
   $user->first_name('Sally');
   $user->last_name('Struthers');
   $user->update;

We know that we have all the columns from the user row, so you
can play around with the data and update it.

In the read-only case it would be more like this:

   my @users = Try::User->find_by_sql(qq{
     SELECT first_name
            ,last_name
            ,num_posts
       FROM users
            LEFT JOIN (
               SELECT user_id
                      ,count(*) AS num_posts
                 FROM posts
              GROUP BY user_id
            ) USING (user_id)
   });

In that case you get the same basic interface to the data, but since
it's not a 1-1 mapping with a single table it's read-only by default.

Instead of trying to figure out how to remap the columns or just save
whatever happens to be present, you get a read-only exception so you
don't shoot yourself (although you can override it if you
prefer that route).

Since I lean more toward the relational camp these days, I tend to think
of columns as the real objects, where you use projection and selection
to combine them in useful ways for the task at hand. Seems like this
approach would work pretty well with that philosophy.

Maurice





More information about the Catalyst mailing list