Tracking data on each request in Yii2 - yii2

What will the best place in the code to track user's last visit date or any data that should be tracked on each request to application? Is it good idea to extend yii\web\Controller?

You can use a base controller and of course it is a good idea. But there is another approach that is more elegant. You can do like below:
1 - Add a component into your components directory, for example(MyTrackingClass):
namespace app\components;
class MyTrackingClass extends \yii\base\Component{
public function init() {
//SOME CODE HERE
//SOME CODE HERE
//SOME CODE HERE
parent::init();
}
}
2 - Add MyTrackingClass component into your components array in config file:
'components' => [
'MyTrackingClass'=>[
'class'=>'app\components\MyTrackingClass'
],
//other components
3 - Add MyTrackingClass into bootstarp array in config file:
'bootstrap' => ['log','MyTrackingClass'],
Now, you can see everything you wrote in your init() method, will be executed in every request, in every module, controller, action and so on...
Please note that, if you do not need to use Events and Behaviors you can use \yii\base\Object instead of \yii\base\Component

Related

How do I override a widget method in Yii2?

I need to override the renderPageButton() method in the Yii2 LinkPager widget. The method documentation specifically says "You may override this method to customize the generation of page buttons" but I can't figure out how to do that. Thanks.
Overriding LinkPager can be done this way:
Create a new file ./widgets/MyLinkPager.php:
<?php
namespace app\widgets;
use yii\widgets\LinkPager;
class MyLinkPager extends LinkPager
{
protected function renderPageButtons()
{
// do whatever you want, it may help to
// copy code from parent::renderPageButtons()
// or even call
return parent::renderPageButtons();
}
}
And then use it this way in your view (see here: https://www.yiiframework.com/doc/guide/2.0/en/output-pagination):
use yii\widgets\LinkPager;
echo LinkPager::widget([
'pagination' => $pagination,
]);
The class you want to override is documented here.
You can override it in the following way:
Create a new directory in your yii2 app root folder, like widgets
Create a new php file (like MyLinkPager.php) and a new class in it (MyLinkPager) which extens yii\widgets\LinkPager
You can use "app\widgets" namespace (i.e. if you are working with the basic yii2 app)
In your class, implement only the function you want to override from the original class
Use your new class wherever you want instead of the original one

accessing an array in a component from the layout in yii2

I need to check the records in the notifications table at every page load of every controller.
So I wrote it in a component and the component is executed in the bootstraping process.
I need the notifications to be available in the layout so that i can show them in the notification menu.
below is what I have tried so far:
component:
namespace admin\components;
use Yii;
use yii\base\Component;
use admin\models\Notification;
class NotificationManager extends \yii\base\Component{
public function init() {
$notifications = Notification::find()->orderBy('id DESC')->asArray()->all();
//echo "<pre>"; print_r($notifications);exit;
if(count($notifications)>0){
foreach ($notifications as $notif) {
if($notif['type'] == 'courier')
$courier_notifications[] = $notif;
elseif($notif['type'] == 'order')
$order_notifications[] = $notif;
}
Yii::$app->view->params['courier_notifications'] = $courier_notifications;
Yii::$app->view->params['order_notifications'] = $order_notifications;
}
}
}
Layout:
$courier_notifications = $this->params['courier_notifications'];
I am not sure which part am I going wrong: in component or in the layout?
I appreciate your help.
Im not sure why your component execution during bootstrap fails to add the value to params.But believe it to be an overkill.
You can rather move the logic to component method and access in layout whenever necessary
Component.
namespace admin\components;
use Yii;
use yii\base\Component;
use admin\models\Notification;
class NotificationManager extends Component{
public function notifications($type = 'courier') {
$notifications = Notification::find()
->where(['type' => $type])
->orderBy('id DESC')
->asArray()->all();
return $notifications;
}
}
Add the component class under Components section in your config file
'notificationManager ' => [
'class' => 'admin\components\NotificationManager'
]
Layout
$courier_notifications = yii::$app->notificationManager->notifications('courier');
If you really want to go bootstrap mode, you need to implement yii\base\BootstrapInterface and put your logic in the bootstrap($app) method in order for the param to be available site-wide by setting the value of Yii::$app->params['notifications'] to the result of your logic.
Another common approach is to add a new method public function displayNotifications or whatever you want to name it, to your component, move all the logic in it and then in your layout/view etc., call it with Yii::$app->notificationManager->displayNotifications(). You can also pass additional parameters to it and enhance your logic.
notificationManager has to be replaced with the name you registered your custom component in the Yii app config (web.php for basic app, main.php for advanced app).
LE - If you only registered your component for bootstrap, you should also register it in the components array.
'notificationManager' => [
'class' => '\admin\components\NotificationManager'
]

Meaning of Yii::$app->user->identity->role->role_name;

In a book called Yii2 for Beginners, which is mainly about the advanced template, I have encountered the following unexplained code, which seems relevant to RBAC:
$userHasRoleName = Yii::$app->user->identity->role->role_name;
What exactly does this mean? For example, I guess that this:
Yii::$app->user
refers to this file:
vendor\yiisoft\yii2\web\User.php
Is this correct?
In any case, what does the rest of the code refer to? Specifically:
->identity->role->role_name
In the above User.php file, I have not been able to find anything like "function identity()", so it can't be that. I have found numerous $identity variables, but I don't know which one the code might be referring to. And there is no $role variable at all.
What is this code referring to:
Yii::$app->user->identity->role->role_name;
Yii described magic methods like __get, __set and so on, to get access for inaccessible properties. Oftenly such methods begins from get or set (in Yii implementation it is). To get access to ->identity, \yii\web\User has method getIdentity. This method return identity wich you described in config with identityClass property for user component. Oftenly identityClass is a AR model which implements IdentityInterface.
'components' => [
'user' => [
'identityClass' => 'common\models\User',
]
]
To get access to ->role for example you must to create a new method
namespace common\models;
class User extends ActiveRecord implements IdentityInterface {
public function getRole(){
// if user can have only one role
return current( \Yii::$app->authManager->getRolesByUser( $this->id ) );
}
}
Btw implementation of ->role->role_name may be very different.

Execute my code before any action of any controller

I would like to check if my user have filled certain fields in his profile before he can access any action of any controller.
For example
if(empty(field1) && empty(field2))
{
header("Location:/site/error")
}
In yii1 I could do it in protected\components\Controller.php in init() function
But in yii2 I'm not sure where to put my code. I cannot modify core files, but not sure what to do in backend of my advanced application to make it work.
I know I can user beforeAction() but I have too many controllers to do that and to keep track of every controller
In case you need to execute a code before every controller and action, you can do like below:
1 - Add a component into your components directory, for example(MyGlobalClass):
namespace app\components;
class MyGlobalClass extends \yii\base\Component{
public function init() {
echo "Hi";
parent::init();
}
}
2 - Add MyGlobalClass component into your components array in config file:
'components' => [
'MyGlobalClass'=>[
'class'=>'app\components\MyGlobalClass'
],
//other components
3 - Add MyGlobalClass into bootstarp array in config file:
'bootstrap' => ['log','MyGlobalClass'],
Now, you can see Hi before every action.
Please note that, if you do not need to use Events and Behaviors you can use \yii\base\Object instead of \yii\base\Component
Just add in config file into $config array:
'on beforeAction' => function ($event) {
echo "Hello";
},
Create a new controller
namespace backend\components;
class Controller extends \yii\web\Controller {
public function beforeAction($event)
{
..............
return parent::beforeAction($event);
}
}
All your controllers should now extend backend\components\Controller and not \yii\web\Controller. with this, you should modify every controller. I would go for this solution.
I believe you might also replace 1 class with another (so no change to any controller necessary), something like
\Yii::$classMap = array_merge(\Yii::$classMap,[
'\yii\web\Controller'=>'backend\components\Controller',
]);
See more details here: http://www.yiiframework.com/doc-2.0/guide-tutorial-yii-integration.html and I took the code from here: https://github.com/mithun12000/adminUI/blob/master/src/AdminUiBootstrap.php
you can put this in your index.php file. However, make sure you document this change very well as somebody that will come and try to debug your code will be totally confused by this.
Just i think this code on config file can help you:
'on beforeAction' => function ($event) {
// To log all request information
},
'components' => [
'response' => [
'on beforeSend' => function($event) {
// To log all response information
},
],
];
Or, https://github.com/yiisoft/yii2/blob/master/docs/guide/security-authorization.md use RBAC, to restrict access to controllers actions one at a time based on rules. Why would you want to restrict access to controller actions based on user fields is beyond me. You will not be able to access anything (including the login form) if you put a restriction there.

How to custom Error Pages in Kohana 3.0

I've been trying to find a full explanation on how to custom error pages in Kohana 3.0 and I haven't been lucky so far. So, based on the solution provided by Kohana Guide can anyone show me how to do it?
When I say full explanation I mean, the location of the classes, their names, which ones to extend, full code, and please, a view for one of the errors.
Many thanks.
I suggest you read http://kohanaframework.org/3.0/guide/kohana/conventions. You can work out the files that need to be created for yourself based on the class names and how Kohana autoloads. The beauty of Kohana for me is that it doesn't try and do everything for you and for that reason its is really important to read the documentation in my opinion.
Note: I haven't used Kohana 3.0 in particular but this should apply.
For example, in part 1, is this code:
<?php defined('SYSPATH') or die('No direct access');
class HTTP_Response_Exception extends Kohana_Exception {}
Kohana uses a 'cascading filesystem' so you would add the code shown in 2 to
application/classes/kohana/exception.php
Inside that file you would extend the Kohana exception handler
class Kohana_Exception extends Kohana_Kohana_Exception {
public static function exception_handler(Exception $e)
{
...
}
}
The route shown gets added to your applications bootstrap which is under
application/bootstrap.php
As errors are being routed you can tell what the controller will be:
Route::set('error', 'error/<action>(/<message>)', array('action' => '[0-9]++', 'message' => '.+'))
->defaults(array(
'controller' => 'error_handler'
));
So there will be a controller created at:
application/classes/controller/error_handler.php
This will look like:
<?php defined('SYSPATH') or die('No direct script access.');
class Controller_Error_Handler extends Controller_Template {
...
public function action_404()
{
$this->template->title = '404 Not Found';
// A view example
$view = View::factory('error/404');
$view->render();
...
}
}
Your views would then (possibly) be placed under:
application/views/error/404.php
The reason the documentation is brief is because the same answers do not apply to everyone. For example the majority of people (I know) use their own templates etc.