I'm working on a project that has a many to many relationship between User and Club. This relationship works and I can get the respective objects with: $user->clubs. The pivot table I've named memberships. I can get the pivot data with $club->pivot. Foreign keys are defined for the memberships table in migrations.
However, I'd like the pivot table to be represented by a model so that I can easily update attributes of Membership such as role (Or even add a Role model to Membership!) or status.
I've looked at "Defining A Custom Pivot Model" in the docs, but what it says doesn't work for me, I get:
ErrorException
Argument 1 passed to Illuminate\Database\Eloquent\Model::__construct() must be of the type array, object given
I've also looked at this description of how to do it, but it's more or less the same as above.
Membership model:
class Membership extends Eloquent {
protected $table = 'memberships';
public function user()
{
return $this->belongsTo('User');
}
public function club()
{
return $this->belongsTo('Club');
}
}
Has anyone done this before?
This has been solved by a suggestion by a reddit user to extend Pivot in my Membership model. I also had to add ->withPivot('status', 'role')->withTimestamps() to the relationship declarations in the User and Club models.
You can do this by adding some manual code.
Your Membership model looks ok. You can get all clubs of a $user easily if you define method $user->clubs() where you get all the clubs manually.
clubs() {
$memberships=$this->memberships;
$clubs=new \Illuminate\Database\Eloquent\Collection();
foreach($memberships as $m) {
$clubs=$clubs->merge($m->clubs);
}
return $clubs;
}
Related
I'm confused on how to get my model setup in laravel with a table that is connected to a pivot table.
Here's the problem
Say I have
Locations
id
name
area_types
id
name
area_type_location (Pivot table)
id
location_id
area_type_id
area_test
id
area_type_location_id
clean
headCount
Relationship between the tables are different areas type belongs to different locations.
i.e: beach, 25m pools, kids pools, bbq, etc
area_test is connected to the pivot table because the test has to be generated from area that exists, in this case it is area that is registered under different locations. Thus it has to be tested daily, measured, etc.
I understand the structure between area_types and locations for many to many relationship, however I can't get over my head of how do i structure my area_test model? How do I get the data from locations table -> where are my test?
Should I create a model for my pivot table? Is that a good practice in laravel?
Does anyone has the same use case?
I read about eloquent has many through
relationship but I understand that it does not mention about getting through pivot table. I don't quite get if my use case is the same.
Thanks
Finally, apparently there are a couple of way to get data from locations table to area_tests
Tried at tinker and it works,
First Option
I need to create a Pivot model for my Pivot table:
class LocationAreaType extends Pivot{
public function location(){
return $this->belongsTo(Location::class);
}
public function areaType(){
return $this->belongsTo(AreaType::class);
}
public function AreaTests(){
return $this->hasMany(AreaTest::class, 'area_type_location_id');
}
}
I can use hasManyThrough relation that I need to create in my Location table
public function areaTests()
{
return $this->hasManyThrough(
AreaTest::class,
LocationAreaType::class,
'location_id',
'area_type_location_id');
}
this way I can get the areaTests easily by $location->areaTests, My problem was not determining the area_type_location_id as foreign. You need to determine this, apparently when I extends pivot and use hasMany laravel does not auto recognise the Foreign key by itself.
Second option
Another way to access it is from the relation table, I can define withPivot in the areaTypes() relation then access it like this:
$location->areaType[0]->pivot->areaTests
Since laravel only recognise foreign key from both tables location_id and area_type_id, I have to include the id of the pivot table to get the AreaTest table data
So in the Location model I have to get the column
public function areaTypes()
{
// Get the ID of the pivot table to get the poolTests table (connected with ID column)
return $this->belongsToMany(AreaType::class)
->using(AreaTypeLocation::class)
->withPivot('id');
}
There is no need to create a new model for pivot table.
Just declare in Location model below code:
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function area_types()
{
return $this->belongsToMany('App\AreaType', 'area_type_location', 'location_id', 'area_type_id');
}
and declare below code in AreaType model:
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function locations()
{
return $this->belongsToMany('App\Location', 'area_type_location', 'area_type_id', 'location_id');
}
every time you need to get for example the locations of an area_type in every controller, you can call the function like this: $areatype->locations()->get();
Don't forget to create area_type_location table migration.
So I have two tables named 'customers' and 'billing_addresses'.
I also have two models named 'Customer' and 'BillingAddress'.
In the billing_addresses table, there are multiple addresses, each having an 'id'.
In the customers table, each customer has an 'billing_address_id' value corresponding to one of the entries in the billing_addresses table.
What I managed to create so far is:
// Customer relationship method(in the model file)
public function address()
{
return $this->hasOne('App\BillingAddress','id');
}
/////////
// BillingAddress relationship method(in the model file)
public function customer()
{
return $this->hasMany('App\Customer','id');
}
I am indeed getting the right data when I do something like Customer::with('address');
but I feel like there is a better approach for all this as I'm also getting
BadMethodCallException
Call to undefined method
Illuminate\Database\Eloquent\Relations\HasOne::getForeignKey()
when I try to filter with datatables(can't tell if its related but first I'd want to have the best approach on the relationships).
Thank you!
What you want to achieve is this:
// Customer Model
public function address()
{
return $this->belongsTo('App\BillingAddress');
}
// BillingAddress Model
public function customer()
{
return $this->hasMany('App\Customer');
}
You need the belongsTo on your Customer Model because you have the Billing id stored within the Customer table. Eloquent will automatically match the billing id to the foreign Key in your customer model. You just need to follow Eloquente naming conventions for foreign keys. Look here for more infos.
Eloquent Relations
A custom can have multiple billing addresses. So it customer can have many addresses and an address belongs to a customer.
// Customer Model
public function addresses()
{
return $this->belongsTo('App\BillingAddress', 'customer_id');
}
// BillingAddress Model
public function customer()
{
return $this->hasMany('App\Customer', 'billing_address_id');
}
Then you can do : Customer::with('addresses')->get();
Secondly, make sure you are passing correct foreign key column as second parameter to the relationship methods. See documentation for reference
The second error is probably causing because of incorrect foreign key column passed as second argument.
I have three tables.
Users: id,name
Courses: id,user_id,name
Order: id,user_id,course_id (
Pivot table)
How can i make sure in course view that this user has purchased this particular course using Laravel eloquent.
Firstly, you need to define a many to many relationship between your models:
class User extends Model {
public function courses() {
return $this->belongsToMany(Course::class);
}
}
Once you have it, you can easily check if User has bought access to a Course with given ID with:
if ($user->courses()->find($courseId)) {
// user has access to course with given $courseId
}
If you simply want to an error to be raised when course was not bought, replace a call to find() with a call to findOrFail():
if ($user->courses()->findOrFail($courseId)) {
// user has access to course with given $courseId
}
The Laravel docs seem to indicate that the hasManyThrough declaration can only be used for relationships that are two levels "deep". What about more complex relationships? For example, a User has many Subjects, each of which has many Decks, each of which has many Cards. It's simple to get all Decks belonging to a User using the hasManyThrough declaration, but what about all Cards belonging to a User?
I created a HasManyThrough relationship with unlimited levels: Repository on GitHub
After the installation, you can use it like this:
class User extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function cards() {
return $this->hasManyDeep(Card::class, [Subject::class, Deck::class]);
}
}
As stated in the comments, hasManyThrough doesn't support this level of specificity. One of the things you can do is return a query builder instance going the opposite direction:
//App\User;
public function cards()
{
Card::whereHas('decks', function($q){
return $q->whereHas('subjects', function($q){
return $q->where('user_id', $this->id);
});
});
}
We're going from Cards -> Decks -> Subjects. The subjects should have a user_id column that we can then latch onto.
When called from the user model, it would be done thussly:
$user->cards()->get();
Well, actually the best solution will be put the extra column to Card table - user_id, if you have so frequent needs to get all cards for the user.
Laravel provides Has-Many-Through relations for 2-depth relation because this is very widely often used relation.
For the relations Laravel does not support, you need to figure out the best table relationship yourself.
Any way, for your purpose, you can use following code snap to grab all cards for the user, with your current relation model.
Assumption
User has hasManyThough relationship to Deck,
So Project model will have following code:
public function decks()
{
return $this->hasManyThrough('Deck', 'Subject');
}
Deck has hasMany relationship to Card
Code
$deck_with_cards = $user->decks()->with("cards")->get();
$cards = [];
foreach($deck_with_cards AS $deck) {
foreach ($deck->cards as $c) {
$cards[] = $c->toArray();
}
}
Now $cards has all cards for the $user.
Details
I have 3 tables :
catalog_downloads
export_frequencies
export_frequencies_catalog_downloads (Pivot Table)
Diagram
I am not sure if I set the relation between them correctly.
Please correct me if I am wrong.
Here is what I did
In CatalogDownload.php
public function export_frequencies(){
return $this->belongsToMany('ExportFrequency','export_frequencies_catalog_downloads','export_frequency_id','catalog_download_id');
}
In ExportFrequency.php
public function catalog_downloads(){
return $this->belongsToMany('CatalogDownload','export_frequencies_catalog_downloads','export_frequency_id','catalog_download_id');
}
Questions
According to my diagram - Did I assign the relationship correctly ?
I hope I didn't mix up between hasMany and belongsTo
Will I need a class or a model for a Pivot Table ?
Thanks
Since export_frequencies is in the CatalogDownload model you have to invert the ID's because the parameters of belongsToMany are as follows:
1. Name of the referenced (target) Model (ExportFrequency)
2. Name of the Pivot table
3. Name of the id colum of the referencing (local) Model (CatalogDownload in this case)
4. Name of the id colum of the referenced (target) Model (ExportFrequency in this case)
what leads to this function:
public function export_frequencies(){
return $this->belongsToMany('ExportFrequency','export_frequencies_catalog_downloads','export_frequency_id','catalog_download_id');
}
The other function was correct.
If you had some data in your pivot table, for instance a colum with the name someCounter then you will have to tell the relation to load that column when creating the pivot object like this:
public function export_frequencies(){
return $this->belongsToMany('ExportFrequency','export_frequencies_catalog_downloads','export_frequency_id','catalog_download_id')->withPivot('someCounter');
}
That will load the column and make it avalible like this:
$catalogDownload->export_frequencies()->first()->pivot->someCounter;
You will need a separate Pivot Model if you need to do some special handling for the fields or if that pivot itself has a relation of its own but then you might consider using a full blown model instead of a pure Pivot Model.
As an added note to the accepted answer, you are able to set up your many to many relationships without referencing the pivot table and the relevant id's as long as you follow a specific convention.
You can name your pivot table using singular references to the related tables, like 'catalog_download_export_frequency'. Notice the alphabetic order of the singular references.
Then you can simply do:
// CatalogDownload Model
public function exportFrequencies()
{
return $this->belongsToMany('ExportFrequency');
}
// ExportFrequency Model
public function catalogDownloads()
{
return $this->belongsToMany('CatalogDownload');
}
This will then allow you to run queries using the query builder or Eloquent like:
$catalogDownload->exportFrequencies()->get(); // Get all export frequencies for a specific CatalogDownload.
Or
$this->catalogDownload->with('exportFrequencies')->find($id); // Using eager loading and dependency injection, when CatalogDownload is assigned to $this->catalogDownload
Hope this helps!