This file is indexed.

/usr/share/perl5/DBIx/Class/Relationship/Base.pm is in libdbix-class-perl 0.082840-3.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
package DBIx::Class::Relationship::Base;

use strict;
use warnings;

use base qw/DBIx::Class/;

use Scalar::Util qw/weaken blessed/;
use Try::Tiny;
use DBIx::Class::_Util 'UNRESOLVABLE_CONDITION';
use namespace::clean;

=head1 NAME

DBIx::Class::Relationship::Base - Inter-table relationships

=head1 SYNOPSIS

  __PACKAGE__->add_relationship(
    spiders => 'My::DB::Result::Creatures',
    sub {
      my $args = shift;
      return {
        "$args->{foreign_alias}.id"   => { -ident => "$args->{self_alias}.id" },
        "$args->{foreign_alias}.type" => 'arachnid'
      };
    },
  );

=head1 DESCRIPTION

This class provides methods to describe the relationships between the
tables in your database model. These are the "bare bones" relationships
methods, for predefined ones, look in L<DBIx::Class::Relationship>.

=head1 METHODS

=head2 add_relationship

=over 4

=item Arguments: $rel_name, $foreign_class, $condition, $attrs

=back

  __PACKAGE__->add_relationship('rel_name',
                                'Foreign::Class',
                                $condition, $attrs);

Create a custom relationship between one result source and another
source, indicated by its class name.

=head3 condition

The condition argument describes the C<ON> clause of the C<JOIN>
expression used to connect the two sources when creating SQL queries.

=head4 Simple equality

To create simple equality joins, supply a hashref containing the remote
table column name as the key(s) prefixed by C<'foreign.'>, and the
corresponding local table column name as the value(s) prefixed by C<'self.'>.
Both C<foreign> and C<self> are pseudo aliases and must be entered
literally. They will be replaced with the actual correct table alias
when the SQL is produced.

For example given:

  My::Schema::Author->has_many(
    books => 'My::Schema::Book',
    { 'foreign.author_id' => 'self.id' }
  );

A query like:

  $author_rs->search_related('books')->next

will result in the following C<JOIN> clause:

  ... FROM author me LEFT JOIN book books ON books.author_id = me.id ...

This describes a relationship between the C<Author> table and the
C<Book> table where the C<Book> table has a column C<author_id>
containing the ID value of the C<Author>.

Similarly:

  My::Schema::Book->has_many(
    editions => 'My::Schema::Edition',
    {
      'foreign.publisher_id' => 'self.publisher_id',
      'foreign.type_id'      => 'self.type_id',
    }
  );

  ...

  $book_rs->search_related('editions')->next

will result in the C<JOIN> clause:

  ... FROM book me
      LEFT JOIN edition editions ON
           editions.publisher_id = me.publisher_id
       AND editions.type_id = me.type_id ...

This describes the relationship from C<Book> to C<Edition>, where the
C<Edition> table refers to a publisher and a type (e.g. "paperback"):

=head4 Multiple groups of simple equality conditions

As is the default in L<SQL::Abstract>, the key-value pairs will be
C<AND>ed in the resulting C<JOIN> clause. An C<OR> can be achieved with
an arrayref. For example a condition like:

  My::Schema::Item->has_many(
    related_item_links => My::Schema::Item::Links,
    [
      { 'foreign.left_itemid'  => 'self.id' },
      { 'foreign.right_itemid' => 'self.id' },
    ],
  );

will translate to the following C<JOIN> clause:

 ... FROM item me JOIN item_relations related_item_links ON
         related_item_links.left_itemid = me.id
      OR related_item_links.right_itemid = me.id ...

This describes the relationship from C<Item> to C<Item::Links>, where
C<Item::Links> is a many-to-many linking table, linking items back to
themselves in a peer fashion (without a "parent-child" designation)

=head4 Custom join conditions

  NOTE: The custom join condition specification mechanism is capable of
  generating JOIN clauses of virtually unlimited complexity. This may limit
  your ability to traverse some of the more involved relationship chains the
  way you expect, *and* may bring your RDBMS to its knees. Exercise care
  when declaring relationships as described here.

