From 9aa7b26184fbbda73c8fed9ebc0cf8cae00b696b Mon Sep 17 00:00:00 2001
From: Qiang Xue <qiang.xue@gmail.com>
Date: Sat, 20 Jul 2013 07:28:22 -0400
Subject: [PATCH] Added LinkSorter.

---
 framework/yii/data/Sort.php                      | 72 ++++++++++++++++++++++++++++++++++++------------------------------------
 framework/yii/widgets/LinkSorter.php             | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/unit/framework/data/SortTest.php           | 26 --------------------------
 tests/unit/framework/helpers/ArrayHelperTest.php | 14 ++++++++------
 4 files changed, 101 insertions(+), 68 deletions(-)
 create mode 100644 framework/yii/widgets/LinkSorter.php

diff --git a/framework/yii/data/Sort.php b/framework/yii/data/Sort.php
index eb66754..5132188 100644
--- a/framework/yii/data/Sort.php
+++ b/framework/yii/data/Sort.php
@@ -65,11 +65,6 @@ use yii\helpers\Inflector;
  * sorted by the orders specified by the Sort object. In the view, we show two hyperlinks
  * that can lead to pages with the data sorted by the corresponding attributes.
  *
- * @property array $orders Sort directions indexed by column names. The sort direction
- * can be either [[Sort::ASC]] for ascending order or [[Sort::DESC]] for descending order.
- * @property array $attributeOrders Sort directions indexed by attribute names. The sort
- * direction can be either [[Sort::ASC]] for ascending order or [[Sort::DESC]] for descending order.
- *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
@@ -130,6 +125,9 @@ class Sort extends Object
 	 *   if it is not currently sorted (the default value is ascending order).
 	 * - The "label" element specifies what label should be used when calling [[link()]] to create
 	 *   a sort link. If not set, [[Inflector::camel2words()]] will be called to get a label.
+	 *
+	 * Note that if the Sort object is already created, you can only use the full format
+	 * to configure every attribute. Each attribute must include these elements: asc, desc and label.
 	 */
 	public $attributes = array();
 	/**
@@ -156,7 +154,7 @@ class Sort extends Object
 	 *
 	 * @see attributeOrders
 	 */
-	public $defaults;
+	public $defaultOrder;
 	/**
 	 * @var string the route of the controller action for displaying the sorted contents.
 	 * If not set, it means using the currently requested route.
@@ -177,7 +175,7 @@ class Sort extends Object
 	 * If the element does not exist, the [[defaults|default order]] will be used.
 	 *
 	 * @see sortVar
-	 * @see defaults
+	 * @see defaultOrder
 	 */
 	public $params;
 	/**
@@ -187,6 +185,29 @@ class Sort extends Object
 	public $urlManager;
 
 	/**
+	 * Normalizes the [[attributes]] property.
+	 */
+	public function init()
+	{
+		$attributes = array();
+		foreach ($this->attributes as $name => $attribute) {
+			if (is_array($attribute)) {
+				$attributes[$name] = $attribute;
+				if (!isset($attribute['label'])) {
+					$attributes[$name]['label'] = Inflector::camel2words($name);
+				}
+			} else {
+				$attributes[$attribute] = array(
+					'asc' => array($attribute => self::ASC),
+					'desc' => array($attribute => self::DESC),
+					'label' => Inflector::camel2words($attribute),
+				);
+			}
+		}
+		$this->attributes = $attributes;
+	}
+
+	/**
 	 * Returns the columns and their corresponding sort directions.
 	 * @param boolean $recalculate whether to recalculate the sort directions
 	 * @return array the columns (keys) and their corresponding sort directions (values).
@@ -197,7 +218,7 @@ class Sort extends Object
 		$attributeOrders = $this->getAttributeOrders($recalculate);
 		$orders = array();
 		foreach ($attributeOrders as $attribute => $direction) {
-			$definition = $this->getAttribute($attribute);
+			$definition = $this->attributes[$attribute];
 			$columns = $definition[$direction === self::ASC ? 'asc' : 'desc'];
 			foreach ($columns as $name => $dir) {
 				$orders[$name] = $dir;
@@ -230,7 +251,7 @@ class Sort extends Object
 						}
 					}
 
-					if (($this->getAttribute($attribute)) !== null) {
+					if (isset($this->attributes[$attribute])) {
 						$this->_attributeOrders[$attribute] = $descending;
 						if (!$this->enableMultiSort) {
 							return $this->_attributeOrders;
@@ -238,8 +259,8 @@ class Sort extends Object
 					}
 				}
 			}
-			if (empty($this->_attributeOrders) && is_array($this->defaults)) {
-				$this->_attributeOrders = $this->defaults;
+			if (empty($this->_attributeOrders) && is_array($this->defaultOrder)) {
+				$this->_attributeOrders = $this->defaultOrder;
 			}
 		}
 		return $this->_attributeOrders;
@@ -279,9 +300,8 @@ class Sort extends Object
 		}
 
 		$url = $this->createUrl($attribute);
-		$definition = $this->getAttribute($attribute);
-		$label = isset($definition['label']) ? $definition['label'] : Html::encode(Inflector::camel2words($attribute));
-		return Html::a($label, $url, $options);
+		$definition = $this->attributes[$attribute];
+		return Html::a($definition['label'], $url, $options);
 	}
 
 	/**
@@ -297,9 +317,10 @@ class Sort extends Object
 	 */
 	public function createUrl($attribute)
 	{
-		if (($definition = $this->getAttribute($attribute)) === null) {
+		if (!isset($this->attributes[$attribute])) {
 			throw new InvalidConfigException("Unknown attribute: $attribute");
 		}
+		$definition = $this->attributes[$attribute];
 		$directions = $this->getAttributeOrders();
 		if (isset($directions[$attribute])) {
 			$descending = !$directions[$attribute];
@@ -324,25 +345,4 @@ class Sort extends Object
 		$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
 		return $urlManager->createUrl($route, $params);
 	}
-
-	/**
-	 * Returns the attribute definition of the specified name.
-	 * @param string $name the attribute name
-	 * @return array the sort definition (column names => sort directions).
-	 * Null is returned if the attribute cannot be sorted.
-	 * @see attributes
-	 */
-	public function getAttribute($name)
-	{
-		if (isset($this->attributes[$name])) {
-			return $this->attributes[$name];
-		} elseif (in_array($name, $this->attributes, true)) {
-			return array(
-				'asc' => array($name => self::ASC),
-				'desc' => array($name => self::DESC),
-			);
-		} else {
-			return null;
-		}
-	}
 }
diff --git a/framework/yii/widgets/LinkSorter.php b/framework/yii/widgets/LinkSorter.php
new file mode 100644
index 0000000..4921af5
--- /dev/null
+++ b/framework/yii/widgets/LinkSorter.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\widgets;
+
+use yii\base\InvalidConfigException;
+use yii\base\Widget;
+use yii\data\Sort;
+use yii\helpers\Html;
+
+/**
+ * LinkSorter renders a list of sort links for the given sort definition.
+ *
+ * LinkSorter will generate a hyperlink for every attribute declared in [[sort]].
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class LinkSorter extends Widget
+{
+	/**
+	 * @var Sort the sort definition
+	 */
+	public $sort;
+	/**
+	 * @var array HTML attributes for the sorter container tag.
+	 * Please refer to [[Html::ul()]] for supported special options.
+	 */
+	public $options = array();
+
+	/**
+	 * Initializes the sorter.
+	 */
+	public function init()
+	{
+		if ($this->sort === null) {
+			throw new InvalidConfigException('The "sort" property must be set.');
+		}
+	}
+
+	/**
+	 * Executes the widget.
+	 * This method renders the sort links.
+	 */
+	public function run()
+	{
+		$links = array();
+		foreach (array_keys($this->sort->attributes) as $name) {
+			$links[] = $this->sort->link($name);
+		}
+		echo Html::ul($links, array_merge($this->options, array('encode' => false)));
+	}
+}
diff --git a/tests/unit/framework/data/SortTest.php b/tests/unit/framework/data/SortTest.php
index f5611cf..80f485c 100644
--- a/tests/unit/framework/data/SortTest.php
+++ b/tests/unit/framework/data/SortTest.php
@@ -93,32 +93,6 @@ class SortTest extends TestCase
 		$this->assertNull($sort->getAttributeOrder('xyz'));
 	}
 
