【问题标题】:Laravel API APP Many-Many Relationship, how to return specific information in JSON?Laravel API APP 多对多关系,如何返回 JSON 中的具体信息?
【发布时间】:2017-12-29 18:50:41
【问题描述】:

我已经尝试解决这个问题一段时间了。基本上我有 2 个模型 'Recipe'、' Ingredient' 和一个 Controller 'RecipeController'。 我正在使用 Postman 来测试我的 API。当我转到使用 RecipeController@getRecipe 的 get 路线时,返回值如下图所示:

Return for Get Route

如果我希望 get 路由的返回值采用下图的格式,我该如何实现?我的意思是我不想看到食谱:created_at 列、updated_at 列和成分:pivot 信息列,只想要名称和数量列信息。

Return Value Format I Want

配方模型:

<?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'];
}

配方控制器

<?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');

谢谢各位!

【问题讨论】:

    标签: laravel api pivot-table


    【解决方案1】:

    我认为您最好的解决方案是使用 Transformer。使用您当前的实现,我建议仅在循环中获取所需的字段,即:

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

    虽然上述方法可能有效,但您当前的实现存在问题,因为将有大量的迭代/循环轮询数据库,我建议改为预先加载关系。 但是为了这个问题,你只需要Transformer

    使用composer安装transformer composer require league/fractal 然后就可以在app目录下创建一个名为Transformers的目录。

    然后创建一个名为 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);
    }
    

    你可以参考一个很好的教程like this以获得更多灵感,或者直接去Fractal's page了解详情。

    更新

    如果您的项目中有 Dingo API,为了让 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,或者您可以关注this simple tutorial,以便在没有不必要的重复的情况下更整洁地处理

    【讨论】:

    • 嗨 Omisakin,感谢您的回复。我需要联盟/分形,创建了类并实现了' return $this->collection(Recipe::all(), new RecipesTransformer); ' 进入我的控制器@getRecipe。我现在得到的问题是:'方法[集合]不存在。 ' .在配方控制器中找不到方法“集合”。
    • 再次感谢 Omisakin 的回复。我将研究 Dingo API,但现在我已经实现了对我的控制器的更新,现在收到 'array_key_exists(): 第一个参数应该是字符串或整数'。 ' .我该如何解决这个问题?
    • 您能否检查问题出处的堆栈跟踪。我猜它可能来自 Transformer 的变换函数。所以注释掉这个并再次测试:'ingredients' =&gt; $recipe-&gt;ingredients-&gt;get(['ingredient_name', 'ingredient_amount'])-&gt;toArray()
    • 酷,我评论了你所说的,它显示了名称和描述属性,只是现在没有成分。我如何让这些成分起作用:P
    • 非常感谢 Omisakin !!!解决了它!将其添加到“成分”模型中,不再有问题。你一直陪着我到最后。再次感谢兄弟:)
    猜你喜欢
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-26
    • 2019-07-24
    相关资源
    最近更新 更多