To specify joins which describe more than a simple equality of column
values, the custom join condition coderef syntax can be used. For
example:

  My::Schema::Artist->has_many(
    cds_80s => 'My::Schema::CD',
    sub {
      my $args = shift;

      return {
        "$args->{foreign_alias}.artist" => { -ident => "$args->{self_alias}.artistid" },
        "$args->{foreign_alias}.year"   => { '>', "1979", '<', "1990" },
      };
    }
  );

  ...

  $artist_rs->search_related('cds_80s')->next;

will result in the C<JOIN> clause:

  ... FROM artist me LEFT JOIN cd cds_80s ON
        cds_80s.artist = me.artistid
    AND cds_80s.year < ?
    AND cds_80s.year > ?

with the bind values:

   '1990', '1979'

C<< $args->{foreign_alias} >> and C<< $args->{self_alias} >> are supplied the
same values that would be otherwise substituted for C<foreign> and C<self>
in the simple hashref syntax case.

The coderef is expected to return a valid L<SQL::Abstract> query-structure, just
like what one would supply as the first argument to
L<DBIx::Class::ResultSet/search>. The return value will be passed directly to
L<SQL::Abstract> and the resulting SQL will be used verbatim as the C<ON>
clause of the C<JOIN> statement associated with this relationship.

While every coderef-based condition must return a valid C<ON> clause, it may
elect to additionally return a simplified B<optional> join-free condition
consisting of a hashref with B<all keys being fully qualified names of columns
declared on the corresponding result source>. This boils down to two scenarios:

=over

=item *

When relationship resolution is invoked after C<< $result->$rel_name >>, as
opposed to C<< $rs->related_resultset($rel_name) >>, the C<$result> object
is passed to the coderef as C<< $args->{self_result_object} >>.

=item *

Alternatively when the user-space invokes resolution via
C<< $result->set_from_related( $rel_name => $foreign_values_or_object ) >>, the
corresponding data is passed to the coderef as C<< $args->{foreign_values} >>,
B<always> in the form of a hashref. If a foreign result object is supplied
(which is valid usage of L</set_from_related>), its values will be extracted
into hashref form by calling L<get_columns|DBIx::Class::Row/get_columns>.

=back

Note that the above scenarios are mutually exclusive, that is you will be supplied
none or only one of C<self_result_object> and C<foreign_values>. In other words if
you define your condition coderef as:

  sub {
    my $args = shift;

    return (
      {
        "$args->{foreign_alias}.artist" => { -ident => "$args->{self_alias}.artistid" },
        "$args->{foreign_alias}.year"   => { '>', "1979", '<', "1990" },
      },
      ! $args->{self_result_object} ? () : {
        "$args->{foreign_alias}.artist" => $args->{self_result_object}->artistid,
        "$args->{foreign_alias}.year"   => { '>', "1979", '<', "1990" },
      },
      ! $args->{foreign_values} ? () : {
        "$args->{self_alias}.artistid" => $args->{foreign_values}{artist},
      }
    );
  }

Then this code:

    my $artist = $schema->resultset("Artist")->find({ id => 4 });
    $artist->cds_80s->all;

Can skip a C<JOIN> altogether and instead produce:

    SELECT cds_80s.cdid, cds_80s.artist, cds_80s.title, cds_80s.year, cds_80s.genreid, cds_80s.single_track
      FROM cd cds_80s
      WHERE cds_80s.artist = ?
        AND cds_80s.year < ?
        AND cds_80s.year > ?

With the bind values:

    '4', '1990', '1979'

While this code:

    my $cd = $schema->resultset("CD")->search({ artist => 1 }, { rows => 1 })->single;
    my $artist = $schema->resultset("Artist")->new({});
    $artist->set_from_related('cds_80s');

Will properly set the C<< $artist->artistid >> field of this new object to C<1>

Note that in order to be able to use L</set_from_related> (and by extension
L<< $result->create_related|DBIx::Class::Relationship::Base/create_related >>),
the returned join free condition B<must> contain only plain values/deflatable
objects. For instance the C<year> constraint in the above example prevents
the relationship from being used to create related objects using
C<< $artst->create_related( cds_80s => { title => 'blah' } ) >> (an
exception will be thrown).

