繁体   English   中英

Laravel 9:隐式 model 绑定不适用于嵌套的一对多关系

[英]Laravel 9: Implicit model binding not working on nested one-to-many relationship

我在使用 Laravel 的隐式 model 绑定时遇到问题,该绑定使用嵌套路由和自定义键(不是 ID)。 我有这些示例模型:

  1. 类别(迁移):
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('name')->unique();
        });
    }
  1. 帖子(迁移):
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('category_id')->nullable()->constrained();
            $table->timestamps();
            $table->string('text');
        });
    }

关系是一对多的:

帖子有一个类别。 一个类别可以有多个帖子(具有相同类别的帖子)。

model 类如:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * The relationships that should always be loaded.
     *
     * @var array
     */
    protected $with = ['category'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'category_id',
    ];

    use HasFactory;

    public function category() {
        return $this->belongsTo(Category::class);
    }
}

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    use HasFactory;

    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

我正在使用这些路由来创建类别/帖子并将帖子附加到类别:

api.php:

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
use App\Http\Controllers\CategoryController;

Route::apiResource('posts', PostController::class);
Route::apiResource('categories', CategoryController::class);
Route::get('posts/{post}/category', [PostController::class, 'getCategory']);
Route::post('posts/{post}/category/{category:name}', [PostController::class, 'addCategory']);

邮政控制器:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;
use App\Models\Category;
use Illuminate\Database\QueryException;

class PostController extends Controller
{
    /**
     * Other functions for other routes
     *
     * 
     */
    
    public function addCategory(Request $request, Post $post, Category $category) 
    {
        $category->posts()->save($post);
        $post->category()->associate($category);
        $post->save();
        return $post;
    }
    public function removeCategory(Request $request, Post $post, Category $category) 
    {
        $post->category()->dissociate($post);
        $post->save();
        return response(['message'=>'Category removed.'], 200);
    }
    public function getCategory(Request $request, Post $post, Category $category) 
    {
        return $post->category;
    }
}

现在我创建一个帖子和一个类别(工作正常),并希望使用以下方法将帖子与类别相关联:

POST请求:http://localhost/api/posts/5/category/life

结果是:

Expected response status code [200] but received 500.

  The following exception occurred during the request:

  BadMethodCallException: Call to undefined method App\Models\Post::categories() in /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php:71

为什么 Laravel 试图在一对多关系上调用该方法?

数据库中存在 ID 为 5 的帖子和具有字符串值“life”的名称属性的类别。

但是我总是在使用路由时遇到 404 错误。 如果我使用类别 ID 代替它工作正常。 因此,如果使用自定义键,隐式 model 绑定不适用于嵌套路由?

如果我重写“addCategory”方法,它工作正常:

    public function addCategory(Request $request, Post $post, $name) 
    { 
        $category = Category::where('name', '=', $name)->firstOrFail();
        $category->posts()->save($post);
        $post->category()->associate($category);
        $post->save();
        return $post;
    }

但我认为 Laravel 的隐式绑定应该完全自动地做到这一点?

有人知道这里出了什么问题吗?

The way laravel works with nested model bindings is when you have more than one wildcard it will assume the last model wildcard belongs to the second last model wildcard, and this one belongs to the third to last place model wildcard and so on... to第二个 model 通配符属于第一个(这就是为什么它调用 a 有很多方法),所以第一个有很多秒,有很多三分之二,我希望你理解,问题是,关系必须已经存在才能做到那,所以你不能把帖子放在类别之前,因为一个类别有很多帖子。 您只需要记住始终将有很多的 model 放在属于的那个之前,即使实例不相关,它们也会被找到。 所以只需像这样交换端点路由Route::post('category/{category:name}/posts/{post}', [PostController::class, 'addCategory']); ,然后交换addCategory方法arguments。 我建议你对你的请求这样做,因为你没有对它做任何事情,看到你有一个没有请求正文的 post 方法很奇怪

该文档指出

... Laravel 将自动注入具有与请求 URI 中的相应值匹配的 ID 的 model 实例。 如果在数据库中未找到匹配的 model 实例,则会自动生成 404 HTTP 响应

所以我认为发生的事情是意料之中的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM