Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
06feccff
Commit
06feccff
authored
Mar 06, 2013
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactored query builder.
finished Sort.
parent
1f522d22
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
257 additions
and
313 deletions
+257
-313
Query.php
framework/db/Query.php
+81
-38
QueryBuilder.php
framework/db/QueryBuilder.php
+36
-70
Pagination.php
framework/web/Pagination.php
+2
-1
Sort.php
framework/web/Sort.php
+123
-195
QueryTest.php
tests/unit/framework/db/QueryTest.php
+15
-9
No files found.
framework/db/Query.php
View file @
06feccff
...
...
@@ -35,9 +35,19 @@ namespace yii\db;
class
Query
extends
\yii\base\Component
{
/**
* @var string|array the columns being selected. This refers to the SELECT clause in a SQL
* statement. It can be either a string (e.g. `'id, name'`) or an array (e.g. `array('id', 'name')`).
* If not set, if means all columns.
* Sort ascending
* @see orderBy
*/
const
SORT_ASC
=
false
;
/**
* Sort ascending
* @see orderBy
*/
const
SORT_DESC
=
true
;
/**
* @var array the columns being selected. For example, `array('id', 'name')`.
* This is used to construct the SELECT clause in a SQL statement. If not set, if means selecting all columns.
* @see select()
*/
public
$select
;
...
...
@@ -52,8 +62,8 @@ class Query extends \yii\base\Component
*/
public
$distinct
;
/**
* @var
string|array the table(s) to be selected from. This refers to the FROM clause in a SQL statement
.
*
It can be either a string (e.g. `'tbl_user, tbl_post'`) or an array (e.g. `array('tbl_user', 'tbl_post')`)
.
* @var
array the table(s) to be selected from. For example, `array('tbl_user', 'tbl_post')`
.
*
This is used to construct the FROM clause in a SQL statement
.
* @see from()
*/
public
$from
;
...
...
@@ -73,20 +83,33 @@ class Query extends \yii\base\Component
*/
public
$offset
;
/**
* @var string|array how to sort the query results. This refers to the ORDER BY clause in a SQL statement.
* It can be either a string (e.g. `'id ASC, name DESC'`) or an array (e.g. `array('id ASC', 'name DESC')`).
* @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
* The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
* can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects.
* If that is the case, the expressions will be converted into strings without any change.
*/
public
$orderBy
;
/**
* @var
string|array how to group the query results. This refers to the GROUP BY clause in a SQL statement
.
*
It can be either a string (e.g. `'company, department'`) or an array (e.g. `array('company', 'department')`)
.
* @var
array how to group the query results. For example, `array('company', 'department')`
.
*
This is used to construct the GROUP BY clause in a SQL statement
.
*/
public
$groupBy
;
/**
* @var string|array how to join with other tables. This refers to the JOIN clause in a SQL statement.
* It can be either a string (e.g. `'LEFT JOIN tbl_user ON tbl_user.id=author_id'`) or an array (e.g.
* `array('LEFT JOIN tbl_user ON tbl_user.id=author_id', 'LEFT JOIN tbl_team ON tbl_team.id=team_id')`).
* @see join()
* @var array how to join with other tables. Each array element represents the specification
* of one join which has the following structure:
*
* ~~~
* array($joinType, $tableName, $joinCondition)
* ~~~
*
* For example,
*
* ~~~
* array(
* array('INNER JOIN', 'tbl_user', 'tbl_user.id = author_id'),
* array('LEFT JOIN', 'tbl_team', 'tbl_team.id = team_id'),
* )
* ~~~
*/
public
$join
;
/**
...
...
@@ -95,9 +118,8 @@ class Query extends \yii\base\Component
*/
public
$having
;
/**
* @var string|Query[] the UNION clause(s) in a SQL statement. This can be either a string
* representing a single UNION clause or an array representing multiple UNION clauses.
* Each union clause can be a string or a `Query` object which refers to the SQL statement.
* @var array this is used to construct the UNION clause(s) in a SQL statement.
* Each array element can be either a string or a [[Query]] object representing a sub-query.
*/
public
$union
;
/**
...
...
@@ -134,6 +156,9 @@ class Query extends \yii\base\Component
*/
public
function
select
(
$columns
,
$option
=
null
)
{
if
(
!
is_array
(
$columns
))
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
select
=
$columns
;
$this
->
selectOption
=
$option
;
return
$this
;
...
...
@@ -161,6 +186,9 @@ class Query extends \yii\base\Component
*/
public
function
from
(
$tables
)
{
if
(
!
is_array
(
$tables
))
{
$tables
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$tables
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
from
=
$tables
;
return
$this
;
}
...
...
@@ -360,10 +388,13 @@ class Query extends \yii\base\Component
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see addGroup()
* @see addGroup
By
()
*/
public
function
groupBy
(
$columns
)
{
if
(
!
is_array
(
$columns
))
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
groupBy
=
$columns
;
return
$this
;
}
...
...
@@ -375,19 +406,16 @@ class Query extends \yii\base\Component
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see group()
* @see group
By
()
*/
public
function
addGroup
(
$columns
)
public
function
addGroup
By
(
$columns
)
{
if
(
empty
(
$this
->
groupBy
))
{
if
(
!
is_array
(
$columns
))
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
if
(
$this
->
groupBy
===
null
)
{
$this
->
groupBy
=
$columns
;
}
else
{
if
(
!
is_array
(
$this
->
groupBy
))
{
$this
->
groupBy
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$this
->
groupBy
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
if
(
!
is_array
(
$columns
))
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
groupBy
=
array_merge
(
$this
->
groupBy
,
$columns
);
}
return
$this
;
...
...
@@ -454,43 +482,58 @@ class Query extends \yii\base\Component
/**
* Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')).
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see addOrder()
* @see addOrder
By
()
*/
public
function
orderBy
(
$columns
)
{
$this
->
orderBy
=
$
columns
;
$this
->
orderBy
=
$
this
->
normalizeOrderBy
(
$columns
)
;
return
$this
;
}
/**
* Adds additional ORDER BY columns to the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')).
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see order()
* @see order
By
()
*/
public
function
addOrderBy
(
$columns
)
{
if
(
empty
(
$this
->
orderBy
))
{
$columns
=
$this
->
normalizeOrderBy
(
$columns
);
if
(
$this
->
orderBy
===
null
)
{
$this
->
orderBy
=
$columns
;
}
else
{
if
(
!
is_array
(
$this
->
orderBy
))
{
$this
->
orderBy
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$this
->
orderBy
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
if
(
!
is_array
(
$columns
))
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
orderBy
=
array_merge
(
$this
->
orderBy
,
$columns
);
}
return
$this
;
}
protected
function
normalizeOrderBy
(
$columns
)
{
if
(
is_array
(
$columns
))
{
return
$columns
;
}
else
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
$result
=
array
();
foreach
(
$columns
as
$column
)
{
if
(
preg_match
(
'/^(.*?)\s+(asc|desc)$/i'
,
$column
,
$matches
))
{
$result
[
$matches
[
1
]]
=
strcasecmp
(
$matches
[
2
],
'desc'
)
?
self
::
SORT_ASC
:
self
::
SORT_DESC
;
}
else
{
$result
[
$column
]
=
self
::
SORT_ASC
;
}
}
return
$result
;
}
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit
...
...
framework/db/QueryBuilder.php
View file @
06feccff
...
...
@@ -60,10 +60,10 @@ class QueryBuilder extends \yii\base\Object
$this
->
buildFrom
(
$query
->
from
),
$this
->
buildJoin
(
$query
->
join
),
$this
->
buildWhere
(
$query
->
where
),
$this
->
buildGroup
(
$query
->
groupBy
),
$this
->
buildGroup
By
(
$query
->
groupBy
),
$this
->
buildHaving
(
$query
->
having
),
$this
->
buildUnion
(
$query
->
union
),
$this
->
buildOrder
(
$query
->
orderBy
),
$this
->
buildOrder
By
(
$query
->
orderBy
),
$this
->
buildLimit
(
$query
->
limit
,
$query
->
offset
),
);
return
implode
(
$this
->
separator
,
array_filter
(
$clauses
));
...
...
@@ -673,7 +673,7 @@ class QueryBuilder extends \yii\base\Object
}
/**
* @param
string|
array $columns
* @param array $columns
* @param boolean $distinct
* @param string $selectOption
* @return string the SELECT clause built from [[query]].
...
...
@@ -689,13 +689,6 @@ class QueryBuilder extends \yii\base\Object
return
$select
.
' *'
;
}
if
(
!
is_array
(
$columns
))
{
if
(
strpos
(
$columns
,
'('
)
!==
false
)
{
return
$select
.
' '
.
$columns
;
}
else
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
}
foreach
(
$columns
as
$i
=>
$column
)
{
if
(
is_object
(
$column
))
{
$columns
[
$i
]
=
(
string
)
$column
;
...
...
@@ -716,7 +709,7 @@ class QueryBuilder extends \yii\base\Object
}
/**
* @param
string|
array $tables
* @param array $tables
* @return string the FROM clause built from [[query]].
*/
public
function
buildFrom
(
$tables
)
...
...
@@ -725,13 +718,6 @@ class QueryBuilder extends \yii\base\Object
return
''
;
}
if
(
!
is_array
(
$tables
))
{
if
(
strpos
(
$tables
,
'('
)
!==
false
)
{
return
'FROM '
.
$tables
;
}
else
{
$tables
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$tables
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
}
foreach
(
$tables
as
$i
=>
$table
)
{
if
(
strpos
(
$table
,
'('
)
===
false
)
{
if
(
preg_match
(
'/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i'
,
$table
,
$matches
))
{
// with alias
...
...
@@ -752,37 +738,36 @@ class QueryBuilder extends \yii\base\Object
/**
* @param string|array $joins
* @return string the JOIN clause built from [[query]].
* @throws Exception if the $joins parameter is not in proper format
*/
public
function
buildJoin
(
$joins
)
{
if
(
empty
(
$joins
))
{
return
''
;
}
if
(
is_string
(
$joins
))
{
return
$joins
;
}
foreach
(
$joins
as
$i
=>
$join
)
{
if
(
is_array
(
$join
))
{
// 0:join type, 1:table name, 2:on-condition
if
(
isset
(
$join
[
0
],
$join
[
1
]))
{
$table
=
$join
[
1
];
if
(
strpos
(
$table
,
'('
)
===
false
)
{
if
(
preg_match
(
'/^(.*?)(?i:\s+as\s+|\s+)(.*)$/'
,
$table
,
$matches
))
{
// with alias
$table
=
$this
->
db
->
quoteTableName
(
$matches
[
1
])
.
' '
.
$this
->
db
->
quoteTableName
(
$matches
[
2
]);
}
else
{
$table
=
$this
->
db
->
quoteTableName
(
$table
);
}
if
(
is_object
(
$join
))
{
$joins
[
$i
]
=
(
string
)
$join
;
}
elseif
(
is_array
(
$join
)
&&
isset
(
$join
[
0
],
$join
[
1
]))
{
// 0:join type, 1:table name, 2:on-condition
$table
=
$join
[
1
];
if
(
strpos
(
$table
,
'('
)
===
false
)
{
if
(
preg_match
(
'/^(.*?)(?i:\s+as\s+|\s+)(.*)$/'
,
$table
,
$matches
))
{
// with alias
$table
=
$this
->
db
->
quoteTableName
(
$matches
[
1
])
.
' '
.
$this
->
db
->
quoteTableName
(
$matches
[
2
]);
}
else
{
$table
=
$this
->
db
->
quoteTableName
(
$table
);
}
$joins
[
$i
]
=
$join
[
0
]
.
' '
.
$table
;
if
(
isset
(
$join
[
2
]))
{
$condition
=
$this
->
buildCondition
(
$join
[
2
]);
if
(
$condition
!==
''
)
{
$joins
[
$i
]
.=
' ON '
.
$this
->
buildCondition
(
$join
[
2
]);
}
}
$joins
[
$i
]
=
$join
[
0
]
.
' '
.
$table
;
if
(
isset
(
$join
[
2
]))
{
$condition
=
$this
->
buildCondition
(
$join
[
2
]);
if
(
$condition
!==
''
)
{
$joins
[
$i
]
.=
' ON '
.
$this
->
buildCondition
(
$join
[
2
]);
}
}
else
{
throw
new
Exception
(
'A join clause must be specified as an array of at least two elements.'
);
}
}
else
{
throw
new
Exception
(
'A join clause must be specified as an array of join type, join table, and optionally join condition.'
);
}
}
...
...
@@ -800,16 +785,12 @@ class QueryBuilder extends \yii\base\Object
}
/**
* @param
string|
array $columns
* @param array $columns
* @return string the GROUP BY clause
*/
public
function
buildGroup
(
$columns
)
public
function
buildGroup
By
(
$columns
)
{
if
(
empty
(
$columns
))
{
return
''
;
}
else
{
return
'GROUP BY '
.
$this
->
buildColumns
(
$columns
);
}
return
empty
(
$columns
)
?
''
:
'GROUP BY '
.
$this
->
buildColumns
(
$columns
);
}
/**
...
...
@@ -823,36 +804,24 @@ class QueryBuilder extends \yii\base\Object
}
/**
* @param
string|
array $columns
* @param array $columns
* @return string the ORDER BY clause built from [[query]].
*/
public
function
buildOrder
(
$columns
)
public
function
buildOrder
By
(
$columns
)
{
if
(
empty
(
$columns
))
{
return
''
;
}
if
(
!
is_array
(
$columns
))
{
if
(
strpos
(
$columns
,
'('
)
!==
false
)
{
return
'ORDER BY '
.
$columns
;
$orders
=
array
();
foreach
(
$columns
as
$name
=>
$direction
)
{
if
(
is_object
(
$direction
))
{
$orders
[]
=
(
string
)
$direction
;
}
else
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
}
foreach
(
$columns
as
$i
=>
$column
)
{
if
(
is_object
(
$column
))
{
$columns
[
$i
]
=
(
string
)
$column
;
}
elseif
(
strpos
(
$column
,
'('
)
===
false
)
{
if
(
preg_match
(
'/^(.*?)\s+(asc|desc)$/i'
,
$column
,
$matches
))
{
$columns
[
$i
]
=
$this
->
db
->
quoteColumnName
(
$matches
[
1
])
.
' '
.
$matches
[
2
];
}
else
{
$columns
[
$i
]
=
$this
->
db
->
quoteColumnName
(
$column
);
}
$orders
[]
=
$this
->
db
->
quoteColumnName
(
$name
)
.
(
$direction
===
Query
::
SORT_DESC
?
' DESC'
:
''
);
}
}
if
(
is_array
(
$columns
))
{
$columns
=
implode
(
', '
,
$columns
);
}
return
'ORDER BY '
.
$columns
;
return
'ORDER BY '
.
implode
(
', '
,
$orders
);
}
/**
...
...
@@ -873,7 +842,7 @@ class QueryBuilder extends \yii\base\Object
}
/**
* @param
string|
array $unions
* @param array $unions
* @return string the UNION clause built from [[query]].
*/
public
function
buildUnion
(
$unions
)
...
...
@@ -881,9 +850,6 @@ class QueryBuilder extends \yii\base\Object
if
(
empty
(
$unions
))
{
return
''
;
}
if
(
!
is_array
(
$unions
))
{
$unions
=
array
(
$unions
);
}
foreach
(
$unions
as
$i
=>
$union
)
{
if
(
$union
instanceof
Query
)
{
$unions
[
$i
]
=
$this
->
build
(
$union
);
...
...
framework/web/Pagination.php
View file @
06feccff
...
...
@@ -65,7 +65,8 @@ use Yii;
class
Pagination
extends
\yii\base\Object
{
/**
* @var string name of the GET variable storing the current page index. Defaults to 'page'.
* @var string name of the parameter storing the current page index. Defaults to 'page'.
* @see params
*/
public
$pageVar
=
'page'
;
/**
...
...
framework/web/Sort.php
View file @
06feccff
...
...
@@ -8,7 +8,6 @@
namespace
yii\web
;
use
Yii
;
use
yii\util\StringHelper
;
use
yii\util\Html
;
/**
...
...
@@ -18,38 +17,24 @@ use yii\util\Html;
* we can use Sort to represent the sorting information and generate
* appropriate hyperlinks that can lead to sort actions.
*
* Sort is designed to be used together with {@link CActiveRecord}.
* When creating a Sort instance, you need to specify {@link modelClass}.
* You can use Sort to generate hyperlinks by calling {@link link}.
* You can also use Sort to modify a {@link CDbCriteria} instance by calling {@link applyOrder} so that
* it can cause the query results to be sorted according to the specified
* attributes.
*
* In order to prevent SQL injection attacks, Sort ensures that only valid model attributes
* can be sorted. This is determined based on {@link modelClass} and {@link attributes}.
* When {@link attributes} is not set, all attributes belonging to {@link modelClass}
* can be sorted. When {@link attributes} is set, only those attributes declared in the property
* can be sorted.
*
* By configuring {@link attributes}, one can perform more complex sorts that may
* consist of things like compound attributes (e.g. sort based on the combination of
* first name and last name of users).
*
* The property {@link attributes} should be an array of key-value pairs, where the keys
* represent the attribute names, while the values represent the virtual attribute definitions.
* For more details, please check the documentation about {@link attributes}.
*
* * Controller action:
* A typical usage example is as follows,
*
* ~~~
* function actionIndex()
* {
* $sort = new Sort(array(
* 'attributes' => Article::attributes(),
* 'attributes' => array(
* 'age',
* 'name' => array(
* 'asc' => array('last_name', 'first_name'),
* 'desc' => array('last_name' => true, 'first_name' => true),
* ),
* ),
* ));
*
* $models = Article::find()
* ->where(array('status' => 1))
* ->orderBy($sort->order
By
)
* ->orderBy($sort->order
s
)
* ->all();
*
* $this->render('index', array(
...
...
@@ -62,21 +47,23 @@ use yii\util\Html;
* View:
*
* ~~~
* // display links leading to sort actions
* echo $sort->link('name', 'Name') . ' | ' . $sort->link('age', 'Age');
*
* foreach($models as $model) {
* // display $model here
* }
*
* // display pagination
* $this->widget('yii\web\widgets\LinkPager', array(
* 'pages' => $pages,
* ));
* ~~~
*
* @property string $orderBy The order-by columns represented by this sort object.
* This can be put in the ORDER BY clause of a SQL statement.
* @property array $directions Sort directions indexed by attribute names.
* The sort direction. Can be either Sort::SORT_ASC for ascending order or
* Sort::SORT_DESC for descending order.
* In the above, we declare two [[attributes]] that support sorting: name and age.
* We pass the sort information to the Article query so that the query results are
* 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
...
...
@@ -86,12 +73,12 @@ class Sort extends \yii\base\Object
/**
* Sort ascending
*/
const
SORT_
ASC
=
false
;
const
ASC
=
false
;
/**
* Sort descending
*/
const
SORT_
DESC
=
true
;
const
DESC
=
true
;
/**
* @var boolean whether the sorting can be applied to multiple attributes simultaneously.
...
...
@@ -100,111 +87,66 @@ class Sort extends \yii\base\Object
public
$enableMultiSort
=
false
;
/**
* @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' of the model {@link modelClass} can be sorted.
* By default, this property is an empty array, which means all attributes in
* {@link modelClass} are allowed to be sorted.
*
* This property can also be used to specify complex sorting. To do so,
* a virtual attribute can be declared in terms of a key-value pair in the array.
* The key refers to the name of the virtual attribute that may appear in the sort request,
* while the value specifies the definition of the virtual attribute.
* @var array list of attributes that are allowed to be sorted. Its syntax can be
* described using the following example:
*
* In the simple case, a key-value pair can be like <code>'user'=>'user_id'</code>
* where 'user' is the name of the virtual attribute while 'user_id' means the virtual
* attribute is the 'user_id' attribute in the {@link modelClass}.
*
* A more flexible way is to specify the key-value pair as
* <pre>
* 'user'=>array(
* 'asc'=>'first_name, last_name',
* 'desc'=>'first_name DESC, last_name DESC',
* 'label'=>'Name'
* )
* </pre>
* where 'user' is the name of the virtual attribute that specifies the full name of user
* (a compound attribute consisting of first name and last name of user). In this case,
* we have to use an array to define the virtual attribute with three elements: 'asc',
* 'desc' and 'label'.
*
* The above approach can also be used to declare virtual attributes that consist of relational
* attributes. For example,
* <pre>
* 'price'=>array(
* 'asc'=>'item.price',
* 'desc'=>'item.price DESC',
* 'label'=>'Item Price'
* ~~~
* array(
* 'age',
* 'user' => array(
* 'asc' => array('first_name' => Sort::ASC, 'last_name' => Sort::ASC),
* 'desc' => array('first_name' => Sort::DESC, 'last_name' => Sort::DESC),
* 'default' => 'desc',
* ),
* )
*
</pre>
*
~~~
*
*
Note, the attribute name should not contain '-' or '.' characters because
*
they are used as {@link separators}.
*
In the above, two attributes are declared: "age" and "user". The "age" attribute is
*
a simple attribute which is equivalent to the following:
*
* Starting from version 1.1.3, an additional option named 'default' can be used in the virtual attribute
* declaration. This option specifies whether an attribute should be sorted in ascending or descending
* order upon user clicking the corresponding sort hyperlink if it is not currently sorted. The valid
* option values include 'asc' (default) and 'desc'. For example,
* <pre>
* 'price'=>array(
* 'asc'=>'item.price',
* 'desc'=>'item.price DESC',
* 'label'=>'Item Price',
* 'default'=>'desc',
* ~~~
* 'age' => array(
* 'asc' => array('age' => Sort::ASC),
* 'desc' => array('age' => Sort::DESC),
* )
*
</pre>
*
~~~
*
* Also starting from version 1.1.3, you can include a star ('*') element in this property so that
* all model attributes are available for sorting, in addition to those virtual attributes. For example,
* <pre>
* 'attributes'=>array(
* 'price'=>array(
* 'asc'=>'item.price',
* 'desc'=>'item.price DESC',
* 'label'=>'Item Price',
* 'default'=>'desc',
* ),
* '*',
* )
* </pre>
* Note that when a name appears as both a model attribute and a virtual attribute, the position of
* the star element in the array determines which one takes precedence. In particular, if the star
* element is the first element in the array, the model attribute takes precedence; and if the star
* element is the last one, the virtual attribute takes precedence.
* The "user" attribute is a composite attribute:
*
* - The "user" key represents the attribute name which will appear in the URLs leading
* to sort actions. Attribute names cannot contain characters listed in [[separators]].
* - The "asc" and "desc" elements specify how to sort by the attribute in ascending
* and descending orders, respectively. Their values represent the actual columns and
* the directions by which the data should be sorted by.
* - And the "default" element specifies if the attribute is not sorted currently,
* in which direction it should be sorted (the default value is ascending order).
*/
public
$attributes
=
array
();
/**
* @var string the name of the
GET
parameter that specifies which attributes to be sorted
* @var string the name of the parameter that specifies which attributes to be sorted
* in which direction. Defaults to 'sort'.
* @see params
*/
public
$sortVar
=
'sort'
;
/**
* @var string the tag appeared in the
GET
parameter that indicates the attribute should be sorted
* @var string the tag appeared in the
[[sortVar]]
parameter that indicates the attribute should be sorted
* in descending order. Defaults to 'desc'.
*/
public
$descTag
=
'desc'
;
/**
* @var mixed the default order that should be applied to the query criteria when
* the current request does not specify any sort. For example, 'name, create_time DESC' or
* 'UPPER(name)'.
* @var array the order that should be used when the current request does not specify any order.
* The array keys are attribute names and the array values are the corresponding sort directions. For example,
*
* Starting from version 1.1.3, you can also specify the default order using an array.
* The array keys could be attribute names or virtual attribute names as declared in {@link attributes},
* and the array values indicate whether the sorting of the corresponding attributes should
* be in descending order. For example,
* <pre>
* 'defaultOrder'=>array(
* 'price'=>Sort::SORT_DESC,
* ~~~
* array(
* 'name' => Sort::ASC,
* 'create_time' => Sort::DESC,
* )
* </pre>
* `SORT_DESC` and `SORT_ASC` are available since 1.1.10. In earlier Yii versions you should use
* `true` and `false` respectively.
* ~~~
*
* Please note when using array to specify the default order, the corresponding attributes
* will be put into {@link directions} and thus affect how the sort links are rendered
* (e.g. an arrow may be displayed next to the currently active sort link).
* @see attributeOrders
*/
public
$default
Order
;
public
$default
s
;
/**
* @var string the route of the controller action for displaying the sorted contents.
* If not set, it means using the currently requested route.
...
...
@@ -218,61 +160,52 @@ class Sort extends \yii\base\Object
*/
public
$separators
=
array
(
'-'
,
'.'
);
/**
* @var array parameters (name
=>
value) that should be used to obtain the current sort directions
* @var array parameters (name
=>
value) that should be used to obtain the current sort directions
* and to create new sort URLs. If not set, $_GET will be used instead.
*
* The array element indexed by [[sortVar]] is considered to be the current sort directions.
* If the element does not exist, the [[defaultOrder]] will be used.
* If the element does not exist, the [[defaults|default order]] will be used.
*
* @see sortVar
* @see defaults
*/
public
$params
;
private
$_directions
;
/**
* @return string the order-by columns represented by this sort object.
* This can be put in the ORDER BY clause of a SQL statement.
* Returns the columns and their corresponding sort directions.
* @return array the columns (keys) and their corresponding sort directions (values).
* This can be passed to [[\yii\db\Query::orderBy()]] to construct a DB query.
*/
public
function
getOrder
By
()
public
function
getOrder
s
()
{
$directions
=
$this
->
getDirections
();
if
(
empty
(
$directions
))
{
return
is_string
(
$this
->
defaultOrder
)
?
$this
->
defaultOrder
:
''
;
}
else
{
$orders
=
array
();
foreach
(
$directions
as
$attribute
=>
$descending
)
{
$definition
=
$this
->
getDefinition
(
$attribute
);
if
(
$descending
)
{
$orders
[]
=
isset
(
$definition
[
'desc'
])
?
$definition
[
'desc'
]
:
$attribute
.
' DESC'
;
}
else
{
$orders
[]
=
isset
(
$definition
[
'asc'
])
?
$definition
[
'asc'
]
:
$attribute
;
}
$attributeOrders
=
$this
->
getAttributeOrders
();
$orders
=
array
();
foreach
(
$attributeOrders
as
$attribute
=>
$direction
)
{
$definition
=
$this
->
getAttribute
(
$attribute
);
$columns
=
$definition
[
$direction
===
self
::
ASC
?
'asc'
:
'desc'
];
foreach
(
$columns
as
$name
=>
$dir
)
{
$orders
[
$name
]
=
$dir
;
}
return
implode
(
', '
,
$orders
);
}
return
$orders
;
}
/**
* Generates a hyperlink that can be clicked to cause sorting.
* @param string $attribute 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 $label the link label. If null, the label will be determined according
* to the attribute (see {@link resolveLabel}).
* Generates a hyperlink that links to the sort action to sort by the specified attribute.
* Based on the sort direction, the CSS class of the generated hyperlink will be appended
* with "asc" or "desc".
* @param string $attribute the attribute name by which the data should be sorted by.
* @param string $label the link label. Note that the label will not be HTML-encoded.
* @param array $htmlOptions additional HTML attributes for the hyperlink tag
* @return string the generated hyperlink
*/
public
function
link
(
$attribute
,
$label
=
null
,
$htmlOptions
=
array
())
public
function
link
(
$attribute
,
$label
,
$htmlOptions
=
array
())
{
if
((
$definition
=
$this
->
getDefinition
(
$attribute
))
===
false
)
{
return
false
;
}
if
(
$label
===
null
)
{
$label
=
isset
(
$definition
[
'label'
])
?
$definition
[
'label'
]
:
StringHelper
::
camel2words
(
$attribute
);
if
((
$definition
=
$this
->
getAttribute
(
$attribute
))
===
false
)
{
return
$label
;
}
if
((
$direction
=
$this
->
get
Direction
(
$attribute
))
!==
null
)
{
if
((
$direction
=
$this
->
get
AttributeOrder
(
$attribute
))
!==
null
)
{
$class
=
$direction
?
'desc'
:
'asc'
;
if
(
isset
(
$htmlOptions
[
'class'
]))
{
$htmlOptions
[
'class'
]
.=
' '
.
$class
;
...
...
@@ -286,17 +219,19 @@ class Sort extends \yii\base\Object
return
Html
::
link
(
$label
,
$url
,
$htmlOptions
);
}
private
$_attributeOrders
;
/**
* Returns the currently requested sort information.
* @param boolean $recalculate whether to recalculate the sort directions
* @return array sort directions indexed by attribute names.
* Sort direction can be either
Sort::SORT_ASC
for ascending order or
*
Sort::SORT_DESC
for descending order.
* Sort direction can be either
[[Sort::ASC]]
for ascending order or
*
[[Sort::DESC]]
for descending order.
*/
public
function
get
Direction
s
(
$recalculate
=
false
)
public
function
get
AttributeOrder
s
(
$recalculate
=
false
)
{
if
(
$this
->
_
direction
s
===
null
||
$recalculate
)
{
$this
->
_
direction
s
=
array
();
if
(
$this
->
_
attributeOrder
s
===
null
||
$recalculate
)
{
$this
->
_
attributeOrder
s
=
array
();
$params
=
$this
->
params
===
null
?
$_GET
:
$this
->
params
;
if
(
isset
(
$params
[
$this
->
sortVar
])
&&
is_scalar
(
$params
[
$this
->
sortVar
]))
{
$attributes
=
explode
(
$this
->
separators
[
0
],
$params
[
$this
->
sortVar
]);
...
...
@@ -308,49 +243,50 @@ class Sort extends \yii\base\Object
}
}
if
((
$this
->
get
Definition
(
$attribute
))
!==
false
)
{
$this
->
_
direction
s
[
$attribute
]
=
$descending
;
if
((
$this
->
get
Attribute
(
$attribute
))
!==
false
)
{
$this
->
_
attributeOrder
s
[
$attribute
]
=
$descending
;
if
(
!
$this
->
enableMultiSort
)
{
return
$this
->
_
direction
s
;
return
$this
->
_
attributeOrder
s
;
}
}
}
}
if
(
$this
->
_
directions
===
array
()
&&
is_array
(
$this
->
defaultOrder
))
{
$this
->
_
directions
=
$this
->
defaultOrder
;
if
(
$this
->
_
attributeOrders
===
array
()
&&
is_array
(
$this
->
defaults
))
{
$this
->
_
attributeOrders
=
$this
->
defaults
;
}
}
return
$this
->
_
direction
s
;
return
$this
->
_
attributeOrder
s
;
}
/**
* Returns the sort direction of the specified attribute in the current request.
* @param string $attribute the attribute name
* @return boolean|null Sort direction of the attribute. Can be either
Sort::SORT_ASC
* for ascending order or
Sort::SORT_DESC for descending order. Value is null
* if the attribute does not need to be sorted.
* @return boolean|null Sort direction of the attribute. Can be either
[[Sort::ASC]]
* for ascending order or
[[Sort::DESC]] for descending order. Null is returned
* if the attribute
is invalid or
does not need to be sorted.
*/
public
function
get
Direction
(
$attribute
)
public
function
get
AttributeOrder
(
$attribute
)
{
$this
->
get
Direction
s
();
return
isset
(
$this
->
_
directions
[
$attribute
])
?
$this
->
_direction
s
[
$attribute
]
:
null
;
$this
->
get
AttributeOrder
s
();
return
isset
(
$this
->
_
attributeOrders
[
$attribute
])
?
$this
->
_attributeOrder
s
[
$attribute
]
:
null
;
}
/**
* Creates a URL for sorting the data by the specified attribute.
* This method will consider the current sorting status given by [[
direction
s]].
* This method will consider the current sorting status given by [[
attributeOrder
s]].
* For example, if the current page already sorts the data by the specified attribute in ascending order,
* then the URL created will lead to a page that sorts the data by the specified attribute in descending order.
* @param string $attribute the attribute name
* @return string|boolean the URL for sorting. False if the attribute is invalid.
* @see attributeOrders
* @see params
*/
public
function
createUrl
(
$attribute
)
{
if
((
$definition
=
$this
->
get
Definition
(
$attribute
))
===
false
)
{
if
((
$definition
=
$this
->
get
Attribute
(
$attribute
))
===
false
)
{
return
false
;
}
$directions
=
$this
->
get
Direction
s
();
$directions
=
$this
->
get
AttributeOrder
s
();
if
(
isset
(
$directions
[
$attribute
]))
{
$descending
=
!
$directions
[
$attribute
];
unset
(
$directions
[
$attribute
]);
...
...
@@ -378,28 +314,20 @@ class Sort extends \yii\base\Object
}
/**
* Returns the real definition of an attribute given its name.
*
* The resolution is based on {@link attributes} and {@link CActiveRecord::attributeNames}.
* <ul>
* <li>When {@link attributes} is an empty array, if the name refers to an attribute of {@link modelClass},
* then the name is returned back.</li>
* <li>When {@link attributes} is not empty, if the name refers to an attribute declared in {@link attributes},
* then the corresponding virtual attribute definition is returned. Starting from version 1.1.3, if {@link attributes}
* contains a star ('*') element, the name will also be used to match against all model attributes.</li>
* <li>In all other cases, false is returned, meaning the name does not refer to a valid attribute.</li>
* </ul>
* @param string $attribute the attribute name that the user requests to sort on
* @return mixed the attribute name or the virtual attribute definition. False if the attribute cannot be sorted.
* Returns the attribute definition of the specified name.
* @param string $name the attribute name
* @return array|boolean the sort definition (column names => sort directions).
* False is returned if the attribute cannot be sorted.
* @see attributes
*/
public
function
get
Definition
(
$attribut
e
)
public
function
get
Attribute
(
$nam
e
)
{
if
(
isset
(
$this
->
attributes
[
$
attribut
e
]))
{
return
$this
->
attributes
[
$
attribut
e
];
}
elseif
(
in_array
(
$
attribut
e
,
$this
->
attributes
,
true
))
{
if
(
isset
(
$this
->
attributes
[
$
nam
e
]))
{
return
$this
->
attributes
[
$
nam
e
];
}
elseif
(
in_array
(
$
nam
e
,
$this
->
attributes
,
true
))
{
return
array
(
'asc'
=>
$attribute
,
'desc'
=>
"
$attribute
DESC"
,
'asc'
=>
array
(
$name
=>
self
::
ASC
)
,
'desc'
=>
array
(
$name
=>
self
::
DESC
)
,
);
}
else
{
return
false
;
...
...
tests/unit/framework/db/QueryTest.php
View file @
06feccff
...
...
@@ -14,13 +14,13 @@ class QueryTest extends \yiiunit\MysqlTestCase
// default
$query
=
new
Query
;
$query
->
select
(
'*'
);
$this
->
assertEquals
(
'*'
,
$query
->
select
);
$this
->
assertEquals
(
array
(
'*'
)
,
$query
->
select
);
$this
->
assertNull
(
$query
->
distinct
);
$this
->
assertEquals
(
null
,
$query
->
selectOption
);
$query
=
new
Query
;
$query
->
select
(
'id, name'
,
'something'
)
->
distinct
(
true
);
$this
->
assertEquals
(
'id, name'
,
$query
->
select
);
$this
->
assertEquals
(
array
(
'id'
,
'name'
)
,
$query
->
select
);
$this
->
assertTrue
(
$query
->
distinct
);
$this
->
assertEquals
(
'something'
,
$query
->
selectOption
);
}
...
...
@@ -29,7 +29,7 @@ class QueryTest extends \yiiunit\MysqlTestCase
{
$query
=
new
Query
;
$query
->
from
(
'tbl_user'
);
$this
->
assertEquals
(
'tbl_user'
,
$query
->
from
);
$this
->
assertEquals
(
array
(
'tbl_user'
)
,
$query
->
from
);
}
function
testWhere
()
...
...
@@ -57,12 +57,12 @@ class QueryTest extends \yiiunit\MysqlTestCase
{
$query
=
new
Query
;
$query
->
groupBy
(
'team'
);
$this
->
assertEquals
(
'team'
,
$query
->
groupBy
);
$this
->
assertEquals
(
array
(
'team'
)
,
$query
->
groupBy
);
$query
->
addGroup
(
'company'
);
$query
->
addGroup
By
(
'company'
);
$this
->
assertEquals
(
array
(
'team'
,
'company'
),
$query
->
groupBy
);
$query
->
addGroup
(
'age'
);
$query
->
addGroup
By
(
'age'
);
$this
->
assertEquals
(
array
(
'team'
,
'company'
,
'age'
),
$query
->
groupBy
);
}
...
...
@@ -86,13 +86,19 @@ class QueryTest extends \yiiunit\MysqlTestCase
{
$query
=
new
Query
;
$query
->
orderBy
(
'team'
);
$this
->
assertEquals
(
'team'
,
$query
->
orderBy
);
$this
->
assertEquals
(
array
(
'team'
=>
false
)
,
$query
->
orderBy
);
$query
->
addOrderBy
(
'company'
);
$this
->
assertEquals
(
array
(
'team'
,
'company'
),
$query
->
orderBy
);
$this
->
assertEquals
(
array
(
'team'
=>
false
,
'company'
=>
false
),
$query
->
orderBy
);
$query
->
addOrderBy
(
'age'
);
$this
->
assertEquals
(
array
(
'team'
,
'company'
,
'age'
),
$query
->
orderBy
);
$this
->
assertEquals
(
array
(
'team'
=>
false
,
'company'
=>
false
,
'age'
=>
false
),
$query
->
orderBy
);
$query
->
addOrderBy
(
array
(
'age'
=>
true
));
$this
->
assertEquals
(
array
(
'team'
=>
false
,
'company'
=>
false
,
'age'
=>
true
),
$query
->
orderBy
);
$query
->
addOrderBy
(
'age ASC, company DESC'
);
$this
->
assertEquals
(
array
(
'team'
=>
false
,
'company'
=>
true
,
'age'
=>
false
),
$query
->
orderBy
);
}
function
testLimitOffset
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment