I have module named dashboard
and PlanningController
inside. The controller has become too big and I want to split it into several separate controllers.
Now I have the following actions in PlanningController
:
/dashboard/planning/purchase
/dashboard/planning/purchase-create
/dashboard/planning/purchase-update
/dashboard/planning/supplier
/dashboard/planning/supplier-create
/dashboard/planning/supplier-update
But I want to have 2 separate controllers PurchaseController
and SupplierController
which will be logically combined into a folder planning
:
/dashboard/planning/purchase
/dashboard/planning/purchase/create
/dashboard/planning/purchase/update
/dashboard/planning/supplier
/dashboard/planning/supplier/create
/dashboard/planning/supplier/update
I read that Yii2 does not support subfolders in controllers. How do I merge two controllers into one folder?
Depending on what exactly you want to achieve there are multiple ways to handle this.
You can use nested modules to structure your code. Create a planning
sub-module inside of modules/dashboard/modules
. Then in the dashboard's Module
class add the nested module for example like this:
namespace app\modules\dashboard;
use app\modules\dashboard\modules\planning\Module as PlanningModule;
use yii\base\Module as BaseModule;
class Module extends BaseModule
{
public function init()
{
parent::init();
$this->modules = [
'planning' => [
'class' => PlanningModule::class,
],
];
}
}
That way you can separate all code related to your planning controllers into its own sub-module. Also, it will help you avoid any potential conflicts in routes.
The property yii\base\Module::$controllerMap
allows you to use controllers that doesn't match the default yii's naming and folder structure conventions. With that you can place your PurchaseController
and SupplierController
into folder modules/dashboard/controllers/planning
then set the map in your module class like this:
namespace app\modules\dashboard;
use app\modules\dashboard\controllers\planning\PurchaseController;
use app\modules\dashboard\controllers\planning\SupplierController;
use yii\base\Module as BaseModule;
class Module extends BaseModule
{
public $controllerMap = [
'purchase' => PurchaseController::class,
'supplier' => SupplierController::class,
];
}
If you use this approach and you want routes to contain the "/planning/" part you will have to set up specific url rules.
If you only want to split the code of PlanningController
because it is getting too big, but you are ok with keeping it as single controller. You can extract the action code into standalone action classes.
For example you can create PurchaseCreateAction
class in modules/dashboard/controllers/actions/planning
folder like this:
namespace app\modules\dashboard\controllers\actions\planning;
use yii\base\Action;
use yii\web\Response;
class PurchaseCreateAction extends Action
{
// string because we will return rendered form view and
// Response because we will return redirect after successful create
public function run(): string|Response
{
// ... action logic
// to redirect
return $this->controller->redirect(...);
// to render view
return $this->controller->render(...);
}
}
You can include the standalone action in your planning controller like this:
namespace app\modules\dashboard\controllers;
use app\modules\dashboard\controllers\actions\planning\PurchaseCreateAction;
use yii\web\Controller;
class PlanningController extends Controller
{
public function actions()
{
return [
'purchase-create' => PurchaseCreateAction::class,
// ... other actions
];
}
}