2014-01-18 4 views
1

Я использую Laravels Eloquent ORM, и у меня возникла небольшая проблема со специальными отношениями. Давайте предположим, у меня есть следующая таблица:Отношения «один-ко-многим» с ярко выраженным

Recipe: 
    id | ... | ingredient1 | ingredient2 | ingredient3 | ingredient4 

Каждый рецепт имеет ровно 4 ингредиенты и я получаю данные из внешнего источника, в данном конкретном формате, вот почему у меня есть ингредиенты, как колонны и не как обычный МНОГИХ to-many отношение.

Я мог бы установить их как 4 отношения «один ко многим», но я хочу, чтобы иметь возможность писать $ingredient->usedInRecipes()->get().
С 4 отношениями один-ко-многим мне нужно было бы написать $ingredient->usedInRecipesAsIngredient1()->get(), [...], $ingredient->usedInRecipesAsIngredient4()->get() и объединить их после этого, что приведет к 4 запросам.

Если вы знаете хороший способ присоединиться к ним перед тем, как запросить базу данных или как сделать работу с отношением «4 к большому», ответьте!

ответ

0

Я нашел решение, которое, кажется, работает во всех моих случаях использования.

В модели рецепта я определил 4 компонента как отношения «один ко многим» и выполнил две вспомогательные функции.

class Recipe extends Eloquent { 
    public function ingredient1() 
     { return $this->belongsTo('Ingredient', 'ingredient1'); } 
    public function ingredient2() 
     { return $this->belongsTo('Ingredient', 'ingredient2'); } 
    public function ingredient3() 
     { return $this->belongsTo('Ingredient', 'ingredient3'); } 
    public function ingredient4() 
     { return $this->belongsTo('Ingredient', 'ingredient4'); } 

    public function scopeHasIngredient($query, Ingredient $ingredient) { 
     return $query-> where('ingredient1', '=', $ingredient->id) 
        ->orWhere('ingredient2', '=', $ingredient->id) 
        ->orWhere('ingredient3', '=', $ingredient->id) 
        ->orWhere('ingredient4', '=', $ingredient->id); 
    } 

    public function scopeWithIngredients($query) { 
     return $query->with('ingredient1', 'ingredient2', 
          'ingredient3', 'ingredient4'); 
    } 
} 

class Ingredient extends Eloquent { 
    public function ingredientForRecipes() { 
     return Recipe::hasIngredient($this)->withIngredients(); 
    } 
} 

Чтобы получить все рецепты для Ингредиента теперь я могу назвать $ingredient->ingredientForRecipes()->get() и использовать компоненты без дополнительных запросов.

1

Из вопроса, который я не могу сказать, если вы уже пытались это или нет, хотя, как я вижу, вам просто нужно использовать одно отношение «многие ко многим».

Каждый ингредиент, предположительно, имеет общий набор свойств, который можно обрабатывать в одной таблице ingredients.

id name  created_at    updated_at 
1  Paprika  01/01/1970 00:00:00  01/01/1970 00:00:00 
1  Rosemary 01/01/1970 00:00:00  01/01/1970 00:00:00 
1  Basil  01/01/1970 00:00:00  01/01/1970 00:00:00 
1  Oregano  01/01/1970 00:00:00  01/01/1970 00:00:00 

Тогда ваш recipes стол

id name  created_at    updated_at 
1  Herb Soup 01/01/1970 00:00:00  01/01/1970 00:00:00 

Чтобы держать отношения, сводную таблицу ingredient_recipe

id recipe_id ingredient_id 
1  1   1 
2  1   2 
3  1   3 
4  1   4 

Теперь все, что вам нужно, это belongsToMany отношения как на вашем Recipe и Ingredient модели.

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

Recipe::with('ingredients')->get(); 

бы получить все ингредиенты вместе с рецептом.

You can read more about this relationship in the documentation here.

Без Повороты

Если вы сохранили столбцы ingredient_1, ingredient_2 и так далее в recipes таблице вы можете добавить что-то подобное для вашей Recipe модели.

public function scopeGetWithIngredients($query) 
{ 
    $query->leftJoin('ingredients i1', 'recipes.ingredient_1', '=', 'i1.id') 
      ->leftJoin('ingredients i2', 'recipes.ingredient_2', '=', 'i2.id') 
      ->leftJoin('ingredients i3', 'recipes.ingredient_3', '=', 'i3.id') 
      ->leftJoin('ingredients i4', 'recipes.ingredient_4', '=', 'i4.id') 
      ->select('recipes.name', 'i1.name AS ing_1', 'i2.name AS ing_2'); 
} 

Вы можете просто получить компоненты в модели с

Recipe::getWithIngredients(); 
+0

Да, я мог бы это сделать, но я искал решение без сводной таблицы, потому что некоторые другие проекты получают доступ к одной и той же базе данных. Если у кого-то нет хорошего решения, я буду использовать сводную таблицу и настроить другие проекты. – darthmaim

+0

@ darthmaim хорошо, справедливо. Я все равно спускаюсь по поворотному маршруту лично, поскольку он чувствует себя намного более нормализованным. Говоря, что я добавил непроверенный подход, вы могли бы использовать, чтобы получить ингредиенты, используя плавные соединения и метод с областью действия на вашей модели. –

+1

Удивительно, я попробую это. – darthmaim

Смежные вопросы