0

(This question is not about databases. I know databases are often regarded when it comes to multi-tenancy. But this is not my question.)

I create an application that will be used by different customers. They can see and do mostly the same things. However, some customers have some extra buttons here and there, some functions are disabled or something else is different.

I want to avoid lots of if/else or switch statements. So I thought I could have a general module and special modules for each customer that override or overwrite different things as needed. Look at the following theoretical structure (all files are the normal Yii files):

app
    general
        controllers
            OrderController
            InvoiceController
            ShippingController
        views
            order
                index, view, update, create, _form
            invoice
                index, view, update, create, _form
            shipping
                index, view, update, create, _form
            layout
                main
        models
            Order
            Invoice
            Shipping
    customerA
        controllers
            OrderController  // some action is different
    customerB
        views
            invoice
                view         // some buttons should be overridden
            layout
                main.php     // other page structure
    customerC
        views
            order
                index        // table gets some additional columns
        model
            Order            // has some more properties
    customerD
        // this customer exists but has nothing that must be changed
        // it uses all what is in general
    ...
web
common
console
...

I think this is a sound file structure. At least it is clear and comprehensible. Now I'd like to realize somehow: if a customer requests anything the application will look first if there are files for the specific customer and uses them, or uses the files in the general modul (fallback). My question is: how can I do that?

If it is possible, how? Or is it only partially possible, e.g. only with controllers and views? I suppose different approaches might be needed for view/controller/model files. I suppose that class autoloading could be adapted. And I would think to utilize routing (though the customer must be authenticated, so it can use only his or her own customer):

http://example.com/customerA/invoice/view?id=1234

Can all this be done with the configuration or with dependency injection? Do I need modules or is the file organization sufficient?

(Thoughts: Overriding controllers could be simple. But I think that view files can be overriden only if the controller gets overridden to set another view path.)

Does anyone have ideas to solve this? I would be glad if this works with controllers and views. But models would be also nice. Or does anyone have other suggestions or alternative approaches? Let me know even if you only know parts of the solution.

I assume a satisfying answer may evolves with the time. But it would be helpful for others to find a good solution. So let's work on that ;-) ... Or is another framework more suitable for this task? :-(

robsch
  • 9,358
  • 9
  • 63
  • 104

1 Answers1

0

you can use separate themes by code like this:

  1. override yii\base\Theme
  2. use dependency injenction to use your custom Theme component
  3. make an empty configuration entry to make your theme component get called
  4. create/copy your custom theme files

read this for theming instructions

read this for dependency injection instructions (or how to replace other models)

this example shows how to replace '@app/views' by '@app/themes/mytheme'.

common\components\Theme.php (custom theme component)

namespace common\components;
use Yii;

class Theme extends \yii\base\Theme 
{

    public function init() {
        // todo: your logic goes here
        $this->pathMap = [
            '@app/views' => [
                '@app/themes/mytheme',
            ]
        ];

        return parent::init();
    }

}

common\extensions\Bootstrap.php (custom bootstrap for DI)

namespace common\extensions;

use Yii;
use yii\base\Application;
use yii\base\BootstrapInterface;
use yii\helpers\Url;

class Bootstrap implements BootstrapInterface
{
    public function bootstrap($app)
    {
        Yii::$container->set(\yii\base\Theme::className(), \common\components\Theme::className());
    }
}

common\config\main.php (register custom bootstrap)

'bootstrap' => [
    'common\extensions\Bootstrap',
],

frontend\config\main.php (register dummy theme configuration)

'components' => [
    'view' => [
        'theme' => [
            'basePath' => '@app/views',
        ],
    ],
],
Community
  • 1
  • 1
e-frank
  • 739
  • 11
  • 21