Commit | Line | Data |
876f6525 |
1 | package DBIx::Class::ResultSource::MultipleTableInheritance; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | use parent qw(DBIx::Class::ResultSource::View); |
876f6525 |
6 | use Method::Signatures::Simple; |
7 | use Carp::Clan qw/^DBIx::Class/; |
ca79850d |
8 | use aliased 'DBIx::Class::ResultSource::Table'; |
7abe3af2 |
9 | use aliased 'DBIx::Class::ResultClass::HashRefInflator'; |
ca79850d |
10 | use namespace::autoclean; |
70d56286 |
11 | |
12 | # how this works: |
13 | # |
14 | # On construction, we hook $self->result_class->result_source_instance |
15 | # if present to get the superclass' source object |
16 | # |
17 | # When attached to a schema, we need to add sources to that schema with |
18 | # appropriate relationships for the foreign keys so the concrete tables |
19 | # get generated |
20 | # |
21 | # We also generate our own view definition using this class' concrete table |
22 | # and the view for the superclass, and stored procedures for the insert, |
23 | # update and delete operations on this view. |
24 | # |
25 | # deploying the postgres rules through SQLT may be a pain though. |
26 | |
876f6525 |
27 | __PACKAGE__->mk_group_accessors(simple => qw(parent_source)); |
28 | |
29 | method new ($class: @args) { |
30 | my $new = $class->next::method(@args); |
31 | my $rc = $new->result_class; |
32 | if (my $meth = $rc->can('result_source_instance')) { |
7abe3af2 |
33 | my $source = $rc->$meth; |
34 | if ($source->result_class ne $new->result_class |
35 | && $new->result_class->isa($source->result_class)) { |
36 | $new->parent_source($source); |
37 | } |
876f6525 |
38 | } |
39 | return $new; |
40 | } |
41 | |
7abe3af2 |
42 | method schema (@args) { |
43 | my $ret = $self->next::method(@args); |
44 | if (@args) { |
45 | $self->_attach_additional_sources; |
46 | } |
47 | return $ret; |
48 | } |
49 | |
876f6525 |
50 | method _attach_additional_sources () { |
4d88a8d7 |
51 | my $raw_name = $self->raw_source_name; |
ca79850d |
52 | my $schema = $self->schema; |
53 | |
54 | # if the raw source is already present we can assume we're done |
55 | return if grep { $_ eq $raw_name } $schema->sources; |
4d88a8d7 |
56 | |
ca79850d |
57 | # our parent should've been registered already actually due to DBIC |
58 | # attaching subclass sources later in load_namespaces |
4d88a8d7 |
59 | |
ca79850d |
60 | my $parent; |
61 | if ($self->parent_source) { |
62 | my $parent_name = $self->parent_source->name; |
63 | ($parent) = |
64 | grep { $_->name eq $parent_name } |
65 | map $schema->source($_), $schema->sources; |
66 | confess "Couldn't find attached source for parent $parent_name - did you use load_classes? This module is only compatible with load_namespaces" |
67 | unless $parent; |
68 | } |
4d88a8d7 |
69 | |
70 | # create the raw table source |
71 | |
72 | my $table = Table->new({ name => $self->raw_table_name }); |
73 | |
ca79850d |
74 | # we don't need to add the PK cols explicitly if we're the root table |
4d88a8d7 |
75 | # since they'll get added below |
76 | |
ca79850d |
77 | if ($parent) { |
78 | my %join; |
79 | foreach my $pri ($self->primary_columns) { |
80 | my %info = %{$self->column_info($pri)}; |
81 | delete @info{qw(is_auto_increment sequence auto_nextval)}; |
7abe3af2 |
82 | $table->add_column($pri => \%info); |
ca79850d |
83 | $join{"foreign.${pri}"} = "self.${pri}"; |
84 | } |
4d88a8d7 |
85 | # have to use source name lookups rather than result class here |
86 | # because we don't actually have a result class on the raw sources |
87 | $table->add_relationship('parent', $parent->raw_source_name, \%join); |
ca79850d |
88 | } |
4d88a8d7 |
89 | |
90 | # add every column that's actually a concrete part of us |
91 | |
92 | $table->add_columns( |
93 | map { ($_ => { %{$self->column_info($_)} }) } |
94 | grep { $self->column_info($_)->{originally_defined_in} eq $self->name } |
95 | $self->columns |
96 | ); |
ca79850d |
97 | $table->set_primary_key($self->primary_columns); |
98 | $schema->register_source($raw_name => $table); |
99 | } |
100 | |
101 | method set_primary_key (@args) { |
102 | if ($self->parent_source) { |
103 | confess "Can't set primary key on a subclass"; |
104 | } |
105 | return $self->next::method(@args); |
876f6525 |
106 | } |
107 | |
4d88a8d7 |
108 | method raw_source_name () { |
876f6525 |
109 | my $base = $self->source_name; |
110 | confess "Can't generate raw source name when we don't have a source_name" |
111 | unless $base; |
112 | return 'Raw::'.$base; |
113 | } |
70d56286 |
114 | |
4d88a8d7 |
115 | method raw_table_name () { |
116 | return '_'.$self->name; |
117 | } |
118 | |
876f6525 |
119 | method add_columns (@args) { |
120 | my $ret = $self->next::method(@args); |
121 | $_->{originally_defined_in} ||= $self->name for values %{$self->_columns}; |
122 | return $ret; |
70d56286 |
123 | } |
124 | |
125 | 1; |