Browse Source

Fixes #9476: Added DI injection via controller action method signature

9899-cache-bug
mdmunir 9 years ago committed by Alexander Makarov
parent
commit
b7020065c8
  1. 14
      docs/guide/concept-di-container.md
  2. 1
      framework/CHANGELOG.md
  3. 19
      framework/console/Controller.php
  4. 9
      framework/web/Controller.php
  5. 86
      tests/framework/console/ControllerTest.php
  6. 56
      tests/framework/console/FakeController.php
  7. 81
      tests/framework/web/ControllerTest.php
  8. 49
      tests/framework/web/FakeController.php
  9. 19
      tests/framework/web/stubs/Bar.php
  10. 24
      tests/framework/web/stubs/OtherQux.php

14
docs/guide/concept-di-container.md

@ -116,6 +116,20 @@ $foo = $container->get('Foo');
By doing so, the person who wants to configure the `Foo` class no longer needs to be aware of how it is built.
### Controller action injection
Controller action injection is a special type of DI where dependecies are resolved per action which is useful for
keeping dependencies number low in MVC controllers.
```php
public function actionSend($email, EmailValidator $validator)
{
if ($validator->validate($email)) {
// ... send email
}
}
```
Registering Dependencies <span id="registering-dependencies"></span>
------------------------

1
framework/CHANGELOG.md

@ -20,6 +20,7 @@ Yii Framework 2 Change Log
- Chg #9369: `Yii::$app->user->can()` now returns `false` instead of erroring in case `authManager` component is not configured (creocoder)
- Chg #9411: `DetailView` now automatically sets container tag ID in case it's not specified (samdark)
- Enh #2106: Added Unprocessable Entity HTTP Exception (janfrs)
- Enh #9476: Added DI injection via controller action method signature (mdmunir)
- Enh #9635: Added default CSS class for `\yii\grid\ActionColumn` header (arogachev, dynasource)
- Enh #9711: Added `yii\widgets\LinkPager::$pageCssClass` that allows to set default page class (ShNURoK42)

19
framework/console/Controller.php

