Commit 4408f520 by Qiang Xue

Merge commit 'ec05c0ea' into feature-restapi

parents 39947be6 ec05c0ea
...@@ -4,7 +4,14 @@ php: ...@@ -4,7 +4,14 @@ php:
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
# - hhvm # commented until composer or hhvm get fixed: https://github.com/facebook/hhvm/issues/1347 - hhvm
# run build against PHP 5.6 and hhvm but allow them to fail
# http://docs.travis-ci.com/user/build-configuration/#Rows-That-are-Allowed-To-Fail
matrix:
allow_failures:
- php: hhvm
- php: 5.6
services: services:
- redis-server - redis-server
......
...@@ -172,7 +172,7 @@ Note that [[yii\db\ActiveRecord::updateAll()|updateAll()]], [[yii\db\ActiveRecor ...@@ -172,7 +172,7 @@ Note that [[yii\db\ActiveRecord::updateAll()|updateAll()]], [[yii\db\ActiveRecor
```php ```php
// to insert a new customer record // to insert a new customer record
$customer = new Customer; $customer = new Customer();
$customer->name = 'James'; $customer->name = 'James';
$customer->email = 'james@example.com'; $customer->email = 'james@example.com';
$customer->save(); // equivalent to $customer->insert(); $customer->save(); // equivalent to $customer->insert();
...@@ -634,7 +634,7 @@ order owned by the customer: ...@@ -634,7 +634,7 @@ order owned by the customer:
```php ```php
$customer = Customer::find(1); $customer = Customer::find(1);
$order = new Order; $order = new Order();
$order->subtotal = 100; $order->subtotal = 100;
$customer->link('orders', $order); $customer->link('orders', $order);
``` ```
......
...@@ -160,7 +160,7 @@ class BlogController extends Controller ...@@ -160,7 +160,7 @@ class BlogController extends Controller
{ {
$post = Post::find($id); $post = Post::find($id);
if (!$post) { if (!$post) {
throw new NotFoundHttpException; throw new NotFoundHttpException();
} }
if (\Yii::$app->request->isPost) { if (\Yii::$app->request->isPost) {
......
...@@ -29,7 +29,7 @@ $posts = $provider->getModels(); ...@@ -29,7 +29,7 @@ $posts = $provider->getModels();
And the following example shows how to use ActiveDataProvider without ActiveRecord: And the following example shows how to use ActiveDataProvider without ActiveRecord:
```php ```php
$query = new Query; $query = new Query();
$provider = new ActiveDataProvider([ $provider = new ActiveDataProvider([
'query' => $query->from('tbl_post'), 'query' => $query->from('tbl_post'),
'pagination' => [ 'pagination' => [
...@@ -62,7 +62,7 @@ because it needs to have [[allModels]] ready. ...@@ -62,7 +62,7 @@ because it needs to have [[allModels]] ready.
ArrayDataProvider may be used in the following way: ArrayDataProvider may be used in the following way:
```php ```php
$query = new Query; $query = new Query();
$provider = new ArrayDataProvider([ $provider = new ArrayDataProvider([
'allModels' => $query->from('tbl_post')->all(), 'allModels' => $query->from('tbl_post')->all(),
'sort' => [ 'sort' => [
......
...@@ -24,7 +24,7 @@ be accessed like the member variables of any object. For example, a `Post` model ...@@ -24,7 +24,7 @@ be accessed like the member variables of any object. For example, a `Post` model
may contain a `title` attribute and a `content` attribute, accessible as follows: may contain a `title` attribute and a `content` attribute, accessible as follows:
```php ```php
$post = new Post; $post = new Post();
$post->title = 'Hello, world'; $post->title = 'Hello, world';
$post->content = 'Something interesting is happening.'; $post->content = 'Something interesting is happening.';
echo $post->title; echo $post->title;
...@@ -35,7 +35,7 @@ Since [[yii\base\Model|Model]] implements the [ArrayAccess](http://php.net/manua ...@@ -35,7 +35,7 @@ Since [[yii\base\Model|Model]] implements the [ArrayAccess](http://php.net/manua
you can also access the attributes as if they were array elements: you can also access the attributes as if they were array elements:
```php ```php
$post = new Post; $post = new Post();
$post['title'] = 'Hello, world'; $post['title'] = 'Hello, world';
$post['content'] = 'Something interesting is happening'; $post['content'] = 'Something interesting is happening';
echo $post['title']; echo $post['title'];
...@@ -160,7 +160,7 @@ class EmployeeController extends \yii\web\Controller ...@@ -160,7 +160,7 @@ class EmployeeController extends \yii\web\Controller
$employee = new Employee(['scenario' => 'managementPanel']); $employee = new Employee(['scenario' => 'managementPanel']);
// second way // second way
$employee = new Employee; $employee = new Employee();
$employee->scenario = 'managementPanel'; $employee->scenario = 'managementPanel';
// third way // third way
...@@ -187,7 +187,7 @@ only, etc. If errors are found in validation, they may be presented to the user ...@@ -187,7 +187,7 @@ only, etc. If errors are found in validation, they may be presented to the user
The following example shows how the validation is performed: The following example shows how the validation is performed:
```php ```php
$model = new LoginForm; $model = new LoginForm();
$model->username = $_POST['username']; $model->username = $_POST['username'];
$model->password = $_POST['password']; $model->password = $_POST['password'];
if ($model->validate()) { if ($model->validate()) {
......
...@@ -9,7 +9,7 @@ The Query Builder provides an object-oriented vehicle for generating queries to ...@@ -9,7 +9,7 @@ The Query Builder provides an object-oriented vehicle for generating queries to
A typical usage of the query builder looks like the following: A typical usage of the query builder looks like the following:
```php ```php
$rows = (new \yii\db\Query) $rows = (new \yii\db\Query())
->select('id, name') ->select('id, name')
->from('tbl_user') ->from('tbl_user')
->limit(10) ->limit(10)
...@@ -17,7 +17,7 @@ $rows = (new \yii\db\Query) ...@@ -17,7 +17,7 @@ $rows = (new \yii\db\Query)
// which is equivalent to the following code: // which is equivalent to the following code:
$query = (new \yii\db\Query) $query = (new \yii\db\Query())
->select('id, name') ->select('id, name')
->from('tbl_user') ->from('tbl_user')
->limit(10); ->limit(10);
...@@ -116,7 +116,7 @@ You may specify a sub-query using a `Query` object. In this case, the correspond ...@@ -116,7 +116,7 @@ You may specify a sub-query using a `Query` object. In this case, the correspond
as the alias for the sub-query. as the alias for the sub-query.
```php ```php
$subQuery = (new Query)->select('id')->from('tbl_user')->where('status=1'); $subQuery = (new Query())->select('id')->from('tbl_user')->where('status=1');
$query->select('*')->from(['u' => $subQuery]); $query->select('*')->from(['u' => $subQuery]);
``` ```
...@@ -324,10 +324,10 @@ $query->leftJoin(['u' => $subQuery], 'u.id=author_id'); ...@@ -324,10 +324,10 @@ $query->leftJoin(['u' => $subQuery], 'u.id=author_id');
In Yii in order to build it you can first form two query objects and then use `union` method: In Yii in order to build it you can first form two query objects and then use `union` method:
```php ```php
$query = new Query; $query = new Query();
$query->select("id, 'post' as type, name")->from('tbl_post')->limit(10); $query->select("id, 'post' as type, name")->from('tbl_post')->limit(10);
$anotherQuery = new Query; $anotherQuery = new Query();
$anotherQuery->select('id, 'user' as type, name')->from('tbl_user')->limit(10); $anotherQuery->select('id, 'user' as type, name')->from('tbl_user')->limit(10);
$query->union($anotherQuery); $query->union($anotherQuery);
...@@ -347,7 +347,7 @@ Batch query can be used like the following: ...@@ -347,7 +347,7 @@ Batch query can be used like the following:
```php ```php
use yii\db\Query; use yii\db\Query;
$query = (new Query) $query = (new Query())
->from('tbl_user') ->from('tbl_user')
->orderBy('id'); ->orderBy('id');
...@@ -376,7 +376,7 @@ will still keep the proper index. For example, ...@@ -376,7 +376,7 @@ will still keep the proper index. For example,
```php ```php
use yii\db\Query; use yii\db\Query;
$query = (new Query) $query = (new Query())
->from('tbl_user') ->from('tbl_user')
->indexBy('username'); ->indexBy('username');
......
...@@ -158,7 +158,7 @@ in controllers or widgets: ...@@ -158,7 +158,7 @@ in controllers or widgets:
```php ```php
$content = Yii::$app->view->renderFile($viewFile, $params); $content = Yii::$app->view->renderFile($viewFile, $params);
// You can also explicitly create a new View instance to do the rendering // You can also explicitly create a new View instance to do the rendering
// $view = new View; // $view = new View();
// $view->renderFile($viewFile, $params); // $view->renderFile($viewFile, $params);
``` ```
...@@ -186,7 +186,7 @@ New methods called [[yii\base\Model::load()|load()] and [[yii\base\Model::loadMu ...@@ -186,7 +186,7 @@ New methods called [[yii\base\Model::load()|load()] and [[yii\base\Model::loadMu
introduced to simplify the data population from user inputs to a model. For example, introduced to simplify the data population from user inputs to a model. For example,
```php ```php
$model = new Post; $model = new Post();
if ($model->load($_POST)) {...} if ($model->load($_POST)) {...}
// which is equivalent to: // which is equivalent to:
if (isset($_POST['Post'])) { if (isset($_POST['Post'])) {
...@@ -394,7 +394,7 @@ In 1.1, query building is scattered among several classes, including `CDbCommand ...@@ -394,7 +394,7 @@ In 1.1, query building is scattered among several classes, including `CDbCommand
and [[yii\db\QueryBuilder|QueryBuilder]] to generate SQL statements from query objects. For example: and [[yii\db\QueryBuilder|QueryBuilder]] to generate SQL statements from query objects. For example:
```php ```php
$query = new \yii\db\Query; $query = new \yii\db\Query();
$query->select('id, name') $query->select('id, name')
->from('tbl_user') ->from('tbl_user')
->limit(10); ->limit(10);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
"yiisoft/yii2": "*", "yiisoft/yii2": "*",
"yiisoft/yii2-bootstrap": "*", "yiisoft/yii2-bootstrap": "*",
"phpdocumentor/reflection": ">=1.0.3", "phpdocumentor/reflection": ">=1.0.3",
"phpdocumentor/reflection-docblock": ">2.0.1",
"nikic/php-parser": "0.9.*" "nikic/php-parser": "0.9.*"
}, },
"autoload": { "autoload": {
......
...@@ -150,8 +150,10 @@ Yii Framework 2 Change Log ...@@ -150,8 +150,10 @@ Yii Framework 2 Change Log
- Enh: Added support for reading page size from query parameters by `Pagination` (qiangxue) - Enh: Added support for reading page size from query parameters by `Pagination` (qiangxue)
- Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe) - Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe)
- Enh: Added `yii\web\UrlRuleInterface` and `yii\web\CompositeUrlRule` (qiangxue) - Enh: Added `yii\web\UrlRuleInterface` and `yii\web\CompositeUrlRule` (qiangxue)
- Enh: Added `yii\web\Request::getAuthUser()` and `getAuthPassword()` (qiangxue)
- Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue) - Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue)
- Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue) - Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)
- Chg #1564: Removed `yii\web\Session::autoStart` and added `hasSessionId`. Session will be automatically started when accessing session data (qiangxue)
- Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue) - Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
- Chg #1610: `Html::activeCheckboxList()` and `Html::activeRadioList()` will submit an empty string if no checkbox/radio is selected (qiangxue) - Chg #1610: `Html::activeCheckboxList()` and `Html::activeRadioList()` will submit an empty string if no checkbox/radio is selected (qiangxue)
- Chg #1643: Added default value for `Captcha::options` (qiangxue) - Chg #1643: Added default value for `Captcha::options` (qiangxue)
......
...@@ -86,6 +86,10 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -86,6 +86,10 @@ class ActiveQuery extends Query implements ActiveQueryInterface
* @see onCondition() * @see onCondition()
*/ */
public $on; public $on;
/**
* @var array a list of relations that this query should be joined with
*/
public $joinWith;
/** /**
...@@ -232,6 +236,10 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -232,6 +236,10 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/ */
protected function createCommandInternal($db) protected function createCommandInternal($db)
{ {
if (!empty($this->joinWith)) {
$this->buildJoinWith();
}
/** @var ActiveRecord $modelClass */ /** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass; $modelClass = $this->modelClass;
if ($db === null) { if ($db === null) {
...@@ -330,24 +338,32 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -330,24 +338,32 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/ */
public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN') public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')
{ {
$with = (array)$with; $this->joinWith[] = [(array)$with, $eagerLoading, $joinType];
$this->joinWithRelations(new $this->modelClass, $with, $joinType); return $this;
}
if (is_array($eagerLoading)) { private function buildJoinWith()
foreach ($with as $name => $callback) { {
if (is_integer($name)) { foreach ($this->joinWith as $config) {
if (!in_array($callback, $eagerLoading, true)) { list ($with, $eagerLoading, $joinType) = $config;
$this->joinWithRelations(new $this->modelClass, $with, $joinType);
if (is_array($eagerLoading)) {
foreach ($with as $name => $callback) {
if (is_integer($name)) {
if (!in_array($callback, $eagerLoading, true)) {
unset($with[$name]);
}
} elseif (!in_array($name, $eagerLoading, true)) {
unset($with[$name]); unset($with[$name]);
} }
} elseif (!in_array($name, $eagerLoading, true)) {
unset($with[$name]);
} }
} elseif (!$eagerLoading) {
$with = [];
} }
} elseif (!$eagerLoading) {
$with = [];
}
return $this->with($with); $this->with($with);
}
} }
/** /**
......
...@@ -819,6 +819,22 @@ class Request extends \yii\base\Request ...@@ -819,6 +819,22 @@ class Request extends \yii\base\Request
return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null; return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
} }
/**
* @return string the username sent via HTTP authentication, null if the username is not given
*/
public function getAuthUser()
{
return isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
}
/**
* @return string the password sent via HTTP authentication, null if the username is not given
*/
public function getAuthPassword()
{
return isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;
}
private $_port; private $_port;
/** /**
......
...@@ -75,10 +75,6 @@ use yii\base\InvalidParamException; ...@@ -75,10 +75,6 @@ use yii\base\InvalidParamException;
class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable
{ {
/** /**
* @var boolean whether the session should be automatically started when the session component is initialized.
*/
public $autoStart = true;
/**
* @var string the name of the session variable that stores the flash message data. * @var string the name of the session variable that stores the flash message data.
*/ */
public $flashParam = '__flash'; public $flashParam = '__flash';
...@@ -100,9 +96,6 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -100,9 +96,6 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
public function init() public function init()
{ {
parent::init(); parent::init();
if ($this->autoStart) {
$this->open();
}
register_shutdown_function([$this, 'close']); register_shutdown_function([$this, 'close']);
} }
...@@ -123,10 +116,32 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -123,10 +116,32 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function open() public function open()
{ {
if (session_status() == PHP_SESSION_ACTIVE) { if ($this->getIsActive()) {
return; return;
} }
$this->registerSessionHandler();
$this->setCookieParamsInternal();
@session_start();
if ($this->getIsActive()) {
Yii::info('Session started', __METHOD__);
$this->updateFlashCounters();
} else {
$error = error_get_last();
$message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
Yii::error($message, __METHOD__);
}
}
/**
* Registers session handler.
* @throws \yii\base\InvalidConfigException
*/
protected function registerSessionHandler()
{
if ($this->handler !== null) { if ($this->handler !== null) {
if (!is_object($this->handler)) { if (!is_object($this->handler)) {
$this->handler = Yii::createObject($this->handler); $this->handler = Yii::createObject($this->handler);
...@@ -145,18 +160,6 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -145,18 +160,6 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
[$this, 'gcSession'] [$this, 'gcSession']
); );
} }
$this->setCookieParamsInternal();
@session_start();
if (session_id() == '') {
$error = error_get_last();
$message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
Yii::error($message, __METHOD__);
} else {
$this->updateFlashCounters();
}
} }
/** /**
...@@ -164,7 +167,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -164,7 +167,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function close() public function close()
{ {
if (session_id() !== '') { if ($this->getIsActive()) {
@session_write_close(); @session_write_close();
} }
} }
...@@ -174,7 +177,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -174,7 +177,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function destroy() public function destroy()
{ {
if (session_id() !== '') { if ($this->getIsActive()) {
@session_unset(); @session_unset();
@session_destroy(); @session_destroy();
} }
...@@ -188,6 +191,42 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -188,6 +191,42 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
return session_status() == PHP_SESSION_ACTIVE; return session_status() == PHP_SESSION_ACTIVE;
} }
private $_hasSessionId;
/**
* Returns a value indicating whether the current request has sent the session ID.
* The default implementation will check cookie and $_GET using the session name.
* If you send session ID via other ways, you may need to override this method
* or call [[setHasSessionId()]] to explicitly set whether the session ID is sent.
* @return boolean whether the current request has sent the session ID.
*/
public function getHasSessionId()
{
if ($this->_hasSessionId === null) {
$name = $this->getName();
$request = Yii::$app->getRequest();
if (ini_get('session.use_cookies') && !empty($_COOKIE[$name])) {
$this->_hasSessionId = true;
} elseif (!ini_get('use_only_cookies') && ini_get('use_trans_sid')) {
$this->_hasSessionId = $request->get($name) !== null;
} else {
$this->_hasSessionId = false;
}
}
return $this->_hasSessionId;
}
/**
* Sets the value indicating whether the current request has sent the session ID.
* This method is provided so that you can override the default way of determining
* whether the session ID is sent.
* @param boolean $value whether the current request has sent the session ID.
*/
public function setHasSessionId($value)
{
$this->_hasSessionId = $value;
}
/** /**
* @return string the current session ID * @return string the current session ID
*/ */
...@@ -506,6 +545,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -506,6 +545,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function get($key, $defaultValue = null) public function get($key, $defaultValue = null)
{ {
$this->open();
return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue; return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue;
} }
...@@ -517,6 +557,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -517,6 +557,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function set($key, $value) public function set($key, $value)
{ {
$this->open();
$_SESSION[$key] = $value; $_SESSION[$key] = $value;
} }
...@@ -527,6 +568,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -527,6 +568,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function remove($key) public function remove($key)
{ {
$this->open();
if (isset($_SESSION[$key])) { if (isset($_SESSION[$key])) {
$value = $_SESSION[$key]; $value = $_SESSION[$key];
unset($_SESSION[$key]); unset($_SESSION[$key]);
...@@ -541,6 +583,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -541,6 +583,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function removeAll() public function removeAll()
{ {
$this->open();
foreach (array_keys($_SESSION) as $key) { foreach (array_keys($_SESSION) as $key) {
unset($_SESSION[$key]); unset($_SESSION[$key]);
} }
...@@ -552,6 +595,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -552,6 +595,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function has($key) public function has($key)
{ {
$this->open();
return isset($_SESSION[$key]); return isset($_SESSION[$key]);
} }
...@@ -560,6 +604,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -560,6 +604,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function toArray() public function toArray()
{ {
$this->open();
return $_SESSION; return $_SESSION;
} }
...@@ -689,6 +734,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -689,6 +734,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function offsetExists($offset) public function offsetExists($offset)
{ {
$this->open();
return isset($_SESSION[$offset]); return isset($_SESSION[$offset]);
} }
...@@ -699,6 +745,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -699,6 +745,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function offsetGet($offset) public function offsetGet($offset)
{ {
$this->open();
return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
} }
...@@ -709,6 +756,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -709,6 +756,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function offsetSet($offset, $item) public function offsetSet($offset, $item)
{ {
$this->open();
$_SESSION[$offset] = $item; $_SESSION[$offset] = $item;
} }
...@@ -718,6 +766,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ...@@ -718,6 +766,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*/ */
public function offsetUnset($offset) public function offsetUnset($offset)
{ {
$this->open();
unset($_SESSION[$offset]); unset($_SESSION[$offset]);
} }
} }
...@@ -258,6 +258,16 @@ class ActiveRecordTest extends DatabaseTestCase ...@@ -258,6 +258,16 @@ class ActiveRecordTest extends DatabaseTestCase
$this->assertTrue($orders[0]->isRelationPopulated('customer')); $this->assertTrue($orders[0]->isRelationPopulated('customer'));
$this->assertTrue($orders[1]->isRelationPopulated('customer')); $this->assertTrue($orders[1]->isRelationPopulated('customer'));
// inner join filtering, eager loading, conditions on both primary and relation
$orders = Order::find()->innerJoinWith([
'customer' => function ($query) {
$query->where(['tbl_customer.id' => 2]);
},
])->where(['tbl_order.id' => [1, 2]])->orderBy('tbl_order.id')->all();
$this->assertEquals(1, count($orders));
$this->assertEquals(2, $orders[0]->id);
$this->assertTrue($orders[0]->isRelationPopulated('customer'));
// inner join filtering without eager loading // inner join filtering without eager loading
$orders = Order::find()->innerJoinWith([ $orders = Order::find()->innerJoinWith([
'customer' => function ($query) { 'customer' => function ($query) {
...@@ -270,6 +280,16 @@ class ActiveRecordTest extends DatabaseTestCase ...@@ -270,6 +280,16 @@ class ActiveRecordTest extends DatabaseTestCase
$this->assertFalse($orders[0]->isRelationPopulated('customer')); $this->assertFalse($orders[0]->isRelationPopulated('customer'));
$this->assertFalse($orders[1]->isRelationPopulated('customer')); $this->assertFalse($orders[1]->isRelationPopulated('customer'));
// inner join filtering without eager loading, conditions on both primary and relation
$orders = Order::find()->innerJoinWith([
'customer' => function ($query) {
$query->where(['tbl_customer.id' => 2]);
},
], false)->where(['tbl_order.id' => [1, 2]])->orderBy('tbl_order.id')->all();
$this->assertEquals(1, count($orders));
$this->assertEquals(2, $orders[0]->id);
$this->assertFalse($orders[0]->isRelationPopulated('customer'));
// join with via-relation // join with via-relation
$orders = Order::find()->innerJoinWith('books')->orderBy('tbl_order.id')->all(); $orders = Order::find()->innerJoinWith('books')->orderBy('tbl_order.id')->all();
$this->assertEquals(2, count($orders)); $this->assertEquals(2, count($orders));
...@@ -282,6 +302,9 @@ class ActiveRecordTest extends DatabaseTestCase ...@@ -282,6 +302,9 @@ class ActiveRecordTest extends DatabaseTestCase
// join with sub-relation // join with sub-relation
$orders = Order::find()->innerJoinWith([ $orders = Order::find()->innerJoinWith([
'items' => function ($q) {
$q->orderBy('tbl_item.id');
},
'items.category' => function ($q) { 'items.category' => function ($q) {
$q->where('tbl_category.id = 2'); $q->where('tbl_category.id = 2');
}, },
...@@ -361,7 +384,11 @@ class ActiveRecordTest extends DatabaseTestCase ...@@ -361,7 +384,11 @@ class ActiveRecordTest extends DatabaseTestCase
$this->assertNull($customers[1]->profile); $this->assertNull($customers[1]->profile);
// hasMany // hasMany
$customers = Customer::find()->active()->joinWith('orders')->orderBy('tbl_customer.id DESC, tbl_order.id')->all(); $customers = Customer::find()->active()->joinWith([
'orders' => function ($q) {
$q->orderBy('tbl_order.id');
}
])->orderBy('tbl_customer.id DESC, tbl_order.id')->all();
$this->assertEquals(2, count($customers)); $this->assertEquals(2, count($customers));
$this->assertEquals(2, $customers[0]->id); $this->assertEquals(2, $customers[0]->id);
$this->assertEquals(1, $customers[1]->id); $this->assertEquals(1, $customers[1]->id);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment