mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-05 07:44:05 +01:00
292 lines
9.9 KiB
PHP
292 lines
9.9 KiB
PHP
<?php
|
|
/**
|
|
* CSort class file.
|
|
*
|
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
|
* @link http://www.yiiframework.com/
|
|
* @copyright Copyright © 2008-2009 Yii Software LLC
|
|
* @license http://www.yiiframework.com/license/
|
|
*/
|
|
|
|
/**
|
|
* CSort represents information relevant to sorting.
|
|
*
|
|
* When data needs to be sorted according to one or several attributes,
|
|
* we can use CSort to represent the sorting information and generate
|
|
* appropriate hyperlinks that can lead to sort actions.
|
|
*
|
|
* CSort is designed to be used together with {@link CActiveRecord}.
|
|
* When creating a CSort instance, you need to specify {@link modelClass}.
|
|
* You can use CSort to generate hyperlinks by calling {@link link}.
|
|
* You can also use CSort to modify a {@link CDbCriteria} instance so that
|
|
* it can cause the query results to be sorted according to the specified
|
|
* attributes.
|
|
*
|
|
* CSort is primarily used for active record involving a single DB table.
|
|
* In order to use it with relational active record, special care needs to be taken.
|
|
* We use an example to illustrate this use case. Assume 'Post' is the main
|
|
* active record class, and 'author' is one of its related attributes. We would
|
|
* like to sort by post's title as well as its author's name. First, we need
|
|
* to define aliases for the two attributes by setting the {@link attributes} property:
|
|
* <pre>
|
|
* array(
|
|
* 'title',
|
|
* 'author.name'=>'authorName',
|
|
* )
|
|
* </pre>
|
|
*
|
|
* We also need to modify the 'author' relation in 'Post' class and explicitly
|
|
* specify the 'alias' option with value 'author':
|
|
* <pre>
|
|
* 'author'=>array(self::BELONGS_TO, 'User', 'alias'=>'author')
|
|
* </pre>
|
|
*
|
|
* Finally, we can use the following code to generate hyperlinks:
|
|
* <pre>
|
|
* echo CSort::link('title');
|
|
* echo CSort::link('author.name');
|
|
* </pre>
|
|
*
|
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
|
* @version $Id$
|
|
* @package system.web
|
|
* @since 1.0.1
|
|
*/
|
|
class CSort extends CComponent
|
|
{
|
|
/**
|
|
* @var boolean whether the sorting can be applied to multiple attributes simultaneously.
|
|
* Defaults to true. If false, each time the data can only be sorted by one attribute.
|
|
*/
|
|
public $multiSort=true;
|
|
/**
|
|
* @var string the class name of data models that need to be sorted.
|
|
* This should be a child class of {@link CActiveRecord}.
|
|
*/
|
|
public $modelClass;
|
|
/**
|
|
* @var array list of attributes that are allowed to be sorted.
|
|
* For example, array('user_id','create_time') would specify that only 'user_id'
|
|
* and 'create_time' can be sorted.
|
|
* Defaults to null, meaning all attributes of the {@link modelClass} can be sorted.
|
|
* This property can also be used to specify attribute aliases that should appear
|
|
* in the 'sort' GET parameter in place of the original attribute names.
|
|
* In this case, the aliases should be array values while the attribute names
|
|
* should be the corresponding array keys. Do not use '-' and '.' in the aliases
|
|
* as they are used as {@link separators}.
|
|
*/
|
|
public $attributes;
|
|
/**
|
|
* @var string the name of the GET parameter that specifies which attributes to be sorted
|
|
* in which direction. Defaults to 'sort'.
|
|
*/
|
|
public $sortVar='sort';
|
|
/**
|
|
* @var string the default order that should be applied to the query criteria when
|
|
* the current request does not specify any sort.
|
|
*/
|
|
public $defaultOrder;
|
|
/**
|
|
* @var string the route (controller ID and action ID) for generating the sorted contents.
|
|
* Defaults to empty string, meaning using the currently requested route.
|
|
*/
|
|
public $route='';
|
|
/**
|
|
* @var array separators used in the generated URL. This must be an array consisting of
|
|
* two elements. The first element specifies the character separating different
|
|
* attributes, while the second element specifies the character separating attribute name
|
|
* and the corresponding sort direction. Defaults to array('-','.').
|
|
*/
|
|
public $separators=array('-','.');
|
|
|
|
private $_directions;
|
|
|
|
/**
|
|
* Constructor.
|
|
* @param string the class name of data models that need to be sorted.
|
|
* This should be a child class of {@link CActiveRecord}.
|
|
*/
|
|
public function __construct($modelClass)
|
|
{
|
|
$this->modelClass=$modelClass;
|
|
}
|
|
|
|
/**
|
|
* Modifies the query criteria by changing its ORDER BY property.
|
|
* @param CDbCriteria the query criteria
|
|
*/
|
|
public function applyOrder($criteria)
|
|
{
|
|
$directions=$this->getDirections();
|
|
if(empty($directions))
|
|
$order=$this->defaultOrder;
|
|
else
|
|
{
|
|
$schema=CActiveRecord::model($this->modelClass)->getDbConnection()->getSchema();
|
|
$orders=array();
|
|
foreach($directions as $attribute=>$descending)
|
|
{
|
|
if(($pos=strpos($attribute,'.'))!==false)
|
|
$attribute=$schema->quoteTableName(substr($attribute,0,$pos)).'.'.$schema->quoteColumnName(substr($attribute,$pos+1));
|
|
else
|
|
$attribute=$schema->quoteColumnName($attribute);
|
|
$orders[]=$descending?$attribute.' DESC':$attribute;
|
|
}
|
|
$order=implode(', ',$orders);
|
|
}
|
|
|
|
if(!empty($order))
|
|
{
|
|
if(!empty($criteria->order))
|
|
$criteria->order.=', ';
|
|
$criteria->order.=$order;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a hyperlink that can be clicked to cause sorting.
|
|
* @param string the attribute name. This must be the actual attribute name, not alias.
|
|
* If it is an attribute of a related AR object, the name should be prefixed with
|
|
* the relation name (e.g. 'author.name', where 'author' is the relation name).
|
|
* @param string the link label. If null, the label will be determined according
|
|
* to the attribute (see {@link CActiveRecord::getAttributeLabel}).
|
|
* @param array additional HTML attributes for the hyperlink tag
|
|
* @return string the generated hyperlink
|
|
*/
|
|
public function link($attribute,$label=null,$htmlOptions=array())
|
|
{
|
|
$directions=$this->getDirections();
|
|
if(isset($directions[$attribute]))
|
|
{
|
|
$descending=!$directions[$attribute];
|
|
unset($directions[$attribute]);
|
|
}
|
|
else
|
|
$descending=false;
|
|
if($this->multiSort)
|
|
$directions=array_merge(array($attribute=>$descending),$directions);
|
|
else
|
|
$directions=array($attribute=>$descending);
|
|
|
|
if($label===null)
|
|
$label=$this->resolveLabel($attribute);
|
|
$url=$this->createUrl(Yii::app()->getController(),$directions);
|
|
|
|
return $this->createLink($attribute,$label,$url,$htmlOptions);
|
|
}
|
|
|
|
/**
|
|
* Resolves the attribute label based on label definition in the AR class.
|
|
* @param string the attribute name.
|
|
* @return string the attribute label
|
|
* @since 1.0.2
|
|
*/
|
|
protected function resolveLabel($attribute)
|
|
{
|
|
if(($pos=strpos($attribute,'.'))!==false)
|
|
{
|
|
$baseModel=CActiveRecord::model($this->modelClass);
|
|
if(($relation=$baseModel->getActiveRelation(substr($attribute,0,$pos)))!==null)
|
|
return CActiveRecord::model($relation->className)->getAttributeLabel(substr($attribute,$pos+1));
|
|
else
|
|
return $baseModel->getAttributeLabel(substr($attribute,$pos+1));
|
|
}
|
|
return CActiveRecord::model($this->modelClass)->getAttributeLabel($attribute);
|
|
}
|
|
|
|
/**
|
|
* Returns the currently requested sort information.
|
|
* @return array sort directions indexed by attribute names.
|
|
* The sort direction is true if the corresponding attribute should be
|
|
* sorted in descending order.
|
|
*/
|
|
public function getDirections()
|
|
{
|
|
if($this->_directions===null)
|
|
{
|
|
$this->_directions=array();
|
|
if(isset($_GET[$this->sortVar]))
|
|
{
|
|
$attributes=explode($this->separators[0],$_GET[$this->sortVar]);
|
|
foreach($attributes as $attribute)
|
|
{
|
|
if(($pos=strpos($attribute,$this->separators[1]))!==false)
|
|
{
|
|
$descending=substr($attribute,$pos+1)==='desc';
|
|
$attribute=substr($attribute,0,$pos);
|
|
}
|
|
else
|
|
$descending=false;
|
|
|
|
if(($attribute=$this->validateAttribute($attribute))!==false)
|
|
$this->_directions[$attribute]=$descending;
|
|
}
|
|
if(!$this->multiSort)
|
|
{
|
|
foreach($this->_directions as $attribute=>$descending)
|
|
return $this->_directions=array($attribute=>$descending);
|
|
}
|
|
}
|
|
}
|
|
return $this->_directions;
|
|
}
|
|
|
|
/**
|
|
* Creates a URL that can lead to generating sorted data.
|
|
* @param CController the controller that will be used to create the URL.
|
|
* @param array the sort directions indexed by attribute names.
|
|
* The sort direction is true if the corresponding attribute should be
|
|
* sorted in descending order.
|
|
* @return string the URL for sorting
|
|
*/
|
|
public function createUrl($controller,$directions)
|
|
{
|
|
$sorts=array();
|
|
foreach($directions as $attribute=>$descending)
|
|
{
|
|
if(is_array($this->attributes) && isset($this->attributes[$attribute]))
|
|
$attribute=$this->attributes[$attribute];
|
|
$sorts[]=$descending ? $attribute.$this->separators[1].'desc' : $attribute;
|
|
}
|
|
$params=$_GET;
|
|
$params[$this->sortVar]=implode($this->separators[0],$sorts);
|
|
return $controller->createUrl($this->route,$params);
|
|
}
|
|
|
|
/**
|
|
* Validates an attribute that is requested to be sorted.
|
|
* The validation is based on {@link attributes} and {@link CActiveRecord::attributeNames}.
|
|
* False will be returned if the attribute is not allowed to be sorted.
|
|
* If the attribute is aliased via {@link attributes}, the original
|
|
* attribute name will be returned.
|
|
* @param string the attribute name (could be an alias) that the user requests to sort on
|
|
* @return string the real attribute name. False if the attribute cannot be sorted
|
|
*/
|
|
protected function validateAttribute($attribute)
|
|
{
|
|
if(empty($this->attributes))
|
|
$attributes=CActiveRecord::model($this->modelClass)->attributeNames();
|
|
else
|
|
$attributes=$this->attributes;
|
|
foreach($attributes as $name=>$alias)
|
|
{
|
|
if($alias===$attribute)
|
|
return is_string($name) ? $name : $alias;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Creates a hyperlink based on the given label and URL.
|
|
* You may override this method to customize the link generation.
|
|
* @param string the name of the attribute that this link is for
|
|
* @param string the label of the hyperlink
|
|
* @param string the URL
|
|
* @param array additional HTML options
|
|
* @return string the generated hyperlink
|
|
*/
|
|
protected function createLink($attribute,$label,$url,$htmlOptions)
|
|
{
|
|
return CHtml::link($label,$url,$htmlOptions);
|
|
}
|
|
} |