Loading data into Eloquent models - php

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);

Related

Yii2 rbac Role assignment is throwing error on signup

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')

How do you query a child and parent in laravel eloquent

I want to figure out how to query to get a result that was either created by the current user or the current users parent.
I have two tables users and workouts. Both tables have a created_by column, which stores the user id of whoever created said user or workout record.
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_by` int(10) unsigned DEFAULT NULL,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `users_created_by_foreign` (`created_by`),
CONSTRAINT `users_created_by_foreign` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`)
);
CREATE TABLE `workouts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_by` int(10) unsigned NOT NULL,
`description` text COLLATE utf8_unicode_ci,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `workouts_created_by_foreign` (`created_by`),
CONSTRAINT `workouts_created_by_foreign` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`)
);
In my user model I have a function to return all the workouts that the user has created.
public function createdWorkouts(): HasMany
{
return $this->hasMany(Workout::class, 'created_by');
}
What I want to figure out is, how can I query to find a workout using a ID but only for workouts the current user has created or the creator of the current user has created. Below is my current route logic, which only returns a workout created by the current user.
$user = User::find(1); // This would come from the JWT
if (!$workout = $user->createdWorkouts()->find($args[‘workout_id’])) {
return $response->withJson([], 404);
}
For this instance, I probably wouldn't use an eloquent relationship as it could be either the users id or their created_by value.
Instead, I would change the method on your user model to read:
public function createdWorkouts()
{
return Workout::where('created_by', $this->id)->orWhere('created_by', $this->created_by)->get();
}
Then you can do $user->createdWorkouts() which will return you a collection of all workouts where it was created by the user, or created by the user that created the user.
As it is a collection, you then have access to all of the other methods on a collection such as find etc

JSON array sorting structure

Is there an easy (easier) way to split up my JSON? I'm not so good with JSON but here is my situation;
I have a table of payments and a table of locations. There are 10 payments at location A and 5 payments at location B.
my payments table is like;
CREATE TABLE `payments` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`amount` decimal(15,2) NOT NULL,
`location_id` int(7) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=63678 DEFAULT CHARSET=latin1;
my locations table is like;
CREATE TABLE `locations` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL DEFAULT '',
`auth_key` varchar(32) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
So I'd like my JSON to be something like;
location:[
{"A":
"auth_key": "--justanexample--",
"payments":[
{"id":"1", "amount":"15.15"},
{"id":"3", "amount":"11.85"},
{"id":"4", "amount":"18.95"}
]},
{"B":
"auth_key": "--justanotherexample--",
"payments":[
{"id":"2", "amount":"15.00"},
{"id":"5", "amount":"12.77"},
{"id":"6", "amount":"13.45"}
]}
]
At the moment, I am using Eloquent and I'm creating two nested loops, first to get all the locations and then to get all the payments per location;
$locations = $app->location->get();
foreach ($locations as $location) {
$payments = $app->payment->get();
foreach ($payments as $payment) {
echo json_encode($payment);
Or should I use an array function here instead and then encode that?

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);

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