Re: How to avoid $class->new($value) with inflate
[prev]
[thread]
[next]
[Date index for 2005/03/03]
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