thechrise/permalink

Laravel的永久链接路由系统。高级数据库驱动的路由。直接从数据库中处理您的永久链接和SEO参数。

2.0.0 2023-08-23 17:46 UTC

README

Latest Stable Version

此包允许您直接从数据库创建动态路由,就像WordPress和其他CMS一样。

重要 虽然此包的功能非常简单,但有一些事情和良好实践需要考虑。我强烈建议您仔细阅读整个文档,以深入了解该包的工作原理,因为您将替换默认的Laravel路由系统,并且不想弄乱您的URL和SEO!

路线图

文档

安装

安装包

composer require thechrise/permalink

运行迁移

php artisan migrate

入门(请阅读)

此包直接从我们的数据库处理动态路由。也支持嵌套路由,因此我们可以轻松创建如 /jobs/frontend-web-developer 这样的路由。

大多数解决方案都与具有多态关系的模型紧密绑定,但是当处理没有模型的路由时,这并不灵活。此包支持两者,既支持绑定模型的路由,也支持常规路由。

基本上,此包将路由存储在包含每个路由信息的 permalinks 表中

  • 别名
  • 父级(嵌套路由的父级路由)
  • 模型(如果有的话)
  • 操作(控制器动作或模型默认动作)
  • SEO选项(标题、元数据...)

默认情况下,此包将尝试在单个SQL查询中查找是否在 permalinks 表中存在与当前请求路径匹配的永久链接。这对于大多数用例来说是可以的。如果出于某种原因,您想将永久链接信息缓存到Laravel路由缓存中,请参阅缓存永久链接部分。

示例

让我们通过一个简单的示例来了解其内部工作原理

它将执行以下操作(此示例尽量明确,内部使用预加载和一些其他性能优化)

$router->get('users', 'UserController@index');
$router->get('users/israel-ortuno', 'UserController@show');

// Which will produce:
//    /users                UserController@index
//    /users/israel-ortuno

注意: show 方法将接收用户作为参数 App\User::find(1),该路由绑定到该模型。

替换默认路由器

此包有自己的路由器,它扩展了默认的Laravel路由器。要替换默认路由器以使用此包中包含的扩展版本,您有两个选项

php artisan permalink:install {--default}

控制台将提示您两个选项

  [0] Http/Kernel.php (Default & Recommended)
  [1] bootstrap/app.php (Advanced)

选择适合您需求的选项。对于大多数情况,我推荐通过 Http\Kernel.php 进行操作。使用 --default 选项以避免阻塞提示(也可以使用默认Laravel命令的标志 --no-interaction)。

这两种方法都将默认的Laravel路由器替换为此包提供的扩展版本,该版本包含永久链接管理逻辑。

重要 使用 Http\Kernel.phpbootstrap/app.php不要 同时使用两者,因为这可能会导致意外的行为。

创建永久链接

动态路由系统的设置基本就这些了。让我们创建一个永久链接记录并测试一下!

Permalink::create([
    'slug' => 'home',
    'action' => 'App\Http\Controllers\HomeController@index'
]);
// Then visit /home

如果你的永久链接绑定到了一个模型(请参阅下一节),你可以按照以下方式创建你的永久链接记录

// Note: when using the User::create method, even if permalinkHandling (read more about it below)
// is disabled, it will create the permalink record.
$user = User::create([
    'name' => 'israel',
    'permalink' => [
        'slug' => 'israel-ortuno',
        'action' => 'user.show',
        'seo' => [...] // omit this attribute until you read more about it
    ]
]);

// Or

