Yii init singleton ApplicationComponent onBeginRequest - php

I am developing an component that will take a GET variable from the URL, store it in an accessible variable and place it in a cookie. If the GET variable is not set it will load the cookie value into the accessible variable:
MyComponent extends ApplicationComponent {
protected $_var = null;
public init(){
// if isset($_GET['var']), set value to $_var and cookie;
// elseif cookie set value to $_var;
// else nothing;
}
public getVar(){
return $_var;
}
}
I always want one instance of the component and I want to run init on every frontend request (even if it is not explicitly referred to).
How do I hook this up? I am aware of the onBeginRequest, but doesn't this only allow static methods?
I could hack it in and set an app component in a separate method, but that doesn't sit well.. I'd like this to be portable across sites and set-up in my config if possible.

You can just add it to the preload section of your config:
protected/config/main.php
return array(
// some parameters
'preload' => array( 'myComponent' ),
'components' => array(
'myComponent' => array(
'class' => 'path.to.your.component.MyComponent'
),
),
);
This will automatically instantiate the component on each frontend request. Please refer to the corresponding section of The Definitive Guide to Yii.

Related

execute global function automatically on running controller in yii2

We have web pages, where user will be redirected to $this->goHome(), if the session timeouts or user logouts. We have to destroy the all the session so, we have to add a function with destroying session. This function should be executed before running any action/controller in Yii2 i.e. similar to hooks in codeigniter. We have tried a helper function with destroying session and we have called the function as HomeHelper::getHelpDocUrlForCurrentPage(); in main.php layout, but the layout will be executed after running action in controller, it should work on running any controller as we have 100+ controllers. How this can be achieved, please suggest us in right way. Thanks in advance.
in
config/main.php
you could try using 'on beforeAction'
return [
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'bootstrap' => [
'log',
....
],
'on beforeAction' => function($event){
// your code ..
} ,
'modules' => [
....
],
...
];
While #ScaisEdge solution would work I believe application config is not proper place to hold application logic.
You should use filters to achieve result you want.
First you need to implement filter with your logic. For example like this:
namespace app\components\filters;
class MyFilter extends yii\base\ActionFilter
{
public function beforeAction() {
// ... your logic ...
// beforeAction method should return bool value.
// If returned value is false the action is not run
return true;
}
}
Then you want to attach this filter as any other behavior to any controller you want to apply this filter on. Or you can attach the filter to application if you want to apply it for each action/controller. You can do that in application config:
return [
'as myFilter1' => \app\components\filters\MyFilter::class,
// ... other configurations ...
];
You might also take a look at existing core filters if some of them can help you.

Yii2 Component class not loading config when instantiated

I have created a simple custom Component that extends from yii\base\Component.
namespace app\components\managers;
use yii\base\Component;
use yii\base\InvalidConfigException;
class HubspotDataManager extends Component
{
public $hubspotApiKey;
private $apiFactory;
public function init()
{
if (empty($this->hubspotApiKey)) {
throw new InvalidConfigException('Hubspot API Key cannot be empty.');
}
parent::init();
// initialise Hubspot factory instance after configuration is applied
$this->apiFactory = $this->getHubspotApiFactoryInstance();
}
public function getHubspotApiFactoryInstance()
{
return new \SevenShores\Hubspot\Factory([
'key' => $this->hubspotApiKey,
'oauth' => false, // default
'base_url' => 'https://api.hubapi.com' // default
]);
}
}
I have registered the component in my config/web.php application config, where I have also added a custom parameter.
'components' => [
...
'hubspotDataManager' => [
'class' => app\components\managers\HubspotDataManager::class,
'hubspotApiKey' => 'mycustomkeystringhere',
],
...
],
However, I find that when I instantiate my component like so:
$hubspot = new HubspotDataManager();
this hubspotApiKey config parameter is not passed into the __construct($config = []) - $config is just an empty array, so in init() the config does not set the component hubspotApiKey property the the value of hubspotApiKey in the config, so accordingly I see this from my thrown exception:
Invalid Configuration – yii\base\InvalidConfigException
Hubspot API Key cannot be empty.
However, if I call up the component like so:
Yii::$app->hubspotDataManager
it does pass this config variable in! Why is this? What extra legwork must I do to get the component to load it's application config data for standard class instantiation? I cannot find anything about this particular scenario in the docs.
NOTE: Using latest Yii2 version 2.0.15.1 using basic application template.
When you create an instance without using the Service Locator, the configuration is of course not known.
The flow is like this, Yii::$app is a Service Locator. It will pass the configuration to the Dependency Injector containter Yii::$container.
If you want to pass the config without using the Service Locator Yii::$app, you could set the container:
Yii::$container->set(app\components\managers\HubspotDataManager::class, [
'hubspotApiKey' => 'mycustomkeystringhere',
]);
and
$hubspot = Yii::$container->get(app\components\managers\HubspotDataManager::class);
the result would be the same as using the service locator Yii::$app.
You could also instantiate the a new instance of the class like this and pass the configuration to it.
$hubspot = new HubspotDataManager([
'hubspotApiKey' => 'mycustomkeystringhere',
]);

