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



我在使用嵌套路由和自定义密钥(而不是ID(的Laravel隐式模型绑定时遇到问题。我有以下示例模型:

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

这种关系是一对多的:

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

模型类如:

<?php
namespace AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;
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 AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;
class Category extends Model
{
use HasFactory;
public function posts()
{
return $this->hasMany(Post::class);
}
}

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

api.php:

<?php
use IlluminateHttpRequest;
use IlluminateSupportFacadesRoute;
use AppHttpControllersPostController;
use AppHttpControllersCategoryController;
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']);

PostController:

<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use AppModelsPost;
use AppModelsCategory;
use IlluminateDatabaseQueryException;
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 AppModelsPost::categories() in /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php:71

为什么Laravel试图在一对多关系中使用这种方法?

ID为5的Post和具有字符串值"的name属性的类别;生命;存在于数据库中。

但我在使用路线时总是会出现404错误。如果我改为使用类别ID,它可以正常工作。如果使用自定义键,那么隐式模型绑定对嵌套路由无效?

如果我重写"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处理嵌套模型绑定的方式是,当您有多个通配符时,它会假设最后一个模型通配符属于倒数第二个模型通配符,而这个属于倒数第三个模型通配符等等……到第二个模型通配符属于第一个(这就是它调用has many方法的原因(,所以第一个有很多秒,我希望你能理解,问题是,为了做到这一点,关系必须已经存在,所以你不能把帖子放在类别之前,因为一个类别有很多帖子。你只需要记住,总是把一个有很多的模型放在所属的模型之前,即使实例不相关,它们也会被找到。因此,只需像这样交换端点路由Route::post('category/{category:name}/posts/{post}', [PostController::class, 'addCategory']);,然后交换addCategory方法参数。我建议你对你的请求这样做,因为你没有对它做任何事情,看到你有一个没有请求体的post方法是很奇怪的

文档指出:

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

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

最新更新