Laravel 5 Eloquent Many to Many 2ndary table - php

I have a question that I'm not sure how to solve or even phrase for finding an answer.
I have a Company Model & User Model that are related Many-to-Many.
I want to have a user_pins table. A user can belong to multiple companies and therefore have a different pin within each company. The pin is unique within a company, no two users within a company can have the same one. Users in different companies can have the same one.
So for the company it is One to Many, for the user it is One to Many, but altogether it is many to many, Im not sure if that makes sense.
I have the table set up as
Schema::create('user_pins', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->integer('company_id')->unsigned();
$table->string('pin');
$table->foreign('user_id')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade');
$table->foreign('company_id')->references('id')->on('companies')->onUpdate('cascade')->onDelete('cascade');
$table->primary(['user_id', 'company_id', 'pin']);
});
How do I relate this table in the models and use Eloquent to access/create/update it so it stores both the user and company?

Firstly, I would change the name to company_user so that it follows the same naming convention that Laravel would use out of the box. (you wouldn't have to do this as you can specify the pivot table name in the relationship but if there isn't a reason to stick with user_pin it makes sence to follow convention :) )
Then I would remove the primary key from being a compound of all 3 fields and just have it on the company_id and user_id.
Lastly, as a PIN only has to be unique for a company, I would just put the unique index on those two columns e.g.
Schema::create('company_user', function (Blueprint $table) {
$table->integer('company_id')->unsigned()->index();
$table->integer('user_id')->unsigned()->index();
$table->string('pin');
$table->foreign('user_id')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade');
$table->foreign('company_id')->references('id')->on('companies')->onUpdate('cascade')->onDelete('cascade');
$table->primary(['company_id', 'user_id']);
$table->unique(['company_id', 'pin']);
});
Then for the relationship in the model I would have something like:
return $this->belongsToMany('App\Company')->withPivot('pin');
and
return $this->belongsToMany('App\User')->withPivot('pin');
Examples of use with pivot
All user pins for a company:
$company->users->lists('pivot.pin');
Users pin for a specific company
$user->companies()->where('id', $id)->get()->pivot->pin;
Users pin for the first company relationship:
$user->companies->first()->pivot->pin;
Hope this helps!

Related

Laravel 7 physical relationship between tables not present

I followed a tutorial about many-to-many relationships with Laravel (7 in my case).
The result is good, I learned a lot, but what I find strange is that I do not have physical relationships between the different tables.
I created a relationship many to many, which should link 3 tables, products, categories and products_categories
My questions :
1- Is it essential to have a physical relationship in the schema of the database?
2- How can I make these relationships appear in my diagram?
Here is a current photo of the database schema :
In this database, I have links between tables :
The Laravel relationships are not the same as your database relationships (MySQL, or others).
You don't need to have a database relationship to have your application working. it is really depending on what you are trying to achieve.
If you want to see the relationships between your tables, make sure to specify the foreign keys in your migration Schema (https://laravel.com/docs/7.x/migrations#foreign-key-constraints) such as:
Schema::table('posts', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
});
For pivot tables you can also use a migration Schema as follow:
Schema::table('category_product', function (Blueprint $table) {
$table->unsignedBigInteger('category_id');
$table->unsignedBigInteger('product_id');
$table->foreign('category_id')->references('id')->on('categories');
$table->foreign('product_id')->references('id')->on('products');
});

How to use two foreignId in Laravel

I have two tables like:
User:
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('loginid')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
IP:
Schema::create('i_p_s', function (Blueprint $table) {
$table->id();
$table->string('address')->unique();
$table->foreignId('user_id')->nullable();
$table->string('hostname');
$table->string('description')->nullable();
$table->timestamps();
$table->index('user_id');
});
IP Model:
public function User() {
return $this->belongsTo(User::class);
}
User Model:
public function IPs() {
return $this->hasMany(IP::class);
}
The user_id column means this IP is using by which user.
And now I want to add a new column last_modified which means who is the last editor of this row.
So I think the last_modified should be $table->foreignId('user_id')->nullable(); too.
But how to define the relationship in IP model?
Additionally, I call the user_id like this now.
$ips = IP::with('user')->get();
#foreach ($ips as $ip)
{{ $ip->user }}
#endforeach
So how can I call the last_modified after the definition?
Thanks a lot
As shown in the docs (https://laravel.com/docs/7.x/migrations#foreign-key-constraints),
$table->foreignId('user_id')->nullable();
is just a shortcut of the "old" way
Schema::table('i_p_s', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
});
The problem with your code would be, that you need also the constrained()-method. It will dissolve a given column name like user_id into like "Laravel, please use the column id of the table users here".
I'm not sure if the nullable()-method will be useable for this shortcut.
In the same way, your relations will be dissolved within your models. If you're not adding additional values to the belongsTo() and haveMany()-methods, Laravel will try to find its way through your databse by assuming standard naming conventions for primary keys and table names (if the table names are not set within your model).
primary keys are assumed as id. This is also the reason why $table->ip() works.
table names are assumed as the plural of the model name. That means also, you have to make sure to set the name of your i_p_s table within your IP-model as it does not follow the convention. Event better would be to think about an adaption to the convention and call your table ips.
foreign keys should be (to be able to dissolve things that way) named by the singular table name, underscore, primary key. in other words user_id.
So, your assumption should be right apart from the fact that you cannot add a second column called user_id. For your second foreign key, your code should look like the "normal/ traditional" way of doing this.
Schema::table('i_p_s', function (Blueprint $table) {
$table->unsignedBigInteger('last_modified')->nullable();
$table->foreign('last_modified')->references('id')->on('users');
});
I'm pretty sure that this will work, although I didn't tested this yet. Unfortunately, I'm not sure if you can provide the column name and table also within the constrained method. If so, that would be pretty handy. Give it a try, otherwise use the traditional way.
The relation within the model should then look like this:
public function hasChanged() {
$this->hasMany(IP::class, 'last_modified', 'id');
}
last_modified is the foreign key within the i_p_s-table while id is the local column of your owning User-model.
Bringing this into reverse for the IP-model:
public function wasChangedBy() {
$this->belongsTo(User::class, 'last_modified', 'id');
}
In both cases you can dispense on setting the 'id' column as primary key of your users-table as it is standard.
The relations are the same as in your example because of the construction/ architecture. In 99% this is always a one-to-many relation.
Last but not least, it was a bit strange to see this construction of the same foreign key two times referencing in the same table. But I found this post, which says it is eventually totally normal to do so.
Mysql: using two foreign keys to the same table
The only other way I could think of would be to have an intermediate table between i_p_s and users but this would lead to a loop in your database between these two tables which also is a thing you should avoid. So I would say this is ok.
Hope this helps ;)

