mirror of
https://github.com/yiisoft/yii2.git
synced 2026-03-03 05:54:32 +01:00
Fixes #9476: Added DI injection via controller action method signature
This commit is contained in:
committed by
Alexander Makarov
parent
59208e5fc6
commit
b7020065c8
@@ -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>
|
||||
------------------------
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!isset($args[$i])) {
|
||||
$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 {
|
||||
|
||||
@@ -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
Normal file
86
tests/framework/console/ControllerTest.php
Normal file
@@ -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
Normal file
56
tests/framework/console/FakeController.php
Normal file
@@ -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
Normal file
81
tests/framework/web/ControllerTest.php
Normal file
@@ -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
Normal file
49
tests/framework/web/FakeController.php
Normal file
@@ -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
Normal file
19
tests/framework/web/stubs/Bar.php
Normal file
@@ -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
Normal file
24
tests/framework/web/stubs/OtherQux.php
Normal file
@@ -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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user