Multi user type design in Laravel - laravel

I'm a bit puzzled on how to best create an architecture for a multiple user type account. Watching the Laracasts episode on users and roles I figured a single User model with a Role model would be a good approach. However, rethinking the data I need for my different type of users varies, as for some users I need full address data and for others a name would be enough (to gently great them upon login). However, a Vistor might turn into a Customer. In fact, even the MerchantEmployee could become a Customer. This makes me wondering what my database design would become, e.g.
users => UserModel <fields> email, password
role: visitor => VistorModel <fields> name
role: customer => CustomerModel <fields> name, street, zipcode, ...
role: merchantowner => MerchantOwnerModel <fields> name, street, zipcode, ...
role: merchantemployee => MerchantEmployeeModel <fields> name
Would then all the Vistor, Customer, MerchantOwner and MerchantEmployee models have a user_id-column? Or how would you design such functionality?

I think to create two tables
First one is users
+----------+---------+-------+
| Col | Type | Notes |
+----------+---------+-------+
| id | bigInt | PK/AI |
| name | VARCHAR | NN |
| email | VARCHAR | NN|UN |
| password | VARCHAR | NN |
| role | ENUM | NN |
+----------+---------+-------+
The second one is user_data
+----------------------+---------+-------+
| Col | Type | Notes |
+----------------------+---------+-------+
| id | bigInt | PK/AI |
| user_id | bigInt | FK |
| address | VARCHAR | NN |
| {WHAT EVER YOU WANT) | | |
+----------------------+---------+-------+
And the relation to be one to one.
I recommend to use Laravel validation "required_if" to valid data before process it to database.
$this->validation($request, [
'address' => 'required_if:role,merchant_owner'
]);

Related

Laravel - how to handle ENUMs

Considering "enums", for instance:
gender: male, female
marital status: Single, Married, Separated, Divorced, Widowed
What is the best way to work with them in Models in a Laravel application? How to retrieve the list of possibilities and how to store them?
I tried using a table/model named "parameters". Simplifying, consider two tables:
people
|-----|-------|---------|----------------|
| id | name | gender | marital_status |
|-----|-------|---------|----------------|
| 1 | Clark | 1 | 4 |
| 2 | Bruce | 1 | 3 |
| 3 | Diana | 2 | 3 |
| 4 | Louis | 2 | 4 |
|-----|-------|---------|----------------|
parameters (I called like this, but it could be "domains", "enums", "lists", etc).
|----|----------------|---------|
| id | type | name |
|----|----------------|---------|
| 1 | gender | Male |
| 2 | gender | Female |
| 3 | marital_status | Single |
| 4 | marital_status | Married |
|----|----------------|---------|
But here I faced some doubts: how to create relationship between these models on Laravel, considering that just part of parameters table can be used on relationship? I.e. I shouldn't be able to set $person->gender = 3;
Maybe, the ideal is to create a table for each "type of parameter", but actually, there are lots of parameter types.
Other approach is to hard code the enums as config options, as presented in How to show to handle enums in Laravel?.
So, what is the best way to work with them in a Laravel application?
A static information like gender and marital status should not saved in database, something like this are saved in config files. you could add a new config file to config directory and call it for example general.php then you could save this info in it, like:
<?php
return [
'gender' => [
'male',
'female'
],
'marital_status' => [
'Single',
'Married',
'Separated',
'Divorced',
'Widowed'
]
];
And then after running php artisan config:cache you could call it when you want as config('general.gender').
Also, you could handle them in your migration file as:
$table->enum('gender', config('general.gender'));
Finally, as a validation rule you could do:
'field' => 'in:' . implode(',', config('general.gender')),

Codeigniter 3 Joins in One table

I want to display the User Name Who created, but in the database the create name is in the form of int which is the id of the author.
How can I display the id to be the real name by using Join in one table.
Or if there is another way I will try.
For the table as below
+----+--------------+-------------+
| Id | user_create | name |
+----+--------------+-------------+
| 1 | Null | Admin |
| 2 | 1 | User |
+----+--------------+-------------+
and I want to display it like this
Detail User
Name : User
User Create : Admin
if I remember correctly the CI 3 syntax
$this->db
->select(['t1.name', 't2.name user_create'])
->join('thetable t2', 't2.user_create = t1.id', 'left')
->get('thetable t1');

