Commit | Line | Data |
57f04bd5 |
1 | # vim: ts=8:sw=4:sts=4:et |
c11aa533 |
2 | package DBIx::Class::Tree::AdjacencyList::Ordered; |
57f04bd5 |
3 | use strict; |
4 | use warnings; |
5 | use base qw( DBIx::Class ); |
6 | use Carp qw( croak ); |
7 | |
8 | __PACKAGE__->load_components(qw( |
9 | Tree::AdjacencyList |
c11aa533 |
10 | Ordered |
57f04bd5 |
11 | )); |
12 | |
13 | =head1 NAME |
14 | |
c11aa533 |
15 | DBIx::Class::Tree::AdjacencyList::Ordered - Glue DBIx::Class::Ordered and DBIx::Class::Tree::AdjacencyList together. (EXPERIMENTAL) |
57f04bd5 |
16 | |
17 | =head1 SYNOPSIS |
18 | |
19 | Create a table for your tree data. |
20 | |
c11aa533 |
21 | CREATE TABLE items ( |
22 | item_id INTEGER PRIMARY KEY AUTOINCREMENT, |
82958127 |
23 | parent_id INTEGER NOT NULL DEFAULT 0, |
57f04bd5 |
24 | position INTEGER NOT NULL, |
25 | name TEXT NOT NULL |
26 | ); |
27 | |
c11aa533 |
28 | In your Schema or DB class add Tree::AdjacencyList::Ordered |
57f04bd5 |
29 | to the top of the component list. |
30 | |
c11aa533 |
31 | __PACKAGE__->load_components(qw( Tree::AdjacencyList::Ordered ... )); |
57f04bd5 |
32 | |
33 | Specify the column that contains the parent ID and position of each row. |
34 | |
35 | package My::Employee; |
82958127 |
36 | __PACKAGE__->position_column('position'); |
c11aa533 |
37 | __PACKAGE__->parent_column('parent_id'); |
57f04bd5 |
38 | |
39 | This module provides a few extra methods beyond what |
c11aa533 |
40 | L<DBIx::Class::Ordered> and L<DBIx::Class::Tree::AdjacencyList> |
57f04bd5 |
41 | already provide. |
42 | |
c11aa533 |
43 | my $parent = $item->parent(); |
44 | $item->parent( $parent_obj ); |
45 | $item->parent( $parent_id ); |
57f04bd5 |
46 | |
c11aa533 |
47 | my $children_rs = $item->children(); |
48 | my @children = $item->children(); |
57f04bd5 |
49 | |
50 | $parent->append_child( $child ); |
51 | $parent->prepend_child( $child ); |
52 | |
53 | $this->attach_before( $that ); |
54 | $this->attach_after( $that ); |
55 | |
56 | =head1 DESCRIPTION |
57 | |
c11aa533 |
58 | This module provides methods for working with adjacency lists and ordered |
59 | rows. All of the methods that L<DBIx::Class::Ordered> and |
60 | L<DBIx::Class::Tree::AdjacencyList> provide are available with this module. |
57f04bd5 |
61 | |
62 | =head1 METHODS |
63 | |
82958127 |
64 | =head2 parent_column |
65 | |
66 | __PACKAGE__->parent_column('parent_id'); |
67 | |
68 | Works the same as AdjacencyList's parent_column() method, but it |
69 | declares the children() has many relationship to be ordered by the |
70 | position column. |
71 | |
72 | =cut |
73 | |
74 | sub parent_column { |
75 | my $class = shift; |
76 | if (@_) { |
77 | my $parent_col = shift; |
78 | my $primary_col = ($class->primary_columns())[0]; |
c11aa533 |
79 | my $position_col = $class->position_column() || croak('You must call position_column() before calling parent_column()'); |
82958127 |
80 | $class->belongs_to( '_parent' => $class => { "foreign.$primary_col" => "self.$parent_col" } ); |
c11aa533 |
81 | $class->has_many( 'children' => $class => { "foreign.$parent_col" => "self.$primary_col" }, { order_by=>$position_col } ); |
82958127 |
82 | $class->_parent_column( $parent_col ); |
83 | return 1; |
84 | } |
85 | return $class->_parent_column(); |
86 | } |
87 | |
57f04bd5 |
88 | =head2 parent |
89 | |
c11aa533 |
90 | my $parent = $item->parent(); |
91 | $item->parent( $parent_obj ); |
92 | $item->parent( $parent_id ); |
57f04bd5 |
93 | |
82958127 |
94 | This method overrides AdjacencyList's parent() method but |
95 | modifies it so that the object is moved to the last position, |
96 | then the parent is changed, and then it is moved to the last |
c11aa533 |
97 | position of the new list, thus maintaining the intergrity of |
98 | the ordered lists. |
57f04bd5 |
99 | |
100 | =cut |
101 | |
102 | sub parent { |
82958127 |
103 | my $self = shift; |
104 | if (@_) { |
105 | my $new_parent = shift; |
106 | my $parent_col = $self->_parent_column(); |
57f04bd5 |
107 | if (ref($new_parent)) { |
82958127 |
108 | $new_parent = $new_parent->id() || croak('Parent object does not have an ID');; |
57f04bd5 |
109 | } |
82958127 |
110 | return 0 if ($new_parent == ($self->get_column($parent_col)||0)); |
111 | $self->move_last; |
112 | $self->set_column( $parent_col => $new_parent ); |
57f04bd5 |
113 | $self->set_column( |
e338251b |
114 | $self->position_column() => |
115 | $self->result_source->resultset->search( |
c11aa533 |
116 | {$self->_grouping_clause()} |
e338251b |
117 | )->count() + 1 |
57f04bd5 |
118 | ); |
119 | $self->update(); |
120 | return 1; |
121 | } |
82958127 |
122 | return $self->_parent(); |
57f04bd5 |
123 | } |
124 | |
125 | =head2 children |
126 | |
c11aa533 |
127 | my $children_rs = $item->children(); |
128 | my @children = $item->children(); |
57f04bd5 |
129 | |
130 | This method works just like it does in the |
131 | DBIx::Class::Tree::AdjacencyList module except it |
132 | orders the children by there position. |
133 | |
57f04bd5 |
134 | =head2 append_child |
135 | |
136 | $parent->append_child( $child ); |
137 | |
138 | Sets the child to have the specified parent and moves the |
139 | child to the last position. |
140 | |
141 | =cut |
142 | |
143 | sub append_child { |
144 | my( $self, $child ) = @_; |
145 | $child->parent( $self ); |
146 | } |
147 | |
148 | =head2 prepend_child |
149 | |
150 | $parent->prepend_child( $child ); |
151 | |
152 | Sets the child to have the specified parent and moves the |
153 | child to the first position. |
154 | |
155 | =cut |
156 | |
157 | sub prepend_child { |
158 | my( $self, $child ) = @_; |
159 | $child->parent( $self ); |
160 | $child->move_first(); |
161 | } |
162 | |
163 | =head2 attach_before |
164 | |
165 | $this->attach_before( $that ); |
166 | |
167 | Attaches the object at the position just before the |
168 | calling object's position. |
169 | |
170 | =cut |
171 | |
172 | sub attach_before { |
173 | my( $self, $sibling ) = @_; |
174 | $sibling->parent( $self->parent() ); |
175 | $sibling->move_to( $self->get_column($self->position_column()) ); |
176 | } |
177 | |
178 | =head2 attach_after |
179 | |
180 | $this->attach_after( $that ); |
181 | |
182 | Attaches the object at the position just after the |
183 | calling object's position. |
184 | |
185 | =cut |
186 | |
187 | sub attach_after { |
188 | my( $self, $sibling ) = @_; |
189 | $sibling->parent( $self->parent() ); |
190 | $sibling->move_to( $self->get_column($self->position_column()) + 1 ); |
191 | } |
192 | |
193 | =head1 PRIVATE METHODS |
194 | |
195 | These methods are used internally. You should never have the |
196 | need to use them. |
197 | |
c11aa533 |
198 | =head2 grouping_column |
e338251b |
199 | |
c11aa533 |
200 | Postional's grouping_column method does not, and should not, be |
e338251b |
201 | defined when using this module. This method just throws out an |
202 | error if you try to use it. |
203 | |
204 | =cut |
205 | |
c11aa533 |
206 | sub grouping_column { |
207 | croak('Use parent_column() instead of grouping_column()'); |
e338251b |
208 | } |
209 | |
c11aa533 |
210 | =head2 _grouping_clause |
57f04bd5 |
211 | |
212 | This method is provided as an override of the method in |
c11aa533 |
213 | L<DBIx::Class::Ordered>. This method is what provides the |
214 | glue between AdjacencyList and Ordered. |
57f04bd5 |
215 | |
216 | =cut |
217 | |
c11aa533 |
218 | sub _grouping_clause { |
57f04bd5 |
219 | my( $self ) = @_; |
c11aa533 |
220 | my $col = $self->_parent_column(); |
57f04bd5 |
221 | return ( |
c11aa533 |
222 | $col => $self->get_column( $col ) |
57f04bd5 |
223 | ); |
224 | } |
225 | |
226 | 1; |
227 | __END__ |
228 | |
229 | =head1 AUTHOR |
230 | |
231 | Aran Clary Deltac <bluefeet@cpan.org> |
232 | |
233 | =head1 LICENSE |
234 | |
235 | You may distribute this code under the same terms as Perl itself. |
236 | |