Yii2 routes definition in modules - php

Is there any solution to add routes from module configuration?
Example. We have main config where we describe
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => require(FILE_PATH_CONFIG_ENV . '_routes.php') // return array
],
]
in each module we load custom config file with private parameters
public function loadConfig($sFileName = 'main', $sFolderName = 'config')
{
Yii::configure($this, require($this->getBasePath() . DS . $sFolderName . DS . $sFileName . '.php'));
return $this;
}
Config file
return [
'components' => [
'urlManager' => [
'class' => 'yii\web\UrlManager',
'rules' => [
'/admin' => '/admin/index/index',
]
]
]
];
And now I need somehow merge current config (main for web application) with config loaded from module. In end I want describe in module config routes only for this module and use them for pretty urls (user friendly url). How can I do this one? This examples not working when I create url /admin/index/index, it shows me /admin/index/index but I want /admin as mentioned in module rules.

So I did this way (this is full answer for a question).
Create Bootstrap class special for module.
namespace app\extensions;
use yii\base\BootstrapInterface;
/**
* Class ModuleBootstrap
*
* #package app\extensions
*/
class ModuleBootstrap implements BootstrapInterface
{
/**
* #param \yii\base\Application $oApplication
*/
public function bootstrap($oApplication)
{
$aModuleList = $oApplication->getModules();
foreach ($aModuleList as $sKey => $aModule) {
if (is_array($aModule) && strpos($aModule['class'], 'app\modules') === 0) {
$sFilePathConfig = FILE_PATH_ROOT . DS . 'modules' . DS . $sKey . DS . 'config' . DS . '_routes.php';
if (file_exists($sFilePathConfig)) {
$oApplication->getUrlManager()->addRules(require($sFilePathConfig));
}
}
}
}
}
Create route file in module folder (/modules/XXX/config/_routes.php)
return [
'/sales' => '/sales/index/index',
'/sales/company' => '/sales/company/index',
'/sales/company/view/<sID:\d+>' => '/sales/company/view',
'/sales/company/update/<sID:\d+>' => '/sales/company/update',
'/sales/company/delete/<sID:\d+>' => '/sales/company/delete',
];
Add boostrap to main config file
$aConfig = [
// some code
'bootstrap' => [
// some code
'app\extensions\ModuleBootstrap',
],
'modules' => [
// some code
'sales' => ['class' => 'app\modules\sales\SalesModule']
]
]
return $aConfig;
That's it. We can define routes only in module 'route' config.
PS: I don't like detection if (is_array($aModule) && strpos($aModule['class'], 'app\modules') === 0) (I mean NOT 'debug', 'log', 'gii' or other native Yii2 modules) maybe someone can suggest better solution?

And this will be more clean and reliable. I have found this on Yii2's github repo here.
<?php
namespace backend\modules\webmasters;
use Yii;
use yii\base\BootstrapInterface;
class Module extends \yii\base\Module implements BootstrapInterface
{
public $controllerNamespace = 'backend\modules\webmasters\controllers';
public function init()
{
parent::init();
// initialize the module with the configuration loaded from config.php
Yii::configure($this, require(__DIR__ . '/config/main.php'));
}
/**
* #inheritdoc
*/
public function bootstrap($app)
{
$app->getUrlManager()->addRules([
[
'class' => 'yii\web\UrlRule',
'pattern' => $this->id,
'route' => $this->id . '/tools/index'
],
[
'class' => 'yii\web\UrlRule',
'pattern' => $this->id . '/<controller:[\w\-]+>/<action:[\w\-]+>',
'route' => $this->id . '/<controller>/<action>'
],
], false);
}
}
and just configure your main.php config file's bootstrap section as
'bootstrap' => [
'log',
'webmasters'
]
'modules' => [
'webmasters' => [
'class' => 'backend\modules\webmasters\Module'
]
]

Separating url rules for modules is mentioned in official documentation here.
And I think this is more optimum approach unlike merging and declaring all rules in one config file.
The rules are parsed in order they are declared (this is mentioned here), so with the separation you can skip other modules urls. In case of large amount of rules it can give perfomance boost.

Related

Yii2: Cannot process to get Session

