File Coverage

blib/lib/Fey/Schema.pm
Criterion Covered Total %
statement 118 118 100.0
branch 27 28 96.4
condition n/a
subroutine 19 19 100.0
pod 6 6 100.0
total 170 171 99.4


line stmt bran cond sub pod time code
1             package Fey::Schema;
2              
3 26     26   137 use strict;
  26         40  
  26         1087  
4 26     26   134 use warnings;
  26         37  
  26         838  
5 26     26   126 use namespace::autoclean;
  26         35  
  26         229  
6              
7             our $VERSION = '0.43';
8              
9 26     26   2531 use Fey::Exceptions qw( param_error );
  26         46  
  26         1722  
10 26     26   138 use Fey::NamedObjectSet;
  26         40  
  26         451  
11 26     26   9132 use Fey::SQL;
  26         88  
  26         975  
12 26     26   189 use Fey::Table;
  26         40  
  26         856  
13             use Fey::Types
14 26     26   113 qw( FK HashRef NamedObjectSet Str Table TableLikeOrName TableOrName );
  26         38  
  26         130  
15 26     26   325218 use Scalar::Util qw( blessed );
  26         63  
  26         1936  
16              
17 26     26   146 use Moose 2.1200;
  26         809  
  26         234  
18 26     26   159209 use MooseX::Params::Validate 0.21 qw( pos_validated_list );
  26         852  
  26         190  
19 26     26   5565 use MooseX::SemiAffordanceAccessor 0.03;
  26         502  
  26         195  
20 26     26   92027 use MooseX::StrictConstructor 0.13;
  26         2286  
  26         216  
