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