I am in the process of porting a project to Laravel.
I have two database tables which are in a One-To-Many relationship with each other. They are joined by three conditions. How do I model this relationship in Eloquent?
I am not supposed to modify the database schema, since it has to remain backwards compatible with other things.
I have tried the following, but it doesn't work.
The owning side:
use Illuminate\Database\Eloquent\Model;
class Route extends Model
{
public function trips()
{
return $this->hasMany('Trip', 'route_name,source_file', 'route_name,source_file')
}
}
The inverse side:
use Illuminate\Database\Eloquent\Model;
class Trip extends Model
{
public function routes()
{
return $this->belongsTo('Route', 'route_name,source_file', 'route_name,source_file');
}
}
Example Route database values:
id | route_name | source_file
---------------------------------------
1 | Berlin - Paris | file1.xls
2 | Madrid - London| file2.xls
3 | Berlin - Paris | file3.xls
Example Trip database values:
id | route_name | source_file | duration
---------------------------------------------------------
1 | Berlin - Paris | file1.xls | 78
2 | Madrid - London | file2.xls | 241
3 | Berlin - Paris | file3.xls | 65
1 | Berlin - Paris | file1.xls | 95
1 | Berlin - Paris | file1.xls | 65
Route and Trip have other attributes, which I did not include here for brevity.
Is this possible in Eloquent?
I had to deal with a similar problem. The solution provided by #fab won't work with eager loading because $this->source_file would be null at the time the relationship is processed. I came up with this solution
After installing Compoships and configuring it in your models, you can define your relationships matching multiple columns.
The owning side:
use Illuminate\Database\Eloquent\Model;
use Awobaz\Compoships\Compoships;
class Route extends Model
{
use Compoships;
public function trips()
{
return $this->hasMany('Trip', ['id', 'route_name', 'source_file'], ['route_id', 'route_name', 'source_file']);
}
}
The inverse side:
use Illuminate\Database\Eloquent\Model;
use Awobaz\Compoships\Compoships;
class Trip extends Model
{
use Compoships;
public function route()
{
return $this->belongsTo('Route', ['route_id', 'route_name', 'source_file'], ['id', 'route_name', 'source_file']);
}
}
Compoships supports eager loading.
As Jonathon already mentioned, you could try to add an where-clause to your relationship:
use Illuminate\Database\Eloquent\Model;
class Route extends Model
{
public function trips()
{
return $this->hasMany('Trip', 'route_name')->where('source_file', $this->source_file);
}
}
The inverse side:
use Illuminate\Database\Eloquent\Model;
class Trip extends Model
{
public function routes()
{
return $this->belongsTo('Route', 'route_name')->where('source_file', $this->source_file);
}
}
How i would tackle this would be to do something like this
class A
{
public function b1()
{
return $this->hasMany('B', 'prop1', 'prop1')
}
public function b2()
{
return $this->hasMany('B', 'prop2', 'prop2')
}
public function b3()
{
return $this->hasMany('B', 'prop3', 'prop3')
}
public function getBsAttribute()
{
$data = collect([$this->b1, $this->b2, $this->b3]);
return $data->unique();
}
}
Collecting all the single relations and returning them as unique collection, and obviously doing the same for the inverse, this should give you some data to work with.
OP was modified so no longer relevant, left in if any one needs an answer similar
Related
i have the following relationship for a belongsToMany relationship
Table:| users | forums |forum_user (pivot table)
| id | id | id
| name | name | user_id
| email | | forum_id
| | |
and have defined the following relationships
pubic function forums() {
return $this->belongsToMany(Forum::class);
}
on the user model and
pubic function forums() {
return $this->belongsToMany(Forum::class);
}
on the forum model
now when i try to do this
User::first()->forums()->get()
it returns the error
Symfony\Component\Debug\Exception\FatalErrorException
Type of Illuminate\Database\Eloquent\Relations\Pivot::$ must be array (as in class
Illuminate\Database\Eloquent\Model)
how can i get around this error
i'm currently using laravel 6 in my project
try this:
User::first()->forums->get();
And maybe rewrite the relation on User model to:
return $this->belongsToMany(Forum::class, 'forum_user', 'user_id', 'forum_id');
On the forum model shouldnt relation reflect user?
pubic function forums() {
return $this->belongsToMany(Forum::class);
}
Shouldnt this be
pubic function users() {
return $this->belongsToMany(User::class);
}
Consider I have the following table structures for tables countries, products and suppliers:
countries
------------------------
id
name
code
product
------------------------
id
name
price
suppliers
------------------------
id
name
A product can be sold in different countries but supplier of that product can be different. With that keeping in mind, I made a relations table to keep track of which supplier is delivering which product in which country:
relations
------------------------
country_id
product_id
supplier_id
Let's say I have a product A which I need to store in country US and CA but the suppliers for these countries are X and Y respectively. The structure would look something like this:
countries
-------------------------------
id | name | code
-------------------------------
1 | United States | US
2 | Canada | CA
product
-------------------------------
id | name | price
-------------------------------
1 | A | 3.99
suppliers
------------
id | name
------------
1 | X
2 | Y
relations
-------------------------------
country_id | product_id | supplier_id
-------------------------------
1 | 1 | 1
2 | 1 | 2
My question is how can I use Eloquent Relationships to this table since many-to-many relationships only work on two tables. Is there any other workaround regarding this? Or is there any other efficient way to implement this scenario?
Thank you for your help.
There is no built-in way to make a relation using three tables. Whenever I encounter something like this myself, the best solution seems to be to make an in-between model that has relations to the three tables.
So in your case, I would create a SupplierProduct that has the relations country, supplier and product.
i have also same scenario class have multiple DaysClassDetails
use this function in your parent model
public function classType()
{
return $this->hasMany('App\DaysClassDetails(middlemodel)');
}
and DaysClassDetails have multiple DaysClassTimeDetails
public function classTime()
{
return $this->hasMany('App\DaysClassTimeDetails(lastchildmodel)');
}
public function classType(){
return $this->belongsTo('App\ManageClass(parentmodel)');
}
As Jerodev suggested, I made an intermediate model SupplierProduct. Instead of making many-to-many relationships, I made one-to-many relationships with SupplierProduct and retrieved data using with functions to retrieve all data related to that record.
This is how my Models look like (database structure is same as described in question):
SupplierProduct.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class SupplierProduct extends Model {
public function country() {
return $this->belongsTo(Country::class);
}
public function product() {
return $this->belongsTo(Product::class);
}
public function supplier() {
return $this->belongsTo(Supplier::class);
}
}
Country.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model {
public function products() {
return $this->hasMany(SupplierProduct::class)->with('product', 'supplier');
}
}
Product.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model {
public function products() {
return $this->hasMany(SupplierProduct::class)->with('country', 'supplier');
}
}
Supplier.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model {
public function products() {
return $this->hasMany(SupplierProduct::class)->with('country', 'product');
}
}
I have the following polymorphic relationship:
// User.php
public function regions()
{
return $this->morphedByMany('App\Region', 'relationship')->withPivot('relationship_level');
}
public function groups()
{
return $this->morphedByMany('App\Group', 'relationship')->withPivot('relationship_level');
}
The pivot table is as follows:
id | user_id | relationship_id | relationship_type | relationship_level
1 | 12 | 8 | App\Region | master
2 | 18 | 12 | App\Group | fan
Is there any way (preferably as scope) where I can get every user who has level "master" when this user can have many relationship level to many relationship_type.
Using the existing relationships:
User::whereHas('groups', function ($query) {
$query->where('relationship_level', 'master');
})->orWhereHas('regions', function ($query) {
$query->where('relationship_level', 'master');
})->get();
Using a pivot model and a new relationship (faster):
class Relationship extends \Illuminate\Database\Eloquent\Relations\MorphPivot
{
//
}
class User extends Model
{
public function relationships()
{
return $this->hasMany(Relationship::class);
}
}
User::whereHas('relationships', function ($query) {
$query->where('relationship_level', 'master');
})->get();
I just started using laravel-translatable package from dimsav which enables to set and retrieve translated strings from the database using Eloquent models. It works great but I still have trouble translating pivot values from belongsToMany relationships.
I have a model called Combination defined as shown below:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Combination extends Model {
public function product() {
return $this->belongsTo('App\Product');
}
public function product_attributes() {
return $this->belongsToMany('App\Attribute')->withPivot('value');
}
}
Here is my Attribute model:
namespace App;
use Illuminate\Database\Eloquent\Model;
use Dimsav\Translatable\Translatable;
class Attribute extends Model {
use Translatable;
public $translatedAttributes = ['name'];
}
class AttributeTranslation extends Model {
public $timestamps = false;
protected $fillable = ['name'];
}
For instance, I have a Product called "T-Shirt" with two Attributes:
Color
Size
This Product has two combinations:
Black / M
Black / L
These values are stored as pivot values in an intermediate table called attribute_combination like this:
+--------------+----------------+-------+
| attribute_id | combination_id | value |
|--------------+----------------+-------|
| 1 | 1 | Black |
| 2 | 1 | M |
| 1 | 2 | Black |
| 2 | 2 | L |
+--------------+----------------+-------+
I have no problem retrieving the translated attribute's name using $attribute->name but I can't find a way to retrieve the translated pivot values. I tried to create a table called attribute_combination_translations and its AttributeCombination model as shown below but it does not work.
namespace App;
use Illuminate\Database\Eloquent\Model;
use Dimsav\Translatable\Translatable;
class AttributeCombination extends Model {
use Translatable;
protected $table = 'attribute_combination';
public $translatedAttributes = ['value'];
}
class AttributeCombinationTranslation extends Model {
public $timestamps = false;
protected $fillable = ['value'];
}
Maybe anyone using this package could help me get through this?
don´t get it why this error occurs when inserting a new zip-code and relate it to an existing company using laravels eloquent belongsToMany feature.
here is my setup:
Error
file: "...\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php"
line: 411
message: "postcode"
type: "Illuminate\Database\Eloquent\MassAssignmentException"
Insertcode
$postcode = new Postcode(array('postcode' => $data['plz']));
Company::find($data['id'])->postcodes()->save($postcode);
Company Model
class Company extends Eloquent {
public function postcodes()
{
return $this->belongsToMany('Postcode');
}
}
Postcode Model
class Postcode extends Eloquent {
public function companies()
{
return $this->belongsToMany('Company');
}
}
Table postcodes
| id | postcode |
Table company_postcode
| id | postcode_id | company_id |
Table companies
| id | name |
Does anybody know why this isn´t working?
The error says
MassAssignmentException
That is because you are trying to mass assign postcode
new Postcode(array('postcode' => $data['plz']));
So make sure you whitelist the postcode variable as being mass assignable
class Company extends Eloquent {
protected $fillable = array('postcode');
public function postcodes()
{
return $this->belongsToMany('Postcode');
}
}