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

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.

Related

Yii2 fixtures not unloading, not loading with dependencies

Unloading issue
I'm trying to create fixtures in Yii2 to be able to fill my tables with some test data. I'm not using Codeception yet. I'm following the Yii2 guide on fixtures. The first table is the User table:
namespace tests\unit\fixtures;
use yii\test\ActiveFixture;
/**
* User fixture
*/
class UserFixture extends ActiveFixture
{
public $modelClass = 'common\models\User';
}
This one works when I ssh into Vagrant and load the fixture, but the entries are still there after I do an unload. According to the terminal output the fixture was successfully unloaded. What am I missing here? Should this work out of the box or should you create your own unload function?
Edit:
What did help was adding this to the User fixture:
public function unload(){
parent::unload();
$this->resetTable();
}
I would expect this to be present in unload anyhow, but I have read the (very slow) discussion in the link posted below. I don't know if the parent::unload() line was necessary, it worked without the line, but BaseActiveFixture defines it and empties $this->data and $this->_models.
Depends issue
My second fixture depends on the User fixture:
namespace tests\unit\fixtures;
use yii\test\ActiveFixture;
/**
* User Libraries fixture
*/
class UserLibrariesFixture extends ActiveFixture
{
public $modelClass = 'common\models\UserLibraries';
// Dependencies
public $depends = [
'tests\unit\fixtures\UserFixture',
];
}
This one also loads correctly according to the terminal, but the UserLibraries table remains empty. It doesn't say it will load the dependencies, but I don't know if it should say that it will.
I've kept the data files as simple as possible and the correct data appears in the User table. I only added data for the required fields for the UserLibraries table, so I don't know if that could be an issue. Is there a log file that I can check for entries regarding the fixtures?
Edit:
The UserLibraries fixture is now able to create data in the User table (but not the UserLibraries table), so disabling the foreign key check works for fixtures with dependencies. That makes me think there is an error in my data file for the UserLibraries. To check that I need a log file.
Edit2:
Fixture loading issue solution
The fixtures would not load because of an underscore in the table names. The table names userLibraries and user_libraries will result in model, controller and view files with identical file names when created with Gii. With the camelcase name table I am able to load fixtures.
Unloading fixtures is a question "under discussion" (see here). But this is my mysql workaround for it (I also commented there) and should be added to each fixture model that has some dependant table:
<?php
namespace tests\codeception\common\fixtures;
use yii\test\ActiveFixture;
class VariationFixture extends ActiveFixture
{
public $modelClass = 'common\models\Variation';
public function beforeLoad() {
parent::beforeLoad();
$this->db->createCommand()->setSql('SET FOREIGN_KEY_CHECKS = 0')->execute();
}
public function afterLoad() {
parent::afterLoad();
$this->db->createCommand()->setSql('SET FOREIGN_KEY_CHECKS = 1')->execute();
}
}
As to the loading, using codeception you can use /tests/codeception/common/_support/FixtureHelper::fixtures() to define the fixtures you want to be loaded before each test case:
public function fixtures()
{
return [
'user' => [
'class' => UserFixture::className(),
'dataFile' => '#tests/codeception/common/fixtures/data/init_login.php',
],
'room' => [
'class' => RoomFixture::className(),
'dataFile' => '#tests/codeception/common/fixtures/company/data/room.php',
],
...
];
}

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.

Laravel - Eloquent: Polymorphic relations with namespace

My situation is: a Calendar belongs to a Customer or Salesman
Because I also have classes like Event and File, I used the namespace App\Models for all my model classes.
so I set up the polymorphic relation:
in Calender.php
public function user() {
return $this->morphTo();
}
in Customer.php and Salesman.php
public function calendars() {
return $this->morphMany('App\Models\Calendar', 'user');
}
Now when i do
$calendar= Calendar::find(1); //calendar from a salesman
$calendar->user; //error here
...
I get this error message:
Symfony \ Component \ Debug \ Exception \ FatalErrorException
Class 'salesman' not found
I noticed that 'salesman' is low cased, maybe this is the problem?
and this is what I get from Laravels stacktrace
open: /var/www/cloudcube/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
// foreign key name by using the name of the relationship function, which
// when combined with an "_id" should conventionally match the columns.
if (is_null($foreignKey))
{
$foreignKey = snake_case($relation).'_id';
}
$instance = new $related; //HIGHLIGHTED
I had a similar error before on this line, when I was messing with the namespaces, so I guess it has something to do with that. Is there any way I can tell the morphTo() method to use the correct namespace?
Or is it something else causing this issue?
Also found this solution, but can't seem to get it working:
Polymorphic Eloquent relationships with namespaces
I found a solution that worked for me.
I always define relationships with the correct namespace, for example in Calendar:
public function events() {
return $this->hasMany('App\Models\Event');
}
My problem consisted out of 2 complications:
$calendar->user() with the morphTo(...) function was not working because my models were in a namespace, and morphTo(...) had no way of giving this namespace.
$salesman->calenders()->get() returned and empty list, although my relations in the database were there. I found out this is because of bindings with the query.
Solution for 1. : Writing a custom morphTo(...) function in Calendar to override the one of Laravel. I used the source of Laravels morphTo(...) as a base. The final statement of this function is return $this->belongsTo($class, $id);
There $class must be the namespaced class name. I used basic string operations to pull that off.
Solution for 2. : Writing a custom morphMany(...) function in Salesman and letting it return a MyMorphMany(...) similar to what Polymorphic Eloquent relationships with namespaces described.
The problem here is that $query that is passed to the MyMorphMany constructor has the wrong (namespaced) binding. It will look for where user_type = "App\\Models\\Salesman".
To fix this I used a custom getResults() function in MyMorphMany which overrides the default Laravels implementation, there I changed the bindings to use the correct, un-namespaced lower cased, class name. Then I called this getResults() function in the get() function of the MyMorphMany class.
I used $query->getBindings() and $query->setBindings() to correct the bindings.
Hope this saves someone else a few days of work, like it would have saved me :)

TYPO3. Passing objects as arguments in FLUID View Helpers

In the following code booksis a list of book object containing certain properties. And by clicking on the title, it goes to an action display
Fluid template is
<f:for each="books" as="book">
<f:link.action action="display" arguments="{book: book}"> {book.title} </f:link.action>
</f:for>
In controller
public function displayAction(){
print_r($this->request->getArguments());
}
The value of book here is not being set. [book] => null. I try printing the class of it, it still gives me null.
It works fine when I send the arguments as book.title instead of the entire object
What am I missing here? Is this the right way to pass objects as arguments ?
EDIT:
Initially I tried this way.
public function displayAction(\TYPO3\MyExt\Domain\Model\Book $book) {}
But this gives me
Exception while property mapping at property path "":No converter found which can be used to convert from "string" to "TYPO3\MyExt\Domain\Model\Book"
The class Book is something which I created manually and is not registered under extension builder.
You could try it with a parameter for the action:
public function myAction(Tx_MyExt_Domain_Model_Book $book) {
$this->view->assignMultiple(array(
'title' => $book->getTitle(),
'label' => $book->getLabel(),
'content' => $book->getContent()
));
}
EDIT: I updated the example.
Update:
It works with book.title because it's just a string. When you want a complete book object it needs to be found in some storage. A database e.g.. Hence that means you need a model and a repository. Also an entry in the tca and the tables files. Better create your Models with the extension builder, it's much easier and safer for the beginning.

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.