varanasi has asked for the wisdom of the Perl Monks concerning the following question:

I am having trouble retrieving a field "through" a relationship. I'll add detail below, but what I'd like to do is this:

$player_static->player_data_currs->team_libl;
(I know this must be answered in the DBIx::Class::ResultSet documentation, but I am somehow blind to it.) The team_libl field exists in PlayerDataCurr and player_data_currs is defined in Schema::Result::PlayerDataStatic this way:
__PACKAGE__->has_many( "player_data_currs", "LIBL::Schema::Result::PlayerDataCurr", { "foreign.id_static" => "self.id" }, { cascade_copy => 0, cascade_delete => 0 }, );
Searching using the player_data_currs relation works. For example, this returns a resultset:
my $roster_rs = $player_static_model->search( { 'player_data_currs.team_libl' => \'IS NOT NULL', }, { prefetch => [ 'player_data_currs', ], }, ); say scalar $roster_rs->all;
Using the search above, this will print a list of hash refs:
foreach my $player_static ($roster_rs->all){ say $player_static->player_data_currs; }
Like this:
LIBL::Schema::Result::PlayerDataCurr=HASH(0x39b40b8) LIBL::Schema::Result::PlayerDataCurr=HASH(0x39b2010) LIBL::Schema::Result::PlayerDataCurr=HASH(0x39b40e8)
But this reports an error:
foreach my $player_static ($roster_rs->all){ say $player_static->player_data_currs->team_libl; }
Here's the error message:
Can't locate object method "team_libl" via package "DBIx::Class::Resul +tSet" at /home/varanasi/Documents/80-004 LIBL-14/bin/monks.pl line 69 +.

Replies are listed 'Best First'.
Re: Trouble Getting Field Through A DBIx::Class::ResultSet Relation
by Your Mother (Archbishop) on Mar 26, 2014 at 20:02 UTC

    I think, haven't tested, the player_data_currs relationship is a has_many so you're calling team_libl on a resultset, not on the object with the relationship. Try this–

    for my $player_static ( $roster_rs->all ) { for my $data_currs ( $player_static->player_data_currs->all ) { say $data_currs->team_libl; } }

    Aside: the => \'IS NOT NULL' can, and maybe should, be replaced by the more idiomatic => { "!=" => undef }.

      It works! Thanks very much!

      I understand, now that you pointed it out (!), what I was doing wrong. The player_data_currs relationship is referring to a resultset because has_many relationships always refer to resultsets. So, although there is only a single record in $player_static->player_data_currs->all, I need to get the single object/record from the resultset and then call team_libl on it.

      $player_static->player_data_currs->all->team_libl->id doesn't work either because, again, I am calling team_libl on a resultset not on a single object.

      I see now that if I go the other way, from PlayerDataCurr to PlayerDataStatic through a belongs_to relation in PlayerDataCurr, I can accomplish what I'm after much more easily. I suppose this is because a belongs_to links to an object not a resultset. Is that right?

      So, in PlayerDataCurr, I have this:
      __PACKAGE__->belongs_to( "id_static", "LIBL::Schema::Result::PlayerDataStatic", { id => "id_static" }, { is_deferrable => 1, join_type => "LEFT", on_delete => "CASCADE", on_update => "CASCADE", }, );
      And then this works:
      my $roster_rs = $player_curr_model->search( { team_libl => { '!=' => undef }, }, { prefetch => [ 'id_static', ], }, ); foreach my $player_curr ( $roster_rs->all ) { say $player_curr->team_libl; }

      (I apparently have only a very fuzzy grasp of the difference in relationships. I have struggled over DBIx::Class::Relationship but never really understood it.)

      Thanks again for your help. Your succinct explanation taught me something that I could not discover on my own.

        Since you're being so nice! Here is something else you might try. Part of the power of DBIC is the chained ResultSet. Customizing them with semantically significant names (beware shortcut names) can let you build up an amazing set of chained stuff. For example–

        package LIBL::Schema::ResultSet::PlayerDataCurr; use strict; use warnings; use parent "DBIx::Class::ResultSet::HashRef"; # <-Nice alternative to +DBIx::Class::ResultSet. sub teams { +shift->search({ team_libl => { '!=' => undef } }); } sub teams_rs { scalar +shift->teams } sub with_id_static { +shift->search({}, { prefetch => [ 'id_static' ] }); } sub with_id_static_rs { scalar +shift->with_id_static } 1; __END__ =pod =head1 Do you even Pod? L<JSON> + L<DBIx::Class::ResultSet::HashRef> for speedy delivery. use JSON; for my $team ( $player_curr_model->teams->hashref_rs->all ) { print encode_json($team), $/; } # OR my @teams = $player_curr_model ->teams ->with_id_static; =cut

        So, that is pretty contrived (and again UNTESTED) with splitting the with_id_static out but I think you can see how to put it back into the other sub and mix and match and build up chained calls that keep it very semantic and easy to test, repurpose, and maintain. DBIx::Class::ResultSet::HashRef is a regular ResultSet class with shortcuts to avoid the penalty of inflating data to objects when you prefer.