In order to allow the user to go truly crazy when generating a custom C<ON>
clause, the C<$args> hashref passed to the subroutine contains some extra
metadata. Currently the supplied coderef is executed as:

  $relationship_info->{cond}->({
    self_resultsource   => The resultsource instance on which rel_name is registered
    rel_name            => The relationship name (does *NOT* always match foreign_alias)

    self_alias          => The alias of the invoking resultset
    foreign_alias       => The alias of the to-be-joined resultset (does *NOT* always match rel_name)

    # only one of these (or none at all) will ever be supplied to aid in the
    # construction of a join-free condition

    self_result_object  => The invocant *object* itself in case of a call like
                           $result_object->$rel_name( ... )

    foreign_values      => A *hashref* of related data: may be passed in directly or
                           derived via ->get_columns() from a related object in case of
                           $result_object->set_from_related( $rel_name, $foreign_result_object )

    # deprecated inconsistent names, will be forever available for legacy code
    self_rowobj         => Old deprecated slot for self_result_object
    foreign_relname     => Old deprecated slot for rel_name
  });

=head3 attributes

The L<standard ResultSet attributes|DBIx::Class::ResultSet/ATTRIBUTES> may
be used as relationship attributes. In particular, the 'where' attribute is
useful for filtering relationships:

     __PACKAGE__->has_many( 'valid_users', 'MyApp::Schema::User',
        { 'foreign.user_id' => 'self.user_id' },
        { where => { valid => 1 } }
    );

The following attributes are also valid:

=over 4

=item join_type

Explicitly specifies the type of join to use in the relationship. Any SQL
join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in the SQL
command immediately before C<JOIN>.

=item proxy =E<gt> $column | \@columns | \%column

The 'proxy' attribute can be used to retrieve values, and to perform
updates if the relationship has 'cascade_update' set. The 'might_have'
and 'has_one' relationships have this set by default; if you want a proxy
to update across a 'belongs_to' relationship, you must set the attribute
yourself.

=over 4

=item \@columns

An arrayref containing a list of accessors in the foreign class to create in
the main class. If, for example, you do the following:

  MyApp::Schema::CD->might_have(liner_notes => 'MyApp::Schema::LinerNotes',
    undef, {
      proxy => [ qw/notes/ ],
    });

Then, assuming MyApp::Schema::LinerNotes has an accessor named notes, you can do:

  my $cd = MyApp::Schema::CD->find(1);
  $cd->notes('Notes go here'); # set notes -- LinerNotes object is
                               # created if it doesn't exist

For a 'belongs_to relationship, note the 'cascade_update':

  MyApp::Schema::Track->belongs_to( cd => 'MyApp::Schema::CD', 'cd,
      { proxy => ['title'], cascade_update => 1 }
  );
  $track->title('New Title');
  $track->update; # updates title in CD

=item \%column

A hashref where each key is the accessor you want installed in the main class,
and its value is the name of the original in the foreign class.

  MyApp::Schema::Track->belongs_to( cd => 'MyApp::Schema::CD', 'cd', {
      proxy => { cd_title => 'title' },
  });

This will create an accessor named C<cd_title> on the C<$track> result object.

=back

NOTE: you can pass a nested struct too, for example:

  MyApp::Schema::Track->belongs_to( cd => 'MyApp::Schema::CD', 'cd', {
    proxy => [ 'year', { cd_title => 'title' } ],
  });

=item accessor

Specifies the type of accessor that should be created for the relationship.
Valid values are C<single> (for when there is only a single related object),
C<multi> (when there can be many), and C<filter> (for when there is a single
related object, but you also want the relationship accessor to double as
a column accessor). For C<multi> accessors, an add_to_* method is also
created, which calls C<create_related> for the relationship.

=item is_foreign_key_constraint

If you are using L<SQL::Translator> to create SQL for you and you find that it
is creating constraints where it shouldn't, or not creating them where it
should, set this attribute to a true or false value to override the detection
of when to create constraints.

=item cascade_copy

If C<cascade_copy> is true on a C<has_many> relationship for an
object, then when you copy the object all the related objects will
be copied too. To turn this behaviour off, pass C<< cascade_copy => 0 >>
in the C<$attr> hashref.

The behaviour defaults to C<< cascade_copy => 1 >> for C<has_many>
relationships.