How to Override Yii2 $app->user->identity

I have a module which uses a secondary database. In it, I am trying to log in to the user table from that secondary database. The problem is that the \Yii::$app->user->identity->id is using the first database. How should I override the class to do it like this? What I got in my LoginForm.php in the module is :
public function login()
{
if ($this->validate() && $this->user) {
$isLogged = Yii::$app->getUser()->login($this->user, $this->rememberMe ? $this->module->rememberFor : 0);
//var_dump($this->user);exit;
if ($isLogged) {
$user = \frontend\modules\store\models\User::findOne(Yii::$app->user->identity->id);
$user->last_login_at = time();
$user->update();
// $this->user->updateAttributes(['last_login_at' => time()]);
}
return $isLogged;
}
return false;
}
As you can see the user class here is overridden and it is using the secondary database. But how should I override the Yii::$app->user->identity->id to use this database also? Thank you in advance!
As you are using Yii2 advanced template, you should consider adding a new sub application. Yii2 advanced template allows you to have different sessions for frontend and backend sub applications. Advanced Template on Same Domain and different sessions
Similarly, you can add a new app, in your case it may be called store. If you do it as a separate app, you can simply override identity class and even have different model for user. Help about adding new app is here.
You can override user identity in config
'user' => [
'identityClass' => 'app\models\User', // User must implement the IdentityInterface
'enableAutoLogin' => true,
// 'loginUrl' => ['user/login'],
// ...
]
more info here

Slim PHP Hook for after route, before render

Currently I'm trying to implement this behavior:
I make several routers
Inside a router, I change an environment variable
That environment variable is accessible on before.dispatch
Basically, I want to be able to do this:
$app->get('(/)', function() use ($app, $env) {
$env['title'] = 'My page title';
$app->render('index.tpl', array(
'test' => 'Test!!!',
));
});
While I also have this:
$app->hook('slim.before.dispatch', function () use ($app, $env) {
$app->view()->appendData(array(
'env' => $env
));
if (!isset($env['partial']) || $env['partial'] != true)
$app->render('header.tpl');
});
Problem is, the $env['title'] is only available in index.tpl, and not header.tpl, I'm assuming because in before.dispatch, I don't apply the route yet.
How can I achieve this behavior without having to applyHook() at every route I need to change header template variables for?
Thank you!
You've got a scope issue, and the way you're attempting to get a variable from your route into the variable in your hook simply isn't going to work.
Instead, you should be adding $env to view via the $app->render() call in your route, like so:
$app->render('index.tpl', array(
'test' => 'Test!!!',
'env' => $env,
));
When you do that, the data in $env will be available in any of the slim.after hooks.
IMPORTANT: Since you're appending 'env' to the view data in your hook, I highly recommend naming your page title array key title, or else the env data you've appended in the render method will be overwritten. The new render arguments would look like:
$app->render('index.tpl', array(
'test' => 'Test!!!',
'title' => 'My page title',
));
Your template engine should now be able to access the title in both header.tpl and index.tpl.

Variable Prefixed Routing in CakePHP

I'm creating an app in CakePHP which requires me to run 'multiple' apps within one CakePHP installation. Something like I have n controllers that behave the same for all applications, but they only differ when I call the database - anyway, I need to create a route which behaves something like this:
/app1/controller/action/a/b/c
/app2/controller/action/a/b/c
(where app1 and app2 are alphanumeric strings that can change to anything)
That would be routed to something like:
/controller/action/app1/a/b/c (or the same for app2, and so on)
The routed route could be just /controller/action/a/b/c too, but I need to have a way to access the app1 / app2 parts of the URL within the controller (for further processing within the controller). Is there a way to do this in CakePHP? Thanks.
Slightly related question: When the above is accomplished, is there a way to set a 'default' app-name (like when I attempt to access /controller/action/a/b/c it will automatically be routed to the equivalent of typing /global/controller/action/a/b/c?)
Thanks!
Effectively: What I want is just to use Routing (or any other CakePHP 'method' that can do this) to handle URLs like /foobar/controller/action/the/rest to /controller/action/the/rest and pass "foobar" to the controller, somehow. "Foobar" is any alphanumeric string.
In app/Config/routes.php add:
Router::connect(
'/:app/:controller/:action/*',
array(),
array( 'pass' => array( 'app' ))
);
This will pass the value of app as the first argument to the action in your controller. So in your controller you would do something like:
class FoosController Extends AppController {
public function view_something($app, $a, $b, $c) {
// ...
}
}
When you request /myApp1/foos/view_something/1/2/3 the value of $app would be 'myApp1', the value of $a would be 1, etc.
To connect other routes, before the above, you can add something like:
Router::connect(
'/pages/:action/*',
array( 'app' => 'global', 'controller' => 'pages' ),
array( 'pass' => array( 'app' )) // to make app 1st arg in controller
);
Instead of routing, you should use Model attribute -> dbconfig to change the databases dynamically. Also you should also have to send some arguments to the method by which you can identify which database needs to be connected with your application.

Categories