23
votes

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?

3
I'm afraid you can't do that with eloquent. It doesn't support multiple foreign key relationships.Amarnasan
You can only have one foreign and local key I think, but you can add extra where clauses to the query. Would that be enough for you? Would you be able to provide more context to the question? That would help us understand the problem better.Jonathon
@Jonathon I replaced the model names A and B with actual names that I use, hopefully it is now easier to understand the problem domain.Genti Saliu
@Jonathon Could you provide an example of what you mean and add it as an answer? I would more than gladly accept it.Genti Saliu

3 Answers

37
votes

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.

9
votes

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);
    }
}
3
votes

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