6da6b95498815159adbefaae0b3e84af66f6b795
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / View.pm
1 package SQL::Translator::Schema::View;
2
3 =pod
4
5 =head1 NAME
6
7 SQL::Translator::Schema::View - SQL::Translator view object
8
9 =head1 SYNOPSIS
10
11   use SQL::Translator::Schema::View;
12   my $view   = SQL::Translator::Schema::View->new(
13       name   => 'foo',                      # name, required
14       sql    => 'select id, name from foo', # SQL for view
15       fields => 'id, name',                 # field names in view
16   );
17
18 =head1 DESCRIPTION
19
20 C<SQL::Translator::Schema::View> is the view object.
21
22 =head1 METHODS
23
24 =cut
25
26 use Moo;
27 use SQL::Translator::Utils qw(parse_list_arg ex2err);
28 use SQL::Translator::Types qw(schema_obj);
29 use List::MoreUtils qw(uniq);
30
31 with qw(
32   SQL::Translator::Schema::Role::BuildArgs
33   SQL::Translator::Schema::Role::Extra
34   SQL::Translator::Schema::Role::Error
35   SQL::Translator::Schema::Role::Compare
36 );
37
38 our ( $TABLE_COUNT, $VIEW_COUNT );
39
40 our $VERSION = '1.59';
41
42 =head2 new
43
44 Object constructor.
45
46   my $view = SQL::Translator::Schema::View->new;
47
48 =head2 fields
49
50 Gets and set the fields the constraint is on.  Accepts a string, list or
51 arrayref; returns an array or array reference.  Will unique the field
52 names and keep them in order by the first occurrence of a field name.
53
54   $view->fields('id');
55   $view->fields('id', 'name');
56   $view->fields( 'id, name' );
57   $view->fields( [ 'id', 'name' ] );
58   $view->fields( qw[ id name ] );
59
60   my @fields = $view->fields;
61
62 =cut
63
64 has fields => (
65     is => 'rw',
66     default => sub { [] },
67     coerce => sub { [uniq @{parse_list_arg($_[0])}] },
68 );
69
70 around fields => sub {
71     my $orig   = shift;
72     my $self   = shift;
73     my $fields = parse_list_arg( @_ );
74     $self->$orig($fields) if @$fields;
75
76     return wantarray ? @{ $self->$orig } : $self->$orig;
77 };
78
79 =head2 tables
80
81 Gets and set the tables the SELECT mentions.  Accepts a string, list or
82 arrayref; returns an array or array reference.  Will unique the table
83 names and keep them in order by the first occurrence of a field name.
84
85   $view->tables('foo');
86   $view->tables('foo', 'bar');
87   $view->tables( 'foo, bar' );
88   $view->tables( [ 'foo', 'bar' ] );
89   $view->tables( qw[ foo bar ] );
90
91   my @tables = $view->tables;
92
93 =cut
94
95 has tables => (
96     is => 'rw',
97     default => sub { [] },
98     coerce => sub { [uniq @{parse_list_arg($_[0])}] },
99 );
100
101 around tables => sub {
102     my $orig   = shift;
103     my $self   = shift;
104     my $fields = parse_list_arg( @_ );
105     $self->$orig($fields) if @$fields;
106
107     return wantarray ? @{ $self->$orig } : $self->$orig;
108 };
109
110 =head2 options
111
112 Gets and sets a list of options on the view.
113
114   $view->options('ALGORITHM=UNDEFINED');
115
116   my @options = $view->options;
117
118 =cut
119
120 has options => (
121     is => 'rw',
122     default => sub { [] },
123     coerce => sub { [uniq @{parse_list_arg($_[0])}] },
124 );
125
126 around options => sub {
127     my $orig    = shift;
128     my $self    = shift;
129     my $options = parse_list_arg( @_ );
130
131     if ( @$options ) {
132         $self->$orig([ @{$self->$orig}, @$options ])
133     }
134
135     return wantarray ? @{ $self->$orig } : $self->$orig;
136 };
137
138 sub is_valid {
139
140 =pod
141
142 =head2 is_valid
143
144 Determine whether the view is valid or not.
145
146   my $ok = $view->is_valid;
147
148 =cut
149
150     my $self = shift;
151
152     return $self->error('No name') unless $self->name;
153     return $self->error('No sql')  unless $self->sql;
154
155     return 1;
156 }
157
158 =head2 name
159
160 Get or set the view's name.
161
162   my $name = $view->name('foo');
163
164 =cut
165
166 has name => ( is => 'rw', default => sub { '' } );
167
168 =head2 order
169
170 Get or set the view's order.
171
172   my $order = $view->order(3);
173
174 =cut
175
176 has order => ( is => 'rw', default => sub { 0 } );
177
178 around order => sub {
179     my ( $orig, $self, $arg ) = @_;
180
181     if ( defined $arg && $arg =~ /^\d+$/ ) {
182         return $self->$orig($arg);
183     }
184
185     return $self->$orig;
186 };
187
188 =head2 sql
189
190 Get or set the view's SQL.
191
192   my $sql = $view->sql('select * from foo');
193
194 =cut
195
196 has sql => ( is => 'rw', default => sub { '' } );
197
198 =head2 schema
199
200 Get or set the view's schema object.
201
202   $view->schema( $schema );
203   my $schema = $view->schema;
204
205 =cut
206
207 has schema => ( is => 'rw', isa => schema_obj('Schema') );
208
209 around schema => \&ex2err;
210
211 =head2 equals
212
213 Determines if this view is the same as another
214
215   my $isIdentical = $view1->equals( $view2 );
216
217 =cut
218
219 around equals => sub {
220     my $orig = shift;
221     my $self = shift;
222     my $other = shift;
223     my $case_insensitive = shift;
224     my $ignore_sql = shift;
225
226     return 0 unless $self->$orig($other);
227     return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
228     #return 0 unless $self->is_valid eq $other->is_valid;
229
230     unless ($ignore_sql) {
231         my $selfSql = $self->sql;
232         my $otherSql = $other->sql;
233         # Remove comments
234         $selfSql =~ s/--.*$//mg;
235         $otherSql =~ s/--.*$//mg;
236         # Collapse whitespace to space to avoid whitespace comparison issues
237         $selfSql =~ s/\s+/ /sg;
238         $otherSql =~ s/\s+/ /sg;
239         return 0 unless $selfSql eq $otherSql;
240     }
241
242     my $selfFields = join(":", $self->fields);
243     my $otherFields = join(":", $other->fields);
244     return 0 unless $case_insensitive ? uc($selfFields) eq uc($otherFields) : $selfFields eq $otherFields;
245     return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
246     return 1;
247 };
248
249 sub DESTROY {
250     my $self = shift;
251     undef $self->{'schema'}; # destroy cyclical reference
252 }
253
254 # Must come after all 'has' declarations
255 around new => \&ex2err;
256
257 1;
258
259 =pod
260
261 =head1 AUTHOR
262
263 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
264
265 =cut