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',
],
...
];
}
Related
So I am have made a laravel authentication application that can log in/register users based on a type and depending on a type they can see different information in this case data tables. I used the laratrust package to do this and works well, but if I want to make this with a vue component that will show the data using some special data grid how would go about doing it as the controller which I am using where the data is collected but is also the place where the view which the user will see depending on the type of user is also checked. So, how will I send the json data to the vue component and what other things do I need to consider.
Here is the controller in laravel:
class DashboardController extends Controller
{
public function index()
{
if(Auth::user()->hasRole('user')){
$posts = DB::select('select * from office');
return view('userdash',['posts'=>$posts]);
}elseif(Auth::user()->hasRole('administrator')){
$posts = Post::all();
return view('administratordash',['posts'=>$posts]);
}elseif(Auth::user()->hasRole('admin')){
$people = DB::select('select * from office');
$posts = Post::all();
return view('dashboard',['posts'=>$posts,'people'=>$people]);
}
}
}
One of the things I tried was
return response(view('userdash',array('posts'=>$posts)),200,['Content-Type' => 'application/json']);
This way I can just send the json data and then render it in the view component. But I am not sure if it is working as I get back a bunch of html and some of the data in the database but not all of it. Also not sure how this can be passed to the view component. Maybe as a prop but not sure.
Any and all help and suggestions are appreciated.
As you are responding from the controller with different views, you can just check for any variables sent with the wiew in your view and once your variables are at your disposal in the view yo can serialize them using $myVar=$myVariable->toJson() if they are laravel collections or json_encode($myVariable) if they are simple arrays.
Then you can
<my-component :data ={{$myVar}}/>
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.
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
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.
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.