Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • Net
  • None
  • PHP
  • system
    • base
    • caching
      • dependencies
    • collections
    • console
    • db
      • ar
      • schema
        • cubrid
        • mssql
        • mysql
        • oci
        • pgsql
        • sqlite
    • i18n
      • gettext
    • logging
    • test
    • utils
    • validators
    • web
      • actions
      • auth
      • filters
      • form
      • helpers
      • renderers
      • services
      • widgets
        • captcha
        • pagers
  • Text
    • Highlighter
  • zii
    • behaviors
    • widgets
      • grid
      • jui

Classes

  • ActionFormModelBase
  • CActiveDataProvider
  • CalendarEventFormModel
  • CallFormModel
  • CArrayDataProvider
  • CAssetManager
  • CBaseController
  • CBaseUrlRule
  • CCacheHttpSession
  • CClientScript
  • CController
  • CCookieCollection
  • CDataProvider
  • CDataProviderIterator
  • CDbHttpSession
  • CExtController
  • CFormModel
  • CHttpCookie
  • CHttpRequest
  • CHttpSession
  • CHttpSessionIterator
  • COutputEvent
  • CPagination
  • CreatePageFormModel
  • CSort
  • CSqlDataProvider
  • CTheme
  • CThemeManager
  • CUploadedFile
  • CUrlManager
  • CUrlRule
  • CWebApplication
  • CWebModule
  • CWidgetFactory
  • EditMobileFormsFormModel
  • EventCommentPublisherFormModel
  • EventFormModel
  • EventPublisherFormModel
  • FileSystemObjectDataProvider
  • MassActionFormModel
  • MobilePagination
  • NoteFormModel
  • NotificationsController
  • TimeFormModel
  • UploadLogoFormModel
  • X2FormModel
  • X2HttpRequest