Relation between 3 Models in Laravel 5.1 ("like many-to-many-through")

I'm developing a simple Schools management app for an exam and have so far built a simple Many-To-Many relation between a School model and the Field model and of course a pivot table.
Now I want to relate my User model to them so that I can query for example
{user} is studying {field_of_study} at {school}
At the end I want to be able to query for example
how many users are studying at School XY or,
how many are studying Field Z or,
how many are studying Field Z at School XY.
Furthermore I want to be able to query all fields of study for a given school and vice versa.
My tables so far
Table users:
+------------+---------------------+
| Field | Type |
+------------+---------------------+
| id | bigint(20) unsigned |
| username | varchar(60) |
| password | varchar(60) |
+------------+---------------------+
Table schools:
+------------+---------------------+
| Field | Type |
+------------+---------------------+
| id | bigint(20) unsigned |
| name | varchar(60) |
+------------+---------------------+
Table fields:
+------------+---------------------+
| Field | Type |
+------------+---------------------+
| id | bigint(20) unsigned |
| name | varchar(60) |
+------------+---------------------+
Table schools_pivot:
+------------+---------------------+
| Field | Type |
+------------+---------------------+
| id | bigint(20) unsigned |
| school_id | bigint(20) |
| field_id | bigint(20) |
+------------+---------------------+
Unfortunatly I absolutly have no clue how to relate three Eloquent models in such a way and couldn't find any thing on the web (I probably searched for the wrong terms).
I'm pretty new to Laravel and Eloquent, so please be kind with me ;)
it's better to define relationship between two table and then alternately the other one.you can read this artical. maybe it will help you three-way-pivot-table-in-eloquent

Laravel 5: Chicken and egg when creating a one-to-one relationship

Let's assume I have two Model elements:
Person
Computer
In my application a Person May have one computer (a person has 0..1 computers).
So
In my User Model, I have:
public function computer()
{
return $this->hasOne('App\Model\Computer');
}
In my Computer Model I have:
public function owner_user() {
return $this->belongsTo('App\Model\User');
}
Sometimes in my application I need to create a computer. computers cannot be without owners, so when I get the information about the computer to create, I also get information about the owner.
What is the most elegant way to create BOTH new objects (computer and person), associate them with the reference IDs without doing multiple saves?
My problem is: the IDs are only being generated when the eloquent model is saved. because of that, I have a chicken and egg problem where I can't save a computer, because I don't know the ID of the owner person, and I can't save a person because I don't know the ID of the owned computer.
I could only do this by doing multiple saves and having a stub ID temporarily saved until the other objects is saved, and then I update the ID of the first one saved.
There must be a more elegant way to save two or more items at the same time, with reference IDs in place, with a single call - and without having to worry about database ID integrity in a high volume web server.
It seems the associate function is for existing records. Is there no other way for my situation than having to save person + save computer + associate?
Here's the schema of my people (users) and computers.
users
+----------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| username | varchar(45) | NO | UNI | NULL | |
| email | varchar(45) | NO | | NULL | |
| password | text | NO | | NULL | |
| remember_token | varchar(100) | NO | | NULL | |
| created_at | timestamp | YES | MUL | NULL | |
| updated_at | timestamp | YES | MUL | NULL | |
+----------------+---------------------+------+-----+---------+----------------+
computers
+----------------------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| user_id | bigint(20) | NO | UNI | NULL | |
| name | varchar(45) | NO | MUL | NULL | |
| created_at | timestamp | YES | MUL | NULL | |
| updated_at | timestamp | YES | MUL | NULL | |
+----------------------------+---------------------+------+-----+---------+----------------+
For the least amount of database work, you want to create the user first and then create the computer. This way, you can have one save for the user, and then one save for the computer, which is only two writes to the database.
If you create the computer first, you would have to create the computer, create the user, and then update the computer with the user id, which would be three writes to the database instead of two.
However, since you're creating two new records (user and computer), the fact is that you're going to have at least two writes to the database, no matter what you do.
This is what you're looking at:
// create the user object in the database (first write)
$user = \App\Model\User::create([
'name' => $request->input('user_name'),
'password' => $request->input('user_password')
]);
// the save on the relationship will update the foreign key on the
// given model and save the whole model to the database (second write).
$user->computer()->save(new \App\Model\Computer([
'name' => $request->input('computer_name')
]));
It sounds like another concern you may have is the referential integrity. What happens if the user saves fine, but there is a problem saving the computer? Do you want to keep the user, or should it seem like the user save never happened?
If you have a multiple database writes that should be treated as an all-or-nothing situation, you'll need a database transaction. If your work is done inside a database transaction, you can have all the database writes automatically rolled back if one of them fails.
DB::transaction(function() {
$user = \App\Model\User::create([
'name' => $request->input('user_name'),
'password' => $request->input('user_password')
]);
$user->computer()->save(new \App\Model\Computer([
'name' => $request->input('computer_name')
]));
});
With this code, if the save of the computer throws an exception, then the user that was created will automatically be undone. Laravel's documentation on transactions is here.
One thing to note regarding transactions is that you need to make sure the database you're using supports them. For example, for MySQL, only the InnoDB and BDB storage engines support transactions. All other storage engines (e.g. MyISAM) do not. The code will not throw any errors, but the database writes that complete will not be rolled back.
This can be achieved easily with the push() method.
Sometimes you may wish to save not only a model, but also all of its relationships. To do so, you may use the push method:
Saving A Model And Relationships
$user->push();
Example code:
$user = new Person();
$user->email = Request::get('email');
$user->password = Request::get('password');
$user->computer = new Computer();
$user->computer->name = Request::get('computer_name');
$user->push();
You can read more about Eloqeunt relations in the documentation.
You are not suppose to have foreign ids in both tables.
You should have user_id only on computers table.
So you will create a user and then a computer.