-	public function testGetAttribute()
-	{
-		$sort = new Sort(array(
-			'attributes' => array(
-				'age',
-				'name' => array(
-					'asc' => array('first_name' => Sort::ASC, 'last_name' => Sort::ASC),
-					'desc' => array('first_name' => Sort::DESC, 'last_name' => Sort::DESC),
-				),
-			),
-			'params' => array(
-				'sort' => 'age.name-desc'
-			),
-			'enableMultiSort' => true,
-		));
-
-		$attribute = array('asc' => array('age' => Sort::ASC), 'desc' => array('age' => Sort::DESC));
-		$this->assertEquals($attribute, $sort->getAttribute('age'));
-		$attribute = array(
-			'asc' => array('first_name' => Sort::ASC, 'last_name' => Sort::ASC),
-			'desc' => array('first_name' => Sort::DESC, 'last_name' => Sort::DESC),
-		);
-		$this->assertEquals($attribute, $sort->getAttribute('name'));
-		$this->assertNull($sort->getAttribute('xyz'));
-	}
-
 	public function testCreateUrl()
 	{
 		$manager = new UrlManager(array(
diff --git a/tests/unit/framework/helpers/ArrayHelperTest.php b/tests/unit/framework/helpers/ArrayHelperTest.php
index 3ec80fd..5ace571 100644
--- a/tests/unit/framework/helpers/ArrayHelperTest.php
+++ b/tests/unit/framework/helpers/ArrayHelperTest.php
@@ -117,9 +117,10 @@ class ArrayHelperTest extends TestCase
 	public function testMultisortUseSort()
 	{
 		// single key
-		$sort = new Sort();
-		$sort->attributes = array('name', 'age');
-		$sort->defaults = array('name' => Sort::ASC);
+		$sort = new Sort(array(
+			'attributes' => array('name', 'age'),
+			'defaultOrder' => array('name' => Sort::ASC),
+		));
 		$orders = $sort->getOrders();
 
 		$array = array(
@@ -133,9 +134,10 @@ class ArrayHelperTest extends TestCase
 		$this->assertEquals(array('name' => 'c', 'age' => 2), $array[2]);
 
 		// multiple keys
-		$sort = new Sort();
-		$sort->attributes = array('name', 'age');
-		$sort->defaults = array('name' => Sort::ASC, 'age' => Sort::DESC);
+		$sort = new Sort(array(
+			'attributes' => array('name', 'age'),
+			'defaultOrder' => array('name' => Sort::ASC, 'age' => Sort::DESC),
+		));
 		$orders = $sort->getOrders();
 
 		$array = array(
--
libgit2 0.27.1