root/fCRUD.php

Revision 721, 12.2 kB (checked in by wbond, 10 months ago)

BackwardsCompatibilityBreak - Removed the $prefix parameter from the methods fSession::delete(), fSession::get() and fSession::set()

Fixed ticket #302 - added the method fSession::add()

Fixed ticket #303 - added a second parameter to fSession::setLength() and added fSession::enablePersistence()

LineHide Line Numbers
1 <?php
2 /**
3  * Provides miscellaneous functionality for [http://en.wikipedia.org/wiki/Create,_read,_update_and_delete CRUD-like] pages
4  *
5  * @copyright  Copyright (c) 2007-2009 Will Bond
6  * @author     Will Bond [wb] <will@flourishlib.com>
7  * @license    http://flourishlib.com/license
8  *
9  * @package    Flourish
10  * @link       http://flourishlib.com/fCRUD
11  *
12  * @version    1.0.0b5
13  * @changes    1.0.0b5  Updated class to use new fSession API [wb, 2009-10-23]
14  * @changes    1.0.0b4  Updated class to use new fSession API [wb, 2009-05-08]
15  * @changes    1.0.0b3  Backwards Compatiblity Break - moved ::printOption() to fHTML::printOption(), ::showChecked() to fHTML::showChecked(), ::removeListItems() and ::reorderListItems() to fException::splitMessage(), ::generateRequestToken() to fRequest::generateCSRFToken(), and ::validateRequestToken() to fRequest::validateCSRFToken() [wb, 2009-05-08]
16  * @changes    1.0.0b2  Fixed a bug preventing loaded search values from being included in redirects [wb, 2009-03-18]
17  * @changes    1.0.0b   The initial implementation [wb, 2007-06-14]
18  */
19 class fCRUD
20 {
21     // The following constants allow for nice looking callbacks to static methods
22     const getColumnClass           = 'fCRUD::getColumnClass';
23     const getRowClass              = 'fCRUD::getRowClass';
24     const getSearchValue           = 'fCRUD::getSearchValue';
25     const getSortColumn            = 'fCRUD::getSortColumn';
26     const getSortDirection         = 'fCRUD::getSortDirection';
27     const printSortableColumn      = 'fCRUD::printSortableColumn';
28     const redirectWithLoadedValues = 'fCRUD::redirectWithLoadedValues';
29     const reset                    = 'fCRUD::reset';
30    
31    
32     /**
33     * Any values that were loaded from the session, used for redirection
34     *
35     * @var array
36     */
37     static private $loaded_values = array();
38    
39     /**
40     * The current row number for alternating rows
41     *
42     * @var integer
43     */
44     static private $row_number = 1;
45    
46     /**
47     * The values for a search form
48     *
49     * @var array
50     */
51     static private $search_values = array();
52    
53     /**
54     * The column to sort by
55     *
56     * @var string
57     */
58     static private $sort_column = NULL;
59    
60     /**
61     * The direction to sort
62     *
63     * @var string
64     */
65     static private $sort_direction = NULL;
66    
67    
68     /**
69     * Return the string `'sorted'` if `$column` is the column that is currently being sorted by, otherwise returns `''`
70     *
71     * This method will only be useful if used with the other sort methods
72     * ::printSortableColumn(), ::getSortColumn() and ::getSortDirection().
73     *
74     * @param  string $column  The column to check
75     * @return string  The CSS class for the column, either `''` or `'sorted'`
76     */
77     static public function getColumnClass($column)
78     {
79         if (self::$sort_column == $column) {
80             return 'sorted';
81         }
82         return '';
83     }
84    
85    
86     /**
87     * Returns the previous values for the specified search field
88     *
89     * @param  string $column  The column to get the value for
90     * @return mixed  The previous value
91     */
92     static private function getPreviousSearchValue($column)
93     {
94         return fSession::get(__CLASS__ . '::' . fURL::get() . '::previous_search::' . $column, NULL);
95     }
96    
97    
98     /**
99     * Return the previous sort column, if one exists
100     *
101     * @return string  The previous sort column
102     */
103     static private function getPreviousSortColumn()
104     {
105         return fSession::get(__CLASS__ . '::' . fURL::get() . '::previous_sort_column', NULL);
106     }
107    
108    
109     /**
110     * Return the previous sort direction, if one exists
111     *
112     * @return string  The previous sort direction
113     */
114     static private function getPreviousSortDirection()
115     {
116         return fSession::get(__CLASS__ . '::' . fURL::get() . '::previous_sort_direction', NULL);
117     }
118    
119    
120     /**
121     * Returns a CSS class name for a row
122     *
123     * Will return `'even'`, `'odd'`, or `'highlighted'` if the two parameters
124     * are equal and not `NULL`. The first call to this method will return
125     * the appropriate class concatenated with `' first'`.
126     *
127     * @param  mixed $row_value       The value from the row
128     * @param  mixed $affected_value  The value that was just added or updated
129     * @return string  The css class
130     */
131     static public function getRowClass($row_value=NULL, $affected_value=NULL)
132     {
133         if ($row_value !== NULL && $row_value == $affected_value) {
134              self::$row_number++;
135              $class = 'highlighted';
136         } else {
137             $class = (self::$row_number++ % 2) ? 'odd' : 'even';
138         }
139        
140         $class .= (self::$row_number == 2) ? ' first' : '';
141         return $class;
142     }
143    
144    
145     /**
146     * Gets the current value of a search field
147     *
148     * If a value is an empty string and no cast to is specified, the value will
149     * become `NULL`.
150     *
151     * If a query string of `?reset` is passed, all previous search values will
152     * be erased.
153     *
154     * @param  string $column   The column that is being pulled back
155     * @param  string $cast_to  The data type to cast to
156     * @param  string $default  The default value
157     * @return mixed  The current value
158     */
159     static public function getSearchValue($column, $cast_to=NULL, $default=NULL)
160     {
161         // Reset values if requested
162         if (self::wasResetRequested()) {
163             self::setPreviousSearchValue($column, NULL);
164             return;
165         }
166        
167         if (self::getPreviousSearchValue($column) && !fRequest::check($column)) {
168             self::$search_values[$column] = self::getPreviousSearchValue($column);
169             self::$loaded_values[$column] = self::$search_values[$column];
170         } else {
171             self::$search_values[$column] = fRequest::get($column, $cast_to, $default);
172             self::setPreviousSearchValue($column, self::$search_values[$column]);
173         }
174         return self::$search_values[$column];
175     }
176    
177    
178     /**
179     * Gets the current column to sort by, defaults to first one specified
180     *
181     * @param  string $possible_column  The columns that can be sorted by, defaults to first
182     * @param  string ...
183     * @return string  The column to sort by
184     */
185     static public function getSortColumn($possible_column)
186     {
187         // Reset value if requested
188         if (self::wasResetRequested()) {
189             self::setPreviousSortColumn(NULL);
190             return;
191         }
192        
193         $possible_columns = func_get_args();
194        
195         if (sizeof($possible_columns) == 1 && is_array($possible_columns[0])) {
196             $possible_columns = $possible_columns[0];
197         }
198        
199         if (self::getPreviousSortColumn() && !fRequest::check('sort')) {
200             self::$sort_column = self::getPreviousSortColumn();
201             self::$loaded_values['sort'] = self::$sort_column;
202         } else {
203             self::$sort_column = fRequest::getValid('sort', $possible_columns);
204             self::setPreviousSortColumn(self::$sort_column);
205         }
206         return self::$sort_column;
207     }
208    
209    
210     /**
211     * Gets the current sort direction
212     *
213     * @param  string $default_direction  The default direction, `'asc'` or `'desc'`
214     * @return string  The direction, `'asc'` or `'desc'`
215     */
216     static public function getSortDirection($default_direction)
217     {
218         // Reset value if requested
219         if (self::wasResetRequested()) {
220             self::setPreviousSortDirection(NULL);
221             return;
222         }
223        
224         if (self::getPreviousSortDirection() && !fRequest::check('dir')) {
225             self::$sort_direction = self::getPreviousSortDirection();
226             self::$loaded_values['dir'] = self::$sort_direction;
227         } else {
228             self::$sort_direction = fRequest::getValid('dir', array($default_direction, ($default_direction == 'asc') ? 'desc' : 'asc'));
229             self::setPreviousSortDirection(self::$sort_direction);
230         }
231         return self::$sort_direction;
232     }
233    
234    
235     /**
236     * Prints a sortable column header `a` tag
237     *
238     * The a tag will include the CSS class `'sortable_column'` and the
239     * direction being sorted, `'asc'` or `'desc'`.
240     *
241     * {{{
242     * #!php
243     * fCRUD::printSortableColumn('name', 'Name');
244     * }}}
245     *
246     * would create the following HTML based on the page context
247     *
248     * {{{
249     * #!html
250     * <!-- If name is the current sort column in the asc direction, the output would be -->
251     * <a href="?sort=name&dir=desc" class="sorted_column asc">Name</a>
252     *
253     * <!-- If name is not the current sort column, the output would be -->
254     * <a href="?sort-name&dir=asc" class="sorted_column">Name</a>
255     * }}}
256     *
257     * @param  string $column       The column to create the sortable header for
258     * @param  string $column_name  This will override the humanized version of the column
259     * @return void
260     */
261     static public function printSortableColumn($column, $column_name=NULL)
262     {
263         if ($column_name === NULL) {
264             $column_name = fGrammar::humanize($column);
265         }
266        
267         if (self::$sort_column == $column) {
268             $sort      = $column;
269             $direction = (self::$sort_direction == 'asc') ? 'desc' : 'asc';
270         } else {
271             $sort      = $column;
272             $direction = 'asc';
273         }
274        
275         $columns = array_merge(array('sort', 'dir'), array_keys(self::$search_values));
276         $values  = array_merge(array($sort, $direction), array_values(self::$search_values));
277        
278         $url         = fHTML::encode(fURL::get() . fURL::replaceInQueryString($columns, $values));
279         $css_class   = (self::$sort_column == $column) ? ' ' . self::$sort_direction : '';
280         $column_name = fHTML::prepare($column_name);
281        
282         echo '<a href="' . $url . '" class="sortable_column' . $css_class . '">' . $column_name . '</a>';
283     }
284        
285    
286     /**
287     * Checks to see if any values (search or sort) were loaded from the session, and if so redirects the user to the current URL with those values added
288     *
289     * @return void
290     */
291     static public function redirectWithLoadedValues()
292     {
293         // If values were reset, redirect to the plain URL
294         if (self::wasResetRequested()) {
295             fURL::redirect(fURL::get() . fURL::removeFromQueryString('reset'));
296         }
297        
298         $query_string = fURL::replaceInQueryString(array_keys(self::$loaded_values), array_values(self::$loaded_values));
299         $url = fURL::get() . $query_string;
300        
301         if ($url != fURL::getWithQueryString() && $url != fURL::getWithQueryString() . '?') {
302             fURL::redirect($url);
303         }
304     }
305    
306    
307     /**
308     * Resets the configuration and data of the class
309     *
310     * @internal
311      *
312     * @return void
313     */
314     static public function reset()
315     {
316         fSession::clear(__CLASS__ . '::');
317        
318         self::$loaded_values  = array();
319         self::$row_number     = 1;
320         self::$search_values  = array();
321         self::$sort_column    = NULL;
322         self::$sort_direction = NULL;
323     }
324    
325    
326     /**
327     * Sets a value for a search field
328     *
329     * @param  string $column  The column to save the value for
330     * @param  mixed  $value   The value to save
331     * @return void
332     */
333     static private function setPreviousSearchValue($column, $value)
334     {
335         fSession::set(__CLASS__ . '::' . fURL::get() . '::previous_search::' . $column, $value);
336     }
337    
338    
339     /**
340     * Set the sort column to be used on returning pages
341     *
342     * @param  string $sort_column  The sort column to save
343     * @return void
344     */
345     static private function setPreviousSortColumn($sort_column)
346     {
347         fSession::set(__CLASS__ . '::' . fURL::get() . '::previous_sort_column', $sort_column);
348     }
349    
350    
351     /**
352     * Set the sort direction to be used on returning pages
353     *
354     * @param  string $sort_direction  The sort direction to save
355     * @return void
356     */
357     static private function setPreviousSortDirection($sort_direction)
358     {
359         fSession::set(__CLASS__ . '::' . fURL::get() . '::previous_sort_direction', $sort_direction);
360     }
361    
362    
363     /**
364     * Indicates if a reset was requested for search values
365     *
366     * @return boolean  If a reset was requested
367     */
368     static private function wasResetRequested()
369     {
370         $tail = substr(fURL::getWithQueryString(), -6);
371         return $tail == '?reset' || $tail == '&reset';
372     }
373    
374    
375     /**
376     * Prevent instantiation
377     *
378     * @return fCRUD
379     */
380     private function __construct() { }
381 }
382  
383  
384  
385 /**
386  * Copyright (c) 2007-2009 Will Bond <will@flourishlib.com>
387  *
388  * Permission is hereby granted, free of charge, to any person obtaining a copy
389  * of this software and associated documentation files (the "Software"), to deal
390  * in the Software without restriction, including without limitation the rights
391  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
392  * copies of the Software, and to permit persons to whom the Software is
393  * furnished to do so, subject to the following conditions:
394  *
395  * The above copyright notice and this permission notice shall be included in
396  * all copies or substantial portions of the Software.
397  *
398  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
399  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
400  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
401  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
402  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
403  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
404  * THE SOFTWARE.
405  */