Yii 2 Modules
Create Module Manually
  1. Create a modules folder on the application base path. This matches the @app alias of the current application. Same procedure for basic or advanced application (backend/frontend). Eg: app/modules
  2. Create a folder for your module corresponding to the Module ID. Eg: app/modules/product
  3. Create the Module Class (eg Module) inside this module folder (eg. app/modules/product/Module.php). The custom module definition must extend \yii\base\Module.

Eg.: For product module class, create file app/modules/product/Module.php:

<?php
 
namespace app\modules\product;
 
class Module extends \yii\base\Module
{
   public $controllerNamespace = 'app\modules\product\controllers';
 
   public function init()
   {
       parent::init();
 
       // custom initialization code goes here
   }
}

Create the module controllers, models and views subfolders on the module folder. Eg:

  • app\modules\Product\controllers
  • app\modules\Product\models
  • app\modules\Product\views

To make the module accessible, add references in the application configuration:

<?php
//......
   'modules' => [
      'product' => [
         'class' => 'app\modules\product\Module',
         'components' => [
              'db' => [
                    'tablePrefix' => 'module_',  // override main 'db' component attributes
              ],
          ],
      ],
   ],
//......
Create Module Automatically (Gii)
  1. Using Gii module, go to the module generator and enter path to module class. Eg: app\modules\product\Module
  2. Preview and Generate all files.
  3. To make the module accessible, add references in the application configuration:
<?php
//......
   'modules' => [
      'product' => [
         'class' => 'app\modules\product\Module',
         'components' => [
              // Main application database
              'db' => [
                    'class' => 'yii\db\Connection',
              ],
              // Custom module database
              //'db-mod-product' => [
              //    'class'       => 'yii\db\Connection',
              //    'dsn'         => 'mysql:host=localhost;dbname=acme_product',
              //    'username'    => 'dbuser',
              //    'password'    => 'secret',
              //    'charset'     => 'utf8',
              //    'tablePrefix' => 'mod_',
              //],
          ],
      ],
   ],
//......
Module Detection or References

To detect (in a view) whether a particular module is installed:

<?php 
    // detect installation of child module whose ID is "requisition"
    $module = \Yii::$app->getModule('requisition');
    $isModuleInstalled = !empty($module);
?>
<?php if ($isModuleInstalled): ?>
    <a class="btn btn-lg btn-default" 
      href="<?= Url::to(['requisition/custom-order/index']); ?>">
      <i class="fa fa-cart-plus" aria-hidden="true"></i> 
      Requisition
    </a>
<?php endif; ?>
 
<?php
  // Alternatively
  $isModuleInstalled = yii\base\Module::has('requisition');
?>

To get a module reference (eg: module “forum”):

// get module reference
$module = \app\modules\forum\Module::getInstance(); // Format: MyModuleClass::getInstance();
 
// get the child module whose ID is "forum" (when you know the module name)
$module = \Yii::$app->getModule('forum');
 
// get the module to which the currently requested controller belongs
// (when you know the controller, but not the module name)
$module = \Yii::$app->controller->module;
// or...
$module = $this->module;  // not tested yet
Target Variable Description
module name Yii::$app→controller→module→id
module name $this→module→id

To reference a model in the module (eg: model “Posts” in module “forum”):

$models = \app\modules\forum\models\Posts::find()->all();

To reference the module url (eg: view “index” of model “Posts” in module “forum”):

http://localhost:8080/[myapp]/web/forum/posts/index
Example View
<?php 
    // detect installation of child module whose ID is "erp"
    $module = \Yii::$app->getModule('erp');
    $isModuleInstalled = !empty($module);