$user->createPermalink([...);

当使用User::createcreatePermalink时,如果你没有向permalink键提供任何数据,它将自动使用默认数据。在创建永久链接时,数据数组中存在的任何键将覆盖其默认值。

注意:这仅在permalinkHandling没有被禁用的情况下才能工作,更多关于它的信息请见下文。

更新永久链接

你可以像更新任何其他Eloquent模型一样轻松地更新一个永久链接。更新永久链接别名时,请务必小心,因为之前的URL将不再可用,并且这个包不处理301/302重定向。

重建最终路径(请阅读)

在更新别名时,包将递归地更新其嵌套永久链接的final_url属性,用新的别名替换之前的别名段。您可以通过在config/permalink.php配置文件中的rebuild_children_on_update选项来控制这种行为。如果您想手动处理这项任务,请禁用此选项(不推荐)。

查看Devio\Permalink\Services\PathBuilder类,以了解可用于执行手动更新的方法。

注意:请确保在当前请求的生命周期中重建子级的最终路径。

将模型绑定到永久链接

您可能想将永久链接绑定到模型资源,以便您可以创建一个唯一的URL来访问该特定资源。如果您想这样做,您只需要使用HasPermalinks属性并实现模型上的Permalinkable协议。

class User extends Model implements \Devio\Permalink\Contracts\Permalinkable;
{
    use \Devio\Permalink\HasPermalinks;

    public function permalinkAction()
    {
        return UserController::class . '@show';
    }

    public function permalinkSlug(): array
    {
        return ['entity.name'];
    }
}

一旦您设置了这些,这个包将自动为该模型的每个新记录生成一个永久链接。

此外,Permalinkable接口将强制您定义两个简单的方法

permalinkAction()

此方法将返回负责处理特定模型请求的默认控制器操作。模型本身将被注入到操作中(正如Laravel通常为路由模型绑定所做的那样)。

public function show($user)
{
    return view('users.show', $user);
}

注意:此操作将被永久链接记录中action列中存在的任何现有值覆盖,因此,如果您需要,可以为同一模型具有多个操作。

permalinkSlug()

这个方法稍微有点复杂。由于所有别名任务都由出色的Sluggable包处理,我们必须向其sluggable方法提供该包所需的信息。

永久链接模型将公开一个多态关系entity到该模型。由于别名发生在Permalink模型类中,我们必须指定哪个将成为别名的来源。您可以将entity视为$this,因此在这种情况下,entity.name就相当于$this->name。如果需要连接多个属性,请返回多个项。

['entity.name', 'entity.city']

注意:此方法应返回与Sluggable包兼容的数组,如果您想深入了解,请查看包文档

自动处理永久链接

默认情况下,此包会根据绑定模型的操作创建/更新/删除您的永久链接。如果您不想这样做,并想决定何时为该特定模型创建/更新/删除永久链接的精确时刻,您可以通过两种方式禁用永久链接处理

// Temporally disable/enable:
$model->disablePermalinkHandling();
$model->enablePermalinkHandling();

// Permanent disable or return a condition.
// Create this method in you model:
public function permalinkHanlding()
{
    return false;
}

创建

当您的资源触发 saved 事件时,将自动创建一个永久链接。除非您已向创建数组提供了 peramlink 键数组或使用了 setPermalinkAttribute 修改器,否则它将填充默认数据。

User::create(['name' => 'israel', 'permalink' => ['slug' => 'israel']]);
//
$user = new User;
$user->permalink = ['slug' => 'israel'];
$user->save();

如果禁用了 permalinkHandling,您将能够决定何时创建永久链接

// Assume permalinkHanlding() returns false
$user = User::create(['name' => 'israel']);
// Perform other tasks...
$user->createPermalink(); // Array is optional, provide data to override default values

注意:即使提供的数据中包含 permalink 键,永久链接记录仍会被创建。

更新

您可以像创建一样更新您的永久链接

$user = User::find(1);

$user->updatePermalink(['seo' => ['title' => 'changed']]);

注意:默认情况下,如果您更新永久链接的 slug,它将递归地更新其所有嵌套元素的新段。有关更多信息,请参阅更新永久链接

删除

如果您删除了一个绑定到永久链接记录的资源,该包将自动为我们销毁永久链接。再次提醒,如果您不想这样,并想自己处理,请禁用模型中的永久链接处理。

支持软删除

软删除支持是开箱即用的,因此如果您的资源被软删除,永久链接也会被软删除。如果您恢复资源,它也会自动恢复。禁用处理以手动处理此任务。

注意:如果您对资源调用 forceDelete(),永久链接也将被永久删除。

嵌套永久链接

您可能希望有一个嵌套的永久链接结构,例如,对于您的博客。父级将是 /blog,每篇文章都应该在这个路径中,因此您可以执行类似以下操作:

/blog           -> Blog index, show all blog posts
/blog/post-1
/blog/post-2
...

此包会为您处理这些操作

自动永久链接嵌套

永久链接表 permalinks 有一个用于自动嵌套模型的列:parent_for。此属性应包含您希望其成为父级的模型的完全限定名 (FQN) 类名。一旦设置,当您为指定的模型创建新的永久链接时,它将自动嵌套到给定的父级。

这通常是一个手动过程,您将在数据库中执行,所以它可能看起来像上面的示例

禁用自动嵌套

如果您深入到这个包中并想手动管理永久链接的嵌套(您为什么要这样做?但以防万一...),请从配置中禁用此功能。

// Globally disable this feature for all models in your permalink.php config file
'nest_to_parent_on_create' => false
// or
config()->set('permalink.nest_to_parent_on_create', false);

// Disable this feature for a particular model. Define this method in your model class:
public function permalinkNestToParentOnCreate()
{
    return false;
}

手动嵌套

如果您希望手动将永久链接嵌套到其他内容中,您只需要将父级永久链接的 id 设置为子级永久链接的 parent_id 属性。

Permalink::create(['slug' => 'my-article', 'parent_id' => 1, 'action' => '...']);

永久链接操作

您永久链接记录上的 action 属性将提供当该永久链接与当前请求 URI 匹配时要处理请求的信息。

控制器作为动作

每个永久链接都应该有一个动作,特别是那些未绑定到模型的永久链接。您应该在永久链接记录的 action 列中指定 controller@action

如果有一个模型绑定到永久链接(实体),它将被作为参数传递到控制器动作。

class UserController {
    public function show($user)
    {
        return view('users.show', compact('user'));
    }
}

视图作为动作

对于简单的用例,您可以将视图的路径指定为永久链接的动作。如上所述,永久链接实体(如果绑定到模型)也将在此视图中可用。

Permalink::create(['slug' => 'users', 'action' => 'users.index']);

如果绑定到模型...

Permalink::create(['slug' => 'israel-ortuno', 'entity_type' => User::class, 'entity_id' => 1, 'action' => 'users.show']);

// And then in users/show.blade.php
<h1>Welcome {{ $user->name }}</h1>

使用自定义控制器进行视图操作

在底层,视图操作由此包提供的控制器处理:Devio\Permalink\Http\PermalinkController。如果需要,您可以使用自己的实现更新此控制器。也许您想应用一些中间件,或者以不同的方式解析视图...

您只需在 AppServiceProvider(或其他)中将您的实现绑定到容器中。

// In your AppServiceProvider.php
public function register()
{
    $this->bind('Devio\Permalink\Http\PermalinkController', YourController::class);
}

// And then...
class YourController
{
    use Devio\Permalink\Http\ResolvesPermalinkView;

    public function __construct()
    {
        // Do your stuff.
    }
}

这样,Laravel 现在将根据容器解析您的实现。

如果您想要为解析视图实现自己的版本,不要使用Devio\Permalink\Http\ResolvesPermalinkView特性,并创建自己的view()方法。

默认操作(在模型中)

如果您将模型绑定到永久链接,您可以在模型中定义默认操作,如下所示

public function permalinkAction()
{
    return UserController::class . '@show'; // Or a view
}

一旦实现Permalinkable接口,此方法就是必需的。

重写默认操作

默认情况下,永久链接将根据永久链接实体的permlainkAction方法解析操作。但是,如果您在永久链接记录的action列中指定了一个值,它将覆盖默认操作。例如

class User extends Model
{
    use HasPermalinks;
    ...
    public function permalinkAction()
    {
        return UserController::class . '@index';
    }
    ...
}

// And then...
$user = User::create([
    'name' => 'israel',
    'permalink' => [
        'action' => 'user.show'
    ]
]);
// Or just update the action attribute as you like

当访问此特定实体的永久链接时,user/show.blade.php将负责处理请求,而不是默认控制器。这不是很酷吗?

删除永久链接

默认情况下,如果

支持软删除

缓存永久链接(请仔细阅读!)

如上所述,此包将在每个请求上执行单个SQL查询,以找到当前URI的匹配永久链接。这非常高效,对于大多数用例应该没问题。如果需要,此查询也可以被缓存以实现超快速访问。

您可以将永久链接路由缓存到默认的Laravel路由缓存系统中,但请注意,这将为permalinks表中的每个记录生成一个路由,因此如果您有大量永久链接,我强烈不推荐这样做,因为您可能会在bootstrap/cache/routes.php中结束于一个巨大的base64编码字符串,这可能会大大减慢应用程序的引导速度。对您打算缓存的路线数量进行一些测试,以了解您是否真的提高了性能。

为了缓存永久链接,您只需将整个permalinks数据集加载到路由器中,然后运行路由缓存命令

Router::loadPermalinks();
Artisan::call('route:cache');

您可以创建一个命令来执行这两个操作或任何您认为合适的事情。从现在起,每当永久链接记录被更新时,您都必须手动更新此缓存。

处理SEO属性

如果无法为每个永久链接记录配置SEO属性,此包将不完整!它几乎毫无用处!

自动SEO生成

对于SEO标签生成,使用ARCANDEV/SEO-Helper。此包提供了一套强大的工具来管理您的SEO元标签。

{
  "meta": {
    "title": "Specific title",                  // The <title>
    "description": "The meta description",      // The page meta description
    "robots": "noindex,nofollow"                // Robots control
  },
  "opengraph":{
    "title": "Specific OG title",               // The og:title tag
    "description": "The og description",        // The og:description tag
    "image": "path/to/og-image.jpg"             // The og:image tag
  },
  "twitter":{
    "title": "Specific Twitter title",          // The twitter:title tag
    "description": "The twitter description",   // The twitter:description tag
    "image": "path/to/og-image.jpg"             // The twitter:image tag
  }
}

注意:这只是最常见标签的一个示例,但您可以使用ARCANDEV/SEO-Helper支持的任何类型的标签(索引、noindex...),只需确保正确嵌套即可。

为了在HTML中渲染所有这些内容,您应该将以下内容添加到您的<meta>

<head>
    {!! seo_helper()->render() !!}
</head>
或者
<head>
    {{ seo_helper()->renderHtml() }}
</head>

请访问SEO-Helper – Laravel Usage了解有关渲染什么和如何渲染的更多信息。

了解其工作原理

底层,此JSON结构在调用不同的SEO助手(元数据、opengraph和twitter)。让我们了解

{
  "title": "Generic title",
  "image": "path/to/image.jpg",
  "description": "Generic description",

  "meta": {
    "title": "Default title",
  },
  "opengraph": {
    "image": "path/to/og-image.jpg"
  }
}

此结构将允许您为所有构建器设置title的基准值,同时仅更改部分的标题。同样,图像,Twitter和OpenGraph将继承父图像,但OpenGraph将替换其构建器上的图像。这样,您将能够在每个部分上显示不同的信息!

这将调用来自 SeoMeta 辅助类的 setTitle 和来自 SeoOpenGraph 辅助类的 setImage。对于Twitter也是如此。花点时间审查这三个契约,以便了解所有可用方法

为了匹配任何辅助方法,每个JSON选项都将转换为以 setadd 为前缀的 studly_case,因此 title 将转换为 setTitle,而 google_analytics 将转换为 setGoogleAnalytics。这有多么酷?

所有方法都是通过 call_user_func_array 调用的,因此如果选项包含一个数组,每个键都将作为参数传递给辅助方法。参见 setTitleaddWebmaster,这允许多个参数。

填充SEO属性

您只需通过传递包含数据的数组到 seo 属性,就可以指定您永久链接的SEO属性

Peramlink::create([
  'slug' => 'foo',
  'seo' => [
    'title' => 'this is a title',
    'description' => 'this is a description',
    'opengraph' => [
      'title' => 'this is a custom title for og:title'
    ]
  ]
);

使用默认内容填充SEO属性

通常,您希望直接从您绑定模型的信息自动填充您的SEO信息。您可以通过在模型中创建以下所示的回退方法来实现这一点

public function getPeramlinkSeoTitleAttribute()
{
  return $this->name;
}

public function getPermalinkSeoOpenGraphTitleAttribute()
{
  return $this->name . ' for OpenGraph';
}

如果这些回退确实存在,并且该字段的值在创建永久链接时未提供,则将使用这些回退。请注意,这些方法应作为Eloquent访问器调用。使用 permalinkSeo 前缀,然后是到默认值的路径,以 StudlyCase 的形式,例如

seo.title                   => getPermalinkSeoTitleAttribute()
seo.description             => getPermalinkSeoDescriptionAttribute()
seo.twitter.title           => getPermalinkSeoTwitterTitleAttribute()
seo.twitter.description     => getPermalinkSeoTwitterDescriptionAttribute()
seo.opengraph.title         => getPermalinkSeoTwitterOpenGraphAttribute()
seo.opengraph.description   => getPermalinkSeoOpenGraphDescriptionAttribute()

包将查找任何匹配的方法,因此您可以创建尽可能多的方法,以满足您的SEO设置需求,即使您只是创建自定义元标签,getPermalinkMyCustomMetaDescriptionAttribute 也会匹配,如果存在 seo.my.custom.meta.description 对象。

SEO构建器

为了提供更多的灵活性,方法调用通过3个类(每个辅助类一个)进行管道传输,称为Builders。这些构建器负责调用ARCANDEV/SEO-Helper包的正确方法。

如果这些构建器中的任何方法与JSON选项匹配,则包将执行该方法,而不是默认行为,即调用(如果存在)来自 SEO-Helper 包的方法。

MetaBuilder 为例进行审查。此构建器包含一个 setCanonical 方法,该方法基本上用作 setUrl 的别名(仅为了更明确)。

扩展构建器

为了修改这些构建器中的任何行为的任何一种,您可以创建自己的构建器,该构建器应扩展 Devio\Permalink\Contracts\SeoBuilder 接口或继承 Devio\Permalink\Builders\Builder 类。

一旦创建了您自己的构建器,只需将其替换为Container中的默认构建器。将以下内容添加到您应用程序中任何服务提供商的 register 方法中

// Singleton or not, whatever you require
$this->app->singleton("permalink.meta", function ($app) { // meta, opengraph, twitter or base
  return new MyCustomBuilder;

  // Or if you are inheriting the default builder class

  return (new MyCustomBuilder($app->make(SeoHelper::class)));
});

如果您希望使用其他包生成SEO元标签,则扩展和修改构建器可以做到这一点。

禁用SEO生成

如果您希望防止渲染三个构建器(元数据、OpenGraph或Twitter)中的任何一个,只需将其JSON选项设置为false

{
  "meta": { },
  "opengraph": false,
  "twitter": false
}

这将禁用OpenGraph和Twitter构建器的执行。