clean up code a bit more
[dbsrgits/DBIx-Class-ResultSource-MultipleTableInheritance.git] / lib / DBIx / Class / ResultSource / MultipleTableInheritance.pm
CommitLineData
876f6525 1package DBIx::Class::ResultSource::MultipleTableInheritance;
2
3use strict;
4use warnings;
5use parent qw(DBIx::Class::ResultSource::View);
876f6525 6use Method::Signatures::Simple;
7use Carp::Clan qw/^DBIx::Class/;
ca79850d 8use aliased 'DBIx::Class::ResultSource::Table';
7abe3af2 9use aliased 'DBIx::Class::ResultClass::HashRefInflator';
ca79850d 10use 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
29method 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 42method schema (@args) {
43 my $ret = $self->next::method(@args);
44 if (@args) {
45 $self->_attach_additional_sources;
46 }
47 return $ret;
48}
49
876f6525 50method _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
101method 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 108method 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 115method raw_table_name () {
116 return '_'.$self->name;
117}
118
876f6525 119method 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
1251;