Как использовать Laravel hasManyThrough через 4 таблицы

У меня есть 4 таблицы со структурой и потоком:

User Accounts Contacts Orders 

Это соотношение выглядит следующим образом:

$ User-> hasMany ( 'счета') -> hasMany ( 'контакты') -> hasMany ( 'Заказы');

 /** User Model **/ class User extend Eloquent { public function accounts(){ return $this->hasMany('Accounts'); } public function contacts(){ return $this->hasManyThrough('Contact', 'Account', 'owner_id'); } //how do I get this? public function orders(){ } } /** Account Model */ class Account extends Eloquent { public function $user(){ return $this->belongsTo('User'); } public function contacts(){ return $this->hasMany('Contact'); } } /** Contact Model **/ class Contact extends Eloquent { public function account(){ return $this->belongsTo('Account'); } public function orders(){ return $this->hasMany('Order'); } } /** Order Model **/ class Order extends Eloquent { public function contact(){ return $this->belongsTo('contact'); } } 

Классический hasManyThrough невозможен, потому что у нас есть 4 таблицы.

Как я могу создать отношения, чтобы один пользователь мог иметь доступ к своим заказам без цепочки методов каждой модели, например:

 User::find(8)->orders; 

Вначале было довольно сложно точно определить, как построитель запросов объединяет вещи. Я знал, как я хотел отформатировать запрос с помощью необработанного оператора SQL, но я хотел использовать Eloquent для этой конкретной задачи, учитывая другие определенные отношения.

Я смог решить это, перегружая атрибут и вручную создавая отношение. Тем не менее, фактический запрос и hasMany необходимо также создать вручную. Давайте посмотрим, как я это достиг. Помните, что цель состоит в том, чтобы получить Orders от пользователя через 2 имеет-многие отношения.

Во-первых, перегрузка атрибута.

 public function getOrdersAttribute() { if( ! array_key_exists('orders', $this->relations)) $this->orders(); return $this->getRelation('orders'); } 

Идея в вышеуказанной функции заключается в том, чтобы захватить, когда вызывается ленивый загруженный ->orders . например $user->orders . Это проверяет, находится ли метод orders в массиве relations для существующей User model . Если это не так, то он называет нашу следующую функцию, чтобы заполнить релятизацию, и, наконец, возвращает отношения, которые мы только что создали.

Это то, что функция, которая фактически запрашивает Заказы, выглядит так:

 public function orders() { $orders = Order::join('contacts', 'orders.contact_id', '=', 'contacts.id') ->join('accounts', 'contacts.account_id', '=', 'accounts.id') ->where('accounts.owner_id', $this->getkey()) ->get(); $hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(User::query(), $this, 'accounts.owner_id', 'id'); $hasMany->matchMany(array($this), $orders, 'orders'); return $this; } 

В приведенном выше belongsTo() мы сообщаем таблице Orders присоединиться к ее контактам (который является установленным маршрутом, учитывая принадлежность belongsTo() в этом сценарии). Затем из контактов мы присоединяемся к учетным записям, а затем из учетных записей, которые мы можем получить от нашего пользователя, сопоставляя наш столбец owner_id с нашим существующим $user->id , поэтому нам больше не нужно ничего делать.

Затем нам нужно вручную создать наши отношения, hasMany экземпляр hasMany из конструктора Eloquent Relationship .

Учитывая, что метод HasMany фактически расширяет абстрактный класс HasOneOrMany , мы можем обратиться к абстрактному классу HasOneOrMany , передав наши аргументы непосредственно в HasMany , как HasMany ниже:

 $hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(User::query(), $this, 'accounts.owner_id', 'id'); 

HasOneOrMany ожидает от своего конструктора следующего:

 Builder $query, Model $parent, $foreignKey, $localKey 

Таким образом, для нашего запроса строителя мы передали экземпляр нашей модели, с которым мы хотим установить связь, причем второй аргумент является экземпляром нашей модели ($ this), третий аргумент – ограничение внешнего ключа от нашего Current- > 2-й модели, и, наконец, последним аргументом является столбец, соответствующий нашей текущей модели, против ограничения внешнего ключа на нашей Текущей-2-й модели.

После того, как мы создали наш экземпляр Relation из нашего объявления HasMany выше, нам тогда нужно сопоставить результаты отношений с их многочисленными родителями. Мы делаем это с помощью метода matchMany() который принимает 3 аргумента:

 array $models, Collection $results, $relation 

Таким образом, в этом случае массив моделей будет экземпляром массива нашей текущей красноречивой модели (пользователя), которую можно обернуть в массив для достижения нашего эффекта.

Второй аргумент будет результатом нашего запроса intitial $orders в нашей функции orders() .

Наконец, третий аргумент будет string отношения, которую мы хотим использовать, чтобы получить наш экземпляр этой связи; который для нас является order .

Теперь вы можете исправить использование Eloquent или Lazy Loading для получения наших заказов для нашего пользователя.

 User::find(8)->orders(); $user->orders; 

Надеюсь, это поможет кому-то, кто сталкивается с подобной проблемой.

вы можете перейти от пользователя к контакту, затем join к Orders

 public function orders(){ return $this->hasManyThrough('Contact', 'Account', 'owner_id')->join('orders','contact.id','=','orders.contact_ID')->select('orders.*'); } 

Это работает для меня в одном и том же случае, вся обратная связь приветствуется