=item cascade_delete

By default, DBIx::Class cascades deletes across C<has_many>,
C<has_one> and C<might_have> relationships. You can disable this
behaviour on a per-relationship basis by supplying
C<< cascade_delete => 0 >> in the relationship attributes.

The cascaded operations are performed after the requested delete,
so if your database has a constraint on the relationship, it will
have deleted/updated the related records or raised an exception
before DBIx::Class gets to perform the cascaded operation.

=item cascade_update

By default, DBIx::Class cascades updates across C<has_one> and
C<might_have> relationships. You can disable this behaviour on a
per-relationship basis by supplying C<< cascade_update => 0 >> in
the relationship attributes.

The C<belongs_to> relationship does not update across relationships
by default, so if you have a 'proxy' attribute on a belongs_to and want to
use 'update' on it, you must set C<< cascade_update => 1 >>.

This is not a RDMS style cascade update - it purely means that when
an object has update called on it, all the related objects also
have update called. It will not change foreign keys automatically -
you must arrange to do this yourself.

=item on_delete / on_update

If you are using L<SQL::Translator> to create SQL for you, you can use these
attributes to explicitly set the desired C<ON DELETE> or C<ON UPDATE> constraint
type. If not supplied the SQLT parser will attempt to infer the constraint type by
interrogating the attributes of the B<opposite> relationship. For any 'multi'
relationship with C<< cascade_delete => 1 >>, the corresponding belongs_to
relationship will be created with an C<ON DELETE CASCADE> constraint. For any
relationship bearing C<< cascade_copy => 1 >> the resulting belongs_to constraint
will be C<ON UPDATE CASCADE>. If you wish to disable this autodetection, and just
use the RDBMS' default constraint type, pass C<< on_delete => undef >> or
C<< on_delete => '' >>, and the same for C<on_update> respectively.

=item is_deferrable

Tells L<SQL::Translator> that the foreign key constraint it creates should be
deferrable. In other words, the user may request that the constraint be ignored
until the end of the transaction. Currently, only the PostgreSQL producer
actually supports this.

=item add_fk_index

Tells L<SQL::Translator> to add an index for this constraint. Can also be
specified globally in the args to L<DBIx::Class::Schema/deploy> or
L<DBIx::Class::Schema/create_ddl_dir>. Default is on, set to 0 to disable.

=back

=head2 register_relationship

=over 4

=item Arguments: $rel_name, $rel_info

=back

Registers a relationship on the class. This is called internally by
DBIx::Class::ResultSourceProxy to set up Accessors and Proxies.

=cut

sub register_relationship { }

=head2 related_resultset

=over 4

=item Arguments: $rel_name

=item Return Value: L<$related_resultset|DBIx::Class::ResultSet>

=back

  $rs = $cd->related_resultset('artist');

Returns a L<DBIx::Class::ResultSet> for the relationship named
$rel_name.

=head2 $relationship_accessor

=over 4

=item Arguments: none

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> | L<$related_resultset|DBIx::Class::ResultSet> | undef

=back

  # These pairs do the same thing
  $result = $cd->related_resultset('artist')->single;  # has_one relationship
  $result = $cd->artist;
  $rs = $cd->related_resultset('tracks');           # has_many relationship
  $rs = $cd->tracks;

This is the recommended way to traverse through relationships, based
on the L</accessor> name given in the relationship definition.

This will return either a L<Result|DBIx::Class::Manual::ResultClass> or a
L<ResultSet|DBIx::Class::ResultSet>, depending on if the relationship is
C<single> (returns only one row) or C<multi> (returns many rows).  The
method may also return C<undef> if the relationship doesn't exist for
this instance (like in the case of C<might_have> relationships).

=cut

