Yii2 rbac Role assignment is throwing error on signup - php

I am trying to assign a role named 'admin' (already present in auth_item table in database) during signup in yii2. signup() is present inside SignupForm model in common models.
$auth = Yii::$app->authManager;
$authorRole = $auth->getRole('admin');
$auth->assign($authorRole, $user->getId());
but it is throwing an error at vendor\yiisoft\yii2\rbac\DbManager.php as Trying to get property 'name' of non-object .
public function assign($role, $userId)
{
$assignment = new Assignment([
'userId' => $userId,
'roleName' => $role->name,
'createdAt' => time(),
]);
.....
}
this is the function where the error is getting triggered

Implementing a role based access control is a very easy process and you can even load your roles from the database if you want.
Step1: Creating necessary tables in the database [ You can also apply
migrations with console command yii migrate instead of step 1 ]
The first step is to create necessary tables in the database.Below is the sql you need to run in the database.
drop table if exists `auth_assignment`;
drop table if exists `auth_item_child`;
drop table if exists `auth_item`;
drop table if exists `auth_rule`;
create table `auth_rule`
(
`name` varchar(64) not null,
`data` text,
`created_at` integer,
`updated_at` integer,
primary key (`name`)
) engine InnoDB;
create table `auth_item`
(
`name` varchar(64) not null,
`type` integer not null,
`description` text,
`rule_name` varchar(64),
`data` text,
`created_at` integer,
`updated_at` integer,
primary key (`name`),
foreign key (`rule_name`) references `auth_rule` (`name`) on delete set null on update cascade,
key `type` (`type`)
) engine InnoDB;
create table `auth_item_child`
(
`parent` varchar(64) not null,
`child` varchar(64) not null,
primary key (`parent`, `child`),
foreign key (`parent`) references `auth_item` (`name`) on delete cascade on update cascade,
foreign key (`child`) references `auth_item` (`name`) on delete cascade on update cascade
) engine InnoDB;
create table `auth_assignment`
(
`item_name` varchar(64) not null,
`user_id` varchar(64) not null,
`created_at` integer,
primary key (`item_name`, `user_id`),
foreign key (`item_name`) references `auth_item` (`name`) on delete cascade on update cascade
) engine InnoDB;
Step2: Setting up the config file
Now you can set up the config file to use the authmanager as DbManager. This is done by adding the following lines to the components section of your config file
'authManager' => [
'class' => 'yii\rbac\DbManager',
'defaultRoles' => ['guest'],
],
Step3: Adding and assigning roles.
Now you can add roles by simply writing the following code to your corresponding controller.
use yii\rbac\DbManager;
$r=new DbManager;
$r->init();
$test = $r->createRole('test');
$r->add($test);
And you can assign it to the users by
$r->assign($test, 2);
http://www.yiiframework.com/doc-2.0/guide-security-authorization.html
Same answer has been provided here: Yii2 role management with rbac and database storage

I faced the same problem, the reason is because getRole() method returns null as you can see in your app.log file yii\rbac\DbManager->assign(NULL, 1222049)
SOLUTION
Use $auth->getPermission('admin') instead of $auth->getRole('admin')

Related

Loading data into Eloquent models