?>
<?php if ($isModuleInstalled): ?>
    <?= /* ### METHOD 1 ### */
        $form->field($model, 'item_code')
        ->textInput([
            'maxlength' => true,
            'onchange' => '$.get( "'.Yii::$app->homeUrl.'/erp/inventory/getdescription?item_code="+$(this).val(), function( data ) {
                $( "#proformaitem-description1" ).val( data );
             });'
        ]);
        /* ### METHOD 2: This lookup solution is too slow  ### */
        //$form->field($model, 'item_code')->dropDownList(
        //yii\helpers\ArrayHelper::map(\app\modules\erp\models\Inventory::find()->all(), 'item_code', 'description1'), 
        //[
        //    //'prompt' => '--Select One--',
        //    'onchange' => '$.get( "'.Yii::$app->homeUrl.'/erp/inventory/getdescription?item_code="+$(this).val(), function( data ) {
        //        $( "#proformaitem-description1" ).val( data );
        //     });'
        //]);
    ?>
<?php else: ?>
    <?= $form->field($model, 'item_code')->textInput(['maxlength' => true]) ?>
<?php endif; ?>
 
<?= $form->field($model, 'description1')->textInput(['maxlength' => true]) ?>
Using Custom Database

Sometimes your module needs to use a separate custom database. You can do so in Yii by adding a new db section in its configuration, and pointing the module models to new private database.

Steps: Add a new database file configuration (or new db array in config). Eg: file @app/config/db-mod-label.php (in addition to the app's db.php file)

<?php
return [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=localhost;dbname=acme_labels',
    'username' => 'dbuser',
    'password' => 'secret',
    'charset' => 'utf8',
    //'tablePrefix' => 'main_',
];

Add a reference to this file in the app config section. Eg: @app/config/web.php

<?php
 
$params = require(__DIR__ . '/params.php');
 
$config = [
    'id' => 'basic',
    //...
    'components' => [
        //...
        'db' => require(__DIR__ . '/db.php'),
        'db-mod-label' => require(__DIR__ . '/db-mod-label.php'),  // custom database for module
        //...
    ],
    'params' => $params,
];
//...

Alternatively:

application configuration:
<code php>
<?php
//...
$config = [
    'id' => 'basic',
    //...
    'components' => [
        //...
        'db' => require(__DIR__ . '/db.php'),
        //...
    ],
   'modules' => [
      'label' => [
         'class' => 'app\modules\label\Module',
         'components' => [
              // Option 1: custom database for module
              //
              // In @app/config/db-mod-label.php:
              //'db-mod-label' => require(__DIR__ . '/db-mod-label.php'),
              //  
              // In @app/modules/label/config/db-mod-label.php:
              //'db-mod-label' => require(__DIR__ . 
              //    DIRECTORY_SEPARATOR . '..' . 
              //    DIRECTORY_SEPARATOR . 'modules' . 
              //    DIRECTORY_SEPARATOR . 'printlog' . 
              //    DIRECTORY_SEPARATOR . 'config' . 
              //    DIRECTORY_SEPARATOR . 'db-mod-printlog.php'
              //),  
 
              // Option 2: custom database for module
              'db-mod-label' => [
                  'class'       => 'yii\db\Connection',
                  'dsn'         => 'mysql:host=localhost;dbname=acme_labels',
                  'username'    => 'dbuser',
                  'password'    => 'secret',
                  'charset'     => 'utf8',
                  'tablePrefix' => 'mod_',
              ],
          ],
      ],
   ],
   'params' => $params,
];
//...

Now, in each model found in the module, override the function getDb() to connect to this custom database:

//...
class ItemProduct extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return "{{%item_product}}";  // formatted to allow table prefix (where % goes)
 
        // NOTE: To query the raw table name, use the following:
        //   echo Yii::$app->db->schema->getRawTableName(app\models\ItemProduct::tableName());
        // Displays as follows (assuming 'mod_' table prefix):
        //   mod_item_product
    }
 
    /**
     * Override parent getDb() so we can connect to private module database.
     */
    public static function getDb() 
    {
       // Get custom connection from app.
       //return Yii::$app->get('db');   // use main application database
 
       // Get custom connection from module (preferable).
       // In Yii 2.0.13 and newer, it is preferable to use $module->get('db')
       // instead of getting a connection from app (using Yii::$app->get('db'))
       $module = \Yii::$app->controller->module;
       return $module->get("db-mod-label");   // use private database for module 'label'
    }
 
    //...
}
References