Interfaces

  • IDataProvider
  • IWidgetFactory
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * CSort class file.
  4:  *
  5:  * @author Qiang Xue <qiang.xue@gmail.com>
  6:  * @link http://www.yiiframework.com/
  7:  * @copyright 2008-2013 Yii Software LLC
  8:  * @license http://www.yiiframework.com/license/
  9:  */
 10: 
 11: /**
 12:  * CSort represents information relevant to sorting.
 13:  *
 14:  * When data needs to be sorted according to one or several attributes,
 15:  * we can use CSort to represent the sorting information and generate
 16:  * appropriate hyperlinks that can lead to sort actions.
 17:  *
 18:  * CSort is designed to be used together with {@link CActiveRecord}.
 19:  * When creating a CSort instance, you need to specify {@link modelClass}.
 20:  * You can use CSort to generate hyperlinks by calling {@link link}.
 21:  * You can also use CSort to modify a {@link CDbCriteria} instance by calling {@link applyOrder} so that
 22:  * it can cause the query results to be sorted according to the specified
 23:  * attributes.
 24:  *
 25:  * In order to prevent SQL injection attacks, CSort ensures that only valid model attributes
 26:  * can be sorted. This is determined based on {@link modelClass} and {@link attributes}.
 27:  * When {@link attributes} is not set, all attributes belonging to {@link modelClass}
 28:  * can be sorted. When {@link attributes} is set, only those attributes declared in the property
 29:  * can be sorted.
 30:  *
 31:  * By configuring {@link attributes}, one can perform more complex sorts that may
 32:  * consist of things like compound attributes (e.g. sort based on the combination of
 33:  * first name and last name of users).
 34:  *
 35:  * The property {@link attributes} should be an array of key-value pairs, where the keys
 36:  * represent the attribute names, while the values represent the virtual attribute definitions.
 37:  * For more details, please check the documentation about {@link attributes}.
 38:  *
 39:  * @property string $orderBy The order-by columns represented by this sort object.
 40:  * This can be put in the ORDER BY clause of a SQL statement.
 41:  * @property array $directions Sort directions indexed by attribute names.
 42:  * The sort direction. Can be either CSort::SORT_ASC for ascending order or
 43:  * CSort::SORT_DESC for descending order.
 44:  *
 45:  * @author Qiang Xue <qiang.xue@gmail.com>
 46:  * @package system.web
 47:  */
 48: class CSort extends CComponent
 49: {
 50:     /**
 51:      * Sort ascending
 52:      * @since 1.1.10
 53:      */
 54:     const SORT_ASC = false;
 55: 
 56:     /**
 57:      * Sort descending
 58:      * @since 1.1.10
 59:      */
 60:     const SORT_DESC = true;
 61: 
 62:     /**
 63:      * @var boolean whether the sorting can be applied to multiple attributes simultaneously.
 64:      * Defaults to false, which means each time the data can only be sorted by one attribute.
 65:      */
 66:     public $multiSort=false;
 67:     /**
 68:      * @var string the name of the model class whose attributes can be sorted.
 69:      * The model class must be a child class of {@link CActiveRecord}.
 70:      */
 71:     public $modelClass;
 72:     /**
 73:      * @var array list of attributes that are allowed to be sorted.
 74:      * For example, array('user_id','create_time') would specify that only 'user_id'
 75:      * and 'create_time' of the model {@link modelClass} can be sorted.
 76:      * By default, this property is an empty array, which means all attributes in
 77:      * {@link modelClass} are allowed to be sorted.
 78:      *
 79:      * This property can also be used to specify complex sorting. To do so,
 80:      * a virtual attribute can be declared in terms of a key-value pair in the array.
 81:      * The key refers to the name of the virtual attribute that may appear in the sort request,
 82:      * while the value specifies the definition of the virtual attribute.
 83:      *
 84:      * In the simple case, a key-value pair can be like <code>'user'=>'user_id'</code>
 85:      * where 'user' is the name of the virtual attribute while 'user_id' means the virtual
 86:      * attribute is the 'user_id' attribute in the {@link modelClass}.
 87:      *
 88:      * A more flexible way is to specify the key-value pair as
 89:      * <pre>
 90:      * 'user'=>array(
 91:      *     'asc'=>'first_name, last_name',
 92:      *     'desc'=>'first_name DESC, last_name DESC',
 93:      *     'label'=>'Name'
 94:      * )
 95:      * </pre>
 96:      * where 'user' is the name of the virtual attribute that specifies the full name of user
 97:      * (a compound attribute consisting of first name and last name of user). In this case,
 98:      * we have to use an array to define the virtual attribute with three elements: 'asc',
 99:      * 'desc' and 'label'.
100:      *
101:      * The above approach can also be used to declare virtual attributes that consist of relational
102:      * attributes. For example,
103:      * <pre>
104:      * 'price'=>array(
105:      *     'asc'=>'item.price',
106:      *     'desc'=>'item.price DESC',
107:      *     'label'=>'Item Price'
108:      * )
109:      * </pre>
110:      *
111:      * Note, the attribute name should not contain '-' or '.' characters because
112:      * they are used as {@link separators}.
113:      *
114:      * Starting from version 1.1.3, an additional option named 'default' can be used in the virtual attribute
115:      * declaration. This option specifies whether an attribute should be sorted in ascending or descending
116:      * order upon user clicking the corresponding sort hyperlink if it is not currently sorted. The valid
117:      * option values include 'asc' (default) and 'desc'. For example,
118:      * <pre>
119:      * 'price'=>array(
120:      *     'asc'=>'item.price',
121:      *     'desc'=>'item.price DESC',
122:      *     'label'=>'Item Price',
123:      *     'default'=>'desc',
124:      * )
125:      * </pre>
126:      *
127:      * Also starting from version 1.1.3, you can include a star ('*') element in this property so that
128:      * all model attributes are available for sorting, in addition to those virtual attributes. For example,
129:      * <pre>
130:      * 'attributes'=>array(
131:      *     'price'=>array(
132:      *         'asc'=>'item.price',
133:      *         'desc'=>'item.price DESC',
134:      *         'label'=>'Item Price',
135:      *         'default'=>'desc',
136:      *     ),
137:      *     '*',
138:      * )
139:      * </pre>
140:      * Note that when a name appears as both a model attribute and a virtual attribute, the position of
141:      * the star element in the array determines which one takes precedence. In particular, if the star
142:      * element is the first element in the array, the model attribute takes precedence; and if the star
143:      * element is the last one, the virtual attribute takes precedence.
144:      */
145:     public $attributes=array();
146:     /**
147:      * @var string the name of the GET parameter that specifies which attributes to be sorted
148:      * in which direction. Defaults to 'sort'.
149:      */
150:     public $sortVar='sort';
151:     /**
152:      * @var string the tag appeared in the GET parameter that indicates the attribute should be sorted
153:      * in descending order. Defaults to 'desc'.
154:      */
155:     public $descTag='desc';
156:     /**
157:      * @var mixed the default order that should be applied to the query criteria when
158:      * the current request does not specify any sort. For example, 'name, create_time DESC' or
159:      * 'UPPER(name)'.
160:      *
161:      * Starting from version 1.1.3, you can also specify the default order using an array.
162:      * The array keys could be attribute names or virtual attribute names as declared in {@link attributes},
163:      * and the array values indicate whether the sorting of the corresponding attributes should
164:      * be in descending order. For example,
165:      * <pre>
166:      * 'defaultOrder'=>array(
167:      *     'price'=>CSort::SORT_DESC,
168:      * )
169:      * </pre>
170:      * `SORT_DESC` and `SORT_ASC` are available since 1.1.10. In earlier Yii versions you should use
171:      * `true` and `false` respectively.
172:      *
173:      * Please note when using array to specify the default order, the corresponding attributes
174:      * will be put into {@link directions} and thus affect how the sort links are rendered
175:      * (e.g. an arrow may be displayed next to the currently active sort link).
176:      */
177:     public $defaultOrder;
178:     /**
179:      * @var string the route (controller ID and action ID) for generating the sorted contents.
180:      * Defaults to empty string, meaning using the currently requested route.
181:      */
182:     public $route='';
183:     /**
184:      * @var array separators used in the generated URL. This must be an array consisting of
185:      * two elements. The first element specifies the character separating different
186:      * attributes, while the second element specifies the character separating attribute name
187:      * and the corresponding sort direction. Defaults to array('-','.').
188:      */
189:     public $separators=array('-','.');
190:     /**
191:      * @var array the additional GET parameters (name=>value) that should be used when generating sort URLs.
192:      * Defaults to null, meaning using the currently available GET parameters.
193:      */
194:     public $params;
195: 
196:     private $_directions;
197: 
198:     /**
199:      * Constructor.
200:      * @param string $modelClass the class name of data models that need to be sorted.
201:      * This should be a child class of {@link CActiveRecord}.
202:      */
203:     public function __construct($modelClass=null)
204:     {
205:         $this->modelClass=$modelClass;
206:     }
207: 
208:     /**
209:      * Modifies the query criteria by changing its {@link CDbCriteria::order} property.
210:      * This method will use {@link directions} to determine which columns need to be sorted.
211:      * They will be put in the ORDER BY clause. If the criteria already has non-empty {@link CDbCriteria::order} value,
212:      * the new value will be appended to it.
213:      * @param CDbCriteria $criteria the query criteria
214:      */
215:     public function applyOrder($criteria)
216:     {
217:         $order=$this->getOrderBy($criteria);
218:         if(!empty($order))
219:         {
220:             if(!empty($criteria->order))
221:                 $criteria->order.=', ';
222:             $criteria->order.=$order;
223:         }
224:     }
225: 
226:     /**
227:      * @param CDbCriteria $criteria the query criteria
228:      * @return string the order-by columns represented by this sort object.
229:      * This can be put in the ORDER BY clause of a SQL statement.
230:      * @since 1.1.0
231:      */
232:     public function getOrderBy($criteria=null)
233:     {
234:         $directions=$this->getDirections();
235:         if(empty($directions))
236:             return is_string($this->defaultOrder) ? $this->defaultOrder : '';
237:         else
238:         {
239:             if($this->modelClass!==null)
240:                 $schema=$this->getModel($this->modelClass)->getDbConnection()->getSchema();
241:             $orders=array();
242:             foreach($directions as $attribute=>$descending)
243:             {
244:                 $definition=$this->resolveAttribute($attribute);
245:                 if(is_array($definition))
246:                 {
247:                     if($descending)
248:                         $orders[]=isset($definition['desc']) ? (is_array($definition['desc']) ? implode(', ',$definition['desc']) : $definition['desc']) : $attribute.' DESC';
249:                     else
250:                         $orders[]=isset($definition['asc']) ? (is_array($definition['asc']) ? implode(', ',$definition['asc']) : $definition['asc']) : $attribute;
251:                 }
252:                 elseif($definition!==false)
253:                 {
254:                     $attribute=$definition;
255:                     if(isset($schema))
256:                     {
257:                         if(($pos=strpos($attribute,'.'))!==false)
258:                             $attribute=$schema->quoteTableName(substr($attribute,0,$pos)).'.'.$schema->quoteColumnName(substr($attribute,$pos+1));
259:                         else
260:                             $attribute=($criteria===null || $criteria->alias===null ? $this->getModel($this->modelClass)->getTableAlias(true) : $schema->quoteTableName($criteria->alias)).'.'.$schema->quoteColumnName($attribute);
261:                     }
262:                     $orders[]=$descending?$attribute.' DESC':$attribute;
263:                 }
264:             }
265:             return implode(', ',$orders);
266:         }
267:     }
268: 
269:     /**
270:      * Generates a hyperlink that can be clicked to cause sorting.
271:      * @param string $attribute the attribute name. This must be the actual attribute name, not alias.
272:      * If it is an attribute of a related AR object, the name should be prefixed with
273:      * the relation name (e.g. 'author.name', where 'author' is the relation name).
274:      * @param string $label the link label. If null, the label will be determined according
275:      * to the attribute (see {@link resolveLabel}).
276:      * @param array $htmlOptions additional HTML attributes for the hyperlink tag
277:      * @return string the generated hyperlink
278:      */
279:     public function link($attribute,$label=null,$htmlOptions=array())
280:     {
281:         if($label===null)
282:             $label=$this->resolveLabel($attribute);
283:         if(($definition=$this->resolveAttribute($attribute))===false)
284:             return $label;
285:         $directions=$this->getDirections();
286:         if(isset($directions[$attribute]))
287:         {
288:             $class=$directions[$attribute] ? 'desc' : 'asc';
289:             if(isset($htmlOptions['class']))
290:                 $htmlOptions['class'].=' '.$class;
291:             else
292:                 $htmlOptions['class']=$class;
293:             $descending=!$directions[$attribute];
294:             unset($directions[$attribute]);
295:         }
296:         elseif(is_array($definition) && isset($definition['default']))
297:             $descending=$definition['default']==='desc';
298:         else
299:             $descending=false;
300: 
301:         if($this->multiSort)
302:             $directions=array_merge(array($attribute=>$descending),$directions);
303:         else
304:             $directions=array($attribute=>$descending);
305: 
306:         $url=$this->createUrl(Yii::app()->getController(),$directions);
307: 
308:         return $this->createLink($attribute,$label,$url,$htmlOptions);
309:     }
310: 
311:     /**
312:      * Resolves the attribute label for the specified attribute.
313:      * This will invoke {@link CActiveRecord::getAttributeLabel} to determine what label to use.
314:      * If the attribute refers to a virtual attribute declared in {@link attributes},
315:      * then the label given in the {@link attributes} will be returned instead.
316:      * @param string $attribute the attribute name.
317:      * @return string the attribute label
318:      */
319:     public function resolveLabel($attribute)
320:     {
321:         $definition=$this->resolveAttribute($attribute);
322:         if(is_array($definition))
323:         {
324:             if(isset($definition['label']))
325:                 return $definition['label'];
326:         }
327:         elseif(is_string($definition))
328:             $attribute=$definition;
329:         if($this->modelClass!==null)
330:             return $this->getModel($this->modelClass)->getAttributeLabel($attribute);
331:         else
332:             return $attribute;
333:     }
334: 
335:     /**
336:      * Returns the currently requested sort information.
337:      * @return array sort directions indexed by attribute names.
338:      * Sort direction can be either CSort::SORT_ASC for ascending order or
339:      * CSort::SORT_DESC for descending order.
340:      */
341:     public function getDirections()
342:     {
343:         if($this->_directions===null)
344:         {
345:             $this->_directions=array();
346:             if(isset($_GET[$this->sortVar]) && is_string($_GET[$this->sortVar]))
347:             {
348:                 $attributes=explode($this->separators[0],$_GET[$this->sortVar]);
349:                 foreach($attributes as $attribute)
350:                 {
351:                     if(($pos=strrpos($attribute,$this->separators[1]))!==false)
352:                     {
353:                         $descending=substr($attribute,$pos+1)===$this->descTag;
354:                         if($descending)
355:                             $attribute=substr($attribute,0,$pos);
356:                     }
357:                     else
358:                         $descending=false;
359: 
360:                     if(($this->resolveAttribute($attribute))!==false)
361:                     {
362:                         $this->_directions[$attribute]=$descending;
363:                         if(!$this->multiSort)
364:                             return $this->_directions;
365:                     }
366:                 }
367:             }
368:             if($this->_directions===array() && is_array($this->defaultOrder))
369:                 $this->_directions=$this->defaultOrder;
370:         }
371:         return $this->_directions;
372:     }
373: 
374:     /**
375:      * Returns the sort direction of the specified attribute in the current request.
376:      * @param string $attribute the attribute name
377:      * @return mixed Sort direction of the attribute. Can be either CSort::SORT_ASC
378:      * for ascending order or CSort::SORT_DESC for descending order. Value is null
379:      * if the attribute doesn't need to be sorted.
380:      */
381:     public function getDirection($attribute)
382:     {
383:         $this->getDirections();
384:         return isset($this->_directions[$attribute]) ? $this->_directions[$attribute] : null;
385:     }
386: 
387:     /**
388:      * Creates a URL that can lead to generating sorted data.
389:      * @param CController $controller the controller that will be used to create the URL.
390:      * @param array $directions the sort directions indexed by attribute names.
391:      * The sort direction can be either CSort::SORT_ASC for ascending order or
392:      * CSort::SORT_DESC for descending order.
393:      * @return string the URL for sorting
394:      */
395:     public function createUrl($controller,$directions)
396:     {
397:         $sorts=array();
398:         foreach($directions as $attribute=>$descending)
399:             $sorts[]=$descending ? $attribute.$this->separators[1].$this->descTag : $attribute;
400:         $params=$this->params===null ? $_GET : $this->params;
401:         $params[$this->sortVar]=implode($this->separators[0],$sorts);
402:         return $controller->createUrl($this->route,$params);
403:     }
404: 
405:     /**
406:      * Returns the real definition of an attribute given its name.
407:      *
408:      * The resolution is based on {@link attributes} and {@link CActiveRecord::attributeNames}.
409:      * <ul>
410:      * <li>When {@link attributes} is an empty array, if the name refers to an attribute of {@link modelClass},
411:      * then the name is returned back.</li>
412:      * <li>When {@link attributes} is not empty, if the name refers to an attribute declared in {@link attributes},
413:      * then the corresponding virtual attribute definition is returned. Starting from version 1.1.3, if {@link attributes}
414:      * contains a star ('*') element, the name will also be used to match against all model attributes.</li>
415:      * <li>In all other cases, false is returned, meaning the name does not refer to a valid attribute.</li>
416:      * </ul>
417:      * @param string $attribute the attribute name that the user requests to sort on
418:      * @return mixed the attribute name or the virtual attribute definition. False if the attribute cannot be sorted.
419:      */
420:     public function resolveAttribute($attribute)
421:     {
422:         if($this->attributes!==array())
423:             $attributes=$this->attributes;
424:         elseif($this->modelClass!==null)
425:             $attributes=$this->getModel($this->modelClass)->attributeNames();
426:         else
427:             return false;
428:         foreach($attributes as $name=>$definition)
429:         {
430:             if(is_string($name))
431:             {
432:                 if($name===$attribute)
433:                     return $definition;
434:             }
435:             elseif($definition==='*')
436:             {
437:                 if($this->modelClass!==null && $this->getModel($this->modelClass)->hasAttribute($attribute))
438:                     return $attribute;
439:             }
440:             elseif($definition===$attribute)
441:                 return $attribute;
442:         }
443:         return false;
444:     }
445: 
446:     /**
447:      * Given active record class name returns new model instance.
448:      *
449:      * @param string $className active record class name.
450:      * @return CActiveRecord active record model instance.
451:      *
452:      * @since 1.1.14
453:      */
454:     protected function getModel($className)
455:     {
456:         return CActiveRecord::model($className);
457:     }
458: 
459:     /**
460:      * Creates a hyperlink based on the given label and URL.
461:      * You may override this method to customize the link generation.
462:      * @param string $attribute the name of the attribute that this link is for
463:      * @param string $label the label of the hyperlink
464:      * @param string $url the URL
465:      * @param array $htmlOptions additional HTML options
466:      * @return string the generated hyperlink
467:      */
468:     protected function createLink($attribute,$label,$url,$htmlOptions)
469:     {
470:         return CHtml::link($label,$url,$htmlOptions);
471:     }
472: }
473: 
API documentation generated by ApiGen 2.8.0