按 slug 而不是 id 搜索对象

Posted

技术标签:

【中文标题】按 slug 而不是 id 搜索对象【英文标题】:Search object by slug and not by id 【发布时间】:2016-04-01 19:08:49 【问题描述】:

我是 Laravel(使用 5.2.3 版)的相对初学者,并且一直在学习 Laracasts 教程,然后做了一些我自己的实验。

我成功设置了一个通过ID从表中获取项目的路由,如下所示

Route::get('/wiseweasel/id', 'WiseweaselController@singleArticle');

为简单起见,控制器只是 dd 的文章

public function singleArticle($id)

  $article = ww_articles::find($id);
  dd($article);

这绝对没问题 - 我访问例如 /wiseweasel/2 并使用 id2 获取记录的内容。

所以,我想使用记录中的 slug 字段而不是 id。由于我知道 ID 方法有效,因此我尝试仅修改此路由和控制器(也尝试重新创建,但均无效)所以我现在有了:

Route::get('/wiseweasel/slug', 'WiseweaselController@singleArticle');

public function singleArticle($slug)

  $article = ww_articles::find($slug);
  dd($article);

第二条记录的 slug 是“secondarticle”。因此,访问 url /wiseweasel/secondarticle,我希望看到与之前删除的相同记录。相反,我最终得到了 null。

更奇怪的是,使用原始 id 路由 (/wiseweasel/2) 仍然返回记录...当我从路由和控制器中删除了所有跟踪时,我希望这会失败...

这让我想知道这是否可能是一些奇怪的缓存问题?我试过了

php工匠路线:清除

如果路由被缓存。我也尝试过重新启动 Apache 和 mysql(我都在使用 XAMMP)。

但仍然没有运气......不确定我是否误解了某些事情的工作原理或发生了什么......所以如果有人对我可能做错了什么有任何建议,或者有什么可以尝试的,我会非常感谢! :)

【问题讨论】:

【参考方案1】:

如果你有两条这样的路线

Route::get('/wiseweasel/id', 'WiseweaselController@singleArticle');
Route::get('/wiseweasel/slug', 'WiseweaselController@singleArticle');

它将始终使用第一个。显然,没有 id 'secondarticle',所以它返回 null (虽然在这种情况下它并不重要,它们都指向同一个方法)。

原因是 route 会搜索可能的路线,直到找到匹配项,该匹配项始终是具有 id 的那个。为什么?你没有告诉 Route id 必须匹配一个整数!

您可以确保 id 被理解为一个整数,但是我建议使用这样的 url 是一个更好的选择

/wiseweasel/id/slug?

另一个建议。不要为模型使用诸如 xx_articles 之类的名称,而应使用 Article。这样您就可以使用新的implicit route binding。所以使用隐式路由绑定你的 url 看起来像这样(假设你的模型叫做 Article)

Route::get('/wiseweasel/article', 'WiseweaselController@singleArticle');

【讨论】:

感谢 Bojan - 我只有一条路线,因为我只是修改了“id”路线以创建“slug”路线。但这是一个需要牢记的好点,因为就 laravel 而言,您的示例中的两者基本上是相同的路线!我使用了 ww_ 前缀,因为我可能有多个不同的文章表(包含站点不同区域的文章),因此模型与表名匹配。还是型号名称使用非复数形式更多? 它们不应该是复数,不。此外,如果您有不同的类型,您可以以不同的方式处理,请查看 morphTo 看起来它可能会派上用场......一旦我掌握了更多基础知识,我可能会回到它,但谢谢! :)【参考方案2】:

Laravel 不会自动知道 slug 它应该以不同的方式搜索记录。

使用时:

$article = ww_articles::find($slug);

你在告诉 Laravel - 通过 ID 查找 www_articles 的记录。 (不管你称这个 id 为 $slug)。

为了实现你想要的改变:

$article = ww_articles::find($slug);

进入

$article = ww_articles::where('slug', $slug)->first();

这样就可以解决问题(对于slug,将列的名称放入数据库的表中)。当然请记住,在这种情况下,slug 在所有记录中应该是唯一的,否则您将无法获得所有 slug。

【讨论】:

感谢 Marcin,效果很好!这是有道理的,因为我猜 laravel 无法知道 $id 或 $slug 在这种情况下是什么意思,因为它只是通过 url 输入的任何内容的占位符。也感谢您的解释:) 当您说 slug 必须是唯一的时,如果我首先选择一个类别然后选择 slug 会是这种情况吗 - 大概在这种情况下,slug 只需要在该类别中是唯一的吗? (尽管如果我想在没有类别的情况下尝试访问,这仍然是一个问题) 我的意思是每个类别都应该有独特的 slug。如果 2 个类别将具有相同的 slug,那将是一个问题,因为您将无法确定您想要获得的类别只是 slug。因此,在为新类别填充 slug 时,您需要确保它是唯一的 好的,我明白了,谢谢 :) 无论如何它们都应该是独一无二的(通用的“第一篇文章”slug 只是为了测试!)【参考方案3】:

您还可以选择使用路由模型绑定来处理此问题并将解析的实例注入您的方法中。

使用新的隐式路由模型绑定,您可以告诉模型它应该使用哪个键来进行路由绑定。

// routes
Route::get('/wiseweasel/article', 'WiseweaselController@singleArticle');


// Article model
public function getRouteKeyName()

    return 'slug';


// controller
public function singleArticle(Article $article)

    dd($article);

Laravel Docs - Route Model Binding

【讨论】:

知道作为替代方法很方便,谢谢! :)【参考方案4】:

也许答案有点晚了,但还有另一种方法可以继续使用 find 方法并使用 slug 作为表标识符。您必须在模型中将受保护的 $primaryKey 属性设置为 'slug'

class ww_articles extends Model

    protected $primaryKey = 'slug';
    ...

这会起作用,因为 find 方法在内部使用了 Model 类中的 getQualifiedKeyName 方法,该方法使用了 $primaryKey 属性。

【讨论】:

感谢塞尔吉奥,知道这很有用!在这种情况下,我认为我可能会将 ID 作为主键,但在其他情况下,将其更改为其他字段会很方便:)(我的问题主要是由于没有完全理解 find 方法,所以 Marcin 的答案对我来说解释得很好,但这也可能是一个很好的解决方案!)

以上是关于按 slug 而不是 id 搜索对象的主要内容,如果未能解决你的问题,请参考以下文章

Django 空请求对象

在 URL 中显示类别 Slug 而不是 ID

如何按值而不是按引用复制对象[重复]

使用 slug 而不是 ID 获取记录

CakePHP 通过 slug 而不是 ID 过滤列表

在 Laravel 8 中为 crud 显示 slug 而不是 id