1DBIx::Class::Ordered(3)User Contributed Perl DocumentatioDnBIx::Class::Ordered(3)
2
3
4

NAME

6       DBIx::Class::Ordered - Modify the position of objects in an ordered
7       list.
8

SYNOPSIS

10       Create a table for your ordered data.
11
12         CREATE TABLE items (
13           item_id INTEGER PRIMARY KEY AUTOINCREMENT,
14           name TEXT NOT NULL,
15           position INTEGER NOT NULL
16         );
17
18       Optionally, add one or more columns to specify groupings, allowing you
19       to maintain independent ordered lists within one table:
20
21         CREATE TABLE items (
22           item_id INTEGER PRIMARY KEY AUTOINCREMENT,
23           name TEXT NOT NULL,
24           position INTEGER NOT NULL,
25           group_id INTEGER NOT NULL
26         );
27
28       Or even
29
30         CREATE TABLE items (
31           item_id INTEGER PRIMARY KEY AUTOINCREMENT,
32           name TEXT NOT NULL,
33           position INTEGER NOT NULL,
34           group_id INTEGER NOT NULL,
35           other_group_id INTEGER NOT NULL
36         );
37
38       In your Schema or DB class add "Ordered" to the top of the component
39       list.
40
41         __PACKAGE__->load_components(qw( Ordered ... ));
42
43       Specify the column that stores the position number for each row.
44
45         package My::Item;
46         __PACKAGE__->position_column('position');
47
48       If you are using one grouping column, specify it as follows:
49
50         __PACKAGE__->grouping_column('group_id');
51
52       Or if you have multiple grouping columns:
53
54         __PACKAGE__->grouping_column(['group_id', 'other_group_id']);
55
56       That's it, now you can change the position of your objects.
57
58         #!/use/bin/perl
59         use My::Item;
60
61         my $item = My::Item->create({ name=>'Matt S. Trout' });
62         # If using grouping_column:
63         my $item = My::Item->create({ name=>'Matt S. Trout', group_id=>1 });
64
65         my $rs = $item->siblings();
66         my @siblings = $item->siblings();
67
68         my $sibling;
69         $sibling = $item->first_sibling();
70         $sibling = $item->last_sibling();
71         $sibling = $item->previous_sibling();
72         $sibling = $item->next_sibling();
73
74         $item->move_previous();
75         $item->move_next();
76         $item->move_first();
77         $item->move_last();
78         $item->move_to( $position );
79         $item->move_to_group( 'groupname' );
80         $item->move_to_group( 'groupname', $position );
81         $item->move_to_group( {group_id=>'groupname', 'other_group_id=>'othergroupname'} );
82         $item->move_to_group( {group_id=>'groupname', 'other_group_id=>'othergroupname'}, $position );
83

DESCRIPTION

85       This module provides a simple interface for modifying the ordered
86       position of DBIx::Class objects.
87

AUTO UPDATE

89       All of the move_* methods automatically update the rows involved in the
90       query.  This is not configurable and is due to the fact that if you
91       move a record it always causes other records in the list to be updated.
92

METHODS

94   position_column
95         __PACKAGE__->position_column('position');
96
97       Sets and retrieves the name of the column that stores the positional
98       value of each record.  Defaults to "position".
99
100   grouping_column
101         __PACKAGE__->grouping_column('group_id');
102
103       This method specifies a column to limit all queries in this module by.
104       This effectively allows you to have multiple ordered lists within the
105       same table.
106
107   null_position_value
108         __PACKAGE__->null_position_value(undef);
109
110       This method specifies a value of "position_column" which would never be
111       assigned to a row during normal operation. When a row is moved, its
112       position is set to this value temporarily, so that any unique
113       constraints can not be violated. This value defaults to 0, which should
114       work for all cases except when your positions do indeed start from 0.
115
116   siblings
117         my $rs = $item->siblings();
118         my @siblings = $item->siblings();
119
120       Returns an ordered resultset of all other objects in the same group
121       excluding the one you called it on.
122
123       The ordering is a backwards-compatibility artifact - if you need a
124       resultset with no ordering applied use "_siblings"
125
126   previous_siblings
127         my $prev_rs = $item->previous_siblings();
128         my @prev_siblings = $item->previous_siblings();
129
130       Returns a resultset of all objects in the same group positioned before
131       the object on which this method was called.
132
133   next_siblings
134         my $next_rs = $item->next_siblings();
135         my @next_siblings = $item->next_siblings();
136
137       Returns a resultset of all objects in the same group positioned after
138       the object on which this method was called.
139
140   previous_sibling
141         my $sibling = $item->previous_sibling();
142
143       Returns the sibling that resides one position back.  Returns 0 if the
144       current object is the first one.
145
146   first_sibling
147         my $sibling = $item->first_sibling();
148
149       Returns the first sibling object, or 0 if the first sibling is this
150       sibling.
151
152   next_sibling
153         my $sibling = $item->next_sibling();
154
155       Returns the sibling that resides one position forward. Returns 0 if the
156       current object is the last one.
157
158   last_sibling
159         my $sibling = $item->last_sibling();
160
161       Returns the last sibling, or 0 if the last sibling is this sibling.
162
163   move_previous
164         $item->move_previous();
165
166       Swaps position with the sibling in the position previous in the list.
167       Returns 1 on success, and 0 if the object is already the first one.
168
169   move_next
170         $item->move_next();
171
172       Swaps position with the sibling in the next position in the list.
173       Returns 1 on success, and 0 if the object is already the last in the
174       list.
175
176   move_first
177         $item->move_first();
178
179       Moves the object to the first position in the list.  Returns 1 on
180       success, and 0 if the object is already the first.
181
182   move_last
183         $item->move_last();
184
185       Moves the object to the last position in the list.  Returns 1 on
186       success, and 0 if the object is already the last one.
187
188   move_to
189         $item->move_to( $position );
190
191       Moves the object to the specified position.  Returns 1 on success, and
192       0 if the object is already at the specified position.
193
194   move_to_group
195         $item->move_to_group( $group, $position );
196
197       Moves the object to the specified position of the specified group, or
198       to the end of the group if $position is undef.  1 is returned on
199       success, and 0 is returned if the object is already at the specified
200       position of the specified group.
201
202       $group may be specified as a single scalar if only one grouping column
203       is in use, or as a hashref of column => value pairs if multiple
204       grouping columns are in use.
205
206   insert
207       Overrides the DBIC insert() method by providing a default position
208       number.  The default will be the number of rows in the table +1, thus
209       positioning the new record at the last position.
210
211   update
212       Overrides the DBIC update() method by checking for a change to the
213       position and/or group columns.  Movement within a group or to another
214       group is handled by repositioning the appropriate siblings.  Position
215       defaults to the end of a new group if it has been changed to undef.
216
217   delete
218       Overrides the DBIC delete() method by first moving the object to the
219       last position, then deleting it, thus ensuring the integrity of the
220       positions.
221

METHODS FOR EXTENDING ORDERED

223       You would want to override the methods below if you use sparse (non-
224       linear) or non-numeric position values. This can be useful if you are
225       working with preexisting non-normalised position data, or if you need
226       to work with materialized path columns.
227
228   _position_from_value
229         my $num_pos = $item->_position_from_value ( $pos_value )
230
231       Returns the absolute numeric position of an object with a position
232       value set to $pos_value. By default simply returns $pos_value.
233
234   _position_value
235         my $pos_value = $item->_position_value ( $pos )
236
237       Returns the value of "position_column" of the object at numeric
238       position $pos. By default simply returns $pos.
239
240   _initial_position_value
241         __PACKAGE__->_initial_position_value(0);
242
243       This method specifies a value of "position_column" which is assigned to
244       the first inserted element of a group, if no value was supplied at
245       insertion time. All subsequent values are derived from this one by
246       "_next_position_value" below. Defaults to 1.
247
248   _next_position_value
249         my $new_value = $item->_next_position_value ( $position_value )
250
251       Returns a position value that would be considered "next" with regards
252       to $position_value. Can be pretty much anything, given that
253       "$position_value < $new_value" where "<" is the SQL comparison operator
254       (usually works fine on strings). The default method expects
255       $position_value to be numeric, and returns "$position_value + 1"
256
257   _shift_siblings
258         $item->_shift_siblings ($direction, @between)
259
260       Shifts all siblings with positions values in the range @between
261       (inclusive) by one position as specified by $direction (left if < 0,
262        right if > 0). By default simply increments/decrements each
263       "position_column" value by 1, doing so in a way as to not violate any
264       existing constraints.
265
266       Note that if you override this method and have unique constraints
267       including the "position_column" the shift is not a trivial task.  Refer
268       to the implementation source of the default method for more
269       information.
270

CAVEATS

272   Resultset Methods
273       Note that all Insert/Create/Delete overrides are happening on
274       DBIx::Class::Row methods only. If you use the DBIx::Class::ResultSet
275       versions of update or delete, all logic present in this module will be
276       bypassed entirely (possibly resulting in a broken order-tree). Instead
277       always use the update_all and delete_all methods, which will invoke the
278       corresponding row method on every member of the given resultset.
279
280   Race Condition on Insert
281       If a position is not specified for an insert, a position will be chosen
282       based either on "_initial_position_value" or "_next_position_value",
283       depending if there are already some items in the current group. The
284       space of time between the necessary selects and insert introduces a
285       race condition.  Having unique constraints on your position/group
286       columns, and using transactions (see "txn_do" in DBIx::Class::Storage)
287       will prevent such race conditions going undetected.
288
289   Multiple Moves
290       If you have multiple same-group result objects already loaded from
291       storage, you need to be careful when executing "move_*" operations on
292       them: without a "position_column" reload the "_position_value" of the
293       "siblings" will be out of sync with the underlying storage.
294
295       Starting from version 0.082800 DBIC will implicitly perform such
296       reloads when the "move_*" happens as a part of a transaction (a good
297       example of such situation is "$ordered_resultset->delete_all").
298
299       If it is not possible for you to wrap the entire call-chain in a
300       transaction, you will need to call "discard_changes" in
301       DBIx::Class::Row to get an object up-to-date before proceeding,
302       otherwise undefined behavior will result.
303
304   Default Values
305       Using a database defined default_value on one of your group columns
306       could result in the position not being assigned correctly.
307

FURTHER QUESTIONS?

309       Check the list of additional DBIC resources.
310
312       This module is free software copyright by the DBIx::Class (DBIC)
313       authors. You can redistribute it and/or modify it under the same terms
314       as the DBIx::Class library.
315
316
317
318perl v5.30.1                      2020-01-29           DBIx::Class::Ordered(3)
Impressum