Browse Source

Fixes #14184: Module service locator now falls back to its parent module service locator in case component isn't found

tags/2.0.13
Sam 7 years ago committed by Alexander Makarov
parent
commit
4081a4b203
  1. 12
      docs/guide/concept-service-locator.md
  2. 28
      docs/guide/structure-modules.md
  3. 1
      framework/CHANGELOG.md
  4. 18
      framework/base/Module.php
  5. 1
      framework/di/ServiceLocator.php
  6. 13
      tests/framework/base/ModuleTest.php

12
docs/guide/concept-service-locator.md

@ -12,7 +12,7 @@ The most commonly used service locator in Yii is the *application* object, which
`urlManager` components. You may configure these components, or even replace them with your own implementations, easily `urlManager` components. You may configure these components, or even replace them with your own implementations, easily
through functionality provided by the service locator. through functionality provided by the service locator.
Besides the application object, each module object is also a service locator. Besides the application object, each module object is also a service locator. Modules implement [tree traversal](#tree-traversal).
To use a service locator, the first step is to register components with it. A component can be registered To use a service locator, the first step is to register components with it. A component can be registered
via [[yii\di\ServiceLocator::set()]]. The following code shows different ways of registering components: via [[yii\di\ServiceLocator::set()]]. The following code shows different ways of registering components:
@ -118,3 +118,13 @@ return [
This alternative approach is most preferable when you are releasing a Yii component which encapsulates some non-Yii This alternative approach is most preferable when you are releasing a Yii component which encapsulates some non-Yii
3rd-party library. You use the static method like shown above to represent the complex logic of building the 3rd-party library. You use the static method like shown above to represent the complex logic of building the
3rd-party object, and the user of your component only needs to call the static method to configure the component. 3rd-party object, and the user of your component only needs to call the static method to configure the component.
## Tree traversal <span id="tree-traversal"></span>
Modules allow arbitrary nesting; a Yii application is essentially a tree of modules.
Since each of these modules is a service locator it makes sense for children to have access to their parent.
This allows modules to use `$this->get('db')` instead of referencing the root service locator `Yii::$app->get('db')`.
Added benefit is the option for a developer to override configuration in a module.
Any request for a service to be retrieved from a Module will be passed on to its parent in case the module is not able to satisfy it.

28
docs/guide/structure-modules.md

@ -269,6 +269,34 @@ in the `admin` module which is a child module of the `forum` module.
to its parent. The [[yii\base\Application::loadedModules]] property keeps a list of loaded modules, including both to its parent. The [[yii\base\Application::loadedModules]] property keeps a list of loaded modules, including both
direct children and nested ones, indexed by their class names. direct children and nested ones, indexed by their class names.
## Accessing components from within modules
Since version 2.0.13 modules support [tree traversal](concept-service-locator.md#tree-traversal). This allows module
developers to reference (application) components via the service locator that is their module.
This means that it is preferable to use `$module->get('db')` over `Yii::$app->get('db')`.
The user of a module is able to specify a specific component to be used for the module in case a different component
(configuration) is required.
For example consider this application configuration:
```php
'components' => [
'db' => [
'tablePrefix' => 'main_',
],
],
'modules' => [
'mymodule' => [
'components' => [
'db' => [
'tablePrefix' => 'module_',
],
],
],
],
```
The application database tables will be prefixed with `main_`, while all module tables will be prefixed with `module_`.
## Best Practices <span id="best-practices"></span> ## Best Practices <span id="best-practices"></span>

1
framework/CHANGELOG.md

@ -4,6 +4,7 @@ Yii Framework 2 Change Log
2.0.13 under development 2.0.13 under development
------------------------ ------------------------
- Enh #14184: Module service locator now falls back to its parent module service locator in case component isn't found (SamMousa)
- Bug #14596: Fix event call on init in `yii\widgets\BaseListView` (panchenkodv) - Bug #14596: Fix event call on init in `yii\widgets\BaseListView` (panchenkodv)
- New #14151: Added `AttributesBehavior` that assigns values specified to one or multiple attributes of an AR object when certain events happen (bscheshirwork) - New #14151: Added `AttributesBehavior` that assigns values specified to one or multiple attributes of an AR object when certain events happen (bscheshirwork)
- Bug #6526: Fixed `yii\db\Command::batchInsert()` casting of double values correctly independent of the locale (cebe, leammas) - Bug #6526: Fixed `yii\db\Command::batchInsert()` casting of double values correctly independent of the locale (cebe, leammas)

18
framework/base/Module.php

@ -711,4 +711,22 @@ class Module extends ServiceLocator
$this->trigger(self::EVENT_AFTER_ACTION, $event); $this->trigger(self::EVENT_AFTER_ACTION, $event);
return $event->result; return $event->result;
} }
/**
* @inheritdoc
* Since version 2.0.13, if a component isn't defined in the module, it will be looked up in the parent module.
* The parent module may be the application.
*/
public function get($id, $throwException = true)
{
if (!isset($this->module)) {
return parent::get($id, $throwException);
}
$component = parent::get($id, false);
if ($component === null) {
$component = $this->module->get($id, $throwException);
}
return $component;
}
} }

1
framework/di/ServiceLocator.php

@ -40,6 +40,7 @@ use yii\base\InvalidConfigException;
* ``` * ```
* *
* Because [[\yii\base\Module]] extends from ServiceLocator, modules and the application are all service locators. * Because [[\yii\base\Module]] extends from ServiceLocator, modules and the application are all service locators.
* Modules add [tree traversal](guide:concept-service-locator#tree-traversal) for service resolution.
* *
* For more details and usage information on ServiceLocator, see the [guide article on service locators](guide:concept-service-locator). * For more details and usage information on ServiceLocator, see the [guide article on service locators](guide:concept-service-locator).
* *

13
tests/framework/base/ModuleTest.php

@ -9,6 +9,8 @@ namespace yiiunit\framework\base;
use Yii; use Yii;
use yii\base\Controller; use yii\base\Controller;
use yii\base\Module;
use yii\base\Object;
use yiiunit\TestCase; use yiiunit\TestCase;
/** /**
@ -84,6 +86,17 @@ class ModuleTest extends TestCase
$this->assertNotNull(Yii::$app->controller->action); $this->assertNotNull(Yii::$app->controller->action);
$this->assertEquals('test/test-controller1/test1', Yii::$app->controller->action->uniqueId); $this->assertEquals('test/test-controller1/test1', Yii::$app->controller->action->uniqueId);
} }
public function testServiceLocatorTraversal()
{
$parent = new Module('parent');
$child = new Module('child', $parent);
$grandchild = new Module('grandchild', $child);
$parent->set('test', new Object());
$this->assertInstanceOf(Object::className(), $grandchild->get('test'));
}
} }
class TestModule extends \yii\base\Module class TestModule extends \yii\base\Module

Loading…
Cancel
Save