add relationship to added values in pivot table laravel - laravel

I'm new to laravel relationships, but i'm familiar with SQL. I'm trying to add a pivot table with other values than just the two main ids.
I have some users, and some teams. Each user can play multiple instruments, and can be in multiple teams, but they can only play one instrument per team.
My pivot migration:
Schema::create('team_members', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->integer('team_id')->unsigned();
$table->integer('instrument_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('team_id')->references('id')->on('teams')->onDelete('cascade');
$table->foreign('instrument_id')->references('id')->on('user_instruments')->onDelete('cascade');
});
I can already see this:
$team = Team::first();
$team->members->first()->details->instrument_id; // "2"
How can I define the relationship between instrument_id on user_instruments, so I can do $team->members->first()->details->instrument
UPDATE:
I found this one: https://medium.com/#DarkGhostHunter/laravel-has-many-through-pivot-elegantly-958dd096db
Not sure if it will work...

You should create an intermediate model TeamMember, in which you will define a relationship to Instrument. To achieve that, modify your many-to-many relationship on both Team and User, adding the option "using", like this:
// Team.php
public function members() {
return $this->belongsToMany(App\User::class, 'team_members', 'user_id')
->using(App\TeamMember:class);
}
Then you need to create a model for your pivot table called TeamMember, which will extend the class Pivot and define the relationship with Instrument.
class TeamMember extends Pivot {
public function instrument() {
return $this->belongsTo(App\Instrument::class);
}
}
Then you should be able to get the instrument like this (assuming you set a custom name for your pivot table as "details"): $team->members->first()->details->instrument.

Related

Updating a Laravel pivot table when the pivot has a unique field

I have two models, User and Service, with a ManyToMany relationship. The pivot table has two additional fields, id_code and alias. There may be many relationships between User and Service, but each one is uniquely identified by the field id_code. I need to retrieve a specific record in the pivot table by the id_code, and update the alias for that record only.
My models:
User:
class User extends Authenticatable
{
//All attributes & other functions here...
public function linked_services(){
return $this->belongsToMany(Service::class)
->withPivot('alias', 'id_code')
->withTimestamps();
}
}
Service:
class Service extends Model
{
//All attributes & other functions here...
public function linked_users(){
return $this->belongsToMany(User::class)
->withPivot('alias', 'id_code')
->withTimestamps();
}
}
Service-User migration:
Schema::create('service_user', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id')->nullable();
$table->unsignedBigInteger('service_id');
$table->string('alias', 50)->nullable();
$table->string('id_code', 50);
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('service_id')->references('id')->on('services');
$table->timestamps();
});
In the update function, I get the specific $id_code from a view, but I don't know how to update the "alias" for that specific "id_code".
What I already tried and is not working:
public function update(Request $request, String $id_code){
foreach(Auth::user()->linked_services as $service){
if($service->pivot->id_code === $id_code){
$service->pivot->alias = $request->alias;
$service->pivot->save();
}
}
return redirect()->route('services_user.index');
}
This function updates ALL the existing pivot records for the service_user, I need to update only the specific record for an given "id_code".
I solved it!!! I tell you how:
Auth::user()->linked_services()
->wherePivot('id_code', $id_code)
->update(['alias' => $request->alias]);
I learned that $user->linked_services() (unlike $user->linked_services) returns a relationship object (in this case, belongsToMany), which contains all the relationship data (intermediate table, pivots, FKs, parent / related objects, etc), and the desired pivot can be accessed on that object with ->wherePivot().

Laravel defining a many-to-many relationship with the same table