sub related_resultset {
  my $self = shift;

  $self->throw_exception("Can't call *_related as class methods")
    unless ref $self;

  my $rel = shift;

  return $self->{related_resultsets}{$rel}
    if defined $self->{related_resultsets}{$rel};

  return $self->{related_resultsets}{$rel} = do {

    my $rsrc = $self->result_source;

    my $rel_info = $rsrc->relationship_info($rel)
      or $self->throw_exception( "No such relationship '$rel'" );

    my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
    $attrs = { %{$rel_info->{attrs} || {}}, %$attrs };

    $self->throw_exception( "Invalid query: @_" )
      if (@_ > 1 && (@_ % 2 == 1));
    my $query = ((@_ > 1) ? {@_} : shift);

    # condition resolution may fail if an incomplete master-object prefetch
    # is encountered - that is ok during prefetch construction (not yet in_storage)
    my ($cond, $is_crosstable) = try {
      $rsrc->_resolve_condition( $rel_info->{cond}, $rel, $self, $rel )
    }
    catch {
      $self->throw_exception ($_) if $self->in_storage;
      UNRESOLVABLE_CONDITION;  # RV, no return()
    };

    # keep in mind that the following if() block is part of a do{} - no return()s!!!
    if ($is_crosstable and ref $rel_info->{cond} eq 'CODE') {

      # A WHOREIFFIC hack to reinvoke the entire condition resolution
      # with the correct alias. Another way of doing this involves a
      # lot of state passing around, and the @_ positions are already
      # mapped out, making this crap a less icky option.
      #
      # The point of this exercise is to retain the spirit of the original
      # $obj->search_related($rel) where the resulting rset will have the
      # root alias as 'me', instead of $rel (as opposed to invoking
      # $rs->search_related)

      # make the fake 'me' rel
      local $rsrc->{_relationships}{me} = {
        %{ $rsrc->{_relationships}{$rel} },
        _original_name => $rel,
      };

      my $obj_table_alias = lc($rsrc->source_name) . '__row';
      $obj_table_alias =~ s/\W+/_/g;

      $rsrc->resultset->search(
        $self->ident_condition($obj_table_alias),
        { alias => $obj_table_alias },
      )->search_related('me', $query, $attrs)
    }
    else {
      # FIXME - this conditional doesn't seem correct - got to figure out
      # at some point what it does. Also the entire UNRESOLVABLE_CONDITION
      # business seems shady - we could simply not query *at all*
      if ($cond eq UNRESOLVABLE_CONDITION) {
        my $reverse = $rsrc->reverse_relationship_info($rel);
        foreach my $rev_rel (keys %$reverse) {
          if ($reverse->{$rev_rel}{attrs}{accessor} && $reverse->{$rev_rel}{attrs}{accessor} eq 'multi') {
            weaken($attrs->{related_objects}{$rev_rel}[0] = $self);
          } else {
            weaken($attrs->{related_objects}{$rev_rel} = $self);
          }
        }
      }
      elsif (ref $cond eq 'ARRAY') {
        $cond = [ map {
          if (ref $_ eq 'HASH') {
            my $hash;
            foreach my $key (keys %$_) {
              my $newkey = $key !~ /\./ ? "me.$key" : $key;
              $hash->{$newkey} = $_->{$key};
            }
            $hash;
          } else {
            $_;
          }
        } @$cond ];
      }
      elsif (ref $cond eq 'HASH') {
       foreach my $key (grep { ! /\./ } keys %$cond) {
          $cond->{"me.$key"} = delete $cond->{$key};
        }
      }

      $query = ($query ? { '-and' => [ $cond, $query ] } : $cond);
      $rsrc->related_source($rel)->resultset->search(
        $query, $attrs
      );
    }
  };
}

=head2 search_related

=over 4

=item Arguments: $rel_name, $cond?, L<\%attrs?|DBIx::Class::ResultSet/ATTRIBUTES>

=item Return Value: L<$resultset|DBIx::Class::ResultSet> (scalar context) | L<@result_objs|DBIx::Class::Manual::ResultClass> (list context)

=back

Run a search on a related resultset. The search will be restricted to the
results represented by the L<DBIx::Class::ResultSet> it was called
upon.

See L<DBIx::Class::ResultSet/search_related> for more information.

=cut

sub search_related {
  return shift->related_resultset(shift)->search(@_);
}

=head2 search_related_rs

This method works exactly the same as search_related, except that
it guarantees a resultset, even in list context.

=cut

sub search_related_rs {
  return shift->related_resultset(shift)->search_rs(@_);
}

=head2 count_related

=over 4

=item Arguments: $rel_name, $cond?, L<\%attrs?|DBIx::Class::ResultSet/ATTRIBUTES>

