Laravel many to many with a pivot relationship. - php

User
uid
Provider
pid
Resolution
rid
ProviderResolution
prid
pid
rid
active
ProviderResolutionUser
prid
uid
class Provider extends Model {
public function resolutions()
{
return $this->belongsToMany('App\Models\Resolution')->withPivot('active')->withTimestamps();
}
}
class Resolution extends Model {
public function providers()
{
return $this->belongsToMany('App\Models\Provider')->withPivot('active')->withTimestamps();
}
}
class User extends Model
{
}
Trying to create a Eloquent relationship with this.
I'm trying to figure out how to fit user into this model. It seems like it's suppose to belongsToMany. Do I need to create a class that represents the pivot?
Then from the case of the User how would I query a list resolutions?

You didn't ask but I personally think it's a lot easier to let the primary key of each table be 'id.' Also, in the case of ProviderResolution, unless you have a specific case for it, you don't need (and shouldn't use) 'prid' at all. Just 'pid', 'rid' and 'active' should be sufficient. The 'pid' and 'rid' make the composite primary key on their own. If you add yet another key ('prid'), then there will be a three-key composite which will technically enable you to have duplicates with your other two primary keys. Yuck. Example: PRID:1, PID:1, RID:1, then PRID:2, PID:1, RID:1. Now you have duplicates but your record is technically still unique because of the PRID key. But, maybe you want it this way for some reason?
For the answer I'm going to assume you are using Laravel 5.4+.
So first off, you don't need a class for the pivot. And secondly, you are currently trying to create a relationship between the user and the existing pivot table between Provider and Resolution by creating a table called 'provider_resolution_user'. If you want to query resolutions for a user, just use the relationship methods which gives you access to the attributes on the pivot table and the related models/tables.
First, setup the 'hasMany' relationships in both classes: Users and Resolutions (Providers already has a relationship to Resolutions, so you can use that relationship if you want to see the related Provider.) Then you'll need a pivot table called 'resolution_user'. Put the 'uid' and the 'rid' in the table. Make the relationships to the corresponding foreign key fields to their parent tables.
Now you can access the relationship directly like:
$user->resolutions->rid (or whatever the attribute is you want)
The previous example assumes you have already created a way to insert records into the pivot table (resolution_user) that relate the user and the resolution together.
If you want to access one of the attributes on the pivot table, 'pivot' creates an object instance with it's own attributes (from the table). You can access it like this:
$user->resolutions->pivot->active;
Of course, these methods are chainable so if you just wanted to see the active resolutions, you could also add a ->where statement.
Hope that helps and wasn't too muddy. I'm happy to clarify any points if need be.
EDITED ANSWER:
Because what you want to do is to disable a row in the provider_resolution table and have that reflect on the correct user, then just create a relationship in both the User model and the Resolution model. So when you disable a row in provider_resolution (pid, rid, active), you can lookup the appropriate user to update by using the inverse relationship between resolution and user. This should give you the user that is assigned to that particular resolution/provider combination. If for some reason you do need to find the user based on a unique combination of the TWO: resolution AND provider, then we might need to talk about polymorphic relationships. Let me know.

Related

Laravel insert into many to many relation table [duplicate]

A simple and straight one:
How can I attach or detach new records when using a Laravel hasManyThrough relation the Laravel way?
Model retrieving is obvious, from the docs here.
EDIT: In other words, is there a Laravelish way of doing the following smarter (model names taken from docs)?
$user = $country->users()->first;
$post->user_id = $user->id;
$post->save();
Thanks in advance.
hasManyThrough() requires an existing intermediate relationship. In the docs example, User is the intermediate relationship. Directly attaching a Post to a Country is not possible because it doesn't know which User owns it. You need to first attach it to the User.
https://laravel.com/api/5.8/Illuminate/Database/Eloquent/Relations/HasManyThrough.html
Yes, but you have to specify all the params.
firstOrNew
// (Doesn't persist to DB, you have to manually call the save method later on the created model)
updateOrCreate
// (Persists to DB)
rawUpdate
// (Persists to DB, not recommended)
push is supposed to work too, according to the docs.
On a 1:M relationship, there's the save method available out of the box. That's because the only field Laravel has to fill in is the foreign key.
For example, Let's say you've got a Parent and Child models. When you call
$parent->children()->save(new Child(...));
Laravel fills in the foreign key and persists the model
If we had a GrandParent model as well, and we tried to save a child through a HasManyThrough relationship:
$grandparent->grandchildren()
Laravel would not only have to fill in for the Parent foreign key, but maybe even create a new Parent model as well since we're not sure it exists. That's why there's not a save method implemented.
Therefore, you can make something like
$grandparent->grandchildren()->firstOrNew(['parent_id' => $parent_id])->save();
// Or
$grandparent->grandchildren()->updateOrCreate(['parent_id' => $parent_id]);
You need a valid key too or else you'll get a SQL constraint violation.