I am using Yii2 Advanced version.
This is Login Model:
namespace common\models;
use Yii;
use yii\base\Model;
use common\models\User;
class LoginForm extends Model{
public $username;
public $password;
public $rememberMe = true;
public $verifyCode;
const BACKEND_TEST = 'none';
const BACKEND_ID = 'test';
const BACKEND_USERNAME = 'backend_username';
private $user;
public function rules(){
return [ [['username','password'],'required','message'=>'{attribute}required...'],
['username','validateUser'], ['verifyCode','captcha','captchaAction'=>'login/captcha','message'=>'Wrong'],
];
}
public function validateUser($attribute,$params){
$user = User::findOne(['username'=>$this->username]);
if(!$user || (md5($this->password) != $user['password'])){
$this->addError('password','Wrong>_<...');
}else{
$this->user = $user;
}
}
public function login(){
if(!$this->user){
return false;
}
var_dump($this->userInfo());
$this->createSession();
return true;
}
private function createSession(){
//Yii::$app->session->open();
Yii::$app->set(self::BACKEND_ID,$this->user['id']);
Yii::$app->set(self::BACKEND_USERNAME,$this->user['username']);
}
public function userInfo(){
return $this->user;
}
Also, there is LoginController that I think have no issue, and next thing is when user try to login and session will be opened, and direct to site page.
Here is the sitecontroller:
namespace backend\controllers;
use Yii;
use yii\web\Controller;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
use common\models\LoginForm;
/**
* Site controller
*/
class SiteController extends Controller
{
public function actionIndex()
{
//var_dump(Yii::$app->session->get(common\models\LoginForm::BACKEND_ID));
var_dump(LoginForm::userInfo());
return $this->renderPartial('index');
}
Every time I try to login and the Error message comes out and provides:
Invalid Configuration – yii\base\InvalidConfigException
Unexpected configuration type for the "test" component: integer
How to solve the issue, and I try to get $user that stores all the data and it seems to fail?
main.php:
<?php
$params = array_merge(
require(__DIR__ . '/../../common/config/params.php'),
require(__DIR__ . '/../../common/config/params-local.php'),
require(__DIR__ . '/params.php'),
require(__DIR__ . '/params-local.php')
);
return [
'id' => 'app-backend',
'basePath' => dirname(__DIR__),
'controllerNamespace' => 'backend\controllers',
'bootstrap' => ['log'],
'modules' => ['smister' => [
'class' => 'backend\modules\smister\smister',
],],
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'errorHandler' => [
'errorAction' => 'site/error',
],
/*
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
],
],
*/
],
'params' => $params,
];
You are using
Yii::$app->set(self::BACKEND_ID,$this->user['id']);
probably for set a param value ..
but the Class yii\web\Application (alias Yii::$app->set ) contain a function named set() that register component ..(so your error : Unexpected configuration type for the "test" component: integer) in this way your code is misundestood by Yii2 because your costant BACKEND_ID = 'test'; is not a component id but the key for a param
see this reference for check
http://www.yiiframework.com/doc-2.0/yii-web-application.html
http://www.yiiframework.com/doc-2.0/yii-di-servicelocator.html#set()-detail
for you scope if you need param you can use the file param.php
returning the param you need
file config/param.php
<?php
return [
'test' => 'my_initial_value',
];
and you can access this param simply using
\Yii::$app->params['test'],
or you can simply setting runtime
\Yii::$app->params['test'] = 'Your_value';

Yii 2 and jQuery

Due project requirements I have to use a different version of jQuery than the one which comes with Yii2. I know I can disable it by using:
'assetManager' => [
'bundles' => [
// you can override AssetBundle configs here
'yii\web\JqueryAsset' => [
'sourcePath' => null,
'js' => []
],
],
],
Then I go to AppAsset:
class AppAsset extends AssetBundle
{
public $sourcePath = "#app/themes/v1/assets";
public $jsOptions = [ 'position' => \yii\web\View::POS_END ];
public $js = [
"js/lib/jquery/jquery-1.9.1.js",
];
}
But this brings one big issue, jquery loads after yii.validation, etc:
</script><script src="/assets/5b466ff1/yii.js?v=1465857632"></script>
<script src="/assets/5b466ff1/yii.validation.js?v=1465857632"></script>
<script src="/assets/5b466ff1/yii.activeForm.js?v=1465857632"></script>
<script src="/assets/98d185b3/js/lib/jquery/jquery-1.9.1.js?v=1466702133"></script>
My temporal solution is changing POS_END to POS_HEAD, but that affects page SEO right?, as far as I know is considered bad practice to load js on head.
So my question is:
How can I load jQuery from my theme assets, using POS_END and loading before yii.validation etc?
A possibility is to add it on web/js/jquery.js but I would prefer to keep it on the assets bundle.
Any ideas?
Update:
Adding this to AppAsset allows me to have control on the theme itself. It is cleaner than modifying the global config.
public function init()
{
parent::init();
Yii::$app->assetManager->bundles['yii\\web\\JqueryAsset'] = [
'js' => ['lib/jquery/jquery-1.9.1.js'],
'sourcePath' => '#app/themes/v1/assets/js'
];
}
Just Change Your Config Code as :
'assetManager' => [
'bundles' => [
'yii\web\JqueryAsset' => [
'js' => ['lib/jquery/jquery-1.9.1.js'],
'sourcePath' => '#app/themes/v1/assets/js'
]
]
]
No Need to Define jquery in AppAsset
class AppAsset extends AssetBundle
{
public $sourcePath = "#app/themes/v1/assets";
public $jsOptions = [ 'position' => \yii\web\View::POS_END ];
public $js = [
//"js/lib/jquery/jquery-1.9.1.js" Not Required
];
}

Doctrine 2 in ZF2 application: Entities are not being found "No Metadata Classes to process."

In a Zend Framework 2 Project, Doctrine 2 is not able to find my Entities and I am not sure why. To me everything looks correct, but the php ./vendor/bin/doctrine-module orm:schema-tool:create command to create the database based on the entities results in No Metadata Classes to process..
Of course I made sure first that all modules are loaded, so it's not that easy ;-) I also already did some research on that problem and it seems that most of the time, it's namespace related, but again, I don't see anything wrong there. I also generated the autoload files.
Are you able to spot anything wrong in these files, why is Doctrine not able to find the Entities? Could it be related to something else than an error in my files?
In module.config.php, Doctrine is configured like this:
'doctrine' => [
'driver' => [
'Catalog_entities' => [
'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => [__DIR__ . '/../src/Catalog/Entity']
],
],
'orm_default' => [
'drivers' => [
'Catalog\Entity' => 'Catalog_entities'
],
],
],
module.config.php has no namespace declaration since it's included into Module.php which has namespace Catalog; at the top.
The Entity at module/Catalog/src/Catalog/Entity/WorkEntity.php looks like this:
namespace Catalog\Entity;
use Doctrine\ORM\Mapping as ORM;
/** #ORM\Entity */
class Work {
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
}
First fix #Crisp his comment. File names and class names should always correspond.
If that doesn't solve it you could check if you module.config.php is loaded correctly in your Module.php like this:
/**
* #return array
*/
public function getConfig()
{
return __DIR__ . '/../../config/module.config.php';
}
I would also suggest using the namespace constant so you can simply copy your configs to different modules without changing a thing and minimizing the risk of overwriting another config when you forget to change something. So like this:
'driver' => [
__NAMESPACE__ . '_driver' => [
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => [__DIR__ . '/../src/' . __NAMESPACE__ . '/Entity']
],
'orm_default' => [
'drivers' => [
__NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
]
]
],

Yii::configure does not work in module init()

I have a module calls API, and i want to load config file for it. The guide says that i have to use function \Yii::configure. I use it, but it doesn't apply any new configs. And i tried to use array instead config file, the result is same
class API extends \yii\base\Module
{
public $controllerNamespace = 'api\client\controllers';
public function init()
{
parent::init();
// \Yii::configure($this, require(__DIR__ . '/config/main.php'));
\yii::configure($this, [
'components' => [
'user' => [
'class' => 'yii\web\UserTest',
'identityClass' => 'api\client\models\User',
],
]
]);
echo \yii::$app->user->className();
die();
}
}
How I can override config in my module ?
UPDATE
You have to use setComponents method of Yii::$app
Yii::$app->setComponents(
[
'errorHandler'=>[
'errorAction'=>'forum/forum/error',
'class'=>'yii\web\ErrorHandler',
],
'user' => [
'class' => 'yii\web\User',
'identityClass' => 'app\modules\profile\models\User',
],
]
);
OLD ANSWER
Didn't it give you errors? Your casing are wrong and so instead of "yii" in small letters use "Yii" capitalized
class API extends \yii\base\Module
{
public $controllerNamespace = 'api\client\controllers';
public function init()
{
parent::init();
\Yii::configure($this, [
'components' => [
'user' => [
'class' => 'yii\web\UserTest',
'identityClass' => 'api\client\models\User',
],
]
]);
echo \Yii::$app->user->className();
die();
}
}
I see no reason to override the application components here. I'd use #StefanoMtangoo trick but to set the component to the Module itself instead of Yii::$app:
public function init()
{
parent::init();
$this->setComponents([
'db' => [
'class' => 'yii2tech\filedb\Connection',
'path' => '#app/builder/data',
]
]);
}
Then the tricky part is to differentiate between any app's components and your module's own components. For example if my Module had a model extending yii\db\ActiveRecord I'd override its getDB() as follow (original code here):
public static function getDb()
{
return Yii::$app->getModule('api')->get('db');
// instead of: return Yii::$app->getDb();
}
So whatever the app that is using my module has or hasn't a db component it won't matter.

How to use Twig template engine with Yii2 framework?

I followed the instructions on yii2 documentation about using twig template engine
in config/web.php (which is included from index.php) I have:
'view' => [
'class' => 'yii\web\View',
'renderers' => [
'twig' => [
'class' => 'yii\twig\ViewRenderer',
'cachePath' => '#runtime/Twig/cache',
'options' => ['auto_reload' => true], /* Array of twig options */
'globals' => ['html' => '\yii\helpers\Html'],
],
],
],
in SiteController.php:
public function actionIndex()
{
echo $this->render('index.twig');
}
in views/site/index.twig I have some text:
But instead of seeing raw html I see template based on views/layouts/main.php with index.twig content used as variable in main layout.
It was required to set layout to false in order to skip layout processing during redner
class BaseController extends Controller
{
public $layout = false;
}

Categories