Связанная модель Laravel Eloquent должна соответствовать двум внешним ключам - PullRequest
0 голосов
/ 25 апреля 2018

Я использую eloquent в фреймворке Laravel.Я хотел бы получить модели из таблицы D, которые связаны с таблицей A через ОБА таблицу B и таблицу C.

Есть ли способ сделать это?

Вот ситуация в базе данных:enter image description here

Это пример данных для базы данных:

INSERT INTO A (idA) VALUES(1);
INSERT INTO A (idA) VALUES(2);
INSERT INTO A (idA) VALUES(3);

INSERT INTO B (idB, A_idA) VALUES(1, 1);
INSERT INTO B (idB, A_idA) VALUES(2, 2);

INSERT INTO C (idC, A_idA) VALUES(1, 1);
INSERT INTO C (idC, A_idA) VALUES(2, 2);
INSERT INTO C (idC, A_idA) VALUES(3, 3);

INSERT INTO D (idD, B_idB, C_idC) VALUES(1, 1, 1);
INSERT INTO D (idD, B_idB, C_idC) VALUES(2, 2, 2);
INSERT INTO D (idD, B_idB, C_idC) VALUES(3, 2, 3);

Вот модели:

class A extends Model
{
    protected $table = "A";
    protected $primaryKey = "idA";
    public $timestamps = false;

    public function B()
    {
        return $this->hasMany("App\B", "A_idA");
    }

    public function C()
    {
        return $this->hasMany("App\C", "A_idA");
    }
}

class B extends Model
{
    protected $table = "B";
    protected $primaryKey = "idB";
    public $timestamps = false;

    public function D()
    {
        return $this->hasMany("App\D", "B_idB");
    }

    public function A()
    {
        return $this->belongsTo("App\A", "A_idA");
    }
}

class C extends Model
{
    protected $table = "C";
    protected $primaryKey = "idC";
    public $timestamps = false;

    public function D()
    {
        return $this->hasMany("App\D", "C_idC");
    }

    public function A()
    {
        return $this->belongsTo("App\A", "A_idA");
    }
}

class D extends Model
{
    protected $table = "D";
    protected $primaryKey = "idD";
    public $timestamps = false;

    public function B()
    {
        return $this->belongsTo("App\B", "B_idB");
    }

    public function C()
    {
        return $this->belongsTo("App\C", "C_idC");
    }
}

Вотконтроллер

class TestController extends Controller
{
    public function test()
    {
        $aWithRelatedDs = A::with(['B.D'])->whereHas("B.D")->whereHas("C.D")->get();
        dd($aWithRelatedDs);
    }
}

Это вывод:

Collection {#380 ▼
  #items: array:2 [▼
    0 => A {#390 ▶}
    1 => A {#388 ▼
      #table: "A"
      ... snip ...
      #relations: array:1 [▼
        "B" => Collection {#387 ▼
          #items: array:1 [▼
            0 => B {#398 ▼
              #table: "B"
              ... snip ...
              #relations: array:1 [▼
                "D" => Collection {#395 ▼
                  #items: array:2 [▼
                    0 => D {#407 ▼
                      #table: "D"
                      ... snip ...
                      #attributes: array:3 [▼
                        "idD" => 2
                        "B_idB" => 2
                        "C_idC" => 2
                      ]
                      ... snip ...
                    }
                    1 => D {#408 ▼
                      #table: "D"
                      ... snip ...
                      #attributes: array:3 [▼
                        "idD" => 3
                        "B_idB" => 2
                        "C_idC" => 3
                      ]
                      ... snip ...
                    }]}]
... snip ...

Проблема: A # 2 не имеет двух D, связанных через B и C, он имеет только D # 2, связанный черезобе промежуточные таблицы, но в приведенном выше результате она все равно указана.Как я могу ограничить это, чтобы обе ассоциации были для одной и той же модели?

Ответы [ 2 ]

0 голосов
/ 26 апреля 2018

Если вы хотите получить все A, которые имеют хотя бы один D, который подключен к A через B и C, тогда необработанный SQL-запрос будет выглядеть примерно так:

SELECT *
FROM A
WHERE EXISTS (
  SELECT D.id
  FROM D
    INNER JOIN B ON D.b_id = B.id
    INNER JOIN C ON D.c_id = C.id
  WHERE B.a_id = A.id
    AND C.a_id = A.id
)

Я также подготовил для этого SQL Fiddle .

Я бы посоветовал создать новое отношение на A, которое находит все D - * 1015.*, для которого мы находим C, который также связан с A.Звучит странно, но довольно просто и выглядит так:

public function doubleLinkedDs(): HasManyThrough
{
    return $this->hasManyThrough(D::class, B::class)
        ->whereHas('C', function ($q) {
            $q->where('a_id', $this->id);
        });
}

Кстати, это предполагает, что у вас установлены правильные отношения.Но, насколько я вижу из вопроса, это должно быть хорошо.

Если у нас есть определенные отношения, мы должны быть в состоянии выполнить следующий очень простой запрос:

A::has('doubleLinkedDs')
    ->with('doubleLinkedDs')
    ->get();
0 голосов
/ 26 апреля 2018

Использование оператора wh для каждого отношения должно работать примерно так:

D::whereHas('B', function ($query) {
    $query->whereExists(function ($query) { 
        $query->select(DB::raw(1))
                    ->from('B')
                    ->whereRaw('B.A_id = A.id');
    });
})->whereHas('C', function ($query) {
    $query->whereExists(function ($query) { 
        $query->select(DB::raw(1))
                    ->from('C')
                    ->whereRaw('C.A_id = A.id');
    });
})->get();

Полностью не проверено, удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...