=item Return Value: $count

=back

Returns the count of all the rows in the related resultset, restricted by the
current result or where conditions.

=cut

sub count_related {
  shift->search_related(@_)->count;
}

=head2 new_related

=over 4

=item Arguments: $rel_name, \%col_data

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass>

=back

Create a new result object of the related foreign class.  It will magically set
any foreign key columns of the new object to the related primary key columns
of the source object for you.  The newly created result will not be saved into
your storage until you call L<DBIx::Class::Row/insert> on it.

=cut

sub new_related {
  my ($self, $rel, $data) = @_;

  return $self->search_related($rel)->new_result( $self->result_source->_resolve_relationship_condition (
    infer_values_based_on => $data,
    rel_name => $rel,
    self_result_object => $self,
    foreign_alias => $rel,
    self_alias => 'me',
  )->{inferred_values} );
}

=head2 create_related

=over 4

=item Arguments: $rel_name, \%col_data

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass>

=back

  my $result = $obj->create_related($rel_name, \%col_data);

Creates a new result object, similarly to new_related, and also inserts the
result's data into your storage medium. See the distinction between C<create>
and C<new> in L<DBIx::Class::ResultSet> for details.

=cut

sub create_related {
  my $self = shift;
  my $rel = shift;
  my $obj = $self->new_related($rel, @_)->insert;
  delete $self->{related_resultsets}->{$rel};
  return $obj;
}

=head2 find_related

=over 4

=item Arguments: $rel_name, \%col_data | @pk_values, { key => $unique_constraint, L<%attrs|DBIx::Class::ResultSet/ATTRIBUTES> }?

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass> | undef

=back

  my $result = $obj->find_related($rel_name, \%col_data);

Attempt to find a related object using its primary key or unique constraints.
See L<DBIx::Class::ResultSet/find> for details.

=cut

sub find_related {
  #my ($self, $rel, @args) = @_;
  return shift->search_related(shift)->find(@_);
}

=head2 find_or_new_related

=over 4

=item Arguments: $rel_name, \%col_data, { key => $unique_constraint, L<%attrs|DBIx::Class::ResultSet/ATTRIBUTES> }?

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass>

=back

Find a result object of a related class.  See L<DBIx::Class::ResultSet/find_or_new>
for details.

=cut

sub find_or_new_related {
  my $self = shift;
  my $obj = $self->find_related(@_);
  return defined $obj ? $obj : $self->new_related(@_);
}

=head2 find_or_create_related

=over 4

=item Arguments: $rel_name, \%col_data, { key => $unique_constraint, L<%attrs|DBIx::Class::ResultSet/ATTRIBUTES> }?

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass>

=back

Find or create a result object of a related class. See
L<DBIx::Class::ResultSet/find_or_create> for details.

=cut

sub find_or_create_related {
  my $self = shift;
  my $obj = $self->find_related(@_);
  return (defined($obj) ? $obj : $self->create_related(@_));
}

=head2 update_or_create_related

=over 4

=item Arguments: $rel_name, \%col_data, { key => $unique_constraint, L<%attrs|DBIx::Class::ResultSet/ATTRIBUTES> }?

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass>

=back

Update or create a result object of a related class. See
L<DBIx::Class::ResultSet/update_or_create> for details.

=cut

sub update_or_create_related {
  #my ($self, $rel, @args) = @_;
  shift->related_resultset(shift)->update_or_create(@_);
}

=head2 set_from_related

=over 4

=item Arguments: $rel_name, L<$result|DBIx::Class::Manual::ResultClass>

=item Return Value: not defined

=back

  $book->set_from_related('author', $author_obj);
  $book->author($author_obj);                      ## same thing

Set column values on the current object, using related values from the given
related object. This is used to associate previously separate objects, for
example, to set the correct author for a book, find the Author object, then
call set_from_related on the book.

This is called internally when you pass existing objects as values to
L<DBIx::Class::ResultSet/create>, or pass an object to a belongs_to accessor.

The columns are only set in the local copy of the object, call
L<update|DBIx::Class::Row/update> to update them in the storage.

=cut

