Skip Menu |

This queue is for tickets about the DBIx-Class CPAN distribution.

Report information
The Basics
Id: 131624
Status: open
Priority: 0/
Queue: DBIx-Class

People
Owner: Nobody in particular
Requestors: KES [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: Lookup related field does not work: $row->User->email
Doing $row->User->email cause error: Can't call method "email" on an undefined value this error happen when $row is: ``` Show quoted text
DBG>$row
App::Schema::Result::Right { _column_data => { id => 151, role_id => 2, user_id => 150, }, _in_storage => 1, _inflated_column => {}, _relationship_data => IGNORED, _result_source => IGNORED, related_resultsets => IGNORED, } ``` And its relationship data looks like. (Notice `User => undef`): ``` Show quoted text
DBG> $row->{ _relationship_data}
{ Role => App::Schema::Result::Role { _column_data => { name => user, }, _in_storage => 1, _result_source => IGNORED, }, User => undef, } ``` For previous $row in my `while( my $row = $rows->next ) { ` it was: ``` $self->{_relationship_data} { Role => App::Schema::Result::Role { _column_data => { name => user, }, _in_storage => 1, _result_source => IGNORED, }, User => App::Schema::Result::User { _column_data => { email => some@mail, }, _in_storage => 1, _result_source => IGNORED, }, } ``` The target query for $rows is: ``` $rows->as_query \[ (SELECT "me"."id", "me"."user_id", "me"."role_id", "User"."email", "Role"."name" FROM "users_roles" "me" JOIN "users" "User" ON "User"."id" = "me"."user_id" JOIN "roles" "Role" ON "Role"."id" = "me"."role_id" ORDER BY "me"."id" DESC LIMIT ? OFFSET ?), [ { sqlt_datatype => integer, }, 20, ], [ { sqlt_datatype => integer, }, 20, ], ] ``` We can check that `user_id => 150` exists: ``` $c->db->resultset( 'User' )->find( 150 ); App::Schema::Result::User { _column_data => { activation => undef, email => undef, fax => undef, firstname => Alain, id => 150, }, _in_storage => 1, _result_source => IGNORED, } ``` And `email` is available if I write: $c->db->resultset( 'Right' )->find( 151 )->User->email undef Also: $c->db->resultset( 'Right' )->find( 151 )->User->firstname Alian But: $row->User->firstname Can't call method "firstname" on an undefined value at ... **WHY** DBIx::Class by mistake count that whole relation `User` does not exists because its field `email` is undef. **TODO** DBIx::Class should allow to access to fields of related table even if one of them is `undef` additional info: rows is constructed as: ``` ->search_rs( {}, { columns => [ me.id, me.user_id, me.role_id, User.email, Role.name, ], order_by => [ { -desc => me.id, }, ], }) ```
On Fri Jan 31 11:33:34 2020, KES wrote: Show quoted text
> Doing $row->User->email cause error: > > Can't call method "email" on an undefined value >
Thank you very much for the detailed report. It is however missing (arguably) the most important portion: what file/line did you get the above error at? Also which version of DBIC ( so I can match the file/error against the actual spot ) Thanks!
DBIx::Class - 0.082841 The error is at my file where I do $row->User->email. So file:line will not be helpful for you. The temporary workaround is to select additionally id field (that is guaranted is not null) so relationship now is correct Thus columns now are: [qw/ me.id me.user_id me.role_id User.email User.id Role.name /] and $row->{ _relationship_data } { Role => App::Schema::Result::Role { _column_data => { name => user, }, _in_storage => 1, _result_source => IGNORED, }, User => App::Schema::Result::User { _column_data => { email => undef, id => 150, }, _in_storage => 1, _result_source => IGNORED, }, }
The expected relationship_data when I do not select User.id is: $row->{ _relationship_data } { Role => App::Schema::Result::Role { _column_data => { name => user, }, _in_storage => 1, _result_source => IGNORED, }, User => App::Schema::Result::User { _column_data => { email => undef, }, _in_storage => 1, _result_source => IGNORED, }, } instead of actual: { Role => App::Schema::Result::Role { _column_data => { name => user, }, _in_storage => 1, _result_source => IGNORED, }, User => undef, }
On Fri Jan 31 11:50:00 2020, KES wrote: Show quoted text
> DBIx::Class - 0.082841 > > > The error is at my file where I do $row->User->email. So file:line > will not be helpful for you. >
My bad, I misunderstood your original example. That should be all I need. I'll get back to you some time over the weekend.
Show quoted text
> I'll get back to you some time over the weekend.
Hi, have you get around to it?
On Thu Apr 02 12:36:45 2020, KES wrote: Show quoted text
> > I'll get back to you some time over the weekend.
> > Hi, have you get around to it? > >
Sadly not. OSS activity is paused until the crazy times pass. If you have some thoughts on what could be the culprit, I will review, but no promises as to timelines. Sorry
On Fri Jan 31 11:51:31 2020, KES wrote: Show quoted text
> The expected relationship_data when I do not select User.id is: > > $row->{ _relationship_data } > { > Role => App::Schema::Result::Role { > _column_data => { > name => user, > }, > _in_storage => 1, > _result_source => IGNORED, > }, > User => App::Schema::Result::User { > _column_data => { > email => undef, > }, > _in_storage => 1, > _result_source => IGNORED, > }, > } > > instead of actual: > > { > Role => App::Schema::Result::Role { > _column_data => { > name => user, > }, > _in_storage => 1, > _result_source => IGNORED, > }, > User => undef, > }
Please give me the relationship definitions (both directions) and the column metadata (everything you gave to add_columns) for both Role and User. Nothing jumps out as "ok, this is why" based on what you gave me.
Here they are
Subject: Right.pm
package App::Schema::Result::Right; use Modern::Perl; use base 'App::Schema::Result'; my $T = __PACKAGE__; $T->table( 'users_roles' ); # Rights $T->add_columns(() ,id => {() ,data_type => 'integer' ,is_auto_increment => 1 } ,user_id => {() ,data_type => 'integer' } ,role_id => {() ,data_type => 'integer' } ); $T->set_primary_key( 'id' ); $T->add_unique_constraint([ 'user_id', 'role_id' ]); ## Establish many-to-many relationship my $U = 'App::Schema::Result::User'; $T->belongs_to( User => $U, 'user_id' ); my $R = 'App::Schema::Result::Role'; $T->belongs_to( Role => $R, 'role_id' ); local $ENV{ DBIC_OVERWRITE_HELPER_METHODS_OK } = 1; require App::Schema::Result::User; $U->has_many ( Rights => $T, 'user_id' ); $U->many_to_many( Roles => 'Rights', 'Role' ); require App::Schema::Result::Role; $R->has_many ( Users => $T, 'role_id' ); $R->many_to_many( Users => 'Users', 'User' ); 1;
Subject: Role.pm
package App::Schema::Result::Role; use Modern::Perl; use base 'App::Schema::Result'; my $T = __PACKAGE__; $T->table( 'roles' ); $T->add_columns(() ,id => {() ,data_type => 'integer' ,is_auto_increment => 1 } ,name => {() ,data_type => 'varchar' ,size => 64 } ); $T->set_primary_key( 'id' ); $T->add_unique_constraint([ 'name' ]); 1;
Subject: User.pm
package App::Schema::Result::User; use Modern::Perl; use base 'App::Schema::Result'; my $T = __PACKAGE__; $T->table( 'users' ); $T->add_columns(() ,id => {() ,data_type => 'integer' ,is_auto_increment => 1 } ,level => {() ,default_value => '0' ,data_type => 'varchar' ,size => 8 } ,email => {() ,data_type => 'varchar' ,size => 128 ,is_nullable => 1 } ,phone => {() ,data_type => 'varchar' ,size => 20 ,is_nullable => 1 } ,data => {() ,data_type => 'json' ,serializer_class => 'JSON' ,default_value => \"'{}'::json" } ,name => {() ,data_type => 'varchar' ,size => 32 ,is_nullable => 1 } ,password => {() ,data_type => 'varchar' ,size => 128 } ,activation => {() ,data_type => 'varchar' ,size => 64 ,is_nullable => 1 }, ,since => { data_type => 'datetime', default_value => \'now()', retrieve_on_insert => 1, }, lastseen => { data_type => 'datetime', is_nullable => 1, }, seen_from_ip => { data_type => 'text', is_nullable => 1, }, seen_from_city => { data_type => 'text', is_nullable => 1, }, ## Persona info firstname => { data_type => 'varchar', size => 128, is_nullable => 1, }, middlename => { data_type => 'varchar', size => 128, is_nullable => 1, }, lastname => { data_type => 'varchar', size => 128, is_nullable => 1, }, ## Communication info skype => { data_type => 'varchar', size => 128, is_nullable => 1, }, cellular => { data_type => 'varchar', size => 128, is_nullable => 1, }, fax => { data_type => 'varchar', size => 128, is_nullable => 1, }, ## Location info zip => { data_type => 'varchar', size => 12, is_nullable => 1, }, country_code => { data_type => 'varchar', size => 2, is_nullable => 1, }, country => { data_type => 'varchar', size => 64, is_nullable => 1, }, state => { data_type => 'varchar', size => 64, is_nullable => 1, }, city => { data_type => 'varchar', size => 64, is_nullable => 1, }, address => { data_type => 'varchar', size => 256, is_nullable => 1, }, # Company info company => { data_type => 'varchar', size => 128, is_nullable => 1, }, branch => { data_type => 'varchar', size => 64, is_nullable => 1, }, department => { data_type => 'varchar', size => 64, is_nullable => 1, }, options => { data_type => 'json', serializer_class => 'JSON', default_value => \"'{}'::json", }, ); $T->set_primary_key( 'id' ); $T->add_unique_constraint([ 'email' ]); $T->add_unique_constraint([ 'phone' ]); ## Data manipulation methods sub domain { my( $d ) = @_; return unless my $domain = $d->email; $domain =~ s/^.+\@//; return $domain } # Return user's role or all roles if requested # returns the empty string if user has no any role sub role { my( $self, $all ) = @_; my $roles = $self->Roles( undef, { order_by => 'id' }); $roles = [ map{ $_->name } $roles->all ]; return $all? $roles : $roles->[0] // ''; } 1;