first cut at additional source code
[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';
9use 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
28method 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
40method _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
79method 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
86method _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 93method 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
991;