📜 ⬆️ ⬇️

Yii2: Making a module for managing modules

Greetings to all! On the current project, we are using Yii2 and in the development process we needed some entity as a module.

In Yii2, a modular system is already implemented, but there is one drawback in that the module does not allow displaying one module in another module, and the use of widgets is also not suitable, since this is part of the view and cannot handle actions, for example, an incoming POST request (although at one time we used widgets like this with some crutches).

We will create a module that will contain nested modules that can be used in any controller.

As a result, we get:
')

The idea is based on the implementation of modules in CMS OpenCart.

Start by creating the dispatcher module via gii and enable it in the web.php config

'dispatcher' => [ 'class' => 'app\modules\dispatcher\Module', ], 

In the app \ modules \ dispatcher module directory, create a BasicModule class that inherits from \ yii \ base \ Module .

BasicModule.php
 <?php namespace app\modules\dispatcher; use app\modules\dispatcher\components\Controller; use app\modules\dispatcher\models\LayoutModule; /** * * Class Module * @package app\modules\dispatcher\components * */ class BasicModule extends \yii\base\Module { const POSITION_HEADER = 'header'; const POSITION_FOOTER = 'footer'; const POSITION_LEFT = 'left'; const POSITION_RIGHT = 'right'; /** * @var array of positions */ static protected $positions = [ self::POSITION_HEADER, self::POSITION_FOOTER, self::POSITION_LEFT, self::POSITION_RIGHT, ]; /** * @var string controller name */ public $defaultControllerName = 'DefaultController'; /** * @var string dir of modules catalog */ public $modulesDir = 'catalog'; /** * @var string modules namespace */ private $_modulesNamespace; /** * @var string absolute path to modules dir */ public $modulePath; /** * * @throws \yii\base\InvalidParamException */ public function init() { parent::init(); $this->_setModuleVariables(); $this->loadModules(); } /** * Load modules from directory by path * @throws \yii\base\InvalidParamException */ protected function loadModules() { $handle = opendir($this->modulePath); while (($dir = readdir($handle)) !== false) { if ($dir === '.' || $dir === '..') { continue; } $class = $this->_modulesNamespace . '\\' . $dir . '\\Module'; if (class_exists($class)) { $this->modules = [ $dir => [ 'class' => $class, ], ]; } } closedir($handle); } /** * @param $layout * @param array $positions * @return array * @throws \yii\base\InvalidConfigException */ public function run($layout, array $positions = []) { $model = $this->findModel($layout, $positions); $data = []; foreach ($model as $item) { if ($controller = $this->findModuleController($item['module'])) { $data[$item['position']][] = \Yii::createObject($controller, [$item['module'], $this])->index(); } } return $data; } /** * @param $layout_id * @param array $positions * @return array|\yii\db\ActiveRecord[] * @internal param $layout */ public function findModel($layout_id, array $positions = []) { if (empty($positions)) { $positions = self::$positions; } return LayoutModule::find() ->where([ 'layout_id' => $layout_id, 'position' => $positions, 'status' => LayoutModule::STATUS_ACTIVE, ])->orderBy([ 'sort_order' => SORT_ASC ])->asArray()->all(); } /** * @param $name * @return null|string */ public function findModuleController($name) { $className = $this->_modulesNamespace . '\\' . $name . '\controllers\\' . $this->defaultControllerName; return is_subclass_of($className, Controller::class) ? $className : null; } /** * Set modules namespace and path */ private function _setModuleVariables() { $class = new \ReflectionClass($this); $this->_modulesNamespace = $class->getNamespaceName() . '\\' . $this->modulesDir; $this->modulePath = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . $this->modulesDir; } } 


We inherit the module class app \ modules \ dispatcher \ Module from BasicModule

Module.php
 <?php namespace app\modules\dispatcher; /** * dispatcher module definition class */ class Module extends BasicModule { /** * @inheritdoc */ public function init() { parent::init(); } } 


Create and execute migration:

Migration
  public $table = '{{%layout_module}}'; public function safeUp() { $tableOptions = null; if ($this->db->driverName === 'mysql') { $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; } $this->createTable($this->table, [ 'id' => $this->primaryKey(), 'layout_id' => $this->integer()->notNull(), // id      'module' => $this->string(150)->notNull(), //  'status' => $this->boolean()->defaultValue(true), 'position' => $this->string(30)->notNull(), 'sort_order' => $this->integer()->defaultValue(1), ], $tableOptions); } public function safeDown() { $this->dropTable($this->table); } 

Fill the created table:

 INSERT INTO `layout_module` VALUES ('1', '1', 'test', '1', 'header', '1'); INSERT INTO `layout_module` VALUES ('2', '1', 'test', '1', 'footer', '1'); INSERT INTO `layout_module` VALUES ('3', '1', 'test', '1', 'left', '1'); 

At the root of our dispatcher module, add the components directory. Create a Controller class that will inherit \ yii \ web \ Controller . Override its render () method.

Controller.php
 <?php namespace app\modules\dispatcher\components; /** * * Class Controller * @package app\modules\dispatcher\components */ class Controller extends \yii\web\Controller { /** * @param string $view * @param array $params * @return string * @throws \yii\base\InvalidParamException * @throws \yii\base\ViewNotFoundException * @throws \yii\base\InvalidCallException */ public function render($view, $params = []) { $controller = str_replace('Controller', '', $this->module->defaultControllerName); $path = '@app/modules/dispatcher/' . $this->module->modulesDir . '/' . $this->id . '/views/' . $controller; return $this->getView()->render($path . '/' . 'index', $params, $this); } } 


At the root of the dispatcher module, add the catalog directory - this is the parent directory for our modules.

Next we create our first module, which in its structure is no different from the usual Yii2 module. Create a test directory, create a Module class in it:

Module.php
 <?php namespace app\modules\dispatcher\catalog\test; /** * test module definition class */ class Module extends \yii\base\Module { /** * @inheritdoc */ public $controllerNamespace = 'app\modules\dispatcher\catalog\test\controllers'; } 


We create the controllers directory and in it the DefaultController class which we inherit from our app \ modules \ dispatcher \ components \ Controller .

DefaultController.php
 <?php namespace app\modules\dispatcher\catalog\test\controllers; use app\modules\dispatcher\components\Controller; /** * Default controller for the `test` module */ class DefaultController extends Controller { /** * Renders the index view for the module * @return string * @throws \yii\base\InvalidParamException * @throws \yii\base\ViewNotFoundException * @throws \yii\base\InvalidCallException */ public function index() { return $this->render('index'); } } 


Important: for our module to work, it must always inherit from app \ modules \ dispatcher \ components \ Controller and contain the index method

Create directories for the views / default view and file our view:

index.php
 <div class="dispatcher-default-index"> <p> You may customize this page by editing the following file:<br> <code><?= __FILE__ ?></code> </p> </div> 


Almost everything is ready, it remains only to make a call to our modules. To do this, create the Dispatcher component in app \ modules \ dispatcher \ components :

Dispatcher.php
 <?php namespace app\modules\dispatcher\components; use yii\base\Object; class Dispatcher extends Object { /** * @var \app\modules\dispatcher\Module */ private $_module; public $module = 'dispatcher'; /** * Dispatcher constructor. * @param array $config */ public function __construct(array $config = []) { parent::__construct($config); $this->_module = \Yii::$app->getModule($this->module); } /** * Get modules by layout * * @param $layout * @param array $positions * @return array * @throws \yii\base\InvalidConfigException */ public function modules($layout, array $positions = []) { return $this->_module->run($layout, $positions); } } 


Now we need to connect our component to web.php

  'dispatcher' => [ 'class' => 'app\modules\dispatcher\components\Dispatcher', ], 

Do not forget that the component must be added to the components array.

In any controller, for example SiteController , in the actionIndex () method we add

  /* @var $modules Dispatcher */ $modules = \Yii::$app->dispatcher->modules(1); return $this->render('index', compact('modules')); 

It remains only to add to our presentation position for the output modules views / site / index.php :

index.php
 <?php /* @var $this yii\web\View */ $this->title = 'My Yii Application'; use app\modules\dispatcher\Module; ?> <div class="site-index"> <?php if (isset($modules[Module::POSITION_HEADER])) { ?> <div class="row"> <?php foreach ($modules[Module::POSITION_HEADER] as $module) { echo $module; } ?> </div> <?php } ?> <div class="jumbotron"> <h1>Congratulations!</h1> <p class="lead">You have successfully created your Yii-powered application.</p> <p><a class="btn btn-lg btn-success" href="http://www.yiiframework.com">Get started with Yii</a></p> </div> <div class="body-content"> <div class="row"> <div class="col-lg-4"> <h2>Heading</h2> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> <p><a class="btn btn-default" href="http://www.yiiframework.com/doc/">Yii Documentation »</a></p> </div> <div class="col-lg-4"> <h2>Heading</h2> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> <p><a class="btn btn-default" href="http://www.yiiframework.com/forum/">Yii Forum »</a> </p> </div> <div class="col-lg-4"> <h2>Heading</h2> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> <p><a class="btn btn-default" href="http://www.yiiframework.com/extensions/">Yii Extensions »</a></p> </div> </div> </div> <?php if (isset($modules[Module::POSITION_FOOTER])) { ?> <div class="row"> <?php foreach ($modules[Module::POSITION_FOOTER] as $module) { echo $module; } ?> </div> <?php } ?> </div> 


I recommend the official documentation on the modules .

All code is posted on GitHub .

Source: https://habr.com/ru/post/317950/


All Articles