So I have a posts table with a corresponding Post model. I want to have related posts for every post. Since a post can have many other related posts, it is a many-to-many relationship between the posts table and the posts table (same table).
So I created a related_posts pivot table with its corresponding model RelatedPost. I want to define this relationship in the two models. Like so:
Post model:
public function related()
{
return $this->belongsToMany(RelatedPost::class, 'related_posts', 'related_id', 'post_id');
}
RelatedPost model:
public function posts()
{
return $this->belongsToMany(Post::class, 'related_posts', 'post_id', 'related_id');
}
Now in my post controller after selecting a particular post, I want to get all its related posts. So I do this:
$post->related()->get();
But when I do this I get the following error message:
"SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'related_posts' (SQL: select related_posts.*, related_posts.related_id as pivot_related_id, related_posts.post_id as pivot_post_id from related_posts inner join related_posts on related_posts.id = related_posts.post_id where related_posts.related_id = 1) "
This is my migration for the pivot table:
Schema::create('related_posts', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('post_id');
$table->unsignedInteger('related_id');
$table->timestamps();
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->foreign('related_id')->references('id')->('posts')->onDelete('cascade');
});
I have searched all over everywhere and though the solutions I've found really make sense I haven't been able to get any of them to work.
Any help will be very much appreciated!
Thanks to #d3jn's comment on my question I was able to solve my problem. So I am posting the solution here just in case someone else might need it.
I am relating the Post model to itself not to the pivot model RelatedPost. So I don't need a RelatedPost model. I only need a pivot table (related_post), and the relation's ids namely related_id and post_id.
So with my migration unchanged, I only need to do away with the RelatedPost model and change my related() method in the Post model to look like this:
public function related()
{
return $this->belongsToMany(Post::class, 'related_posts', 'post_id', 'related_id');
}
And now everything works.

Laravel hasManyThrough with ManyToMany pivot

I am making a game and I have users which have facilities and for this I use ManyToMany
user_facilities
-user_id
-facility_id
But each relation must have a facility level, so I've added facility_levels table and each of this levels must be connected to the ManyToMany relation. So user_facilities now looks like this
user_facilities
-user_id
-facility_id
-level_id
level_id is the connections between the facility which the user owns and which level it is.
My question is how do I connect this in the models?
The User model now has this
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities');
}
And Facility
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities');
}
So how do I get the level of the facility which the user owns?
In blade I hope there is a way I can use something like
{{ $user->facility->level->property }}
level is part of the user_facilities table not of facility
Therefore, you should be able to access the level_id from the many to many relationship of user and facility
One thing you can do is to access the immediate table (also called pivot table).
First, edit your relationship to include the extra attributes.
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities')
->withPivot('level_id');
}
public function users()
{ // if you omit this EDIT/UPDATE, you cannot do this:
// $facility->users()->first()->pivot->level_id;
return $this->belongsToMany('App\User', 'user_facilities')
->withPivot('level_id');
}
Take note that when accessing a many to many relationship, Laravel will immediately assign a pivot attribute onto the result which contains details about the pivot table of the two models
Now try accessing the extra column:
$facility = $user->facilities->first();
$level_id = $facility->pivot->level_id;
// now you can use $level_id for finding the level.
$level = Level::find($level_id);
Now, since you can do that, you can also create a model for the many to many relationship of user and facility that will have that property of level_id
Let's create a new model called UserFacility that will extend Pivot.
This will be your Pivot model for many to many relationship of user and facilities.
use Illuminate\Database\Eloquent\Relations\Pivot;
class UserFacility extends Pivot
{
}
Then update your users and facilities relationships as follows.
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities')
->using('App\UserFacility');
}
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities')
->using('App\UserFacility');
}
Notice that using method.
$userfac = $users->facilities->pivot; // <-- pivot will now be an instance of App\UserFacility
echo $userfac->level_id;
Lastly,
If you don't want the pivot attribute name, you can change it using the as method, chain it after the belongsToMany method, like this:
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities')
->as('UFac')
->using('App\UserFacility');
}
$userfac = $users->facilities->UFac; // <-- you can now access the pivot table using the property `UFac`
echo $userfac->level_id;
It may also be possible that your pivot table has a relationship with a level since it has a level_id. Don't worry, it's possible, just add this function in your UserFacility model.
public function level()
{
return $this->belongsTo('App\Level');
}
Now you can do this!
$user->facilities->first()->UFac->level; // <-- this will be an instance of App\Level
source: https://laravel.com/docs/5.5/eloquent-relationships#many-to-many

create list for dropdown with value from relationed table

