File Coverage

blib/lib/Interchange6/Schema/Result/Navigation.pm
Criterion Covered Total %
statement 36 45 82.2
branch 8 12 66.6
condition 3 3 100.0
subroutine 9 12 83.3
pod 5 5 100.0
total 61 77 81.8


line stmt bran cond sub pod time code
1 2     2   1467 use utf8;
  2         14  
  2         76  
2              
3             package Interchange6::Schema::Result::Navigation;
4              
5             =head1 NAME
6              
7             Interchange6::Schema::Result::Navigation
8              
9             =cut
10              
11 2     2   106 use base 'Interchange6::Schema::Base::Attribute';
  2         4  
  2         307  
12              
13 2         21 use Interchange6::Schema::Candy -components =>
14 2     2   15 [qw(Tree::AdjacencyList InflateColumn::DateTime TimeStamp)];
  2         5  
15              
16 2     2   6963 use Encode;
  2         6  
  2         206  
17 2     2   15 use Try::Tiny;
  2         6  
  2         2269  
18              
19             =head1 DESCRIPTION
20              
21             Navigation is where all navigation, category and static page details are stored. In addition
22             information such as page title can be linked to these records as attributes.
23              
24             =over 4
25              
26             =item B<Attribute>
27              
28             Common attribute names for a Navigation records include these examples.
29              
30             meta_title
31             meta_description
32             meta_keywords
33             head_js
34             head_css
35              
36             =back
37              
38             =cut
39              
40             =head1 SYNOPSIS
41              
42             NOTE: with items such as head_css which may contain more than one record you must set the priority of the record.
43             This ensures each record has a unique value and also allows for proper ordering.
44              
45             $nav->add_attribute({name => 'head_css', priority => '1'}, '/css/main.css');
46             $nav->add_attribute({name => 'head_css', priority => '2'}, '/css/fancymenu.css');
47              
48             =head1 ACCESSORS
49              
50             =head2 navigation_id
51              
52             Primary key.
53              
54             =cut
55              
56             primary_column navigation_id => {
57             data_type => "integer",
58             is_auto_increment => 1,
59             sequence => "navigation_navigation_id_seq",
60             };
61              
62             =head2 uri
63              
64             URI.
65              
66             Unique constraint. Is nullable.
67              
68             See L</generate_uri> method for details of how L</uri> can be created
69             automatically based on the value of L</name>.
70              
71             =cut
72              
73             unique_column uri => {
74             data_type => "varchar",
75             size => 255,
76             is_nullable => 1,
77             };
78              
79             =head2 type
80              
81             Type, e.g.: nav, category.
82              
83             =cut
84              
85             column type =>
86             { data_type => "varchar", default_value => "", size => 32 };
87              
88             =head2 scope
89              
90             Scope, e.g.: menu-main, top-login.
91              
92             =cut
93              
94             column scope =>
95             { data_type => "varchar", default_value => "", size => 32 };
96              
97             =head2 name
98              
99             Name, e.g.: Hand Tools, Fly Fishing Rods.
100              
101             Defaults to empty string.
102              
103             =cut
104              
105             column name => {
106             data_type => "varchar",
107             default_value => "",
108             size => 255
109             };
110              
111             =head2 description
112              
113             Description, e.g.: All of our hand tools, Our collection of top fly fishing rods.
114              
115             Defaults to empty string.
116              
117             =cut
118              
119             column description => {
120             data_type => "varchar",
121             default_value => "",
122             size => 1024
123             };
124              
125             =head2 alias
126              
127             FK on L<Interchange6::Schema::Result::Navigation/navigation_id>.
128              
129             Can be used for things such as menus in different languages which link back
130             to the primary navigation menu.
131              
132             Is nullable.
133              
134             =cut
135              
136             column alias =>
137             { data_type => "integer", is_nullable => 1 };
138              
139             =head2 parent_id
140              
141             Used by L<DBIx::Class::Tree::AdjacencyList> to setup parent/child relationships.
142              
143             Is nullable.
144              
145             =cut
146              
147             column parent_id => { data_type => "integer", is_nullable => 1 };
148              
149             =head2 priority
150              
151             Signed integer priority. We normally order descending.
152              
153             Defaults to 0.
154              
155             =cut
156              
157             column priority =>
158             { data_type => "integer", default_value => 0 };
159              
160             =head2 product_count
161              
162             Can be used to cache product counts.
163              
164             Default to 0.
165              
166             =cut
167              
168             column product_count =>
169             { data_type => "integer", default_value => 0 };
170              
171             =head2 created
172              
173             Date and time when this record was created returned as L<DateTime> object.
174             Value is auto-set on insert.
175              
176             =cut
177              
178             column created =>
179             { data_type => "datetime", set_on_create => 1 };
180              
181             =head2 last_modified
182              
183             Date and time when this record was last modified returned as L<DateTime> object.
184             Value is auto-set on insert and update.
185              
186             =cut
187              
188             column last_modified => {
189             data_type => "datetime",
190             set_on_create => 1,
191             set_on_update => 1,
192             };
193              
194             =head2 active
195              
196             Whether navigation is active and therefore should be displayed.
197              
198             Boolean defaults to true (1).
199              
200             =cut
201              
202             column active =>
203             { data_type => "boolean", default_value => 1 };
204              
205             =head1 METHODS
206              
207             Attribute methods are provided by the L<Interchange6::Schema::Base::Attribute> class.
208              
209             =head2 new
210              
211             Override inherited method to call L</generate_uri> method in case L</name>
212             has been supplied as an argument but L</uri> has not.
213              
214             B<NOTE:> is uri is supplied and is undefined then L</generate_uri> is not
215             called.
216              
217             =cut
218              
219             sub new {
220 141     141 1 411356 my ( $class, $attrs ) = @_;
221              
222 141         658 my $new = $class->next::method($attrs);
223 141 100 100     234012 if ( defined $attrs->{name} && ! exists $attrs->{uri} ) {
224 11         47 $new->generate_uri;
225             }
226 140         2589 return $new;
227             }
228              
229             =head2 active_child_count
230              
231             See L<Interchange6::Schema::ResultSet::Navigation/with_active_child_count>
232             for a resultset method which will prefill this data.
233              
234             =cut
235              
236             sub active_child_count {
237 0     0 1 0 my $self = shift;
238 0 0       0 if ( $self->has_column_loaded('active_child_count') ) {
239 0         0 return $self->get_column('active_child_count');
240             }
241 0         0 return $self->active_children->count;
242             }
243              
244             =head2 active_product_count
245              
246             See L<Interchange6::Schema::ResultSet::Navigation/with_active_product_count>
247             for a resultset method which will prefill this data.
248              
249             =cut
250              
251             sub active_product_count {
252 0     0 1 0 my $self = shift;
253 0 0       0 if ( $self->has_column_loaded('active_product_count') ) {
254 0         0 return $self->get_column('active_product_count');
255             }
256 0         0 return $self->products->active->count;
257             }
258              
259             =head2 generate_uri($attrs)
260              
261             Called by L</new> if no uri is given as an argument.
262              
263             The following steps are taken:
264              
265             =over
266              
267             1. Stash C<< $self->name >> in C<$uri> to allow manipulation via filters
268              
269             2. Remove leading and trailing spaces and replace remaining spaces and
270             C</> with C<->
271              
272             3. Search for all rows in L<Interchange6::Schema::Result::Setting> where
273             C<scope> is C<Navigation> and C<name> is <generate_uri_filter>
274              
275             4. For each row found eval C<< $row->value >>
276              
277             5. Finally set the value of column L</uri> to C<$uri>
278              
279             =back
280              
281             Filters stored in L<Interchange6::Schema::Result::Setting> are executed via
282             eval and have access to C<$uri> and also the navigation result held in
283             C<$self>
284              
285             Examples of filters stored in Setting might be:
286              
287             {
288             scope => 'Navigation',
289             name => 'generate_uri_filter',
290             value => '$uri =~ s/badstuff/goodstuff/gi',
291             },
292             {
293             scope => 'Navigation',
294             name => 'generate_uri_filter',
295             value => '$uri = lc($uri)',
296             },
297              
298             =cut
299              
300             sub generate_uri {
301 11     11 1 22 my $self = shift;
302              
303 11         303 my $uri = $self->name;
304              
305             # make sure we have clean utf8
306             try {
307 11 100   11   579 $uri = Encode::decode( 'UTF-8', $uri, Encode::FB_CROAK )
308             unless utf8::is_utf8($uri);
309             }
310             catch {
311             # I don't know of a way to reach this catch so for now mark uncoverable
312             # uncoverable subroutine
313             # uncoverable statement
314 0     0   0 $self->throw_exception(
315             "Navigation->generate_uri failed to decode UTF-8 text: $_" );
316 11         241 };
317              
318 11         567 $uri =~ s/^\s+//; # remove leading space
319 11         70 $uri =~ s/\s+$//; # remove trailing space
320 11         115 $uri =~ s{[\s/]+}{-}g; # change space and / to -
321              
322 11         56 my $filters = $self->result_source->schema->resultset('Setting')->search(
323             {
324             scope => 'Navigation',
325             name => 'generate_uri_filter',
326             },
327             );
328              
329 11         6604 while ( my $filter = $filters->next ) {
330 3         7916 eval $filter->value;
331 3 100       130 $self->throw_exception("Navigation->generate_uri filter croaked: $@")
332             if $@;
333             }
334              
335 10         20502 $self->uri($uri);
336             }
337              
338              
339             =head2 siblings_with_self
340              
341             Similar to the inherited L<siblings|DBIx::Class::Tree::AdjacencyList/siblings> method but also returns the object itself in the result set/list.
342              
343             =cut
344              
345             sub siblings_with_self {
346 2     2 1 21509 my $self = shift;
347 2         11 my $rs = $self->result_source->resultset->search(
348             {
349             parent_id => $self->parent_id,
350             }
351             );
352 2 100       1171 return $rs->all() if (wantarray());
353 1         25 return $rs;
354             }
355              
356             =head1 INHERITED METHODS
357              
358             =head2 DBIx::Class::Tree::AdjacencyList
359              
360             =over 4
361              
362             =item *
363              
364             L<parent|DBIx::Class::Tree::AdjacencyList/parent>
365              
366             =item *
367              
368             L<ancestors|DBIx::Class::Tree::AdjacencyList/ancestors>
369              
370             =item *
371              
372             L<has_descendant|DBIx::Class::Tree::AdjacencyList/has_descendant>
373              
374             =item *
375              
376             L<parents|DBIx::Class::Tree::AdjacencyList/parents>
377              
378             =item *
379              
380             L<children|DBIx::Class::Tree::AdjacencyList/children>
381              
382             =item *
383              
384             L<attach_child|DBIx::Class::Tree::AdjacencyList/attach_child>
385              
386             =item *
387              
388             L<siblings|DBIx::Class::Tree::AdjacencyList/siblings>
389              
390             =item *
391              
392             L<attach_sibling|DBIx::Class::Tree::AdjacencyList/attach_sibling>
393              
394             =item *
395              
396             L<is_leaf|DBIx::Class::Tree::AdjacencyList/is_leaf>
397              
398             =item *
399              
400             L<is_root|DBIx::Class::Tree::AdjacencyList/is_root>
401              
402             =item *
403              
404             L<is_branch|DBIx::Class::Tree::AdjacencyList/is_branch>
405              
406             =back
407              
408             =cut
409              
410             # define parent column
411              
412             __PACKAGE__->parent_column('parent_id');
413              
414             =head1 RELATIONS
415              
416             =head2 active_children
417              
418             Related object: L<Interchange6::Schema::Result::Navigation>
419              
420             Conditions: self.parent_id = foreign.navigation_id && foreign.active = 1
421              
422             =cut
423              
424             has_many
425             active_children => "Interchange6::Schema::Result::Navigation",
426             sub {
427             my $args = shift;
428              
429             return {
430             "$args->{foreign_alias}.parent_id" =>
431             { -ident => "$args->{self_alias}.navigation_id" },
432             "$args->{foreign_alias}.active" => 1,
433             };
434             };
435              
436             =head2 media_navigations
437              
438             Type: has_many
439              
440             Related object: L<Interchange6::Schema::Result::MediaNavigation>
441              
442             =cut
443              
444             has_many
445             media_navigations => "Interchange6::Schema::Result::MediaNavigation",
446             "navigation_id",
447             { cascade_copy => 0, cascade_delete => 0 };
448              
449              
450             =head2 navigation_products
451              
452             Type: has_many
453              
454             Related object: L<Interchange6::Schema::Result::NavigationProduct>
455              
456             =cut
457              
458             has_many
459             navigation_products => "Interchange6::Schema::Result::NavigationProduct",
460             "navigation_id",
461             { cascade_copy => 0, cascade_delete => 0 };
462              
463             =head2 products
464              
465             Type: many_to_many
466              
467             Accessor to related product results ordered by priority and name.
468              
469             =cut
470              
471             many_to_many
472             products => "navigation_products",
473             "product", { order_by => [ 'product.priority', 'product.name' ] };
474              
475             =head2 navigation_attributes
476              
477             Type: has_many
478              
479             Related object: L<Interchange6::Schema::Result::NavigationAttribute>
480              
481             =cut
482              
483             has_many
484             navigation_attributes => "Interchange6::Schema::Result::NavigationAttribute",
485             "navigation_id",
486             { cascade_copy => 0, cascade_delete => 0 };
487              
488             =head2 attributes
489              
490             Type: many_to_many
491              
492             Accessor to related attribute results.
493              
494             =cut
495              
496             many_to_many
497             attributes => "navigation_attributes", "attribute";
498              
499             =head2 navigation_messages
500              
501             Type: has_many
502              
503             Related object: L<Interchange6::Schema::Result::NavigationMessage>
504              
505             =cut
506              
507             has_many
508             navigation_messages => "Interchange6::Schema::Result::NavigationMessage",
509             "navigation_id", { cascade_copy => 0 };
510              
511             =head2 messages
512              
513             Type: many_to_many
514              
515             Accessor to related Message results.
516              
517             =cut
518              
519             many_to_many messages => "navigation_messages", "message";
520              
521             1;