Commit | Line | Data |
a9c8094b |
1 | package # hide from PAUSE |
2 | DBIx::Class::CDBICompat::Relationships; |
3 | |
4 | use strict; |
5 | use warnings; |
6 | |
7 | use base qw/Class::Data::Inheritable/; |
8 | |
9 | use Clone; |
10 | use DBIx::Class::CDBICompat::Relationship; |
11 | |
12 | __PACKAGE__->mk_classdata('__meta_info' => {}); |
13 | |
14 | |
15 | =head1 NAME |
16 | |
17 | DBIx::Class::CDBICompat::Relationships |
18 | |
19 | =head1 DESCRIPTION |
20 | |
21 | Emulate C<has_a>, C<has_many>, C<might_have> and C<meta_info>. |
22 | |
23 | =cut |
24 | |
25 | sub has_a { |
26 | my ($self, $col, $f_class, %args) = @_; |
27 | $self->throw_exception( "No such column ${col}" ) unless $self->has_column($col); |
28 | $self->ensure_class_loaded($f_class); |
29 | |
1097f5e4 |
30 | my $rel_info; |
a9c8094b |
31 | |
32 | if ($args{'inflate'} || $args{'deflate'}) { # Non-database has_a |
33 | if (!ref $args{'inflate'}) { |
34 | my $meth = $args{'inflate'}; |
35 | $args{'inflate'} = sub { $f_class->$meth(shift); }; |
36 | } |
37 | if (!ref $args{'deflate'}) { |
38 | my $meth = $args{'deflate'}; |
39 | $args{'deflate'} = sub { shift->$meth; }; |
40 | } |
41 | $self->inflate_column($col, \%args); |
42 | |
1097f5e4 |
43 | $rel_info = { |
a9c8094b |
44 | class => $f_class |
45 | }; |
46 | } |
47 | else { |
48 | $self->belongs_to($col, $f_class); |
1097f5e4 |
49 | $rel_info = $self->result_source_instance->relationship_info($col); |
a9c8094b |
50 | } |
51 | |
1097f5e4 |
52 | $rel_info->{args} = \%args; |
53 | |
a9c8094b |
54 | $self->_extend_meta( |
55 | has_a => $col, |
1097f5e4 |
56 | $rel_info |
a9c8094b |
57 | ); |
58 | |
59 | return 1; |
60 | } |
61 | |
62 | |
63 | sub has_many { |
64 | my ($class, $rel, $f_class, $f_key, $args) = @_; |
65 | |
66 | my @f_method; |
67 | |
68 | if (ref $f_class eq 'ARRAY') { |
69 | ($f_class, @f_method) = @$f_class; |
70 | } |
71 | |
72 | if (ref $f_key eq 'HASH' && !$args) { $args = $f_key; undef $f_key; }; |
73 | |
74 | $args ||= {}; |
deff792e |
75 | my $cascade = delete $args->{cascade} || ''; |
76 | if (delete $args->{no_cascade_delete} || $cascade eq 'None') { |
a9c8094b |
77 | $args->{cascade_delete} = 0; |
78 | } |
deff792e |
79 | elsif( $cascade eq 'Delete' ) { |
80 | $args->{cascade_delete} = 1; |
81 | } |
82 | elsif( length $cascade ) { |
83 | warn "Unemulated cascade option '$cascade' in $class->has_many($rel => $f_class)"; |
84 | } |
a9c8094b |
85 | |
86 | if( !$f_key and !@f_method ) { |
4656f62f |
87 | $class->ensure_class_loaded($f_class); |
a9c8094b |
88 | my $f_source = $f_class->result_source_instance; |
89 | ($f_key) = grep { $f_source->relationship_info($_)->{class} eq $class } |
90 | $f_source->relationships; |
91 | } |
92 | |
93 | $class->next::method($rel, $f_class, $f_key, $args); |
94 | |
1097f5e4 |
95 | my $rel_info = $class->result_source_instance->relationship_info($rel); |
96 | $args->{mapping} = \@f_method; |
97 | $args->{foreign_key} = $f_key; |
98 | $rel_info->{args} = $args; |
99 | |
a9c8094b |
100 | $class->_extend_meta( |
101 | has_many => $rel, |
1097f5e4 |
102 | $rel_info |
a9c8094b |
103 | ); |
104 | |
105 | if (@f_method) { |
106 | no strict 'refs'; |
107 | no warnings 'redefine'; |
108 | my $post_proc = sub { my $o = shift; $o = $o->$_ for @f_method; $o; }; |
109 | *{"${class}::${rel}"} = |
110 | sub { |
111 | my $rs = shift->search_related($rel => @_); |
112 | $rs->{attrs}{record_filter} = $post_proc; |
113 | return (wantarray ? $rs->all : $rs); |
114 | }; |
115 | return 1; |
116 | } |
117 | |
118 | } |
119 | |
120 | |
121 | sub might_have { |
122 | my ($class, $rel, $f_class, @columns) = @_; |
123 | |
124 | my $ret; |
125 | if (ref $columns[0] || !defined $columns[0]) { |
126 | $ret = $class->next::method($rel, $f_class, @columns); |
127 | } else { |
128 | $ret = $class->next::method($rel, $f_class, undef, |
129 | { proxy => \@columns }); |
130 | } |
1097f5e4 |
131 | |
132 | my $rel_info = $class->result_source_instance->relationship_info($rel); |
133 | $rel_info->{args}{import} = \@columns; |
134 | |
a9c8094b |
135 | $class->_extend_meta( |
136 | might_have => $rel, |
1097f5e4 |
137 | $rel_info |
a9c8094b |
138 | ); |
139 | |
140 | return $ret; |
141 | } |
142 | |
143 | |
144 | sub _extend_meta { |
145 | my ($class, $type, $rel, $val) = @_; |
146 | my %hash = %{ Clone::clone($class->__meta_info || {}) }; |
147 | |
148 | $val->{self_class} = $class; |
149 | $val->{type} = $type; |
150 | $val->{accessor} = $rel; |
151 | |
152 | $hash{$type}{$rel} = DBIx::Class::CDBICompat::Relationship->new($val); |
153 | $class->__meta_info(\%hash); |
154 | } |
155 | |
aa11d765 |
156 | |
a9c8094b |
157 | sub meta_info { |
158 | my ($class, $type, $rel) = @_; |
159 | my $meta = $class->__meta_info; |
160 | return $meta unless $type; |
161 | |
162 | my $type_meta = $meta->{$type}; |
163 | return $type_meta unless $rel; |
164 | return $type_meta->{$rel}; |
165 | } |
166 | |
167 | |
168 | sub search { |
169 | my $self = shift; |
170 | my $attrs = {}; |
171 | if (@_ > 1 && ref $_[$#_] eq 'HASH') { |
172 | $attrs = { %{ pop(@_) } }; |
173 | } |
174 | my $where = (@_ ? ((@_ == 1) ? ((ref $_[0] eq "HASH") ? { %{+shift} } : shift) |
175 | : {@_}) |
176 | : undef()); |
177 | if (ref $where eq 'HASH') { |
178 | foreach my $key (keys %$where) { # has_a deflation hack |
179 | $where->{$key} = ''.$where->{$key} |
180 | if eval { $where->{$key}->isa('DBIx::Class') }; |
181 | } |
182 | } |
183 | $self->next::method($where, $attrs); |
184 | } |
185 | |
186 | 1; |