21              
22             has 'name' => (
23             is => 'rw',
24             isa => Str,
25             required => 1,
26             );
27              
28             has '_tables' => (
29             is => 'ro',
30             isa => NamedObjectSet,
31             default => sub { return Fey::NamedObjectSet->new() },
32             handles => {
33             tables => 'objects',
34             table => 'object',
35             },
36             init_arg => undef,
37             );
38              
39             has '_fks' => (
40             is => 'ro',
41             isa => HashRef,
42             default => sub { {} },
43             init_arg => undef,
44             );
45              
46             sub add_table {
47 221     221 1 3024 my $self = shift;
48 221         1144 my ($table) = pos_validated_list( \@_, { isa => Table } );
49              
50 221         82895 my $name = $table->name();
51 221 100       1174 param_error "The schema already contains a table named $name."
52             if $self->table($name);
53              
54 220         30850 $self->_tables->add($table);
55              
56 220         7099 $table->_set_schema($self);
57              
58 220         668 return $self;
59             }
60              
61             sub remove_table {
62 3     3 1 999 my $self = shift;
63 3         18 my ($table)
64             = pos_validated_list( \@_, { isa => TableOrName } );
65              
66 3 100       94 $table = $self->table($table)
67             unless blessed $table;
68              
69 3         332 for my $fk ( $self->foreign_keys_for_table($table) ) {
70 1         4 $self->remove_foreign_key($fk);
71             }
72              
73 3         113 $self->_tables()->delete($table);
74              
75 3         131 $table->_set_schema(undef);
76              
77 3         10 return $self;
78             }
79              
80             sub add_foreign_key {
81 140     140 1 1237 my $self = shift;
82 140         570 my ($fk) = pos_validated_list( \@_, { isa => FK } );
83              
84 140         52859 my $fk_id = $fk->id();
85              
86 140         3112 my $source_table_name = $fk->source_table()->name();
87              
88 140         211 for my $col_name ( map { $_->name() } @{ $fk->source_columns() } ) {
  140         3038  
  140         3049  
89 140         3283 $self->_fks()->{$source_table_name}{$col_name}{$fk_id} = $fk;
90             }
91              
92 140         3115 my $target_table_name = $fk->target_table()->name();
93              
94 140         215 for my $col_name ( map { $_->name() } @{ $fk->target_columns() } ) {
  140         2950  
  140         3086  
95 140         3024 $self->_fks()->{$target_table_name}{$col_name}{$fk_id} = $fk;
96             }
97              
98 140         452 return $self;
99             }
100              
101             sub remove_foreign_key {
102 4     4 1 2387 my $self = shift;
103 4         19 my ($fk) = pos_validated_list( \@_, { isa => FK } );
104              
105 4         2482 my $fk_id = $fk->id();
106              
107 4         129 my $source_table_name = $fk->source_table()->name();
108 4         8 for my $col_name ( map { $_->name() } @{ $fk->source_columns() } ) {
  4         140  
  4         126  
109 4         150 delete $self->_fks()->{$source_table_name}{$col_name}{$fk_id};
110             }
111              
112 4         147 my $target_table_name = $fk->target_table()->name();
113 4         9 for my $col_name ( map { $_->name() } @{ $fk->target_columns() } ) {
  4         117  
  4         125  
114 4         120 delete $self->_fks()->{$target_table_name}{$col_name}{$fk_id};
115             }
116              
117 4         36 return $self;
118             }
119              
120             sub foreign_keys_for_table {
121 12     12 1 996 my $self = shift;
122 12         60 my ($table)
123             = pos_validated_list( \@_, { isa => TableOrName } );
124              
125 12 100       521 my $name = blessed $table ? $table->name() : $table;
126              
127 8         271 my %fks = (
128 11         361 map { $_->id() => $_ }
129 11 100       12 map { values %{ $self->_fks()->{$name}{$_} } }
  12         444  
130 12         24 keys %{ $self->_fks()->{$name} || {} }
131             );
132              
133 12         71 return values %fks;
134             }
135              
136             sub foreign_keys_between_tables {
137 43     43 1 1754 my $self = shift;
138 43         206 my ( $table1, $table2 ) = pos_validated_list(
139             \@_,
140             { isa => TableLikeOrName },
141             { isa => TableLikeOrName }
142             );
143              
144 43 100       3342 my $name1
    100          
145             = !blessed $table1 ? $table1
146             : $table1->isa('Fey::Table') ? $table1->name()
147             : $table1->table()->name();
148              
149 43 100       1186 my $name2
    100          
150             = !blessed $table2 ? $table2
151             : $table2->isa('Fey::Table') ? $table2->name()
152             : $table2->table()->name();
153              
154 40         930 my %fks = (
155 73         250 map { $_->id() => $_ }
156 49         1120 grep { $_->has_tables( $name1, $name2 ) }
157 49 100       56 map { values %{ $self->_fks()->{$name1}{$_} } }
  43         1121  
158 43         280 keys %{ $self->_fks()->{$name1} || {} }
159             );
160              
161 86 100       507 return values %fks
162 43 100       94 unless grep { blessed $_ && $_->is_alias() } $table1, $table2;
163              
164 5 50       23 $table1 = $self->table($name1)
165             unless blessed $table1;
166              
167 5 100       25 $table2 = $self->table($name2)
168             unless blessed $table2;
169              
170 5         173 my @fks;
171              
172 5         15 for my $fk ( values %fks ) {
173 1         32 my %p
174             = $table1->name() eq $fk->source_table()->name()
175             ? (
176             source_columns => [
177             $table1->columns(
178 1         32 map { $_->name() } @{ $fk->source_columns() }
  1         38  
179             )
180             ],
181             target_columns => [
182             $table2->columns(
183 1         54 map { $_->name() } @{ $fk->target_columns() }
  4         82  
184             )
185             ],
186             )
187             : (
188             source_columns => [
189             $table2->columns(
190 4         82 map { $_->name() } @{ $fk->source_columns() }
  4         75  
191             )
192             ],
193             target_columns => [
194             $table1->columns(
195 5 100       53 map { $_->name() } @{ $fk->target_columns() }
  4         93  
196             )
197             ],
198             );
199              
200 5         192 push @fks, Fey::FK->new(%p);
201             }
202              
203 5         28 return @fks;
204             }
205              
206             __PACKAGE__->meta()->make_immutable();
207              
208             1;
209              
210             # ABSTRACT: Represents a schema and contains tables and foreign keys
211              
212             __END__
213              
214             =pod
215              
216             =head1 NAME
217              
218             Fey::Schema - Represents a schema and contains tables and foreign keys
219              
220             =head1 VERSION
221              
222             version 0.43
223              
224             =head1 SYNOPSIS
225              
226             my $schema = Fey::Schema->new( name => 'MySchema' );
227              
228             $schema->add_table(...);
229              
230             $schema->add_foreign_key(...);
231              
232             =head1 DESCRIPTION
233              
234             This class represents a schema, which is a set of tables and foreign
235             keys.
236              
237             =head1 METHODS
238              
239             This class provides the following methods:
240              
241             =head2 Fey::Schema->new()
242              
243             my $schema = Fey::Schema->new( name => 'MySchema' );
244              
245             This method constructs a new C<Fey::Schema> object. It takes the
246             following parameters:
247              
248             =over 4
249              
250             =item * name - required
251              
252             The name of the schema.
253              
254             =back
255              
256             =head2 $schema->name()
257              
258             Returns the name of the schema.
259              
260             =head2 $schema->add_table($table)
261              
262             Adds the specified table to the schema. The table must be a
263             C<Fey::Table> object. Adding the table to the schema sets the schema
264             for the table, so that C<< $table->schema() >> returns the correct
265             object.
266              
267             If the table is already part of the schema, an exception will be
268             thrown.
269              
270             =head2 $schema->remove_table($table)
271              
272             Remove the specified table from the schema. Removing the table also
273             removes any foreign keys which reference the table. Removing the table
274             unsets the schema for the table.
275              
276             The table can be specified either by name or by passing in a
277             C<Fey::Table> object.
278              
279             =head2 $schema->table($name)
280              
281             Returns the table with the specified name. If no such table exists,
282             this method returns false.
283              
284             =head2 $schema->tables()
285              
286             =head2 $schema->tables(@names)
287              
288             When this method is called with no arguments, it returns all of the tables in
289             the schema. Tables are returned in the order with which they were added to the
290             schema.
291              
292             If given a list of names, it returns only the specified tables. If a name is
293             given which doesn't match a table in the schema, then it is ignored.
294              
295             =head2 $schema->add_foreign_key($fk)
296              
297             Adds the specified to the schema. The foreign key must be a C<Fey::FK>
298             object.
299              
300             If the foreign key references tables which are not in the schema, an
301             exception will be thrown.
302              
303             =head2 $schema->remove_foreign_key($fk)
304              
305             Removes the specified foreign key from the schema. The foreign key
306             must be a C<Fey::FK> object.
307              
308             =head2 $schema->foreign_keys_for_table($table)
309              
310             Returns all the foreign keys which reference the specified table. The
311             table can be specified as a name or a C<Fey::Table> object.
312              
313             =head2 $schema->foreign_keys_between_tables( $source_table, $target_table )
314              
315             Returns all the foreign keys which reference both tables. The tables
316             can be specified as names, C<Fey::Table> objects, or
317             C<Fey::Table::Alias> objects. If you provide any aliases, the foreign
318             keys returned will contain columns from those aliases, not the real
319             tables. This provides support for joining an alias in a SQL statement.
320              
321             =head1 BUGS
322              
323             See L<Fey> for details on how to report bugs.
324              
325             =head1 AUTHOR
326              
327             Dave Rolsky <autarch@urth.org>
328              
329             =head1 COPYRIGHT AND LICENSE
330              
331             This software is Copyright (c) 2011 - 2015 by Dave Rolsky.
332              
333             This is free software, licensed under:
334              
335             The Artistic License 2.0 (GPL Compatible)
336              
337             =cut