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'; |
9 | use namespace::autoclean; |
70d56286 |
10 | |
11 | # how this works: |
12 | # |
13 | # On construction, we hook $self->result_class->result_source_instance |
14 | # if present to get the superclass' source object |
15 | # |
16 | # When attached to a schema, we need to add sources to that schema with |
17 | # appropriate relationships for the foreign keys so the concrete tables |
18 | # get generated |
19 | # |
20 | # We also generate our own view definition using this class' concrete table |
21 | # and the view for the superclass, and stored procedures for the insert, |
22 | # update and delete operations on this view. |
23 | # |
24 | # deploying the postgres rules through SQLT may be a pain though. |
25 | |
876f6525 |
26 | __PACKAGE__->mk_group_accessors(simple => qw(parent_source)); |
27 | |
28 | method new ($class: @args) { |
29 | my $new = $class->next::method(@args); |
30 | my $rc = $new->result_class; |
31 | if (my $meth = $rc->can('result_source_instance')) { |
32 | $new->parent_source($rc->$meth); |
33 | } |
34 | if ($new->schema) { |
35 | $new->_attach_additional_sources; |
36 | } |
37 | return $new; |
38 | } |
39 | |
40 | method _attach_additional_sources () { |
41 | my $raw_name = $self->_raw_source_name; |
ca79850d |
42 | my $schema = $self->schema; |
43 | |
44 | # if the raw source is already present we can assume we're done |
45 | return if grep { $_ eq $raw_name } $schema->sources; |
46 | # our parent should've been registered already actually due to DBIC |
47 | # attaching subclass sources later in load_namespaces |
48 | my $parent; |
49 | if ($self->parent_source) { |
50 | my $parent_name = $self->parent_source->name; |
51 | ($parent) = |
52 | grep { $_->name eq $parent_name } |
53 | map $schema->source($_), $schema->sources; |
54 | confess "Couldn't find attached source for parent $parent_name - did you use load_classes? This module is only compatible with load_namespaces" |
55 | unless $parent; |
56 | } |
57 | my $table = Table->new({ name => '_'.$self->name }); |
58 | $table->add_columns( |
59 | map { ($_ => { %{$self->column_info($_)} }) } |
60 | grep { $self->column_info($_)->{originally_defined_in} eq $self->name } |
61 | $self->columns |
62 | ); |
63 | # we don't need to add the PK cols explicitly if we're the root table |
64 | # since they already got added above |
65 | if ($parent) { |
66 | my %join; |
67 | foreach my $pri ($self->primary_columns) { |
68 | my %info = %{$self->column_info($pri)}; |
69 | delete @info{qw(is_auto_increment sequence auto_nextval)}; |
70 | $self->add_column($pri => \%info); |
71 | $join{"foreign.${pri}"} = "self.${pri}"; |
72 | } |
73 | $self->add_relationship('parent', $parent->source_name, \%join); |
74 | } |
75 | $table->set_primary_key($self->primary_columns); |
76 | $schema->register_source($raw_name => $table); |
77 | } |
78 | |
79 | method set_primary_key (@args) { |
80 | if ($self->parent_source) { |
81 | confess "Can't set primary key on a subclass"; |
82 | } |
83 | return $self->next::method(@args); |
876f6525 |
84 | } |
85 | |
86 | method _raw_source_name () { |
87 | my $base = $self->source_name; |
88 | confess "Can't generate raw source name when we don't have a source_name" |
89 | unless $base; |
90 | return 'Raw::'.$base; |
91 | } |
70d56286 |
92 | |
876f6525 |
93 | method add_columns (@args) { |
94 | my $ret = $self->next::method(@args); |
95 | $_->{originally_defined_in} ||= $self->name for values %{$self->_columns}; |
96 | return $ret; |
70d56286 |
97 | } |
98 | |
99 | 1; |