@ -109,14 +109,25 @@ class Controller extends \yii\base\Controller
$method = new \ReflectionMethod($action, 'run');
}
$args = array_values($params);
$params = array_values($params);
$args = [];
$missing = [];
foreach ($method->getParameters() as $i => $param) {
if ($param->isArray() && isset($args[$i])) {
$args[$i] = preg_split('/\s*,\s*/', $args[$i]);
if (($class = $param->getClass()) !== null) {
$name = $param->getName();
$className = $class->getName();
if (Yii::$app->has($name) && ($obj = Yii::$app->get($name)) instanceof $className) {
$args[$i] = $obj;
} else {
$args[$i] = Yii::$container->get($className);
}
if (!isset($args[$i])) {
continue;
}
$value = array_shift($params);
if (isset($value)) {
$args[$i] = $param->isArray() ? preg_split('/\s*,\s*/', $value) : $value;
} else {
if ($param->isDefaultValueAvailable()) {
$args[$i] = $param->getDefaultValue();
} else {

9
framework/web/Controller.php

@ -71,7 +71,14 @@ class Controller extends \yii\base\Controller
$actionParams = [];
foreach ($method->getParameters() as $param) {
$name = $param->getName();
if (array_key_exists($name, $params)) {
if (($class = $param->getClass()) !== null) {
$className = $class->getName();
if (Yii::$app->has($name) && ($obj = Yii::$app->get($name)) instanceof $className) {
$args[] = $actionParams[$name] = $obj;
}else{
$args[] = $actionParams[$name] = Yii::$container->get($className);
}
} elseif (array_key_exists($name, $params)) {
if ($param->isArray()) {
$args[] = $actionParams[$name] = (array) $params[$name];
} elseif (!is_array($params[$name])) {

86
tests/framework/console/ControllerTest.php

@ -0,0 +1,86 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\console;
use Yii;
use yiiunit\TestCase;
use yiiunit\framework\di\stubs\Qux;
use yiiunit\framework\web\stubs\Bar;
use yiiunit\framework\web\stubs\OtherQux;
/**
* @group console
*/
class ControllerTest extends TestCase
{
public function testBindActionParams()
{
$this->mockApplication([
'components' => [
'barBelongApp' => [
'class' => Bar::className(),
'foo' => 'belong_app'
],
'quxApp' => [
'class' => OtherQux::className(),
'b' => 'belong_app'
]
]
]);
$controller = new FakeController('fake', Yii::$app);
Yii::$container->set('yiiunit\framework\di\stubs\QuxInterface', [
'class' => Qux::className(),
'a' => 'D426'
]);
Yii::$container->set(Bar::className(), [
'foo' => 'independent'
]);
$params = ['from params'];
list($bar, $fromParam, $other) = $controller->run('aksi1', $params);
$this->assertTrue($bar instanceof Bar);
$this->assertNotEquals($bar, Yii::$app->barBelongApp);
$this->assertEquals('independent', $bar->foo);
$this->assertEquals('from params', $fromParam);
$this->assertEquals('default', $other);
$params = [];
list($barBelongApp, $qux) = $controller->run('aksi2', $params);
$this->assertTrue($barBelongApp instanceof Bar);
$this->assertEquals($barBelongApp, Yii::$app->barBelongApp);
$this->assertEquals('belong_app', $barBelongApp->foo);
$this->assertTrue($qux instanceof Qux);
$this->assertEquals('D426', $qux->a);
$params = [];
list($quxApp) = $controller->run('aksi3', $params);
$this->assertTrue($quxApp instanceof OtherQux);
$this->assertEquals($quxApp, Yii::$app->quxApp);
$this->assertEquals('belong_app', $quxApp->b);
$params = ['d426,mdmunir', 'single'];
$result = $controller->runAction('aksi4', $params);
$this->assertEquals(['independent', 'other_qux', ['d426', 'mdmunir'], 'single'], $result);
$params = ['d426'];
$result = $controller->runAction('aksi5', $params);
$this->assertEquals(['d426', 'independent', 'other_qux'], $result);
$params = ['mdmunir'];
$result = $controller->runAction('aksi6', $params);
$this->assertEquals(['mdmunir', false, true], $result);
$params = ['avaliable'];
$message = Yii::t('yii', 'Missing required arguments: {params}', ['params' => implode(', ', ['missing'])]);
$this->setExpectedException('yii\console\Exception', $message);
$result = $controller->runAction('aksi7', $params);
}
}

56
tests/framework/console/FakeController.php

@ -0,0 +1,56 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\console;
use yii\console\Controller;
use yiiunit\framework\di\stubs\QuxInterface;
use yiiunit\framework\web\stubs\Bar;
use yii\validators\EmailValidator;
/**
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 2.0
*/
class FakeController extends Controller
{
public function actionAksi1(Bar $bar, $fromParam, $other = 'default')
{
return[$bar, $fromParam, $other];
}
public function actionAksi2(Bar $barBelongApp, QuxInterface $qux)
{
return[$barBelongApp, $qux];
}
public function actionAksi3(QuxInterface $quxApp)
{
return[$quxApp];
}
public function actionAksi4(Bar $bar, QuxInterface $quxApp, array $values, $value)
{
return [$bar->foo, $quxApp->quxMethod(), $values, $value];
}
public function actionAksi5($q, Bar $bar, QuxInterface $quxApp)
{
return [$q, $bar->foo, $quxApp->quxMethod()];
}
public function actionAksi6($q, EmailValidator $validator)
{
return [$q, $validator->validate($q), $validator->validate('misbahuldmunir@gmail.com')];
}
public function actionAksi7(Bar $bar, $avaliable, $missing)
{
}
}

81
tests/framework/web/ControllerTest.php

@ -0,0 +1,81 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\web;
use Yii;
use yiiunit\TestCase;
use yiiunit\framework\di\stubs\Qux;
use yiiunit\framework\web\stubs\Bar;
use yiiunit\framework\web\stubs\OtherQux;
use yii\base\InlineAction;
/**
* @group web
*/
class ControllerTest extends TestCase
{
public function testBindActionParams()
{
$this->mockApplication([
'components'=>[
'barBelongApp'=>[
'class'=> Bar::className(),
'foo'=>'belong_app'
],
'quxApp'=>[
'class' => OtherQux::className(),
'b' => 'belong_app'
]
]
]);
$controller = new FakeController('fake', Yii::$app);
$aksi1 = new InlineAction('aksi1', $controller, 'actionAksi1');
$aksi2 = new InlineAction('aksi2', $controller, 'actionAksi2');
$aksi3 = new InlineAction('aksi3', $controller, 'actionAksi3');
Yii::$container->set('yiiunit\framework\di\stubs\QuxInterface', [
'class' => Qux::className(),
'a' => 'D426'
]);
Yii::$container->set(Bar::className(),[
'foo' => 'independent'
]);
$params = ['fromGet'=>'from query params','q'=>'d426','validator'=>'avaliable'];
list($bar, $fromGet, $other) = $controller->bindActionParams($aksi1, $params);
$this->assertTrue($bar instanceof Bar);
$this->assertNotEquals($bar, Yii::$app->barBelongApp);
$this->assertEquals('independent', $bar->foo);
$this->assertEquals('from query params', $fromGet);
$this->assertEquals('default', $other);
list($barBelongApp, $qux) = $controller->bindActionParams($aksi2, $params);
$this->assertTrue($barBelongApp instanceof Bar);
$this->assertEquals($barBelongApp, Yii::$app->barBelongApp);
$this->assertEquals('belong_app', $barBelongApp->foo);
$this->assertTrue($qux instanceof Qux);
$this->assertEquals('D426', $qux->a);
list($quxApp) = $controller->bindActionParams($aksi3, $params);
$this->assertTrue($quxApp instanceof OtherQux);
$this->assertEquals($quxApp, Yii::$app->quxApp);
$this->assertEquals('belong_app', $quxApp->b);
$result = $controller->runAction('aksi4', $params);
$this->assertEquals(['independent', 'other_qux', 'd426'], $result);
$result = $controller->runAction('aksi5', $params);
$this->assertEquals(['d426', 'independent', 'other_qux'], $result);
$result = $controller->runAction('aksi6', $params);
$this->assertEquals(['d426', false, true], $result);
}
}

49
tests/framework/web/FakeController.php

@ -0,0 +1,49 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\web;
use yii\web\Controller;
use yiiunit\framework\di\stubs\QuxInterface;
use yiiunit\framework\web\stubs\Bar;
use yii\validators\EmailValidator;
/**
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 2.0
*/
class FakeController extends Controller
{
public $enableCsrfValidation = false;
public function actionAksi1(Bar $bar, $fromGet, $other = 'default')
{
}
public function actionAksi2(Bar $barBelongApp, QuxInterface $qux)
{
}
public function actionAksi3(QuxInterface $quxApp)
{
}
public function actionAksi4(Bar $bar, QuxInterface $quxApp, $q)
{
return [$bar->foo, $quxApp->quxMethod(), $q];
}
public function actionAksi5($q, Bar $bar, QuxInterface $quxApp)
{
return [$q, $bar->foo, $quxApp->quxMethod()];
}
public function actionAksi6($q, EmailValidator $validator)
{
return [$q, $validator->validate($q), $validator->validate('misbahuldmunir@gmail.com')];
}
}

19
tests/framework/web/stubs/Bar.php

@ -0,0 +1,19 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\web\stubs;
use yii\base\Object;
/**
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 2.0
*/
class Bar extends Object
{
public $foo;
}

24
tests/framework/web/stubs/OtherQux.php

@ -0,0 +1,24 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\web\stubs;
use yii\base\Object;
use yiiunit\framework\di\stubs\QuxInterface;
/**
* @author Misbahul D Munir <misbahuldmunir@gmail.com>
* @since 2.0
*/
class OtherQux extends Object implements QuxInterface
{
public $b;
public function quxMethod()
{
return 'other_qux';
}
}
Loading…
Cancel
Save