sub set_from_related {
  my ($self, $rel, $f_obj) = @_;

  $self->set_columns( $self->result_source->_resolve_relationship_condition (
    infer_values_based_on => {},
    rel_name => $rel,
    foreign_values => $f_obj,
    foreign_alias => $rel,
    self_alias => 'me',
  )->{inferred_values} );

  return 1;
}

=head2 update_from_related

=over 4

=item Arguments: $rel_name, L<$result|DBIx::Class::Manual::ResultClass>

=item Return Value: not defined

=back

  $book->update_from_related('author', $author_obj);

The same as L</"set_from_related">, but the changes are immediately updated
in storage.

=cut

sub update_from_related {
  my $self = shift;
  $self->set_from_related(@_);
  $self->update;
}

=head2 delete_related

=over 4

=item Arguments: $rel_name, $cond?, L<\%attrs?|DBIx::Class::ResultSet/ATTRIBUTES>

=item Return Value: $underlying_storage_rv

=back

Delete any related row, subject to the given conditions.  Internally, this
calls:

  $self->search_related(@_)->delete

And returns the result of that.

=cut

sub delete_related {
  my $self = shift;
  my $obj = $self->search_related(@_)->delete;
  delete $self->{related_resultsets}->{$_[0]};
  return $obj;
}

=head2 add_to_$rel

B<Currently only available for C<has_many>, C<many_to_many> and 'multi' type
relationships.>

=head3 has_many / multi

=over 4

=item Arguments: \%col_data

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass>

=back

Creates/inserts a new result object.  Internally, this calls:

  $self->create_related($rel, @_)

And returns the result of that.

=head3 many_to_many

=over 4

=item Arguments: (\%col_data | L<$result|DBIx::Class::Manual::ResultClass>), \%link_col_data?

=item Return Value: L<$result|DBIx::Class::Manual::ResultClass>

=back

  my $role = $schema->resultset('Role')->find(1);
  $actor->add_to_roles($role);
      # creates a My::DBIC::Schema::ActorRoles linking table result object

  $actor->add_to_roles({ name => 'lead' }, { salary => 15_000_000 });
      # creates a new My::DBIC::Schema::Role result object and the linking table
      # object with an extra column in the link

Adds a linking table object. If the first argument is a hash reference, the
related object is created first with the column values in the hash. If an object
reference is given, just the linking table object is created. In either case,
any additional column values for the linking table object can be specified in
C<\%link_col_data>.

See L<DBIx::Class::Relationship/many_to_many> for additional details.

=head2 set_$rel

B<Currently only available for C<many_to_many> relationships.>

=over 4

=item Arguments: (\@hashrefs_of_col_data | L<\@result_objs|DBIx::Class::Manual::ResultClass>), $link_vals?

=item Return Value: not defined

=back

  my $actor = $schema->resultset('Actor')->find(1);
  my @roles = $schema->resultset('Role')->search({ role =>
     { '-in' => ['Fred', 'Barney'] } } );

  $actor->set_roles(\@roles);
     # Replaces all of $actor's previous roles with the two named

  $actor->set_roles(\@roles, { salary => 15_000_000 });
     # Sets a column in the link table for all roles


Replace all the related objects with the given reference to a list of
objects. This does a C<delete> B<on the link table resultset> to remove the
association between the current object and all related objects, then calls
C<add_to_$rel> repeatedly to link all the new objects.

Note that this means that this method will B<not> delete any objects in the
table on the right side of the relation, merely that it will delete the link
between them.

Due to a mistake in the original implementation of this method, it will also
accept a list of objects or hash references. This is B<deprecated> and will be
removed in a future version.

=head2 remove_from_$rel

B<Currently only available for C<many_to_many> relationships.>

=over 4

=item Arguments: L<$result|DBIx::Class::Manual::ResultClass>

=item Return Value: not defined

=back

  my $role = $schema->resultset('Role')->find(1);
  $actor->remove_from_roles($role);
      # removes $role's My::DBIC::Schema::ActorRoles linking table result object

Removes the link between the current object and the related object. Note that
the related object itself won't be deleted unless you call ->delete() on
it. This method just removes the link between the two objects.

=head1 FURTHER QUESTIONS?

Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.

=head1 COPYRIGHT AND LICENSE

This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
redistribute it and/or modify it under the same terms as the
L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.

=cut

1;