[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