Saving Widget Config To Database

Hi

I’m attempting to build a Yii2 widget management admin interface that I can use use to configure and store certain widget instances to a MySQL database for later retrieval and display.

My main issue is how to store a custom widget config array to the db whilst still allowing calls such as


Yii::$app->user->identity->username

to be called upon retrieval at runtime.

Basic serialization would not allow this (it would store the value from the call rather than the code) and I’m not too keen on using eval.

Essentially, can anyone recommend a good way of storing to db and retrieving something like the following config from the app-advanced template whilst keeping the advantages of hard coded configs? …


NavBar::begin([

                'brandLabel' => 'My Company',

                'brandUrl' => Yii::$app->homeUrl,

                'options' => [

                    'class' => 'navbar-inverse navbar-fixed-top',

                ],

            ]);

            $menuItems = [

                ['label' => 'Home', 'url' => ['/site/index']],

                ['label' => 'About', 'url' => ['/site/about']],

                ['label' => 'Contact', 'url' => ['/site/contact']],

            ];

            if (Yii::$app->user->isGuest) {

                $menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']];

                $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];

            } else {

                $menuItems[] = [

                    'label' => 'Logout (' . Yii::$app->user->identity->username . ')',

                    'url' => ['/site/logout'],

                    'linkOptions' => ['data-method' => 'post']

                ];

            }

            echo Nav::widget([

                'options' => ['class' => 'navbar-nav navbar-right'],

                'items' => $menuItems,

            ]);

            NavBar::end();

Any help, thoughts or ideas will be much appreciated.

You can create placeholder variables before serializing and replace them dynamically after you unserialize them. For example:




use yii\helpers\Json;

use yii\helpers\Url;

// Capture

$menuItems = [

    'label' => 'Logout (~USERID~)',

    'url' => '~URL~',

    'linkOptions' => ['data-method' => 'post']

];

$hashItems = Json::encode($menuItems); // store this in db


//Retrieval

$menuItems = Json::decode(strtr($hashItems, [

    '~USERID~' => Yii::$app->user->identity->username,

    '~URL~' => Url::to(['/site/logout'])

]));

Thanks Kartik V.

I was a bit concerned that doing it that way would cause a headache with the potential amount of tags I might have to create but I guess I could use a helper class or something.

Dealing with the “if (Yii::$app->user->isGuest)” might make things a bit messy, but I’m sure there’s an easy solution for that.

I’m now thinking about combining this type of tags idea with a ‘Config’ component class that I can extend from to more easily manage the configs and then serialize each config class instance into the db. Retrieval should just involve unserializing and calling a toArray() function for sending to the relevant widget class.

Definitely not as easy as I first thought this was going to be.

Glad that helps. Do let know the outcome of your design.

For Kartik V and anyone else interested, here’s how it seems I will solve this …

It’s far from being finished but initial testing suggests the following will work.

The values stored in a ‘widget’ table in the database should look something like the following:




[

    'class' => 'yii\bootstrap\NavBar',

    'config' => serialize([

        'brandLabel' => 'My Company',

        'brandUrl' => '{{homeUrl}}',

        'options' => [

            'class' => 'navbar-inverse navbar-fixed-top',

        ]

    ]),

    'rules' => serialize([

        ['brandUrl', \common\validators\TagValidator::className()],    // this is a custom TagValidator but could be any validator

    ]),

]



Note the use of serialize when storing to db.

I will likely use a nested set db structure to allow for nesting widgets, but that’s for testing another day.

To retrieve the config and display the widget, I use Yii’s DynamicModel class and do the following:




// note: the widget record from the db is in $dbWidget

$widgetConfig = \yii\base\DynamicModel::validateData(unserialize($dbWidget['config']), unserialize($dbWidget['rules']));

$dbWidget['class']::widget($widgetConfig->getAttributes());



By treating the config as a dynamic model I can use scenarios and validators in whatever way I choose making it very flexible.

In this particular case I’ve created a custom TagValidator which basically checks the ‘brandUrl’ attribute for any tags I’ve pre-defined in the validator and changes the value accordingly when validated.

I won’t bother showing the TagValidator here as it was just a basic test and needs a lot more work.

Once I have the nested set db structure in place I can easily find out if a widget has children and, if it does, call begin(), (loop through any child widgets) and end() rather than just widget().

After 4 days of struggling with this, I at least feel like I’m on the right track …

Thanks DynamicModel!

Great I created a wiki for this so we are all benefitted. You can edit/add to the wiki, in case you find anything.

Thank you people - this is pretty useful piece of info.

This is good information - @angelcoding and @Kartik.