1DBIx::Class::Ordered(3)User Contributed Perl DocumentatioDnBIx::Class::Ordered(3)
2
3
4
6 DBIx::Class::Ordered - Modify the position of objects in an ordered
7 list.
8
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
85 This module provides a simple interface for modifying the ordered
86 position of DBIx::Class objects.
87
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
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
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
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
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.38.0 2023-07-20 DBIx::Class::Ordered(3)