Codeigniter 3 Joins in One table - codeigniter

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

Related

Multi user type design in 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'
]);

How to make multiple roles in Spatie Laravel package?

Is possible with Spatie's laravel-permission package to achieve something like what Facebook have with their permissions for pages?
Case study,
On Facebook you can create a page, then automatically you'll become Admin (assuming it's a role) of that page.
Then you can invite you friend and make him/her a Publisher role
After that, your friend can also invite you to their Page and make you an Editor ( that's another role for you)
so at the end you will have two roles for two different profiles/Pages.
I want to achieve something like that on my app.
EDIT
what i know for now is that i can use:
$user->assignRole('editor');
and i can check if the user has a roles with :
$user->hasRole('writer')
But the thing is how do i link that role with the page that i want them to manage?
Like if you have admin role in one page and editor role in another page.
and if you just check if $user->hasRole('admin') the results will always be true. so i want to know if there is anything i can do to assign and also check which page are you admin for.
I hope i am making sense.
Assuming your table structure looks like this:
Pages table
|---------------------|------------------|------------------|------------------|
| id | name | created_at | updated_at |
|---------------------|------------------|------------------|------------------|
| 12 | PHP masters | 2019-02-31 | 2019-03-12 |
|---------------------|------------------|------------------|------------------|
Pages admin
|---------------------|------------------|------------------|------------------|
| id | page_id | role_id | user_id |
|---------------------|------------------|------------------|------------------|
| 1 | 12 | 2 | 2 |
|---------------------|------------------|------------------|------------------|
Roles table
|---------------------|------------------|------------------|------------------|
| id | name | created_at | updated_at |
|---------------------|------------------|------------------|------------------|
| 12 | Editor | 2019-02-31 | 2019-03-12 |
|---------------------|------------------|------------------|------------------|
| 12 | System Admin | 2019-02-31 | 2019-03-12 |
|---------------------|------------------|------------------|------------------|
| 12 | Page Admin | 2019-02-31 | 2019-03-12 |
|---------------------|------------------|------------------|------------------|
Now inside User model add this method
#do not forget to import Roles
#use Spatie\Permission\Models\Role;
public function can_manage($page_id) : bool;
{
#Backdoor for the system admin
if ($this->hasRole(['System Admin'])) {
return true;
}
#now we check if the user has the role for that page
$role = Role::select('name')
->join("page_admins", 'page_admins.role_id', '=', 'roles.id')
->where("page_id", $page_id)
->where("user_id", $this->id)
->first();
#return true if roles was found
if (!empty($role)) {
return true;
}
#else false
return false;
}
Lastly if you want to check if the user can access the page's admin you use:
#for jwt-auth
#$user = $request->user();
if ( $user->can_manage($page_id) )
{
#then do you things
}
Now that you saw how to check if someone is among page administration, you can get creative with that. you can create a method that :
#Get user role in the page
#etc
Yes. You can create the roles with artisan and assign them in php.
php artisan permission:create-role editor
php artisan permission:create-role publisher
$user->assignRole(['editor', 'publisher']);
all of this is easily available in the documentation

Connect a table to many to many table

I have a many to many table from goods and companies and i want to connect it with a one to many table
company_good
+----------------+------------+
| company_ID | good_ID |
+----------------+------------+
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
+----------------+------------+
sales
+----------+----------------+--------------+----------+
| id | date | company_id | good_id |
+----------+----------------+--------------+----------+
| 1 | 2019-02-01 | 1 | 1 |
| 2 | 2019-02-01 | 2 | 1 |
| 2 | 2019-02-01 | 1 | 1 |
+----------+----------------+--------------+----------+
what i want to ask is are there any naming column on sales based on laravel rule? what i know is when a table posts want to connect it to table users the naming on table post should be user_id. but in this case how do i name it?
First off, you'll definitely want to use Eloquent for this.
The column names can be whatever you want them to be. When you set up a many to many model method you can define the related column names in there
Here's an extract from the Laravel documentation:
In addition to customizing the name of the joining table, you may also
customize the column names of the keys on the table by passing
additional arguments to the belongsToMany method. The third argument
is the foreign key name of the model on which you are defining the
relationship, while the fourth argument is the foreign key name of the
model that you are joining to:
return $this->belongsToMany('App\Role', 'role_user', 'user_id',
'role_id');
Read more at https://laravel.com/docs/5.8/eloquent-relationships#many-to-many
I suggest to add an id column to company_good table and name corresponding model as CompanyGood and instead of company_id and good_id in sales table use company_good_id column

Query data based on pivot table

I have a database design which essentially takes the following form (I have removed a lot of fields as they are not really needed for demonstration purposes). I have a users table
users
+----+---------------+-----------------+
| id | name | email |
+----+---------------+-----------------+
| 1 | ProjectA | Something |
+----+---------------+-----------------+
A user can have many projects
projects
+----+---------------+-----------------+-----------------+
| id | name | description | user_id |
+----+---------------+-----------------+-----------------+
| 1 | ProjectA | Something | 1 |
+----+---------------+-----------------+-----------------+
So that is straight forward enough and very easy to query. If I want all projects for the logged in user I can do
$loggedInUser = Auth::user()->getFirstName() . ' ' . Auth::user()->getLastName();
$loggedInUserId = User::where('userName', '=', $loggedInUser)->first();
$projectss = Project::all()->where('user_id', $loggedInUserId);
This is where things get a little more tricky. I have a pivot table for a users groups. It is essentially this
users_user_groups
+----+---------------+-----------------+
| id | user_id | group_id |
+----+---------------+-----------------+
| 1 | 1 | 2 |
+----+---------------+-----------------+
I then have a user_groups table
user_groups
+----+---------------+
| id | group_name |
+----+---------------+
| 1 | Group A |
+----+---------------+
If I want to find what groups a user is a part of I can do
$userGroups = Auth::user()->getGroups();
My question is this. Above, I demonstrate how I can get all projects for a user. Now I know there is a user_group called Group A. What I essentially want to do is get all projects where the user is apart of Group A. So if 5 users create a project and they are all in Group A, then I should be returned 5 projects.
How would I go about doing something like this?
Thanks
Firstly you can get user id using simply Auth::user()->id
Assuming you are trying to get all the projects that belongs to users of a group You can do this.
$group = Group::with('users.projects')->find(1);
Group model
public function users()
{
return $this->belongsToMany('App\User');
}
User Model
public function projects()
{
return $this->hasMany('App\Project');
}
Of course you would have to make the appropriate relation methods depending on the forign/primary keys you used.

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.

Resources