Okay, so lets say I have an api, which returns a object of data in json. The object looks like the following.
{
"id" : 1,
"name":"SynAck",
"sex":"Male",
"age":34,
"roles": [
{
"id":1
"name":"user"
"assigned":"2013-06-10"
},
{
"id":1
"name":"admin"
"assigned":"2014-01-09"
}
],
"created":"2014-06-10"
}
As you can see there are 2 objects returned here namely, the first one, lets call it "user" and another object, which is an array inside the first, "roles". There are 2 roles assigned to the user.
So lets say I want to load this information up into my DB verbatim. I could create 3 tables, 'user' and 'roles' and 'user_roles', with foreign keys linking user->user_roles->roles. One user has many roles.
CREATE TABLE IF NOT EXISTS `users` (
`id` INT(12) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`sex` VARCHAR(10) NOT NULL,
`age` SMALLINT(2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `roles` (
`id` INT(12) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`assigned` DATETIME ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `user_roles` (
`user_id` INT(12) UNSIGNED NOT NULL ,
`role_id` INT(12) UNSIGNED NOT NULL ,
CONSTRAINT `fk_ur_user_id` FOREIGN KEY(`user_id`) REFERENCES users(`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_ur_role_id` FOREIGN KEY(`role_id`) REFERENCES roles(`id`) ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE `idx_user_id_role_id`(`user_id`, `role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
I would then use artisan to create the Eloquent models and in the user model, I would have a hasManyThrough('roles', 'user_roles');
What would be the quickest and cleanest way of loading the data from the json into the models/tables?
Would I just have to assign the values one by one, or is there a way to map the attributes from the json objects to the eloquent model? If this is possible, how does it handle the relations?
I think that is possible to create the models for each table and then use the funcion json_decode to convert the JSON to an array and then use Eloquent methods to save then. Raw example:
$data = json_decode($json);
User::create($data);
Still, you need to extract the relationship data. Raw example:
$data = json_decode($json);
$roles = $data['roles'];
unset($data['roles']);
$user = User::create($data);
$user->roles()->create($roles);

Yii on adding new record error : 1452 Cannot add or update a child row: a foreign key constraint fails

Adding new record it gives an error:
1452 Cannot add or update a child row: a foreign key constraint fails
public function relations()
{
return array(
'data' => array(self::HAS_ONE, 'Data', 'id'),
);
}
Here is my code for adding a new record:
public function actionAdd_Record()
{
$users = new Users();
$data = new Data();
if (isset($_POST['Users']) && isset($_POST['Data'])) {
if(!empty($_POST['Users_password'])) $_POST['Users']['password']=md5($_POST['Users_password']);
$users->created_date=date('Y-m-d H:i:s');
CActiveForm::validate(array($users, $data));
$users->attributes = $_POST['Users'];
$data->attributes = $_POST['Data'];
$valid=$users->validate();
$valid=$data->validate() && $valid;
if($valid){
$users->save();
$data->save();
$this->redirect(
array('view_record',
'id'=> $users->id)
);
}
}
$this->render(
'add_record', array(
'users'=> $users,
'data'=>$data
)
);
}
Here is the first table:
CREATE TABLE IF NOT EXISTS `data` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`investment_amount` float DEFAULT '0' COMMENT '投資額',
`withdrawals` float DEFAULT '0' COMMENT '引出額',
`investment_yield` float DEFAULT '0' COMMENT '運用利回り',
`account_balance` float DEFAULT '0' COMMENT '口座残高',
`status_account` enum('open','closed') DEFAULT 'open',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
and the second table:
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(128) NOT NULL,
`password` varchar(128) NOT NULL,
`name` varchar(300) NOT NULL COMMENT '氏名',
`email` varchar(200) NOT NULL,
`user_type` enum('normal','admin') NOT NULL DEFAULT 'normal',
`created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=9 ;
and it gives this error:
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (money_investment.data, CONSTRAINT FK_data_users FOREIGN KEY (id) REFERENCES users (id)). The SQL statement executed was: INSERT INTO data (investment_amount, withdrawals, investment_yield, account_balance, status_account) VALUES (:yp0, :yp1, :yp2, :yp3, :yp4)
This isn't a yii or PHP issue, but a database issue.
You're trying to insert a record that violates the relationships you've defined regarding primary keys. Things to check would be the relationships, as well as the field lengths.
It looks like you may still have a relationship defined on a column that doesn't exist (money_investment?)
Thank you for help, I fixed my problem.
I added $data->id=$users->id; because second table had no idea about the id. See here.
if($valid){
$users->save();
$data->id=$users->id;
$data->save();
$this->redirect(
array('view_record',
'id'=> $users->id)
);
}
foreign key constraints do not allow you to enter data in child before parents..
sometimes sql engines innoDb and MyIsam ..
read out its functionality u will come to know what sql engine would be preferred to use under foreign key constraint's coming issues....
you surly update your tables.
Best way is to See in Model
public function relations()
and compare these relations with your tables.
Any additional or wrong relation might create such problem.

get mysql table structure with php

I have a script which takes a database and generate export like query for the database as a backup. I need this script for a purpose. My php code is:
mysql_query("SHOW CREATE TABLE `myTable`");
there are 17 tables in the database. I am running a for loop for all 17 tables. I get the create table query with foreign key which I do not want. It returns like-
CREATE TABLE `customers` (
`person_id` int(10) NOT NULL,
`account_number` varchar(255) DEFAULT NULL,
`taxable` int(1) NOT NULL DEFAULT '1',
`deleted` int(1) NOT NULL DEFAULT '0',
UNIQUE KEY `account_number` (`account_number`),
KEY `person_id` (`person_id`),
CONSTRAINT `customers_ibfk_1` FOREIGN KEY (`person_id`) REFERENCES `people` (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
because of the foreign key, it gets an error while running queries for creating tables. is there any way to get the sql without foreign key? like-
CREATE TABLE IF NOT EXISTS `customers` (
`person_id` int(10) NOT NULL,
`account_number` varchar(255) DEFAULT NULL,
`taxable` int(1) NOT NULL DEFAULT '1',
`deleted` int(1) NOT NULL DEFAULT '0',
UNIQUE KEY `account_number` (`account_number`),
KEY `person_id` (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Disable foreign keys during the creation of your tables.
SET FOREIGN_KEY_CHECKS=0
CREATE TABLE a
CREATE TABLE b
...
Enable keys again
SET FOREIGN_KEY_CHECKS=1
You could disable foreign keys checks as suggested elsewhere. If you really want to remove the constraints from the CREATE TABLE statements the following code could be used, it assumes that $createSql will contain the CREATE TABLE SQL statement:
$createSql = preg_replace('/CONSTRAINT[^\n]+/im', '', $createSql);
$createSql = preg_replace('/,\s+\)/im', "\n)", $createSql);

Complex reference maps in Zend_Db_Table to account for multi-column keys

I am going to attempt to keep this as simple as possible, but the use case is outside the original intention of Zend_Db I fear. It concerns a set of tables I have for tagging pages (or anything else eg. documents) in a CMS.
I have three tables:
Pages (pages)
Tags (tags)
TagLink (tags_link) which is a many-to-many linking table between Pages and Tags
Pages is a simple table (I have removed the inconsequential columns from the code below):
CREATE TABLE `pages` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
FULLTEXT KEY `search` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
Tags is quite simple as well although there is a self-referential column (parent_tag_id):
CREATE TABLE `tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tag` varchar(255) NOT NULL,
`parent_tag_id` int(11) NOT NULL DEFAULT '0',
`updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `GetByParentTagId` (`parent_tag_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
TagLink is again fairly simple:
CREATE TABLE `tags_link` (
`tag_id` int(11) NOT NULL,
`module_type` varchar(50) NOT NULL,
`foreign_key` int(11) NOT NULL,
UNIQUE KEY `Unique` (`tag_id`,`module_type`,`foreign_key`),
KEY `Search` (`module_type`,`foreign_key`),
KEY `AllByTagId` (`tag_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
The complicating factor is that TagLink is able to link against any other table in the database and not just Pages. So if for example I had a documents upload section then that could also be tagged. To facilitate this way of working there is effectively a multi-column key.
To make this clearer I will demonstrate a couple of insert queries that might be run when tags are added to a table (eg. Pages):
INSERT INTO `tags_link`
SET `tag_id` = '1',
`module_type` = 'Pages',
`foreign_key` = '2'
INSERT INTO `tags_link`
SET `tag_id` = '1',
`module_type` = 'Documents',
`foreign_key` = '3'
So as you can see the module_type column is simply an arbitrary string that describes where the foreign key can be found. This is not the name of the table however as anything with an ID can have tags linked to it even if it is not necessarily in the MySQL database.
Now to the Zend_Db_Table $_referenceMap in PageTable:
protected $_referenceMap = array(
'TagLink' => array(
'columns' => 'id',
'refTableClass' => 'Models_Tag_TagLinkTable',
'refColumns' => 'foreign_key'
),
);
But this does not take into account my arbitrary module_type column and will return any TagLink with the same foreign key. Obviously this is bad because you get TagLinks for documents mixed in with those for pages for instance.
So my question is how can I take into account this additional column when setting up this reference? The aim is to avoid having a TagLink class for each module_type as I have now.
I would imagine something like the following could explain my requirements although obviously this is not how it would be done:
protected $_referenceMap = array(
'TagLink' => array(
'columns' => 'id',
'refTableClass' => 'Models_Tag_TagLinkTable',
'refColumns' => 'foreign_key',
'where' => 'module_type = "Pages"'
),
);
My current implementation overrides the _fetch method in the Documents_TagLinkTable in the following way:
protected function _fetch(Zend_Db_Table_Select $select) {
$select->where("module_type = 'Documents_Secondary_Tags' OR module_type = 'Documents_Primary_Tags' OR module_type = 'Documents'");
return parent::_fetch($select);
}
As you can see there maybe more than one set of tags added to any object as well.
Example 3 in "Fetching Dependent Rowsets" in the Zend Framework reference demonstrates a technique you could use:
http://framework.zend.com/manual/en/zend.db.table.relationships.html
Whilst it doesnt show a "where" clause being included in the select, it should work.
Duncan

Getting ID after inserting a record that has relation in Doctrine

I'm having a trouble with getting the id after inserting a new Record using PHP Doctrine Project.
In inserting a new record in a table with no parent table (no foreign key) no problem happens.
But when inserting a related record here comes the problem, that I get only the parent id which is useless in my case.
PHP code example:
$city = new City();
$city->name = "Baghdad";
$city->country_id = 6;
$city->save();
print_r($city->identifier());
exit;
The output is:
Array
(
[id] =>
[country_id] => 6
)
Why the ID is empty!, where the row was inserted successfully!.
I need this to do more insertion that based the city.id, like another areas that has this city as a parent.
Note using the $city->id causes this error:
Warning: Invalid argument supplied for foreach() in Doctrine/Record.php on line 1151
Database SQL Dump:
CREATE TABLE IF NOT EXISTS `country` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(64) collate utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=7 ;
CREATE TABLE IF NOT EXISTS `city` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(64) collate utf8_unicode_ci NOT NULL,
`country_id` int(11) NOT NULL,
PRIMARY KEY (`id`,`country_id`),
KEY `fk_city_country` (`country_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=11 ;
ALTER TABLE `city`
ADD CONSTRAINT `fk_city_country` FOREIGN KEY (`country_id`) REFERENCES `country` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION;
BTW: I'm using the Doctrine::generateModelsFromDb() method to generate ORM model classes.
PS: using the Doctrine version 1.2.1, mysql:innodb 5.0.75-0ubuntu10.2, and php 5.2.6-3ubuntu4.5.
A co-worker discovered the solution.
It was because of this line of code:
PRIMARY KEY (`id`,`country_id`),
I used this:
PRIMARY KEY (`id`),
And it works correctly.
It's a MySQL issue I guess. It's not, it's bug in my tables design.
Does print_r($city->get('id')); hold more information?

Categories