2016-12-14 4 views
14

Мне не очень повезло, сортируя это по пути Laravel. Поэтому я задаю два вопроса.Laravel hasMany Many to Many to One Eloquent

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

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

  • объявления (ID)
  • listings_features (listing_id, feature_id)
  • listings_features_types (идентификатор, тип)
  • listings_features_values ​​(идентификатор , listing_feature_type_id, значение)

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

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

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

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 для достижения того же результата установлен, но называть его, как упоминалось выше? В качестве альтернативы, как я могу назвать это, как указано выше, но остановить ошибку?

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

Update:

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

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 форта и изменений функции() для

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" 
 
} 
 
}, 
 
    ...

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

Update 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 (i.e. Safety) 
Schema::create('listings_features_types', function(Blueprint $table){ 
    $table->string('id'); 
    $table->string('listing_feature_type'); 
}); 

// feature values (i.e. 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'); 
}); 

Update 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" 
      } 
     } 
     } 
    ] 
} 
+0

Уход за публикацией сообщений r (* listing_features * tables) ваши функции 'public function()' не чувствуют себя хорошо. –

+0

Добавлено, спасибо! – Spechal

+0

Кроме того, прежде чем вы спросите, я создаю API для существующего приложения, поэтому определены имена таблиц и другие элементы. :) – Spechal

ответ

8

Вы можете использовать многие-ко-многим отношения между Listing и ListingFeatureValue моделей, затем группируйте связанные функции перечисления для данного списка по их типу, используя метод сбора groupBy.

Листинг модель:

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 массива, чтобы заставить Красноречивый модель, чтобы добавить, что любые toArray() вызовы к образцовый экземпляр, который используется при преобразовании модели в JSON toJson().

Это может показаться немного запутанным, чтобы первыми получать все значения листинга затем разделить их, используя методы сбора groupBy и map, но не родной Eloquent механизма для использования hasManyThrough через многие-ко-многим. Есть different approach here, если вам это не нравится.

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

+0

Тем не менее, мне удалось получить версию, но результат странный. Я буду обновлять исходное сообщение. Я также чувствую, что мне нужно приложение, принадлежащее TomanyThrough, на основе генерируемого SQL. – Spechal

+0

Я обновил свой ответ, чтобы включить код перехода для сводной таблицы, который вам понадобится. Вам также нужно будет прикрепить/синхронизировать свои списки с их функциями перечисления, например: '$ listing-> features() -> sync ($ arrayOfListingFeatureIDs)' –

+0

Подробнее об управлении отношениями «многие ко многим» вы можете узнать здесь: https://laravel.com/docs/5.3/eloquent-relationships#updating-many-to-many-relationships –