devio/permalink

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

1.1.0 2020-12-17 14:56 UTC

This package is auto-updated.

Last update: 2024-09-26 03:38:07 UTC


README

Build Status Latest Stable Version

2021-08-25: 寻找维护者

尽管它相当稳定,但我没有时间维护这个包以适应未来的Laravel版本和添加更多功能,因此我正在寻找任何认为这个包有用的并且愿意维护它的人。请随时联系! israel@devio.es

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

重要 尽管此包的功能并不复杂,但仍有一些事项和良好的实践需要考虑。我强烈建议仔细阅读整个文档,以深入理解此包的工作原理,因为您将替换默认的Laravel路由系统,并且不希望弄乱您的URL和SEO!

路线图

文档

安装

安装包

composer require devio/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 来访问该特定资源。如果您想这样做,只需使用 trait 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。此属性应包含您想要作为父级的模型的完全限定类名。设置后,当您为指定的模型创建新的永久链接时,它将自动嵌套到给定的父级。

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

禁用自动嵌套

如果您已经深入了解此软件包并希望手动管理永久链接的嵌套(为什么您会这样做?但以防万一...),请随时从配置中禁用此功能。

// 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匹配时将处理请求的信息。

控制器作为操作

每个永久链接都应该有一个操作,特别是那些未绑定到模型的永久链接。您应指定一个controller@action并将其放入永久链接记录的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 trait,而是创建自己的 view() 方法。

默认行为(在模型中)

如果您有一个绑定到永久链接的模型,您可以在模型中定义默认行为,如下所示:

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

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

重写默认行为

默认情况下,永久链接将根据永久链接实体的 permlinkAction 方法来解析动作。但是,如果您在永久链接记录中的 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 中,然后运行路由缓存命令。

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 支持的任何类型的标签(index,noindex...),只需确保正确嵌套即可。

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

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

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

了解它是如何工作的

在底层,此 JSON 结构正在调用不同的 SEO 辅助程序(meta,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 的基本值,并仅更改 Meta 部分的标题。图像也是如此,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 前缀以及默认值的路径,例如

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

该包将寻找任何匹配的方法,因此您可以创建尽可能多的方法,即使您只是创建自定义元标签,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 类。

创建自己的构建器后,只需将默认构建器替换为容器中的构建器。将以下内容添加到应用程序中任何 Service Provider 的 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 构建器的执行。