How to setup laravel hasOne relationships with itself?

I am working on an application in Laravel where users can be matched up with one users. I am trying to figure out how to setup the hasOne relationship between users.
Here is how I have done it so far. Users when they are created have a match_id which represents the user that they are matched to. When they are matched this record gets updated. Here is my table:
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('event_id');
$table->foreign('event_id')->references('id')->on('events')->onDelete('cascade');
$table->unsignedBigInteger('match_id')->nullable();
$table->foreign('match_id')->references('id')->on('users')->onDelete('cascade');
$table->string('name');
$table->string('email');
$table->timestamps();
});
In the user model I have set up this relationship:
public function match() {
return $this->hasOne(User::class);
}
but as of right now I cannot do something like this and get the expected matched user's information:
$user->match()->get();
After reading the docs here, I see that in such situations you can setup the foreign key and local key. Nevertheless I am having a difficult time figuring this out.
Could someone give me some guidance on how I could do this? I am also open to any suggestions on different implementations.
-----edit------
after discussing with some people in the comments I made some progress in my endeavors. I am able to get some results and have experimented by logging the matches. For whatever reason though I am getting the wrong matches. Look below for more information:
in my controller I log the users name and match after it has been formed:
Log::error($user->match);
Log::error('has matched with:');
Log::error($user);
Log::error('participant:');
in the database here are the users and their matches:
yet in the logs, the match is not the same:
Since the match_id is on the users table, the relationship should be belongsTo, not hasOne.
belongsTo is specified on the model that contains the foreign key.
Also, you should specify the foreign key:
return $this->belongsTo('App\User', 'match_id');

laravel transfer relation from user to another

I have a table called lands which has the lands that belong to users, the user can occupy another user land and take it. So, I want to change the user_id in lands table to make it belong to the occupier.
Lands table:
$table->increments('id');
$table->string('name');
$table->smallInteger('size');
$table->smallInteger('type')->default(0)->comment('0 is village, 1 is city ');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')
->onUpdate('cascade')->onDelete('cascade');
is there a method in eloquent to change the user_id to match new user ?
There are multiple ways via Eloquent to do this:
Making the user_id fillable:
if you make the user_id fillable within the Land model, you can fill it via a simple
$land->update(['user_id' => $newUser->id])
In your code.
Another choice is the recommended Eloquent way:
Use
$land->user()->dissociate();
$land->user()->associate($newUser->id);
$land->save();
To change the user currently stablished under the BelongsTo relationship.

Multiple "statuses" for many-2-many table relationship in a Laravel Data Model? Best approach?

I am new to Laravel (only been coding a few months). I've created a DB model that connects two tables "Players" and "Teams" via a pivot table.
class Player extends Model
{
public function teams()
{
# With timetsamps() will ensure the pivot table has its created_at/updated_at fields automatically maintained
return $this->belongsToMany('p4\Team')->withTimestamps();
}
}
class Team extends Model
{
public function players()
{
return $this->belongsToMany('p4\Player')->withTimestamps();
}
}
Players can be members of (many) teams, "own" teams, be "recruited" to teams, and be "rejected" from teams. Both Teams and Players can initiate requests for each of these statuses, allowing the other party to confirm/reject. In each case, they should be linked and can only occupy one of the four statuses. What is the best way to link these two tables such that the relationship between any two instances can be given 4 "statuses"?
I need to give the users (whom control teams and players in this open management/recruitment environment), the ability to request/approve classification in each of the "statuses".
It strikes me that the cleanest way to do this would be to use a single pivot table that "assigns" these given statuses to each linked ID pair. I, however, have only seen simpler examples of this concept executed and am as a result unsure as to what the best way to do that is. I would appreciate some guidance here.
Schema::create('player_team', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
# `book_id` and `tag_id` will be foreign keys, so they have to be unsigned
# Note how the field names here correspond to the tables they will connect...
# `book_id` will reference the `books table` and `tag_id` will reference the `tags` table.
$table->integer('player_id')->unsigned();
$table->integer('team_id')->unsigned();
# Make foreign keys
$table->foreign('player_id')->references('id')->on('players');
$table->foreign('team_id')->references('id')->on('teams');
});
*Again, I'm pretty fresh. Apologies if this has an obvious solution I'm just missing.
If I understood you correctly, you can add status column to a pivot table:
$table->tinyInteger('status')->unsigned();
Don't forget to add withPivot() to relations:
return $this->belongsToMany('p4\Player')->withPivot('status')->withTimestamps();
You can access this column with pivot and set or unset it by adding an array to attach() and detach() methods:
$team->players()->attach($playerId, ['status' => 3]);
Read more about it in docs:
https://laravel.com/docs/5.3/eloquent-relationships#many-to-many
https://laravel.com/docs/5.3/eloquent-relationships#inserting-and-updating-related-models

Categories