Laravel API APP Многие-многие отношения, как вернуть конкретную информацию в JSON?

Я пытался понять это в течение некоторого времени. В основном я получил 2 модели Recipe, Ingredient и один контроллер Recipe Controller. Я использую Почтальон, чтобы проверить мой API. Когда я иду на мой маршрут get, который использует Recipe Controller @ getRecipe, возвращаемое значение соответствует изображению ниже:

Возвращение для получения маршрута

Если я хочу, чтобы возвращаемое значение маршрута get было в ФОРМАТЕ приведенного ниже рисунка, как мне этого добиться? Под этим я подразумеваю, что не хочу видеть для рецептов: столбец create_at, столбец updated_at и для ингредиентов: столбец сводной информации, требуется только информация об имени и количестве столбцов.

Формат возвращаемого значения, который я хочу

Рецепт модели:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Recipe extends Model
{
    protected $fillable = ['name', 'description'];

    public function ingredients()
    {
      return $this->belongsToMany(Ingredient::class, 
      'ingredient_recipes')->select(array('name', 'amount'));
    }
}

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

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Ingredient extends Model
{
    protected $fillable = ['name', 'amount'];
}

Recipe Controller

<?php

namespace App\Http\Controllers;


use App\Ingredient;
use App\Recipe;
use Illuminate\Http\Request;

class RecipeController extends Controller {

public function postRecipe(Request $request)
{
    $recipe = new Recipe();
    $recipe->name = $request->input('name');
    $recipe->description = $request->input('description');
    $recipe->save();

    $array_ingredients = $request->input('ingredients');
    foreach ($array_ingredients as $array_ingredient) {
        $ingredient = new Ingredient();
        $ingredient->name = $array_ingredient['ingredient_name'];
        $ingredient->amount = $array_ingredient['ingredient_amount'];
        $ingredient->save();
        $recipe->ingredients()->attach($ingredient->id);
    }

    return response()->json(['recipe' => $recipe . $ingredient], 201);
}

public function getRecipe()
{
    $recipes = Recipe::all();
    foreach ($recipes as $recipe) {
        $recipe = $recipe->ingredients;
    }
    $response = [
        'recipes' => $recipes
    ];
    return response()->json($response, 200);
}

API-маршруты:

Route::post('/recipe', 'RecipeController@postRecipe')->name('get_recipe');
Route::get('/recipe', 'RecipeController@getRecipe')->name('post_recipe');

Спасибо, парни!

1 ответ

Решение

Я думаю, что ваше лучшее решение - использовать Transformer. Используя вашу текущую реализацию, я бы порекомендовал выбрать только необходимое поле в вашем цикле, то есть:

foreach ($recipes as $recipe) {
    $recipe = $recipe->ingredients->only(['ingredient_name', 'ingredient_amount']);
}

Хотя вышеприведенное может работать, но есть проблема с вашей текущей реализацией, потому что будет много итераций / циклов, опрашивающих базу данных, я бы рекомендовал вместо этого стремиться к загрузке отношения. Но ради этого вопроса вам нужен только Трансформер.

Установить трансформатор с помощью композитора composer require league/fractal Затем вы можете создать каталог с именем Transformers под app каталог.

Затем создайте класс с именем RecipesTransformer и инициализируйте с помощью:

namespace App\Transformers;

use App\Recipe;

use League\Fractal\TransformerAbstract;

class RecipesTransformer extends TransformerAbstract
{
    public function transform(Recipe $recipe)
    {
        return [
            'name' => $recipe->name,
            'description' => $recipe->description,
            'ingredients' => 
                $recipe->ingredients->get(['ingredient_name', 'ingredient_amount'])->toArray()
        ];
    }
}

Затем вы можете использовать этот преобразователь в вашем методе контроллера следующим образом:

use App\Transformers\RecipesTransformer;
......
public function getRecipe()
{
     return $this->collection(Recipe::all(), new RecipesTransformer);
     //or if you need to get one
     return $this->item(Recipe::first(), new RecipesTransformer);
}

Вы можете обратиться к хорошему учебнику, как это, для большего вдохновения, или просто перейти на страницу Fractal для деталей.

Обновить

Чтобы заставить работать коллекцию Fractal, поскольку приведенный мной пример будет работать, если в вашем проекте есть Dingo API, вы можете вручную создать его следующим образом:

public function getRecipe()
{
    $fractal = app()->make('League\Fractal\Manager');
    $resource = new \League\Fractal\Resource\Collection(Recipe::all(), new RecipesTransformer);

     return response()->json(
        $fractal->createData($resource)->toArray());
}

Если вы хотите сделать Предмет вместо коллекции, тогда вы можете иметь new \League\Fractal\Resource\Item вместо. Я бы порекомендовал, чтобы у вас был установлен Dingo API, или вы можете следовать этому простому учебнику, чтобы сделать его более удобным и аккуратным без лишних повторений.

Другие вопросы по тегам