How to define a one-way one-to-one relationship in Laravel (5.4) Eloquent?

Think I'm missing something obvious here, but I want to define a one way, one to one relationship from table_1 to table_2, e.g. table1 schema:
Schema::create('table1', function (Blueprint $table) {
// Some field definitions
$table->integer('table2_id')->unsigned();
$table->foreign('table2_id')->references('id')->on('table2')->onDelete('cascade');
});
Table 2 doesn't know anything about Table 1, so just has a bunch of fields defined. Model for Table 1 has:
public function table2() // Get table2 record
{
return $this->hasOne('App\Table2');
}
Questions:
a.) Is that relationship in the record necessary just to be able to lookup the relevant table2 record from a table1 record?
b.) How do I set the relationship in my code? Currently my controller code is:
$table_1_record = new Table1();
// What code here to define the relationship, using Eloquent? Or do I just do:
$table_1_record->table2_id = my_table2_record->id;
// But this just sets it manually doesn't it, rather than using Eloquent?
Thing that is confusing me is in here: https://laravel.com/docs/5.6/eloquent-relationships#one-to-one a bit further down from the link, where it says
Eloquent determines the foreign key of the relationship based on the
model name. In this case, the Phone model is automatically assumed to
have a user_id foreign key.
Applying that example to my code, the thing is I don't want to have to define a table1_id in my Table_2 - but sounds from that quote from the docs that I need to, to find a table2 record from table1...?
c.) Is there any point in this line in the migration: $table->foreign('table2_id')->references('id')->on('table2')->onDelete('cascade'); and indeed using Eloquent at all (I want to do things the proper Laravel way), or shall I just simply manually set table2_id as in question b.) above?
Note: I don't want to define the inverse of the relationship, as I don't need to find a table_1 record from a table_2 record.
Thanks for any help.
Relationships in Eloquent only work in the directions that they are defined. If you want to be able to find Table2 based on the foreign key defined in Table1, you would add a relationship to the Table1 model only. You do not need to define relationships on both models in order to go one direction.
Given your table1 schema, you are actually using the inverse of a one-to-one relationship. table1 is considered a child of table2. Your relationship would look like this:
class Table1
{
public function table2()
{
return $this->belongsTo(\App\Table2::class);
}
}
As for setting the relationship, my recommendation is to simply apply the ID from table2 manually when creating the initial record:
`$table1->table2_id = $table2->id;`
If you're updating a Table1 record, you can use associate(). More info on that here in the docs. Note that this does require you to define the other side of the relationship on Table2. If you don't want to do that, just update the column manually when updating also.
Keep in mind that all of this is powered by Eloquent. Just because you're defining a column value manually instead of using a relationship helper method doesn't mean you're doing something ineffectively or incorrectly.
Is there any point in [the foreign key] line in the migration
This creates a foreign key constraint in the database software itself, and has nothing to do with Eloquent. Eloquent doesn't use or care if you have a foreign key defined. There are benefits to using foreign keys, though, and suggest you look into it further to determine if they're a good idea for your application.

How to get value from column that is in a N-N relationship table in Laravel

Im having a problem. I have two tables, places and cuisines, and cuisine_place and in that table I have a column called default (that shows if that cuisine is the default cuisine for that place). But Im having the problem that Im not able to access to that column.
How can I do?
What I want to do is have them in this answer:
$places = Place::all()->with('cuisines')->withPivot('default');
Something like that.
Thanks
You have a many to many relationship between places and cuisines. That relationship would be defined in the following fashion in your models:
public function cuisines(){
return $this->belongsToMany('Cuisine', 'cuisine_place');
}
The above would be a function in your Place class that references its relationship to the cuisines table. By default, Eloquent will pick up the foreign keys in the cuisine_place table (in your case they would probably be called cuisine_id and place_id). If you want to pick up additional columns from that table on calls to the above relationship function, you can use the withPivot function:
public function cuisines(){
return $this->belongsToMany('Cuisine', 'cuisine_place')->withPivot('default');
}
Now, on calls to the cuisines() method in your Place class, you'll receive the default column in the object associated with that table.
Open documentation for Laravel Eloquent Relationships
And read about Retrieving Intermediate Table Columns
You didn't provide your models code so it's difficult to help you with the exact solution

