Re: How to avoid $class->new($value) with inflate

[prev] [thread] [next] [Date index for 2005/03/03]

From: Ofer Nave
Subject: Re: How to avoid $class->new($value) with inflate
Date: 21:25 on 03 Mar 2005
Perrin Harkins wrote:

>On Thu, 2005-03-03 at 09:39 -0800, Ofer Nave wrote:
>  
>
>>I 
>>question even the wisdom of inflating CDBI objects using the current 
>>syntax, since many many people use blah_id as their foreign kay, and 
>>calling $obj->blah_id() to get the $blah object is nonsesnsical (and the 
>>workarounds have problems, too).
>>    
>>
>
>I always have an accessor_name method that strips _id suffixes in my
>base class.  I'd like to see an "alias" method of some kind for renaming
>single columns quickly, but for stripping _id globally it's much simpler
>to just define accessor_name in my base class.  I wouldn't call this a
>"workaround."
>
>  
>
I would.  You shouldn't have to write a custom function that throws a 
regex against a subroutine name.  I call that a workaround.  It's 
ideologically impure.  Anytime an interface is built in such a way that 
it doesn't let you say what you actually mean (such as piggybacking 
inflation on table relationship definitions), it will constantly rub 
wrong against non-mainstream cases.

Starting at the top, you have your database schema, which is a 
collection of named tables containing named columns, and the 
relationships between those tables.  The first thing you want to do is 
tell CDBI what your tables and columns are.  Next, you want to define 
the relationships between the tables.  And you want to be able to do 
this without undesired side-effects (like object inflation) *if you so 
choose*.

If you have a foreign key, like artist_id in the album table, you want 
to inflate the value into an Artist object, because it's cool and 
convenient.  What you want is for the artist_id() method to return the 
actual value, and the artist() method to return the object.  Rather than 
CDBI always inflating foreign keys into objects, you should have more 
control.

If it was possible to turn off this default behavior and supply 
alternative behaviors, here's two neat implicit ones:

1) produce _id-less accessors for inflated values for foreign key 
columns ending in _id (cute, but limited)
2) produce accessors named after the class of the foreign object (if the 
class was MyCompany::MyProject::Artist, it would use the the leaf 
package name (Artist) and probably lowercase it (artist)) for accessing 
inflated foreign key values

And here's an explicit one (with a generic syntax to handle all 
situations - could be simplified for easy ones):

__PACKAGE__->transform( column, inflate => sub {}, deflate => sub{} )

The last one could be used to turn foreign keys into objects, datetime 
values into datetime objects, convert s/m/l to small/medium/large, or 
whatever.

If you simply want the accessor name to be different then the column 
name, than a simple renaming (or aliasing) API would be appropriate:

__PACKAGE__->accessor_name( column, accessor_name )

This let's you explicitly define the accessor name of certain fields, 
and let the rest be created automatically in the usual way.


But a global accessor_name filter sub is ugly and limited.  Besides the 
fact that anything fancy requires a switch statement, it also doesn't 
give an easy way of having both artist() and artist_id().  All it does 
is let you change artist_id() to artist().  Yes, you can probably get 
the id with $album->artist()->artist_id(), but you shouldn't *have* to.


Inflating and converting are identical concepts, although inflating 
additionally implies turning a value into an object, and even more 
specifically turning a value into a CDBI object when referring to 
foreign key values.  But they're all forms of converting values.  
Whether you want to group them into one generic syntax (like 
transform()) or additionally supply convenience methods for the special 
cases is a matter of taste.  Since you generally do want to inflate 
foreign keys, I agree it's good to be able to have that happen as part 
of the table relationship definition, but you must be able to control 
all aspects of it.  Instead of just:

__PACKAGE__->has_a( foreign_key => Class )

which creates an accessor matching the column name that inflates the 
value, how about:

__PACKAGE__->has_a( foreign_key => table, objectified => accessor );

If you used it like this:

__PACKAGE__->has_a( artist_id => 'artist', objectified => 'artist' );

It would form a relationship between the artist_id column and the artist 
table, still create the normal artist_id() accessor method for the value 
in that column, and additionally created an accessor called artist() 
which returns the inflated value by looking up what CDBI class has 
declared itself as wrapping the artist table.

>>It seems to me that the solution is seperating the relationship syntax 
>>from the inflation/conversion/mapping/whatever syntax.  One set of 
>>methods to define relationships (which are *table relationships*, not 
>>the relationship between a datetime value and a datetime object) and 
>>another set of methods to inflate/deflate column values.
>>    
>>
>
>I suppose this all comes down to which things you think are similar.  To
>me, object relationships and "inflation" (which is really just a
>definition of how you find the related object, given a key) are very
>closely tied and belong together.  Applying filters to values on their
>way in and out of the database does not seem closely tied to object
>relationships.
>
>I think people are getting confused by the fact that this thread started
>with a date example, even though it wasn't object-related.  A better
>example of what a filter/transform method would be used for is a case
>where you have a set of values in your database like 'S', 'M', and 'L'
>but you want the accessor for that column to accept and return 'small',
>'medium' and 'large' instead.  Many schema designs have situation where
>short keys are used in the database but more explicit ones are wanted
>for display.  (Maybe this sort of thing should be done in your template,
>but that's a separate issue...)
>
>- Perrin
>
>  
>
-ofer

(message missing)

How to avoid $class->new($value) with inflate
Matija Grabnar 12:18 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Charles Bailey 14:45 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
William McKee 15:17 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Matt S Trout 15:31 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
William McKee 15:40 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Charles Bailey 16:10 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Perrin Harkins 16:15 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
William McKee 16:50 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Perrin Harkins 17:00 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Charles Bailey 17:29 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Perrin Harkins 19:34 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
William Ross 17:10 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Michael Peters 17:08 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Perrin Harkins 17:12 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Perrin Harkins 19:49 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Ofer Nave 21:25 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Carl Johnstone 09:56 on 04 Mar 2005

Re: How to avoid $class->new($value) with inflate
Frank Carnovale 23:19 on 03 Mar 2005

Re: How to avoid $class->new($value) with inflate
Frank Carnovale 23:44 on 06 Mar 2005

Re: How to avoid $class->new($value) with inflate
Matt S Trout 17:34 on 03 Mar 2005

Generated at 20:12 on 07 Mar 2005 by mariachi v0.52