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
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
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
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.
## 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
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>

1
framework/CHANGELOG.md

@ -4,6 +4,7 @@ Yii Framework 2 Change Log
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)
- 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)

18
framework/base/Module.php

@ -711,4 +711,22 @@ class Module extends ServiceLocator
$this->trigger(self::EVENT_AFTER_ACTION, $event);
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.
* 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).
*

13
tests/framework/base/ModuleTest.php

@ -9,6 +9,8 @@ namespace yiiunit\framework\base;
use Yii;
use yii\base\Controller;
use yii\base\Module;
use yii\base\Object;
use yiiunit\TestCase;
/**
@ -84,6 +86,17 @@ class ModuleTest extends TestCase
$this->assertNotNull(Yii::$app->controller->action);
$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

Loading…
Cancel
Save