[Dbix-class] Resultset relation accessors

David Ihnen davidi at norchemlab.com
Mon Jun 8 20:11:31 GMT 2009


Matt S Trout wrote:
> On Tue, May 26, 2009 at 01:49:05PM -0700, David Ihnen wrote:
>   =

>> Sometimes I think i'm missing something obvious.
>>
>> In my application, for instance, a holiday can be assigned to a group.
>>
>> A group has many donors
>> A donor might have many scheduled days
>> A scheduled day is associated with a particular schedule
>>
>> In this spot in my application, the operation is "update group schedule"=
 .
>>
>> I need to say "refresh all of the randomized scheduling for all of the =

>> schedules related to all of the donors in the group this schedule is =

>> on."  To do that I need a list of those schedules.
>>
>> I first wrote that, for an update overload in my holiday class, like =

>> this.  ($old is a hash ref to a copy of the get_columns from before the =

>> update took place)
>>
>> $self->groups->donors->schedule->ivr_schedule->search(
>>            { 'date_start' =3D> { '<=3D' =3D> $old->{holiday} }
>>            , -or =3D>
>>              [ 'date_end' =3D> { '>=3D' =3D> $old->{holiday} }
>>              , 'date_end' =3D> undef
>>              ]
>>            } );
>>
>> But I got an error, something like "No such method 'donors' on class =

>> DBIx::Class::ResultSet"
>>
>> So I rewrote it...
>>
>> $self->groups->search_related('donors')->search_related('schedule')->sea=
rch_related('ivr_schedule'
>>       , ...yaddayadda...
>>
>> and that worked.
>>
>> So it leaves me wondering if I missed something fundamental here.  I =

>> have an object which is a result set of a particular record type.  If I =

>> want the records that are related to that result set - why would I have =

>> to call 'related_resultset' - isn't "give me the related resulset" the =

>> only thing I could mean when I call ->relationship on a result set?  If =

>> that is so, why aren't there accessors created on the resultset for the =

>> valid relations, to cut down on excessive verbosity that doesn't clarify?
>>     =

>
> I believe somebody tried that once.
>
> Then they defined a relationship called 'next'.
>
> Then they realised work would be involved and lost interest.
>
> I don't see why it -can't- be done, just nobody's really tried. I reckon
> you could do it as a schema component that wraps load_namespaces as a
> start ...
>   =

Okay, so I noticed this one time before... the internal methods defined =

in the classes of DBIx::Class are not in a differnet namespace to =

distinguishes them from the methods that are accessors that may be =

defined by merely creating column names.

None of the columns in a table, for instance, may be named the same as =

any of the functions within the namespace of table objects.  This =

nibbled on me at one point during my current development project.

So I'm contemplating how to handle that.  Obviously for flexibility and =

just plain propriety we want to be able to have relationships named =

single, next, and first.  But if we overlay the namespace of the =

resultset object with relationship names, we have the same inherent =

conflict we do with the table column accessors and the table object api.

So what if we reserved part of the object namespace for relationship =

accessors, prefixed with rel_ for explicit compatibility, and aliased =

without the prefix if the namespace slot is available?

If you define a rel called 'next' and call it with $rs->next it carps =

"AMBIGUOUS RS/REL CALL" or just a setup/compiletime warning =

"RELATIONSHIP ACCESSOR 'next' MASKED BY API METHOD 'next'"

Then I could write

$self->groups->donors->schedule->ivr_schedule->search...

or more verbosely

$self->groups->rel_donors->rel_schedule->rel_ivr_schedule->search...

Which is somewhat less verbose than ->search_related, and lets me do =

what I want without the rel distinction alias by default. The namespace =

overlap which is more or less necessary for the purposes of conciseness =

is allowed, both warning when it overlaps AND providing a mechanism to =

be explicit so you can do so anyway.

A similar pattern could be attached to the table objects so that column =

names are allowed to overlap with the api as well - subnamespace prefix =

'col_'

Both of these subnamespace reservations have a fundamental tradeoff though.

if I had two columns named five and col_five, there an ambiguity.  what =

do I mean when I say col_five?  Same for relationships six and rel_six.

On one hand, the prefix is already used for the add_to_RELATIONSHIP =

create accessor, so its an established pattern.  A solution that may =

provide a lesser limitation than the above prefix is to use methods to =

distinguish what is meant.

$self->col->five is obviously different from $self->col->col_five

A tradeoff remaining in that pattern is that if there is a column named =

col that is inflated into an object, even overload logic wouldn't be =

able to tell if you actually meant the default accessor 'col' for the =

column.  If you had a column named col and one named ymd...

$row->col->ymd

could mean 'get the col column, inflate it, and call the ymd method on =

it' or it could mean 'get me the column ymd'.  It would be more clear =

programmatically in the case of

my $d =3D $row->col;
$d->ymd;

and could be arbitrarily documented that you must use the ->col =

designator to inform the class you mean a column, though for convenience =

(and backwards compatibility) we provide all non-conflicting accessors =

for your use and warn you when they conflict.

Would you prefer one method over the other?  I think the important thing =

is that we make it so that no matter what, my column names do not =

override the existing method names, *but* there is a way to get the =

functionality even with the api interfering with the schema. =


Hopefully this is not too long,

David

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/dbix-class/attachments/20090608/8c0=
e650c/attachment-0001.htm


More information about the DBIx-Class mailing list