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.
Related
I am trying to get a ServiceManager instance in my controller to use a factory for Db\Adapter.
I added to module/Application/config/module.config.php:
'service_manager' => [
'factories' => [
Adapter::class => AdapterServiceFactory::class,
],
],
To config/autoload/local.php I added the following lines:
'db' => [
'driver' => 'Mysqli',
'database' => 'mydb',
'username' => 'myuser',
'password' => 'mypassword',
]
An now I want to access the ServiceManager in my module/Application/src/Controller/IndexController.php. How do I do that?
I tried $sm = $this->getPluginManager(); without success. If I run $serviceManager->get(Adapter::class) with the PluginManager it gives me an error:
Too few arguments to function Zend\Db\Adapter\Adapter::__construct(), 0 passed in (...)\vendor\zendframework\zend-servicemanager\src\Factory\InvokableFactory.php on line 30 and at least 1 expected
What can I do, to get a ServiceManager that will get my that Adapter object?
I changed the controller factory from
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class,
],
],
to
'controllers' => [
'factories' => [
Controller\IndexController::class => function(ContainerInterface $serviceManager) {
return new Controller\IndexController($serviceManager);
},
],
],
I also added a getServiceConfig() method to the module.config.php and added a constructor to the IndexController, which receives the ServiceManager. Now I have access inside the controller.
But my question is now: is there a nicer, a more "zend like" way to achieve this?
Thanks to SO's great related topics I finally found the answer. ServiceManager in ZF3
It seems to be done by using Controller Factories, almost like I did.
I'm experienced with ZF1 and now I'm learning ZF3, I wanted to do a simple thing: set the DB configuration in the configuration file, and then get the db adapter at the controller. It took me awhile to figure it out as the official documents have millions of options for different customization. So I'm posting my answer to help anyone looking.
1- Add the db credentials in config/autoload/global.php or config/autoload/local.php, like this:
<?php
return [
'db' => [
'driver' => 'Pdo_Mysql',// can be "Mysqli" or "Pdo_Mysql" or other, refer to this link for the full list: https://docs.zendframework.com/zend-db/adapter/
'hostname' => 'localhost',// optional
'database' => 'my_test_db',
'username' => 'root',
'password' => 'root',
],
];
2- In module/YOUR_MODULE_NAME/config/module.config.php, add this under the controllers factories section:
return [
//...
'controllers' => [
'factories' => [
//...
// Add these lines
Controller\MycontrollernameController::class => function($container) {// $container is actually the service manager
return new Controller\MycontrollernameController(
$container->get(\Zend\Db\Adapter\Adapter::class)
);// this will pass the db adapter to the controller's constructor
},
//...
]
]
//...
];
3- Finally, in your controller module/YOUR_MODULE_NAME/src/Controller/MycontrollernameController, you can get and use the db adapter:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Db\Adapter\Adapter;
class MycontrollernameController extends AbstractActionController
{
private $db;
public function __construct($db)
{
$this->db = $db;
}
public function indexAction()
{
$result = $this->db->query('SELECT * FROM `my_table`', Adapter::QUERY_MODE_EXECUTE);
echo $result->count();// output total result
return new ViewModel();
}
}
There is another way to achieve the same thing by creating a factory for your controller, and inside that factory pass the db adapter to the controller. For beginners trying out ZF3 at hello-world level like me, I think that's too much.
With ZF2, it was very simple to register custom view helpers for custom form elements.
You could simply create an element like such:
use Zend\Form\Element;
class Recaptcha extends Element
{
protected $attributes = [
'type' => 'recaptcha',
];
protected $secret;
public function getSecret()
{
return $this->secret;
}
public function __construct($secret)
{
parent::__construct();
$this->secret = $secret;
}
}
Create a matching helper:
use Zend\Form\ElementInterface;
use Zend\Form\View\Helper\FormElement;
class Recaptcha extends FormElement
{
public function render(ElementInterface $element)
{
return '<div class="form-group">
<div id="register_recaptcha">
<div class="g-recaptcha" data-sitekey="' . $element->getSecret() . '"></div>
</div>
</div>
<script src="//www.google.com/recaptcha/api.js"></script>';
}
}
And then wire it up at config:
return [
'form_elements' => [
'factories' => [
Recaptcha::class => RecaptchaFactory::class,
],
],
'view_helpers' => [
'invokables' => [
'recaptcha' => RecaptchaHelper::class,
],
],
];
IIRC, you would have to wire it up in the Bootstrap too
public function onBootstrap($e)
{
$application = $e->getApplication();
$services = $application->getServiceManager();
$services->get('ViewHelperManager')->get('FormElement')->addType('recaptcha', 'recaptcha');
}
Upgrading a project from ZF2 to ZF3, the custom element now appears as a textfield.
If I call the helper directly on the field, it renders properly:
{{ recaptcha( user_form.get('recaptchafield') ) | raw }}
It's the automatic association that's seemingly vanished. Such that calling formRow on each doesn't invoke the helper.
Anyone have the quick fix? Hopeful to save myself from reviewing the actual zend-form and zend-view code.
Thank you!
I had the same issue and I resolved it by replacing
$services->get('ViewHelperManager')->get('FormElement')->addType('recaptcha', 'recaptcha');
with
$services->get('ViewHelperManager')->get('FormElement')->addClass(Recaptcha::class, RecaptchaHelper::class);
The config also needed some adaption. It now reads like this:
return [
'form_elements' => [
'factories' => [
Recaptcha::class => RecaptchaFactory::class,
],
],
'view_helpers' => [
'invokables' => [
RecaptchaHelper::class => RecaptchaHelper::class,
],
],
];
Hope that helps someone else find the issue faster ;)
Invokables no longer exist in ZF3. You need to move your recatpcha view helper to the factories key instead and wire it up to Zend\ServiceManager\Factory\InvokableFactory::class
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';
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
];
}
I want to change default directory ("pages") of static pages views, using yii\web\ViewAction. Documentation says:
You may configure yii\web\ViewAction::$viewPrefix to change the
directory for searching these views.
Question: where and how need I to change this property? I tried to do it in controller:
use yii\web\ViewAction;
...
public function actions()
{
(new ViewAction)->viewPrefix = ''; //wanted to do it just as '#app/views/site/', without any subdirectory ('pages' or others)
return [
'stat' => [
'class' => 'yii\web\ViewAction',
],
];
}
But no success.
Try to set viewPrefix to null
public function actions()
{
return [
'stat' => [
'class' => 'yii\web\ViewAction',
'viewPrefix' => null,
],
];
}