Laravel hasMany Many to Many to One Eloquent

У меня не было большой удачи, сортируя это по пути Laravel. Поэтому я задаю два вопроса.

Учитывая, что у меня есть автомобиль, и у этого автомобиля может быть много возможностей, но эти функции также разделены Feature Type, как я могу вернуть все функции, разделенные на Feature Type, для упомянутого Car?

У меня есть четыре таблицы, в которых таблица_соединений является сводной таблицей:

  • списки (id)
  • listings_features (listing_id, feature_id)
  • listings_features_types (id, type)
  • listings_features_values ​​(id, listing_feature_type_id, значение)

У меня есть следующий код, который создает то, что мне нужно, но когда я его использую, я получаю ошибку Laravel … «Вызовите функцию-член addEagerConstraints () в строке« … потому что я вызываю ее как таковую:

Listing::with( 'features', )->get(); 

Код, который я использую для получения данных в желаемом формате (не устойчивый), является

 public function features() { $out = array(); $features = $this->hasMany('App\Models\ListingFeature', 'listing_id', 'id')->select('feature_id')->get(); $types = ListingFeatureType::all(); foreach($types as $key => $obj){ $out[$key]['listing_feature_type_id'] = $obj->id; $out[$key]['name'] = $obj->listing_feature_type; $out[$key]['features'] = ListingFeatureValue::whereIn('id', $features->toArray())->where('listing_feature_type_id', '=', $obj->id)->get()->toArray(); } return json_encode($out); } 

