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
3210a217
Commit
3210a217
authored
Mar 01, 2012
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
...
parent
965fe5c4
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
123 additions
and
871 deletions
+123
-871
Component.php
framework/base/Component.php
+1
-1
ActiveFinder.php
framework/db/ar/ActiveFinder.php
+62
-656
ActiveQuery.php
framework/db/ar/ActiveQuery.php
+26
-172
ActiveRecord.php
framework/db/ar/ActiveRecord.php
+7
-8
ActiveRelation.php
framework/db/ar/ActiveRelation.php
+7
-2
ActiveRecordTest.php
tests/unit/framework/db/ar/ActiveRecordTest.php
+20
-32
No files found.
framework/base/Component.php
View file @
3210a217
...
...
@@ -69,7 +69,7 @@ namespace yii\base;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class
Component
extends
Object
class
Component
extends
\yii\base\
Object
{
/**
* @var Vector[] the attached event handlers (event name => handlers)
...
...
framework/db/ar/ActiveFinder.php
View file @
3210a217
...
...
@@ -31,686 +31,92 @@ use yii\db\Exception;
* todo: scope
* todo: test via option
* todo: count, sum, exists
todo: inner join with one or multiple relations as filters
joinType should default to inner join in this case
*
* @property integer $count
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class
ActiveFinder
extends
\yii\base\Object
implements
\IteratorAggregate
,
\ArrayAccess
,
\Countable
class
ActiveFinder
extends
\yii\base\Object
{
/**
* @var
string the name of the ActiveRecord class.
* @var
\yii\db\dao\Connection
*/
public
$modelClass
;
/**
* @var Query the Query object
*/
public
$query
;
/**
* @var array list of relations that this query should be performed with
*/
public
$with
;
/**
* @var string the table alias to be used for query
*/
public
$tableAlias
;
/**
* @var string the name of the column that the result should be indexed by
*/
public
$indexBy
;
/**
* @var boolean whether to return query results as arrays
*/
public
$asArray
;
/**
* @var array list of scopes that should be applied to this query
*/
public
$scopes
;
/**
* @var array list of query results
*/
public
$records
;
public
$sql
;
/**
* @param string $modelClass the name of the ActiveRecord class.
*/
public
function
__construct
(
$modelClass
)
{
$this
->
modelClass
=
$modelClass
;
$this
->
query
=
new
Query
;
}
/**
* Executes query and returns all results as an array.
* @return array the query results. If the query results in nothing, an empty array will be returned.
*/
public
function
all
()
{
if
(
$this
->
records
===
null
)
{
$this
->
records
=
$this
->
findRecords
();
}
return
$this
->
records
;
}
/**
* Executes query and returns a single row of result.
* @return null|array|ActiveRecord the single row of query result. Depending on the setting of [[asArray]],
* the query result may be either an array or an ActiveRecord object. Null will be returned
* if the query results in nothing.
*/
public
function
one
()
{
if
(
$this
->
records
===
null
)
{
$this
->
records
=
$this
->
findRecords
();
}
return
isset
(
$this
->
records
[
0
])
?
$this
->
records
[
0
]
:
null
;
}
public
function
exists
()
{
// todo
return
$this
->
select
(
array
(
'1'
))
->
asArray
(
true
)
->
one
()
!==
null
;
}
public
function
asArray
(
$value
=
true
)
{
$this
->
asArray
=
$value
;
return
$this
;
}
public
function
with
()
{
$this
->
with
=
func_get_args
();
if
(
isset
(
$this
->
with
[
0
])
&&
is_array
(
$this
->
with
[
0
]))
{
// the parameter is given as an array
$this
->
with
=
$this
->
with
[
0
];
}
return
$this
;
}
public
function
indexBy
(
$column
)
{
$this
->
indexBy
=
$column
;
return
$this
;
}
public
function
tableAlias
(
$value
)
{
$this
->
tableAlias
=
$value
;
return
$this
;
}
/**
* Returns the database connection used by this query.
* This method returns the connection used by the [[modelClass]].
* @return \yii\db\dao\Connection the database connection used by this query
*/
public
function
getDbConnection
()
{
$class
=
$this
->
modelClass
;
return
$class
::
getDbConnection
();
}
/**
* Returns the number of items in the vector.
* @return integer the number of items in the vector
*/
public
function
getCount
()
{
return
$this
->
count
();
}
/**
* Sets the parameters about query caching.
* This is a shortcut method to {@link CDbConnection::cache()}.
* It changes the query caching parameter of the {@link dbConnection} instance.
* @param integer $duration the number of seconds that query results may remain valid in cache.
* If this is 0, the caching will be disabled.
* @param CCacheDependency $dependency the dependency that will be used when saving the query results into cache.
* @param integer $queryCount number of SQL queries that need to be cached after calling this method. Defaults to 1,
* meaning that the next SQL query will be cached.
* @return ActiveRecord the active record instance itself.
*/
public
function
cache
(
$duration
,
$dependency
=
null
,
$queryCount
=
1
)
{
$this
->
getDbConnection
()
->
cache
(
$duration
,
$dependency
,
$queryCount
);
return
$this
;
}
public
$connection
;
/**
* Returns an iterator for traversing the items in the vector.
* This method is required by the SPL interface `IteratorAggregate`.
* It will be implicitly called when you use `foreach` to traverse the vector.
* @return VectorIterator an iterator for traversing the items in the vector.
*/
public
function
getIterator
()
public
function
__construct
(
$connection
)
{
if
(
$this
->
records
===
null
)
{
$this
->
records
=
$this
->
findRecords
();
}
return
new
VectorIterator
(
$this
->
records
);
$this
->
connection
=
$connection
;
}
/**
* Returns the number of items in the vector.
* This method is required by the SPL `Countable` interface.
* It will be implicitly called when you use `count($vector)`.
* @param boolean $bySql whether to get the count by performing a SQL COUNT query.
* If this is false, it will count the number of records brought back by this query.
* @return integer number of items in the vector.
* @param \yii\db\ar\ActiveQuery $query
* @param bool $all
* @return array
*/
public
function
count
(
$bySql
=
fals
e
)
public
function
findRecords
(
$query
,
$all
=
tru
e
)
{
if
(
$
bySq
l
)
{
return
$this
->
performCountQuery
()
;
if
(
$
query
->
sql
!==
nul
l
)
{
$sql
=
$query
->
sql
;
}
else
{
if
(
$this
->
records
===
null
)
{
$this
->
records
=
$this
->
findRecords
();
}
return
count
(
$this
->
records
);
}
}
/**
* Returns a value indicating whether there is an item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `isset($vector[$offset])`.
* @param integer $offset the offset to be checked
* @return boolean whether there is an item at the specified offset.
*/
public
function
offsetExists
(
$offset
)
{
if
(
$this
->
records
===
null
)
{
$this
->
records
=
$this
->
findRecords
();
}
return
isset
(
$this
->
records
[
$offset
]);
}
/**
* Returns the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$value = $vector[$offset];`.
* This is equivalent to [[itemAt]].
* @param integer $offset the offset to retrieve item.
* @return ActiveRecord the item at the offset
* @throws Exception if the offset is out of range
*/
public
function
offsetGet
(
$offset
)
{
if
(
$this
->
records
===
null
)
{
$this
->
records
=
$this
->
findRecords
();
}
return
isset
(
$this
->
records
[
$offset
])
?
$this
->
records
[
$offset
]
:
null
;
}
/**
* Sets the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$vector[$offset] = $item;`.
* If the offset is null or equal to the number of the existing items,
* the new item will be appended to the vector.
* Otherwise, the existing item at the offset will be replaced with the new item.
* @param integer $offset the offset to set item
* @param ActiveRecord $item the item value
* @throws Exception if the offset is out of range, or the vector is read only.
*/
public
function
offsetSet
(
$offset
,
$item
)
{
if
(
$this
->
records
===
null
)
{
$this
->
records
=
$this
->
findRecords
();
}
$this
->
records
[
$offset
]
=
$item
;
}
/**
* Unsets the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `unset($vector[$offset])`.
* This is equivalent to [[removeAt]].
* @param integer $offset the offset to unset item
* @throws Exception if the offset is out of range, or the vector is read only.
*/
public
function
offsetUnset
(
$offset
)
{
if
(
$this
->
records
===
null
)
{
$this
->
records
=
$this
->
findRecords
();
}
unset
(
$this
->
records
[
$offset
]);
}
/**
* Sets the SELECT part of the query.
* @param string|array $columns the columns to be selected.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* Columns can contain table prefixes (e.g. "tbl_user.id") and/or column aliases (e.g. "tbl_user.id AS user_id").
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
* in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
* @return ActiveFinder the query object itself
*/
public
function
select
(
$columns
,
$option
=
''
)
{
$this
->
query
->
select
(
$columns
,
$option
);
return
$this
;
}
/**
* Sets the value indicating whether to SELECT DISTINCT or not.
* @param bool $value whether to SELECT DISTINCT or not.
* @return ActiveFinder the query object itself
*/
public
function
distinct
(
$value
=
true
)
{
$this
->
query
->
distinct
(
$value
);
return
$this
;
}
/**
* Sets the FROM part of the query.
* @param string|array $tables the table(s) to be selected from. This can be either a string (e.g. 'tbl_user')
* or an array (e.g. array('tbl_user', 'tbl_profile')) specifying one or several table names.
* Table names can contain schema prefixes (e.g. 'public.tbl_user') and/or table aliases (e.g. 'tbl_user u').
* The method will automatically quote the table names unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return ActiveFinder the query object itself
*/
public
function
from
(
$tables
)
{
$this
->
query
->
from
(
$tables
);
return
$this
;
}
/**
* Sets the WHERE part of the query.
*
* The method requires a $condition parameter, and optionally a $params parameter
* specifying the values to be bound to the query.
*
* The $condition parameter should be either a string (e.g. 'id=1') or an array.
* If the latter, it must be in one of the following two formats:
*
* - hash format: `array('column1' => value1, 'column2' => value2, ...)`
* - operator format: `array(operator, operand1, operand2, ...)`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
* - `array('type'=>1, 'status'=>2)` generates `(type=1) AND (status=2)`.
* - `array('id'=>array(1,2,3), 'status'=>2)` generates `(id IN (1,2,3)) AND (status=2)`.
* - `array('status'=>null) generates `status IS NULL`.
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
* `array('and', 'id=1', 'id=2')` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
* `array('and', 'type=1', array('or', 'id=1', 'id=2'))` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
* For example, `array('between', 'id', 1, 10)` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
* the range of the values that the column or DB expression should be in. For example,
* `array('in', 'id', array(1,2,3))` will generate `id IN (1,2,3)`.
* The method will properly quote the column name and escape values in the range.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
* For example, `array('like', 'name', '%tester%')` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
* using `AND`. For example, `array('like', 'name', array('%test%', '%sample%'))` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
*
* - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
* predicates when operand 2 is an array.
*
* - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
* in the generated condition.
*
* - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
* the `NOT LIKE` predicates.
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @param array $params the parameters (name=>value) to be bound to the query.
* For anonymous parameters, they can alternatively be specified as separate parameters to this method.
* For example, `where('type=? AND status=?', 100, 1)`.
* @return ActiveFinder the query object itself
* @see andWhere()
* @see orWhere()
*/
public
function
where
(
$condition
,
$params
=
array
())
{
if
(
is_array
(
$params
))
{
$this
->
query
->
where
(
$condition
,
$params
);
$this
->
initFrom
(
$query
);
$this
->
applyScopes
(
$query
);
// todo: filter
$sql
=
$this
->
connection
->
getQueryBuilder
()
->
build
(
$query
);
if
(
strpos
(
$sql
,
'@.'
)
!==
false
)
{
if
(
$query
->
tableAlias
!==
null
)
{
$alias
=
$this
->
connection
->
quoteTableName
(
$query
->
tableAlias
)
.
'.'
;
}
else
{
call_user_func_array
(
array
(
$this
->
query
,
__FUNCTION__
),
func_get_args
());
}
return
$this
;
$class
=
$query
->
modelClass
;
$alias
=
$this
->
connection
->
quoteTableName
(
$class
::
tableName
())
.
'.'
;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return ActiveFinder the query object itself
* @see where()
* @see orWhere()
*/
public
function
andWhere
(
$condition
,
$params
=
array
())
{
if
(
is_array
(
$params
))
{
$this
->
query
->
andWhere
(
$condition
,
$params
);
}
else
{
call_user_func_array
(
array
(
$this
->
query
,
__FUNCTION__
),
func_get_args
());
$sql
=
str_replace
(
'@.'
,
$alias
,
$sql
);
}
return
$this
;
}
$command
=
$this
->
connection
->
createCommand
(
$sql
,
$query
->
params
);
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return ActiveFinder the query object itself
* @see where()
* @see andWhere()
*/
public
function
orWhere
(
$condition
,
$params
=
array
())
{
if
(
is_array
(
$params
))
{
$this
->
query
->
orWhere
(
$condition
,
$params
);
if
(
$all
)
{
$rows
=
$command
->
queryAll
();
}
else
{
call_user_func_array
(
array
(
$this
->
query
,
__FUNCTION__
),
func_get_args
());
}
return
$this
;
}
/**
* Appends a JOIN part to the query.
* The first parameter specifies what type of join it is.
* @param string $type the type of join, such as INNER JOIN, LEFT JOIN.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $on the join condition that should appear in the ON part.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* @return Query the query object itself
*/
public
function
join
(
$type
,
$table
,
$on
=
''
,
$params
=
array
())
{
$this
->
query
->
join
(
$type
,
$table
,
$on
,
$params
);
return
$this
;
}
/**
* Appends an INNER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $on the join condition that should appear in the ON part.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return ActiveFinder the query object itself
*/
public
function
innerJoin
(
$table
,
$on
,
$params
=
array
())
{
$this
->
query
->
join
(
$table
,
$on
,
$params
);
return
$this
;
$row
=
$command
->
queryRow
();
if
(
$row
===
false
)
{
return
array
();
}
/**
* Appends a LEFT OUTER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $on the join condition that should appear in the ON part.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query
* @return ActiveFinder the query object itself
*/
public
function
leftJoin
(
$table
,
$on
,
$params
=
array
())
{
$this
->
query
->
leftJoin
(
$table
,
$on
,
$params
);
return
$this
;
}
/**
* Appends a RIGHT OUTER JOIN part to the query.
* @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
* The method will automatically quote the table name unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @param string|array $on the join condition that should appear in the ON part.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query
* @return ActiveFinder the query object itself
*/
public
function
rightJoin
(
$table
,
$on
,
$params
=
array
())
{
$this
->
query
->
rightJoin
(
$table
,
$on
,
$params
);
return
$this
;
}
/**
* Sets the GROUP BY part of the query.
* @param string|array $columns the columns to be grouped by.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return ActiveFinder the query object itself
* @see addGroupBy()
*/
public
function
groupBy
(
$columns
)
{
$this
->
query
->
groupBy
(
$columns
);
return
$this
;
}
/**
* Adds additional group-by columns to the existing ones.
* @param string|array $columns additional columns to be grouped by.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return ActiveFinder the query object itself
* @see groupBy()
*/
public
function
addGroupBy
(
$columns
)
{
$this
->
query
->
addGroupBy
(
$columns
);
return
$this
;
$rows
=
array
(
$row
);
}
/**
* Sets the HAVING part of the query.
* @param string|array $condition the conditions to be put after HAVING.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return ActiveFinder the query object itself
* @see andHaving()
* @see orHaving()
*/
public
function
having
(
$condition
,
$params
=
array
())
{
if
(
is_array
(
$params
))
{
$this
->
query
->
having
(
$condition
,
$params
);
}
else
{
call_user_func_array
(
array
(
$this
->
query
,
__FUNCTION__
),
func_get_args
());
$records
=
array
();
if
(
$query
->
asArray
)
{
if
(
$query
->
indexBy
===
null
)
{
return
$rows
;
}
return
$this
;
foreach
(
$rows
as
$row
)
{
$records
[
$row
[
$query
->
indexBy
]]
=
$row
;
}
/**
* Adds an additional HAVING condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return ActiveFinder the query object itself
* @see having()
* @see orHaving()
*/
public
function
andHaving
(
$condition
,
$params
=
array
())
{
if
(
is_array
(
$params
))
{
$this
->
query
->
andHaving
(
$condition
,
$params
);
}
else
{
call_user_func_array
(
array
(
$this
->
query
,
__FUNCTION__
),
func_get_args
());
}
return
$this
;
$class
=
$query
->
modelClass
;
if
(
$query
->
indexBy
===
null
)
{
foreach
(
$rows
as
$row
)
{
$records
[]
=
$class
::
createRecord
(
$row
);
}
/**
* Adds an additional HAVING condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name=>value) to be bound to the query.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return ActiveFinder the query object itself
* @see having()
* @see andHaving()
*/
public
function
orHaving
(
$condition
,
$params
=
array
())
{
if
(
is_array
(
$params
))
{
$this
->
query
->
orHaving
(
$condition
,
$params
);
}
else
{
call_user_func_array
(
array
(
$this
->
query
,
__FUNCTION__
),
func_get_args
());
}
return
$this
;
}
/**
* 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')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return ActiveFinder the query object itself
* @see addOrderBy()
*/
public
function
orderBy
(
$columns
)
{
$this
->
query
->
orderBy
(
$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')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return ActiveFinder the query object itself
* @see orderBy()
*/
public
function
addOrderBy
(
$columns
)
{
$this
->
query
->
addOrderBy
(
$columns
);
return
$this
;
foreach
(
$rows
as
$row
)
{
$records
[
$row
[
$query
->
indexBy
]]
=
$class
::
createRecord
(
$row
);
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit
* @return ActiveFinder the query object itself
*/
public
function
limit
(
$limit
)
{
$this
->
query
->
limit
(
$limit
);
return
$this
;
}
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset
* @return ActiveFinder the query object itself
*/
public
function
offset
(
$offset
)
{
$this
->
query
->
offset
(
$offset
);
return
$this
;
}
/**
* Appends a SQL statement using UNION operator.
* @param string|Query $sql the SQL statement to be appended using UNION
* @return ActiveFinder the query object itself
*/
public
function
union
(
$sql
)
{
$this
->
query
->
union
(
$sql
);
return
$this
;
return
$records
;
}
public
function
getParams
()
{
return
$this
->
query
->
params
;
}
/**
* Sets the parameters to be bound to the query.
* @param array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return ActiveFinder the query object itself
* @see addParams()
*/
public
function
params
(
$params
)
public
function
findRelatedRecords
(
$record
,
$relation
,
$params
)
{
$this
->
query
->
params
(
$params
);
return
$this
;
}
/**
* Adds additional parameters to be bound to the query.
* @param array list of query parameter values indexed by parameter placeholders.
* For example, `array(':name'=>'Dan', ':age'=>31)`.
* Please refer to [[where()]] on alternative syntax of specifying anonymous parameters.
* @return ActiveFinder the query object itself
* @see params()
*/
public
function
addParams
(
$params
)
{
$this
->
query
->
addParams
(
$params
);
return
$this
;
}
public
function
joinWith
()
{
// todo: inner join with one or multiple relations as filters
}
p
rotected
function
findRecord
s
()
p
ublic
function
findRecordsWithRelation
s
()
{
if
(
!
empty
(
$this
->
with
))
{
// todo: handle findBySql() and limit cases
...
...
@@ -762,32 +168,32 @@ class ActiveFinder extends \yii\base\Object implements \IteratorAggregate, \Arra
}
}
protected
function
performCountQuery
()
{
if
(
$this
->
sql
===
null
)
{
$this
->
initFrom
(
$this
->
query
);
$this
->
query
->
select
=
'COUNT(*)'
;
$command
=
$this
->
query
->
createCommand
(
$this
->
getDbConnection
());
$this
->
sql
=
$command
->
getSql
();
}
else
{
$command
=
$this
->
getDbConnection
()
->
createCommand
(
$this
->
sql
);
$command
->
bindValues
(
$this
->
query
->
params
);
}
return
$command
->
queryScalar
();
}
protected
function
initFrom
(
$query
)
{
if
(
$query
->
from
===
null
)
{
$modelClass
=
$
this
->
modelClass
;
$modelClass
=
$
query
->
modelClass
;
$tableName
=
$modelClass
::
tableName
();
if
(
$
this
->
tableAlias
!==
null
)
{
$tableName
.=
' '
.
$
this
->
tableAlias
;
if
(
$
query
->
tableAlias
!==
null
)
{
$tableName
.=
' '
.
$
query
->
tableAlias
;
}
$query
->
from
=
array
(
$tableName
);
}
}
protected
function
applyScopes
(
$query
)
{
if
(
is_array
(
$query
->
scopes
))
{
foreach
(
$query
->
scopes
as
$scope
=>
$params
)
{
if
(
is_integer
(
$scope
))
{
$scope
=
$params
;
$params
=
array
();
}
array_unshift
(
$params
,
$query
);
call_user_func_array
(
$scope
,
$params
);
}
}
}
protected
function
buildRelationalQuery
()
{
$joinTree
=
new
JoinElement
(
$this
,
null
,
null
);
...
...
framework/db/ar/ActiveQuery.php
View file @
3210a217
...
...
@@ -15,11 +15,12 @@ use yii\db\dao\BaseQuery;
use
yii\db\Exception
;
/**
* 1. eager loading, base limited and has has_many relations
* 2.
* ActiveFinder.php is ...
* todo: add SQL monitor
* todo: better handling on join() support in QueryBuilder: use regexp to detect table name and quote it
* todo: do not support anonymous parameter binding
* todo: add ActiveFinderBuilder
* todo: quote join/on part of the relational query
* todo: modify QueryBuilder about join() methods
* todo: unify ActiveFinder and ActiveRelation in query building process
...
...
@@ -48,6 +49,10 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
*/
public
$with
;
/**
* @var array list of relations that should be used as filters for this query.
*/
public
$filters
;
/**
* @var string the table alias to be used for query
*/
public
$tableAlias
;
...
...
@@ -66,10 +71,14 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
*/
public
$scopes
;
/**
* @var string the SQL statement to be executed to retrieve primary records.
* This is set by [[ActiveRecord::findBySql()]].
*/
public
$sql
;
/**
* @var array list of query results
*/
public
$records
;
public
$sql
;
/**
* @param string $modelClass the name of the ActiveRecord class.
...
...
@@ -95,6 +104,16 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
return
$this
;
}
public
function
filters
()
{
$this
->
filters
=
func_get_args
();
if
(
isset
(
$this
->
filters
[
0
])
&&
is_array
(
$this
->
filters
[
0
]))
{
// the parameter is given as an array
$this
->
filters
=
$this
->
filters
[
0
];
}
return
$this
;
}
public
function
indexBy
(
$column
)
{
$this
->
indexBy
=
$column
;
...
...
@@ -129,7 +148,7 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
{
if
(
$this
->
records
===
null
)
{
// todo: load only one record
$this
->
records
=
$this
->
findRecords
();
$this
->
records
=
$this
->
findRecords
(
false
);
}
return
isset
(
$this
->
records
[
0
])
?
$this
->
records
[
0
]
:
null
;
}
...
...
@@ -276,178 +295,13 @@ class ActiveQuery extends BaseQuery implements \IteratorAggregate, \ArrayAccess,
unset
(
$this
->
records
[
$offset
]);
}
public
function
joinWith
()
{
// todo: inner join with one or multiple relations as filters
}
protected
function
findRecords
()
protected
function
findRecords
(
$all
=
true
)
{
$finder
=
new
ActiveFinder
(
$this
->
getDbConnection
());
if
(
!
empty
(
$this
->
with
))
{
// todo: handle findBySql() and limit cases
$joinTree
=
$this
->
buildRelationalQuery
();
}
if
(
$this
->
sql
===
null
)
{
$this
->
initFrom
(
$this
->
query
);
$command
=
$this
->
query
->
createCommand
(
$this
->
getDbConnection
());
$this
->
sql
=
$command
->
getSql
();
}
else
{
$command
=
$this
->
getDbConnection
()
->
createCommand
(
$this
->
sql
);
$command
->
bindValues
(
$this
->
query
->
params
);
}
$rows
=
$command
->
queryAll
();
if
(
isset
(
$joinTree
))
{
foreach
(
$rows
as
$row
)
{
$joinTree
->
populateData
(
$row
);
}
return
array_values
(
$joinTree
->
records
);
}
if
(
$this
->
asArray
)
{
if
(
$this
->
indexBy
===
null
)
{
return
$rows
;
}
$records
=
array
();
foreach
(
$rows
as
$row
)
{
$records
[
$row
[
$this
->
indexBy
]]
=
$row
;
}
return
$records
;
}
else
{
$records
=
array
();
$class
=
$this
->
modelClass
;
if
(
$this
->
indexBy
===
null
)
{
foreach
(
$rows
as
$row
)
{
$records
[]
=
$class
::
populateData
(
$row
);
}
}
else
{
$attribute
=
$this
->
indexBy
;
foreach
(
$rows
as
$row
)
{
$record
=
$class
::
populateData
(
$row
);
$records
[
$record
->
$attribute
]
=
$record
;
}
}
return
$records
;
}
}
protected
function
initFrom
(
$query
)
{
if
(
$query
->
from
===
null
)
{
$modelClass
=
$this
->
modelClass
;
$tableName
=
$modelClass
::
tableName
();
if
(
$this
->
tableAlias
!==
null
)
{
$tableName
.=
' '
.
$this
->
tableAlias
;
}
$query
->
from
=
array
(
$tableName
);
}
}
protected
function
buildRelationalQuery
()
{
$joinTree
=
new
JoinElement
(
$this
,
null
,
null
);
$this
->
buildJoinTree
(
$joinTree
,
$this
->
with
);
$this
->
buildTableAlias
(
$joinTree
);
$query
=
new
Query
;
foreach
(
$joinTree
->
children
as
$child
)
{
$child
->
buildQuery
(
$query
);
}
$select
=
$joinTree
->
buildSelect
(
$this
->
query
->
select
);
if
(
!
empty
(
$query
->
select
))
{
$this
->
query
->
select
=
array_merge
(
$select
,
$query
->
select
);
return
$finder
->
findRecordsWithRelations
();
}
else
{
$this
->
query
->
select
=
$select
;
}
if
(
!
empty
(
$query
->
where
))
{
$this
->
query
->
andWhere
(
'('
.
implode
(
') AND ('
,
$query
->
where
)
.
')'
);
}
if
(
!
empty
(
$query
->
having
))
{
$this
->
query
->
andHaving
(
'('
.
implode
(
') AND ('
,
$query
->
having
)
.
')'
);
}
if
(
!
empty
(
$query
->
join
))
{
if
(
$this
->
query
->
join
===
null
)
{
$this
->
query
->
join
=
$query
->
join
;
}
else
{
$this
->
query
->
join
=
array_merge
(
$this
->
query
->
join
,
$query
->
join
);
}
}
if
(
!
empty
(
$query
->
orderBy
))
{
$this
->
query
->
addOrderBy
(
$query
->
orderBy
);
}
if
(
!
empty
(
$query
->
groupBy
))
{
$this
->
query
->
addGroupBy
(
$query
->
groupBy
);
}
if
(
!
empty
(
$query
->
params
))
{
$this
->
query
->
addParams
(
$query
->
params
);
}
return
$joinTree
;
}
/**
* @param JoinElement $parent
* @param array|string $with
* @param array $config
* @return null|JoinElement
* @throws \yii\db\Exception
*/
protected
function
buildJoinTree
(
$parent
,
$with
,
$config
=
array
())
{
if
(
is_array
(
$with
))
{
foreach
(
$with
as
$name
=>
$value
)
{
if
(
is_string
(
$value
))
{
$this
->
buildJoinTree
(
$parent
,
$value
);
}
elseif
(
is_string
(
$name
)
&&
is_array
(
$value
))
{
$this
->
buildJoinTree
(
$parent
,
$name
,
$value
);
}
}
return
null
;
}
if
((
$pos
=
strrpos
(
$with
,
'.'
))
!==
false
)
{
$parent
=
$this
->
buildJoinTree
(
$parent
,
substr
(
$with
,
0
,
$pos
));
$with
=
substr
(
$with
,
$pos
+
1
);
}
if
(
isset
(
$parent
->
children
[
$with
]))
{
$child
=
$parent
->
children
[
$with
];
$child
->
joinOnly
=
false
;
}
else
{
$modelClass
=
$parent
->
relation
->
modelClass
;
$relations
=
$modelClass
::
getMetaData
()
->
relations
;
if
(
!
isset
(
$relations
[
$with
]))
{
throw
new
Exception
(
"
$modelClass
has no relation named '
$with
'."
);
}
$relation
=
clone
$relations
[
$with
];
if
(
$relation
->
via
!==
null
&&
isset
(
$relations
[
$relation
->
via
]))
{
$relation
->
via
=
null
;
$parent2
=
$this
->
buildJoinTree
(
$parent
,
$relation
->
via
);
if
(
$parent2
->
joinOnly
===
null
)
{
$parent2
->
joinOnly
=
true
;
}
$child
=
new
JoinElement
(
$relation
,
$parent2
,
$parent
);
}
else
{
$child
=
new
JoinElement
(
$relation
,
$parent
,
$parent
);
}
}
foreach
(
$config
as
$name
=>
$value
)
{
$child
->
relation
->
$name
=
$value
;
}
return
$child
;
}
protected
function
buildTableAlias
(
$element
,
&
$count
=
0
)
{
if
(
$element
->
relation
->
tableAlias
===
null
)
{
$element
->
relation
->
tableAlias
=
't'
.
(
$count
++
);
}
foreach
(
$element
->
children
as
$child
)
{
$this
->
buildTableAlias
(
$child
,
$count
);
return
$finder
->
findRecords
(
$this
,
$all
);
}
}
}
framework/db/ar/ActiveRecord.php
View file @
3210a217
...
...
@@ -427,7 +427,7 @@ abstract class ActiveRecord extends Model
if
(
array_key_exists
(
$name
,
$this
->
_related
))
{
return
$this
->
_related
[
$name
];
}
else
{
return
$this
->
_related
[
$name
]
=
$this
->
loadRelatedRecord
(
$md
->
relations
[
$name
]);
return
$this
->
_related
[
$name
]
=
$this
->
findByRelation
(
$md
->
relations
[
$name
]);
}
}
return
parent
::
__get
(
$name
);
...
...
@@ -501,7 +501,7 @@ abstract class ActiveRecord extends Model
{
$md
=
$this
->
getMetaData
();
if
(
isset
(
$md
->
relations
[
$name
]))
{
return
$this
->
loadRelatedRecord
(
$md
->
relations
[
$name
],
isset
(
$params
[
0
])
?
$params
[
0
]
:
array
());
return
$this
->
findByRelation
(
$md
->
relations
[
$name
],
isset
(
$params
[
0
])
?
$params
[
0
]
:
array
());
}
return
parent
::
__call
(
$name
,
$params
);
}
...
...
@@ -532,14 +532,12 @@ abstract class ActiveRecord extends Model
* or null if the object does not exist.
* If the relation is HAS_MANY or MANY_MANY, it will return an array of objects
* or an empty array.
* @param string $name the relation name (see {@link relations})
* @param boolean $refresh whether to reload the related objects from database. Defaults to false.
* @param ActiveRelation|string $relation the relation object or the name of the relation
* @param array $params additional parameters that customize the query conditions as specified in the relation declaration.
* This parameter has been available since version 1.0.5.
* @return mixed the related object(s).
* @throws Exception if the relation is not specified in
{@link relations}
.
* @throws Exception if the relation is not specified in
[[relations()]]
.
*/
public
function
loadRelatedRecord
(
$relation
,
$params
=
array
())
public
function
findByRelation
(
$relation
,
$params
=
array
())
{
if
(
is_string
(
$relation
))
{
$md
=
$this
->
getMetaData
();
...
...
@@ -548,7 +546,8 @@ abstract class ActiveRecord extends Model
}
$relation
=
$md
->
relations
[
$relation
];
}
$finder
=
$this
->
createActiveQuery
();
$query
=
$this
->
createActiveQuery
();
return
$query
->
findRelatedRecords
(
$this
,
$relation
,
$params
);
}
/**
...
...
framework/db/ar/ActiveRelation.php
View file @
3210a217
...
...
@@ -33,9 +33,10 @@ class ActiveRelation extends BaseQuery
*/
public
$hasMany
;
/**
* @var string the join type (e.g. INNER JOIN, LEFT JOIN). Defaults to 'LEFT JOIN'.
* @var string the join type (e.g. INNER JOIN, LEFT JOIN). Defaults to 'LEFT JOIN' when
* this relation is used to load related records, and 'INNER JOIN' when this relation is used as a filter.
*/
public
$joinType
=
'LEFT JOIN'
;
public
$joinType
;
/**
* @var string the table alias used for the corresponding table during query
*/
...
...
@@ -58,6 +59,10 @@ class ActiveRelation extends BaseQuery
*/
public
$with
;
/**
* @var array the relations that should be used as filters for this query
*/
public
$filters
;
/**
* @var array the scopes that should be applied during query
*/
public
$scopes
;
...
...
tests/unit/framework/db/ar/ActiveRecordTest.php
View file @
3210a217
...
...
@@ -3,7 +3,7 @@
namespace
yiiunit\framework\db\ar
;
use
yii\db\dao\Query
;
use
yii\db\ar\Active
Finder
;
use
yii\db\ar\Active
Query
;
use
yiiunit\data\ar\ActiveRecord
;
use
yiiunit\data\ar\Customer
;
use
yiiunit\data\ar\OrderItem
;
...
...
@@ -45,28 +45,14 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
$customer2
=
Customer
::
find
(
2
)
->
one
();
$this
->
assertEquals
(
'user2x'
,
$customer2
->
name
);
// saveAttributes
$customer
=
Customer
::
find
(
1
)
->
one
();
$this
->
assertEquals
(
'user1'
,
$customer
->
name
);
$this
->
assertEquals
(
'address1'
,
$customer
->
address
);
$customer
->
saveAttributes
(
array
(
'name'
=>
'user1x'
,
'address'
=>
'address1x'
,
));
$this
->
assertEquals
(
'user1x'
,
$customer
->
name
);
$this
->
assertEquals
(
'address1x'
,
$customer
->
address
);
$customer
=
Customer
::
find
(
1
)
->
one
();
$this
->
assertEquals
(
'user1x'
,
$customer
->
name
);
$this
->
assertEquals
(
'address1x'
,
$customer
->
address
);
// saveCounters
// updateCounters
$pk
=
array
(
'order_id'
=>
2
,
'item_id'
=>
4
);
$orderItem
=
OrderItem
::
find
(
$pk
)
->
one
();
$orderItem
=
OrderItem
::
find
(
)
->
where
(
$pk
)
->
one
();
$this
->
assertEquals
(
1
,
$orderItem
->
quantity
);
$ret
=
$orderItem
->
sav
eCounters
(
array
(
'quantity'
=>
-
1
));
$ret
=
$orderItem
->
updat
eCounters
(
array
(
'quantity'
=>
-
1
));
$this
->
assertTrue
(
$ret
);
$this
->
assertEquals
(
0
,
$orderItem
->
quantity
);
$orderItem
=
OrderItem
::
find
(
$pk
)
->
one
();
$orderItem
=
OrderItem
::
find
(
)
->
where
(
$pk
)
->
one
();
$this
->
assertEquals
(
0
,
$orderItem
->
quantity
);
// updateAll
...
...
@@ -81,13 +67,13 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
// updateCounters
$pk
=
array
(
'order_id'
=>
1
,
'item_id'
=>
2
);
$orderItem
=
OrderItem
::
find
(
$pk
)
->
one
();
$orderItem
=
OrderItem
::
find
(
)
->
where
(
$pk
)
->
one
();
$this
->
assertEquals
(
2
,
$orderItem
->
quantity
);
$ret
=
OrderItem
::
updateCounters
(
array
(
$ret
=
OrderItem
::
update
All
Counters
(
array
(
'quantity'
=>
3
,
),
$pk
);
$this
->
assertEquals
(
1
,
$ret
);
$orderItem
=
OrderItem
::
find
(
$pk
)
->
one
();
$orderItem
=
OrderItem
::
find
(
)
->
where
(
$pk
)
->
one
();
$this
->
assertEquals
(
5
,
$orderItem
->
quantity
);
}
...
...
@@ -114,7 +100,7 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
{
// find one
$result
=
Customer
::
find
();
$this
->
assertTrue
(
$result
instanceof
Active
Finder
);
$this
->
assertTrue
(
$result
instanceof
Active
Query
);
$customer
=
$result
->
one
();
$this
->
assertTrue
(
$customer
instanceof
Customer
);
$this
->
assertEquals
(
1
,
$result
->
count
);
...
...
@@ -158,13 +144,15 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
$this
->
assertEquals
(
'user2'
,
$customer
->
name
);
// find by attributes
$customer
=
Customer
::
find
(
array
(
'name'
=>
'user2'
))
->
one
();
$customer
=
Customer
::
find
(
)
->
where
(
array
(
'name'
=>
'user2'
))
->
one
();
$this
->
assertTrue
(
$customer
instanceof
Customer
);
$this
->
assertEquals
(
2
,
$customer
->
id
);
// find by Query
$query
=
new
Query
;
$query
->
where
(
'id=:id'
,
array
(
':id'
=>
2
));
$query
=
array
(
'where'
=>
'id=:id'
,
'params'
=>
array
(
':id'
=>
2
),
);
$customer
=
Customer
::
find
(
$query
)
->
one
();
$this
->
assertTrue
(
$customer
instanceof
Customer
);
$this
->
assertEquals
(
'user2'
,
$customer
->
name
);
...
...
@@ -190,16 +178,16 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
$this
->
assertEquals
(
'user2'
,
$customer
->
name
);
// count
$
finder
=
Customer
::
findBySql
(
'SELECT * FROM tbl_customer ORDER BY id DESC'
);
$
finder
->
one
();
$this
->
assertEquals
(
3
,
$finder
->
count
);
$
finder
=
Customer
::
findBySql
(
'SELECT * FROM tbl_customer ORDER BY id DESC'
);
$this
->
assertEquals
(
3
,
$
finder
->
count
);
$
query
=
Customer
::
findBySql
(
'SELECT * FROM tbl_customer ORDER BY id DESC'
);
$
query
->
one
();
$this
->
assertEquals
(
1
,
$query
->
count
);
$
query
=
Customer
::
findBySql
(
'SELECT * FROM tbl_customer ORDER BY id DESC'
);
$this
->
assertEquals
(
3
,
$
query
->
count
);
}
public
function
testQueryMethods
()
{
$customer
=
Customer
::
find
()
->
where
(
'id=
?'
,
2
)
->
one
();
$customer
=
Customer
::
find
()
->
where
(
'id=
:id'
,
array
(
':id'
=>
2
)
)
->
one
();
$this
->
assertTrue
(
$customer
instanceof
Customer
);
$this
->
assertEquals
(
'user2'
,
$customer
->
name
);
...
...
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