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