Что возвращает:

 [ { "listing_feature_type_id": "0f40888c-3b09-40ed-aef6-0fddc1a155b6", "name": "Safety", "features": [ { "id": "0ed0ad63-a6ed-4818-8c6f-ee048694dcd9", "listing_feature_type_id": "0f40888c-3b09-40ed-aef6-0fddc1a155b6", "listing_feature_text": "Anti-Lock Brakes" }, { "id": "37abeef2-dc22-4995-8503-f89962242ea6", "listing_feature_type_id": "0f40888c-3b09-40ed-aef6-0fddc1a155b6", "listing_feature_text": "Collision Detection" }, { "id": "3c0728e1-91f7-4f44-ac0b-429eda816692", "listing_feature_type_id": "0f40888c-3b09-40ed-aef6-0fddc1a155b6", "listing_feature_text": "Dual Airbags" }, { "id": "4255b8b4-e71c-4059-8a22-1b9894512564", "listing_feature_type_id": "0f40888c-3b09-40ed-aef6-0fddc1a155b6", "listing_feature_text": "Side Airbags" } ] }, { "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "name": "Interior", "features": [ { "id": "1b89581e-1a30-4dce-9455-ab0ad4c49bcf", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "Privacy Glass" }, { "id": "59e3628f-3cef-4447-9cb2-71be4a3046a4", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "Onboard GPS" }, { "id": "66fe416b-98dc-45c8-979d-78f2ea7fe876", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "In-dash Navigation" }, { "id": "8fe836a3-5596-4306-aac1-bae4cb596e20", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "Power Windows" }, { "id": "e4addb5a-1b26-4ae3-b0ee-3b8bce892fb9", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "Tinted Windows" }, { "id": "f95b8253-a2b8-4bfc-90c0-0fc656c3f200", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "CD Player" } ] }, { "listing_feature_type_id": "8d16e8ea-3d38-48dc-8e56-00d1cc736f0d", "name": "Exterior", "features": [ { "id": "3aa6dd05-dd3a-4e93-ad06-295687a8dda1", "listing_feature_type_id": "8d16e8ea-3d38-48dc-8e56-00d1cc736f0d", "listing_feature_text": "Spoiler" } ] } ] 

Вот модели (очень простые, только начинающие):

 <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class ListingFeatureValue extends Model { protected $table = 'listings_features_values'; public $timestamps = false; public $incrementing = false; public function type() { return $this->belongsTo('App\Models\ListingFeatureType', 'listing_feature_type_id', 'id'); } } 
 <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class ListingFeatureType extends Model { protected $table = 'listings_features_types'; public $timestamps = false; public $incrementing = false; public function values() { return $this->hasMany('App\Models\ListingFeatureValue', 'listing_feature_type_id', 'id'); } } 
 <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class ListingFeature extends Model { protected $table = 'listings_features'; public $timestamps = false; public $incrementing = false; } 

Как я могу создать отношения модели Laravel для достижения того же набора результатов, но называть его, как указано выше? В качестве альтернативы, как я могу назвать это, как указано выше, но остановить ошибку?

Если вы сделали это так, спасибо!

Обновить:

Я не смог заставить его работать так, как есть, у меня возникла ошибка SQL, из-за чего мне кажется, что мне нужно иметь значение toManyThrough, поэтому я могу указать имена таблиц:

 SQLSTATE[42S02]: Base table or view not found: 1146 Table 'ad_l5.listing_listing_feature' doesn't exist (SQL: select `listings_features`.*, `listing_listing_feature`.`listing_id` as `pivot_listing_id`, `listing_listing_feature`.`listing_feature_id` as `pivot_listing_feature_id` from `listings_features` inner join `listing_listing_feature` on `listings_features`.`id` = `listing_listing_feature`.`listing_feature_id` where `listing_listing_feature`.`listing_id` in (b266c874-1cef-4f49-b65f-f91ddaaf6aee, e93674ca-3f82-45d8-9961-e8569cac164b)) 

Но используя точный код, размещенный как ответ ниже из @Carter Fort и меняющий метод features (), чтобы

 return $this->belongsToMany(ListingFeatureValue::class, 'listings_features', 'listing_id', 'feature_id'); 

Также добавление к модели ListingFeatureValue

 public function type() { return $this->belongsTo(ListingFeatureType::class, 'listing_feature_type_id', 'id'); } 

Я получаю вывод:

 "featuresByType": { "": [ { "id": "3aa6dd05-dd3a-4e93-ad06-295687a8dda1", "listing_feature_type_id": "8d16e8ea-3d38-48dc-8e56-00d1cc736f0d", "listing_feature_text": "Spoiler", "pivot": { "listing_id": "e93674ca-3f82-45d8-9961-e8569cac164b", "feature_id": "3aa6dd05-dd3a-4e93-ad06-295687a8dda1" }, "type": { "id": "8d16e8ea-3d38-48dc-8e56-00d1cc736f0d", "listing_feature_type": "Exterior" } }, { "id": "f95b8253-a2b8-4bfc-90c0-0fc656c3f200", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "CD Player", "pivot": { "listing_id": "e93674ca-3f82-45d8-9961-e8569cac164b", "feature_id": "f95b8253-a2b8-4bfc-90c0-0fc656c3f200" }, "type": { "id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_type": "Interior" } }, { "id": "66fe416b-98dc-45c8-979d-78f2ea7fe876", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "In-dash Navigation", "pivot": { "listing_id": "e93674ca-3f82-45d8-9961-e8569cac164b", "feature_id": "66fe416b-98dc-45c8-979d-78f2ea7fe876" }, "type": { "id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_type": "Interior" } }, { "id": "0ed0ad63-a6ed-4818-8c6f-ee048694dcd9", "listing_feature_type_id": "0f40888c-3b09-40ed-aef6-0fddc1a155b6", "listing_feature_text": "Anti-Lock Brakes", "pivot": { "listing_id": "e93674ca-3f82-45d8-9961-e8569cac164b", "feature_id": "0ed0ad63-a6ed-4818-8c6f-ee048694dcd9" }, "type": { "id": "0f40888c-3b09-40ed-aef6-0fddc1a155b6", "listing_feature_type": "Safety" } }, ... 

Это лучше, чем я был, но было бы неплохо, если бы результаты были сгруппированы по типу, а затем функции внутри, а не для каждой функции с прикрепленным типом. Это затрудняет анализ для отображения. Спасибо за помощь до сих пор!

Обновление 2

Это будут мои файлы схемы:

 // listings Schema::create('listings', function (Blueprint $table) { $table->string('id'); $table->string('title'); }); // listings to features pivot Schema::create('listings_features', function (Blueprint $table) { $table->string('listing_id'); $table->string('feature_id'); }); // feature types (ie Safety) Schema::create('listings_features_types', function(Blueprint $table){ $table->string('id'); $table->string('listing_feature_type'); }); // feature values (ie Anti-Lock Brakes) Schema::create('listings_features_values', function(Blueprint $table){ $table->string('id'); $table->string('listing_feature_type_id'); // links to listings_features_types $table->string('listing_feature_text'); }); 

Обновление 3 Внесите изменения из нижеприведенного ответа (я буду публиковать весь код после работы). Я приближаюсь к тому, что хотел бы.

 "features": [ { "id": "3aa6dd05-dd3a-4e93-ad06-295687a8dda1", "listing_feature_type_id": "8d16e8ea-3d38-48dc-8e56-00d1cc736f0d", "listing_feature_text": "Spoiler", "pivot": { "listing_id": "e93674ca-3f82-45d8-9961-e8569cac164b", "feature_id": "3aa6dd05-dd3a-4e93-ad06-295687a8dda1" }, "type": { "id": "8d16e8ea-3d38-48dc-8e56-00d1cc736f0d", "listing_feature_type": "Exterior" } }, { "id": "f95b8253-a2b8-4bfc-90c0-0fc656c3f200", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "CD Player", "pivot": { "listing_id": "e93674ca-3f82-45d8-9961-e8569cac164b", "feature_id": "f95b8253-a2b8-4bfc-90c0-0fc656c3f200" }, "type": { "id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_type": "Interior" } }, { "id": "66fe416b-98dc-45c8-979d-78f2ea7fe876", "listing_feature_type_id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text": "In-dash Navigation", "pivot": { "listing_id": "e93674ca-3f82-45d8-9961-e8569cac164b", "feature_id": "66fe416b-98dc-45c8-979d-78f2ea7fe876" }, "type": { "id": "84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_type": "Interior" } 

// ….]

Я бы хотел:

 { "feature_types":[ { "type":{ "id":"8d16e8ea-3d38-48dc-8e56-00d1cc736f0d", "listing_feature_type":"Interior", "features":[ { "id":"3aa6dd05-dd3a-4e93-ad06-295687a8dda1", "listing_feature_type_id":"8d16e8ea-3d38-48dc-8e56-00d1cc736f0d", "listing_feature_text":"GPS" }, { "id":"66fe416b-98dc-45c8-979d-78f2ea7fe876", "listing_feature_type_id":"84dcce5c-34b9-4c8b-9066-5b20a42cf4cd", "listing_feature_text":"In-dash Navigation" }, ] "pivot":{ "listing_id":"e93674ca-3f82-45d8-9961-e8569cac164b", "feature_id":"f95b8253-a2b8-4bfc-90c0-0fc656c3f200" } } } ] } 

Solutions Collecting From Web of "Laravel hasMany Many to Many to One Eloquent"

Вы можете использовать связь «Много-ко-многим» между Listing и моделями ListingFeatureValue , а затем группировать связанные функции перечисления для данного списка по их типу с помощью метода groupBy Collection.

Модель листинга :

 class Listing extends Model { protected $hidden = [ 'features' ]; protected $appends = [ 'feature_types' ]; public function features(){ return $this->belongsToMany(ListingFeatureValue::class, 'listings_features', 'listing_id', 'feature_id'); } public function getFeatureTypesAttribute() { return $this->features->groupBy(function ($feature, $key) { return $feature->type->id; })->map(function($features, $key){ $type = ListingFeatureType::find($key); $type->features = $features; return $type; })->values(); } } 

getFeatureTypesAttribute() является звездой шоу здесь, потому что вы можете комбинировать это с массивом appends чтобы заставить модель Eloquent добавить это к любым вызовам toArray() экземпляра модели, что toJson() используется для использования toJson() при преобразовании вашего модель для JSON.

Может показаться немного запутанным, чтобы сначала получить все значения листинга, а затем разделить их, используя groupBy и map collection, но нет никакого естественного механизма Eloquent для использования hasManyThrough через отношения «многие ко многим». Здесь есть другой подход, если вам это не нравится.

Модель ListingFeatureValue :

 class ListingFeatureValue extends Model { public $table = 'listings_features_values'; public function type() { return $this->belongsTo(ListingFeatureType::class, 'feature_type_id'); } } 

Я показываю эту модель здесь, потому что отношение type() вызывается в getFeaturesByTypeAttribute() выше и не хочет, чтобы у него была какая-то путаница.

И, только для полноты, модель ListingFeatureType :

 class ListingFeatureType extends Model { public $table = "listings_features_types"; public function listings() { return $this->hasMany(ListingFeatureValue::class, 'listing_feature_type_id'); } } 

Если вы захотите загрузить списки с их функциями и типами для полного вывода всех ваших записей, вы можете сделать это следующим образом:

 App\Listing::with('features.type')->get()->toJson(); 

Мои файлы миграции выглядят следующим образом:

 //create_listings_table Schema::create('listings', function (Blueprint $table) { $table->increments('id'); $table->string('uuid'); $table->timestamps(); }); //create_listings_features_values_table Schema::create('listings_features_values', function (Blueprint $table) { $table->increments('id'); $table->string('listing_feature_text'); $table->integer('listing_feature_type_id')->unsigned(); $table->timestamps(); }); //create_listings_features_types_table Schema::create('listings_features_types', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); //create_listings_features_table Schema::create('listings_features', function(Blueprint $table){ $table->integer('listing_id')->unsigned(); $table->integer('listing_feature_id')->unsigned(); }); 

Подробнее о методах коллекции вы можете узнать здесь:

https://laravel.com/docs/5.3/collections#available-methods

… и нетерпеливая загрузка здесь:

https://laravel.com/docs/5.3/eloquent-relationships#eager-loading

… и отношения «многие ко многим»:

https://laravel.com/docs/5.3/eloquent-relationships#many-to-many