Renamed Positional to Ordered and added tests for Ordered.
[dbsrgits/DBIx-Class-Tree.git] / lib / DBIx / Class / Tree / AdjacencyList / Ordered.pm
CommitLineData
57f04bd5 1# vim: ts=8:sw=4:sts=4:et
c11aa533 2package DBIx::Class::Tree::AdjacencyList::Ordered;
57f04bd5 3use strict;
4use warnings;
5use base qw( DBIx::Class );
6use Carp qw( croak );
7
8__PACKAGE__->load_components(qw(
9 Tree::AdjacencyList
c11aa533 10 Ordered
57f04bd5 11));
12
13=head1 NAME
14
c11aa533 15DBIx::Class::Tree::AdjacencyList::Ordered - Glue DBIx::Class::Ordered and DBIx::Class::Tree::AdjacencyList together. (EXPERIMENTAL)
57f04bd5 16
17=head1 SYNOPSIS
18
19Create 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 28In your Schema or DB class add Tree::AdjacencyList::Ordered
57f04bd5 29to the top of the component list.
30
c11aa533 31 __PACKAGE__->load_components(qw( Tree::AdjacencyList::Ordered ... ));
57f04bd5 32
33Specify 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
39This module provides a few extra methods beyond what
c11aa533 40L<DBIx::Class::Ordered> and L<DBIx::Class::Tree::AdjacencyList>
57f04bd5 41already 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 58This module provides methods for working with adjacency lists and ordered
59rows. All of the methods that L<DBIx::Class::Ordered> and
60L<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
68Works the same as AdjacencyList's parent_column() method, but it
69declares the children() has many relationship to be ordered by the
70position column.
71
72=cut
73
74sub 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 94This method overrides AdjacencyList's parent() method but
95modifies it so that the object is moved to the last position,
96then the parent is changed, and then it is moved to the last
c11aa533 97position of the new list, thus maintaining the intergrity of
98the ordered lists.
57f04bd5 99
100=cut
101
102sub 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
130This method works just like it does in the
131DBIx::Class::Tree::AdjacencyList module except it
132orders the children by there position.
133
57f04bd5 134=head2 append_child
135
136 $parent->append_child( $child );
137
138Sets the child to have the specified parent and moves the
139child to the last position.
140
141=cut
142
143sub append_child {
144 my( $self, $child ) = @_;
145 $child->parent( $self );
146}
147
148=head2 prepend_child
149
150 $parent->prepend_child( $child );
151
152Sets the child to have the specified parent and moves the
153child to the first position.
154
155=cut
156
157sub 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
167Attaches the object at the position just before the
168calling object's position.
169
170=cut
171
172sub 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
182Attaches the object at the position just after the
183calling object's position.
184
185=cut
186
187sub 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
195These methods are used internally. You should never have the
196need to use them.
197
c11aa533 198=head2 grouping_column
e338251b 199
c11aa533 200Postional's grouping_column method does not, and should not, be
e338251b 201defined when using this module. This method just throws out an
202error if you try to use it.
203
204=cut
205
c11aa533 206sub grouping_column {
207 croak('Use parent_column() instead of grouping_column()');
e338251b 208}
209
c11aa533 210=head2 _grouping_clause
57f04bd5 211
212This method is provided as an override of the method in
c11aa533 213L<DBIx::Class::Ordered>. This method is what provides the
214glue between AdjacencyList and Ordered.
57f04bd5 215
216=cut
217
c11aa533 218sub _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
2261;
227__END__
228
229=head1 AUTHOR
230
231Aran Clary Deltac <bluefeet@cpan.org>
232
233=head1 LICENSE
234
235You may distribute this code under the same terms as Perl itself.
236