cakephp3 link one table to multiple tables depending on type

So here's my problem.
I need to link an insurance policy to the insured property/item. Now the details vary greatly from car policy to a house or business one. So what I want to do is have something like this on the policies table
Policies
item_id
item_type
and that links to different tables depending on the value of the field "item_type" for example:
item_type = car then link to the cars table
item_type = house then link to the houses table
item_type = business then link to the businesses table
and so on...
I can do that on my own with php and mysql but I want to know the proper way to do it using CakePHP's table relationships and linking. I tried using the through option and a relationship table but it's not the same. Any ideas? or if a relationship table is the only way to do it then tell me how please.
This is actually a lot simpler than it first appears. I've done this a few times so I'll detail the technique that I use.
The first thing to do is create a behavior. This will allow you to enable any Table class in your application to have a policy attached to it.
The behavior is very simple. I've changed it to match your example, as I understand it. I'll talk through it after the code.
namespace App\Model\Behavior;
use Cake\Event\Event;
use Cake\ORM\Behavior;
use Cake\ORM\Query;
class PolicyBehavior extends Behavior
{
public function initialize(array $config)
{
parent::initialize($config);
$this->_table->hasMany('Policies', [
'className' => 'Policies',
'foreignKey' => 'table_foreign_key',
'bindingKey' => 'id',
'conditions' => ['table_class' => $this->_table->registryAlias()],
'propertyName' => 'policies'
]);
}
public function beforeFind(Event $event, Query $query, \ArrayObject $options, $primary)
{
$query->contain(['Policies']);
return $query;
}
}
So the in the initialize method we need to create a relationship to the table we attached the behaviour to. This will create a Table hasMany Policies relationship, meaning that any item in your system can have many policies. You can update this relationship to match how you're working.
You can see that there are a number of options defined in the relationship. These are important, as they link the tables items together. So the table_foreign_key is a field in your policies db table used to store the primaryKey of the related item. So if you're attaching a Policy to a Car, this would be the Car.id. The bindingKey is the key used in the Policy table to join on.
In order to filter the different types of attachments, you need the table_class field in your policies db table. This will be the name of the attached table class. So Cars, Cats, Houses etc. Then we can use this in the conditions, so anything pulling the primary table class will automatically filter the related Policies to match.
I've also configured the propertyName, this means that any item you look for which contains Policies will have an entity property called policies with the related data inside.
The last function in the behaviour is the beforeFind, this just ensures that whenever you look for the primary table class, you always return the related policies, you don't have to use this if you don't want to, but I found it handy to always have the related data in my use-case.
So then, how do we use this new behaviour? Just attach it like you would any other behaviour, and that's it. $this->addBehavior('Policy').
Be aware
This just reads data, you'll need to ensure that you save the table alias, and the foreignKey into the related table when creating new items.
Just for clarity, your policies table schema will need, at a minimum.
policies.id
policies.table_class VARCHAR(255)
policies.table_foreign_key INT(11)

Fetching all pivot table columns via custom pivot type

I have a many-to-many relationship where the pivot table has about 20 additional columns. I am using a custom pivot class, and I have successfully set up the code to return an instance of that class when the ->pivot property is accessed on the relation, e.g.
$supplier->products->pivot returns the custom pivot class.
However, when wanting to access the data, I can manually define all the individual attributes of the pivot class (which extends Pivot by the way) in the belongsToMany relationship like this:
return $this->belongsToMany(Product::class, ['prop1', 'prop2', 'prop3'])
...But, how can I retrieve all the pivot data of the class without manually defining them as it ties the relationship declaration very close to the class? Is this possible. If not, it's going to make maintainability a PITA! Ideally, it'd be really nice if withPivot just had a flag to get it all!
In my circumstances, I found it easier to separate all the data into a separate table and model, and add a foreign key in the pivot table to the additional table record. This allows me to use the 'normal' model handling in Laravel and means I don't have to mess around with problems like this!
My use case was a schema of product and supplier with a many-to-many, and each supplier having their own data for the product, namely price, stock, shipping cost / times etc, so I moved all this from the pivot to a SupplierProduct model.
I'll leave this question here, as although this isn't the direct answer to the question (which I fear the answer is 'no'), this is a solution which is viable and can save quite a bit of coding frustration!

Categories