How to repeat query in Oracle Forms upon dynamically changing ORDER_BY clause?

I have an Oracle Forms 6i form with a data block that consists of several columns.
------------------------------------------------------------------------------
| FIRST_NAME | LAST_NAME | DEPARTMENT | BIRTH_DATE | JOIN_DATE | RETIRE_DATE |
------------------------------------------------------------------------------
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
------------------------------------------------------------------------------
The user can press F7 (to Enter in Query Mode, for example, he/she types JOH% in the first_name and H% in the DEPARTMENT field) , then F8 to execute the query and see the results. In this example, a list of all employees with their last name starting with JOH and working in any department starting with H will be listed. Here is a sample output of that query
------------------------------------------------------------------------------
| FIRST_NAME | LAST_NAME | DEPARTMENT | BIRTH_DATE | JOIN_DATE | RETIRE_DATE |
------------------------------------------------------------------------------
| MIKE | JOHN | HUMAN RES. | 05-MAY-82 | 02-FEB-95 | |
| BEN | JOHNATHAN | HOUSING | 23-APR-76 | 16-AUG-98 | |
| SMITH | JOHN | HOUSING | 11-DEC-78 | 30-JUL-91 | |
| | | | | | |
------------------------------------------------------------------------------
I then added a small button on top of each column to allow the user to sort the data by the desired column, by executing WHEN-BUTTON-PRESSED trigger:
set_block_property('dept', order_by, 'first_name desc');
The good news is that the ORDER_BY does change. The bad news is that the user never notice the change because he/she will need to do another query and execute to see the output ordered by the column they selected. In other words, user will only notice the change in the next query he/she will execute.
I tried to automatically execute the query upon changing the ORDER_BY clause like this:
set_block_property('dept', order_by, 'first_name desc');
go_block('EMPLOYEE');
do_key('EXECUTE_QUERY');
/* EXECUTE_QUERY -- same thing */
but what happens is that all data from the table is selected, ignoring the criteria that the user has initially set during the query mode entry.
I also searched for a solution to this problem and most of them deal with SYSTEM.LAST_QUERY and default_where. The problem is, last_query can refer to a different block from a different form, that is not valid on the currently displayed data bloc.
How can do the following in just one button press:
1- Change the ORDER_BY clause of the currently active datablock
and: 2- Execute the last query that the user has executed, using the same criteria that was set?
Any help will be highly appreciated.
You can get the last query of the block with get_block_property built-in function:
GET_BLOCK_PROPERTY('EMPLOYEE', LAST_QUERY);
Another option is to provide separate search field(s) on the form, instead of using the QBE functionality.

Resources