My model Product return something like this
id = 1
name = Product Name
category_id = 101
category = array
id = 101
name = electronics
and I need to use lists() to create something like this:
lists("category.name", "id")
but it looks that this is impossible. How to create list for dropdown like this:
1 = electronics
2 = furniture
3 = cars
...
thanks a lot
Are you sure you did set up the right relations? Cause that is just what lists() usually does and it's far from impossible to create such a list.
It's quite hard to understand how you did design your Eloquent logic without your code, but here's how I would handle your situation, assuming you've already set up the category_product pivot table.
Given two models, Product and Category, I'd define these 2 relations cause, as far as I can get, a product can have several categories and a categories can be associated with several product :
class Product extends Model
{
////////
public function categories()
{
return $this->belongsToMany('App\Category');
}
////////
}
and
class Category extends Model
{
////////
public function products()
{
return $this->belongsToMany('App\Product');
}
////////
}
At this point, you're good to go, all you have to do in order to get your list is
Product::findOrFail($id)->categories->lists('name','id')
This will return an array holding all the categories associated with the given product
array:[
1 => first-category
2 => second-category
3 => third-category
....
]
Note that with this set up it'll work the other way around as well, if you want to get a list of all the product that match a given category
Category::findOrFail($id)->products->lists('name','id')
At the very top of the answer I assumed you did set up a pivot table but, if you didn't, here's a quick tip:
within your products migration
public function up()
{
Schema::create('products', function (Blueprint $table) {
//
});
Schema::create('category_product', function (Blueprint $table){
$table->integer('category_id')->unsigned()->index();
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->integer('product_id')->unsigned()->index();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->timestamps();
});
}
Then migrate and now everything is properly set up. Last but not least, if you are going to use this migration, you'll need to fix the relation in the model, which one is up to yours app logic: let's say the flow is create a new product -> choose the categories within the creation form, then you need to add timestamps() in the categories method
return $this->belongsToMany('App\Category')->withTimeStamps();

Laravel - Pivot table for three models - how to insert related models?

I have three models with Many to Many relationships: User, Activity, Product.
The tables look like id, name. And in the each model there are functions, for example, in User model:
public function activities()
{
return $this->belongsToMany('Activity');
}
public function products()
{
return $this->belongsToMany('Product');
}
The pivot table User_activity_product is:
id, user_id, activity_id, product_id. The goal is to get data like: User->activity->products.
Is it possible to organize such relations in this way? And how to update this pivot table?
First I suggest you rename the pivot table to activity_product_user so it complies with Eloquent naming convention what makes the life easier (and my example will use that name).
You need to define the relations like this:
// User model
public function activities()
{
return $this->belongsToMany('Activity', 'activity_product_user');
}
public function products()
{
return $this->belongsToMany('Product', 'activity_product_user');
}
Then you can fetch related models:
$user->activities; // collection of Activity models
$user->activities->find($id); // Activity model fetched from the collection
$user->activities()->find($id); // Activity model fetched from the db
$user->activities->find($id)->products; // collection of Product models related to given Activity
// but not necessarily related in any way to the User
$user->activities->find($id)->products()->wherePivot('user_id', $user->id)->get();
// collection of Product models related to both Activity and User
You can simplify working with such relation by setting up custom Pivot model, helper relation for the last line etc.
For attaching the easiest way should be passing the 3rd key as a parameter like this:
$user->activities()->attach($activityIdOrModel, ['product_id' => $productId]);
So it requires some additional code to make it perfect, but it's feasible.
The solution was found with some changes.
In the models relationships look like:
// User model
public function activities()
{
return $this->belongsToMany('Activity', 'user_activity_product', 'user_id', 'activity_id')->withPivot('product_id');
}
public function products()
{
return $this->belongsToMany('Product', 'user_activity_product', 'user_id', 'product_id')->withPivot('activity_id');
}
To update pivot table:
$user->products()->save($product, array('activity_id' => $activity->id));
- where product and activity ids I get from Input.
And, for example, to check if "user -> some activity -> some product is already exists":
if ($company->activities->find($activity_id)->products()->where('product_id', '=', $product_id)->wherePivot('company_id', $company_id)->get()->count() > 0) {
// code...
}
I think it needs improvements but it works for me now.

Resources