honeystone/laravel-seo

Laravel的SEO元数据和JSON-LD包。

1.4.1 2024-09-16 10:22 UTC

This package is auto-updated.

Last update: 2024-09-16 10:23:26 UTC


README

Static Badge GitHub License Latest Version on Packagist Packagist Dependency Version Packagist Dependency Version Static Badge

Honeystone SEO包使您可以从Laravel应用的任何位置轻松配置SEO元数据。

包含通用元数据、X(原Twitter)卡、Open Graph、JSON-LD架构和Favicon(使用RealFaviconGenerator生成)的元数据生成器。

此包旨在具有可扩展性,因此也可以轻松添加您自己的自定义元数据生成器。

支持我们

Support Us

我们致力于提供高质量的由Honeystone团队维护的开源包。如果您想支持我们的努力,只需使用我们的包、推荐它们并贡献。

如果您在项目中需要任何帮助或需要任何定制开发,请联系我们

安装

composer require honeystone/laravel-seo

使用以下命令发布配置文件

php artisan vendor:publish --tag=honeystone-seo-config

用法

该包提供了一个辅助函数seo()和一些Blade指令@metadata@openGraphPrefix。如果您更喜欢使用依赖注入,还可以类型提示Honeystone\Seo\MetadataDirector

设置元数据就像链式调用方法一样简单

seo()
    ->title('A fantastic blog post', 'My Awesome Website!')
    ->description('Theres really a lot of great stuff in here...')
    ->images(
        'https://mywebsite.com/images/blog-1/cover-image.webp',
        'https://mywebsite.com/images/blog-1/another-image.webp',
    );

一旦设置了元数据,您可以使用以下方式渲染它

seo()->generate();

或者,您也可以使用@metadata Blade指令。

渲染结果将类似于以下内容

<title>A fantastic blog post - My Awesome Website!</title>
<meta name="description" content="Theres really a lot of great stuff in here...">
<link rel="canonical" href="https://mywebsite.com/blog/a-fantastic-blog-post">

<!-- Twitter Cards -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="A fantastic blog post">
<meta name="twitter:description" content="Theres really a lot of great stuff in here...">
<meta name="twitter:image" content="https://mywebsite.com/images/blog-1/cover-image.webp">

<!-- Open Graph -->
<meta property="og:type" content="website">
<meta property="og:title" content="A fantastic blog post">
<meta property="og:description" content="Theres really a lot of great stuff in here...">
<meta property="og:image" content="https://mywebsite.com/images/blog-1/cover-image.webp">
<meta property="og:image" content="https://mywebsite.com/images/blog-1/another-image.webp">
<meta property="og:url" content="https://mywebsite.com/blog/a-fantastic-blog-post">

<!-- JSON-LD -->
<script type="application/ld+json">
    {
        "@context": "https://schema.org",
        "@type": "WebPage",
        "name": "A fantastic blog post",
        "description": "Theres really a lot of great stuff in here...",
        "image": [
            "https://mywebsite.com/images/blog-1/cover-image.webp",
            "https://mywebsite.com/images/blog-1/another-image.webp"
        ],
        "url": "https://mywebsite.com"
    }
</script>

默认方法

提供给默认方法的值将自动传播到所有配置的元数据生成器。

以下默认方法可用

seo()
    ->locale('en_GB')
    ->title('A fantastic blog post', template: '🔥🔥 {title} 🔥🔥')
    ->description('Theres really a lot of great stuff in here...')
    ->keywords('foo', 'bar', 'baz')
    ->url('https://mywebsite.com/blog/a-fantastic-blog-post') //defaults to the current url
    ->canonical('https://mywebsite.com/blog/a-fantastic-blog-post') //by default url and canonical are in sync, see config
    ->canonicalEnabled(true) //enabled by default, see config
    ->images(
        'https://mywebsite.com/images/blog-1/cover-image.webp',
        'https://mywebsite.com/images/blog-1/another-image.webp',
    )
    ->robots('🤖', '🤖', '🤖');

完整的基线如下所示

<title>🔥🔥 A fantastic blog post 🔥🔥</title>
<meta name="description" content="Theres really a lot of great stuff in here...">
<meta name="keywords" content="foo,bar,baz">
<link rel="canonical" href="https://mywebsite.com/blog/a-fantastic-blog-post">
<meta name="robots" content="🤖,🤖,🤖">

<!-- Twitter Cards -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="A fantastic blog post">
<meta name="twitter:description" content="Theres really a lot of great stuff in here...">
<meta name="twitter:image" content="https://mywebsite.com/images/blog-1/cover-image.webp">

<!-- Open Graph -->
<meta property="og:site_name" content="My Website">
<meta property="og:title" content="A fantastic blog post">
<meta property="og:description" content="Theres really a lot of great stuff in here...">
<meta property="og:image" content="https://mywebsite.com/images/blog-1/cover-image.webp">
<meta property="og:image" content="https://mywebsite.com/images/blog-1/another-image.webp">
<meta property="og:url" content="https://mywebsite.com/blog/a-fantastic-blog-post">
<meta property="og:locale" content="en_GB">

<!-- JSON-LD -->
<script type="application/ld+json">
    {
        "@context": "https://schema.org",
        "@type": "WebPage",
        "name": "A fantastic blog post",
        "description": "Theres really a lot of great stuff in here...",
        "image": [
            "https://mywebsite.com/images/blog-1/cover-image.webp",
            "https://mywebsite.com/images/blog-1/another-image.webp"
        ],
        "url": "https://mywebsite.com/blog/a-fantastic-blog-post"
    }
</script>

对于您的首页,您可能希望禁用标题模板

seo()->title('My Awesome Website!', template: false);

元数据方法

元数据方法由Honeystone\Seo\Generators\MetaGenerator类提供。

以下是完整列表

seo()
    ->metaTitle('A fantastic blog post')
    ->metaTitleTemplate('🔥🔥 {title} 🔥🔥')
    ->metaDescription('Theres really a lot of great stuff in here...')
    ->metaKeywords('foo', 'bar', 'baz')
    ->metaCanonical('https://mywebsite.com/blog/a-fantastic-blog-post')
    ->metaCanonicalEnabled(true)
    ->metaRobots('🤖', '🤖', '🤖');

所有这些都由默认方法提供并通过元数据生成器传播。

如果您只想渲染元数据生成器,请使用seo()->generate('meta')@metadata('meta')

Twitter方法

元数据方法由Honeystone\Seo\Generators\TwitterGenerator类提供。

以下是完整列表

seo()
    ->twitterEnabled(true) //enabled by default, see config
    ->twitterSite('@MyWebsite')
    ->twitterCreator('@MyTwitter')
    ->twitterTitle('A fantastic blog post') //defaults to title()
    ->twitterDescription('Theres really a lot of great stuff in here...') //defaults to description()
    ->twitterImage('https://mywebsite.com/images/blog-1/cover-image.webp'); //defaults to the first in images()

Open Graph方法

元数据方法由Honeystone\Seo\Generators\TwitterGenerator类提供。

以下是完整列表

seo()
    ->openGraphEnabled(true) //enabled by default, see config
    ->openGraphSite('My Website')
    ->openGraphType('website') //defaults to website, see config
    ->openGraphTitle('A fantastic blog post') //defaults to title()
    ->openGraphDescription('Theres really a lot of great stuff in here...') //defaults to description()
    ->openGraphImage('https://mywebsite.com/images/blog-1/cover-image.webp')
    ->openGraphImages([
        'https://mywebsite.com/images/blog-1/cover-image.webp',
        'https://mywebsite.com/images/blog-1/another-image.webp',
    ]) //defaults to images()
    ->openGraphUrl('https://mywebsite.com/blog/a-fantastic-blog-post') //defaults to url()
    ->openGraphAudio([
        'https://mywebsite.com/music/song1.mp3',
        'https://mywebsite.com/music/song2.mp3',
    ])
    ->openGraphVideo('https://mywebsite.com/films/video1.mp4')
    ->openGraphVideos([
        'https://mywebsite.com/films/video1.mp4',
        'https://mywebsite.com/films/video2.mp4',
    ])
    ->openGraphDeterminer(OpenGraphGenerator::DETERMINER_A)
    ->openGraphLocale('en_GB') //defaults to locale()
    ->openGraphAlternateLocales(['en_US'])
    ->openGraphProperty('custom:property', '💀');

您还可以使用以下非垂直支持类型

use Honeystone\Seo\OpenGraph\ArticleProperties;
use Honeystone\Seo\OpenGraph\BookProperties;
use Honeystone\Seo\OpenGraph\ProfileProperties;

//article
seo()
    ->openGraphType(new ArticleProperties(
        publishedTime: new DateTime('now'),
        modifiedTime: new DateTime('now'),
        expirationTime: null,
        author: new ProfileProperties(
            username: 'PiranhaGeorge',
        ),
        section: 'Foo',
        tag: 'Bar',
    ));

//book
seo()
    ->openGraphType(new BookProperties(
        author: [
            new ProfileProperties(
                firstName: 'Erich',
                lastName: 'Gamma',
            ),
            new ProfileProperties(
                firstName: 'Richard',
                lastName: 'Helm',
            ),
            new ProfileProperties(
                firstName: 'Ralph',
                lastName: 'Johnson',
            ),
            new ProfileProperties(
                firstName: 'John',
                lastName: 'Vlissides',
            ),
        ],
        isbn: '978-0201633610',
        releaseDate: new DateTime('14 March 1995'),
        tag: ['1st', 'GoF'],
    ));

//profile
seo()
    ->openGraphType(new ProfileProperties(
        username: 'PiranhaGeorge'
        firstName: 'George',
        lastName: 'Palmer',
        gender: 'male',
    ));

您可以使用它们各自的属性类提供更多图像、音频和视频数据

    use Honeystone\Seo\OpenGraph\AudioProperties;
    use Honeystone\Seo\OpenGraph\ImageProperties;
    use Honeystone\Seo\OpenGraph\VideoProperties;

    seo()->openGraphAudio(new AudioProperties(
        url: 'http://foo.bar/song.mp3',
        secureUrl: 'https://foo.bar/song.mp3',
        type: 'audio/mpeg',
    ));

    seo()->openGraphImage(new ImageProperties(
        url: 'http://foo.bar/img.png',
        alt: 'Foo',
        width: '800',
        height: '450',
        secureUrl: 'https://foo.bar/img.png',
        type: 'image/png',
    ));

    seo()->openGraphVideo(new VideoProperties(
        url: 'http://foo.bar/movie.mp4',
        alt: 'Foo',
        width: '1920',
        height: '1080',
        secureUrl: 'https://foo.bar/movie.mp4',
        type: 'video/mp4',
    ));

以下是一个使用ArticlePropertiesImageProperties的示例

<!-- Open Graph -->
<meta property="og:site_name" content="My Website">
<meta property="og:type" content="article">
<meta property="article:published_time" content="2024-07-25T21:39:40+00:00">
<meta property="article:modified_time" content="2024-07-25T21:39:40+00:00">
<meta property="article:author:username" content="PiranhaGeorge">
<meta property="article:section" content="Foo">
<meta property="article:tag" content="Bar">
<meta property="og:title" content="A fantastic blog post">
<meta property="og:description" content="Theres really a lot of great stuff in here...">
<meta property="og:image" content="http://foo.bar/img.png">
<meta property="og:image:alt" content="Foo">
<meta property="og:image:width" content="800">
<meta property="og:image:height" content="450">
<meta property="og:image:secure_url" content="https://foo.bar/img.png">
<meta property="og:image:type" content="image/png">
<meta property="og:url" content="https://mywebsite.com">
<meta property="og:determiner" content="a">
<meta property="og:locale" content="en_GB">
<meta property="og:locale:alternate" content="en_US">
<meta property="custom:property" content="💀">

要设置前缀,您可以使用@openGraphPrefix Blade指令或seo()->openGraphPrefix(),如下所示

<head prefix="@openGraphPrefix">
    ...
</head>

JSON-LD方法

元数据方法由Honeystone\Seo\Generators\JsonLdGenerator类提供。

以下是完整列表

seo()
    ->jsonLdEnabled(true) //enabled by default, see config
    ->jsonLdType('WebPage') //defaults to WebPage, see config
    ->jsonLdName('A fantastic blog post') //defaults to title()
    ->jsonLdDescription('Theres really a lot of great stuff in here...') //defaults to description()
    ->jsonLdImage('https://mywebsite.com/images/blog-1/cover-image.webp')
    ->jsonLdImages([
        'https://mywebsite.com/images/blog-1/cover-image.webp',
        'https://mywebsite.com/images/blog-1/another-image.webp',
    ]) //defaults to images()
    ->jsonLdUrl('https://mywebsite.com/blog/a-fantastic-blog-post') //defaults to url()
    ->jsonLdNonce('some-value') //sets a nonce value for your content security policy
    ->jsonLdProperty('alternateName', 'Foo');

输出如下

<!-- JSON-LD -->
<script type="application/ld+json" nonce="some-value">
    {
        "@context": "https://schema.org",
        "@type": "WebPage",
        "name": "A fantastic blog post",
        "description": "Theres really a lot of great stuff in here...",
        "image": [
            "https://mywebsite.com/images/blog-1/cover-image.webp",
            "https://mywebsite.com/images/blog-1/another-image.webp"
        ],
        "url": "https://mywebsite.com/blog/a-fantastic-blog-post",
        "alternateName": "Foo"
    }
</script>

但是,还有更多!

而不是重新发明轮子,此包对令人难以置信的spatie/schema-org包提供了支持。您可以使用jsonLdImport()方法导入现有模式,或使用流畅接口构建您的模式。

    //graph
    seo()->jsonLdGraph()
        ->organization('honeystone')
            ->name('Honeystone')
            ->legalName('Honeystone Consulting Ltd.');

    //or a MultiTypedEntity
    seo()->jsonLdMulti()
        ->organization('honeystone')
            ->name('Honeystone')
            ->legalName('Honeystone Consulting Ltd.');

但是,不要忘记安装spatie/schema-org包以使用此功能。

期望/检查

您很可能会从应用程序的许多位置构建您的图,例如中间件、控制器、视图组合器、视图组件等。

这就是期望发挥作用的地方。只需指定您的期望,然后确保应用程序的其他部分进行检查。如果某些内容未能检查,则将抛出异常。相反,如果某些意外内容进行了检查,也将抛出异常。

//perhaps in a controller
seo()
    ->title('Something awesome')
    ->jsonLdExpect('featured-tags', 'gallery', 'contact');

//maybe in a view composer or component
seo()
    ->jsonLdCheckIn('gallery')
    ->jsonLdGraph()
        ->imageGallery()
            ->image([
                ...
            ]);

如果“推荐标签”或“联系方式”未成功签到,您将立即收到警告。

此功能完全可选。只需不要设定任何期望或签到,就不会抛出任何异常。

图标生成

使用RealFaviconGenerator API,您现在可以使用此包生成图标。只需申请API密钥并将其放入您的配置文件中,配置您的源图像,然后运行命令

php artisan seo:generate-favicons

模型集成

此包不包含任何用于集成模型的特定功能。最终,您始终需要将您的模型属性映射到此包。例如,如果您的模型有一个meta_description属性,您需要将其映射到description,否则此包将不知道如何消费它。

考虑到这一点,我们有一个简单的模式,应该能满足您的需求。

首先,向您的模型添加一个新方法,并在其中使用模型属性设置元数据

use Honeystone\Seo\MetadataDirector;
use Illuminate\Database\Eloquent\Model;

class Page extends Model
{
    public function seo(): MetadataDirector
    {
        return seo()
            ->title($this->meta_title)
            ->description($this->meta_description)
            ->jsonLdExpect('featured-items');
    }
}

然后在您的控制器中,只需调用该方法并链式调用任何附加的元数据

use Illuminate\Contracts\View\View;

class PageController
{
    public function __invoke(Page $page): View
    {
        $page->seo()
            ->jsonLdCheckIn('featured-items')
            ->jsonLdGraph()
                ->itemList()
                    ->name('Featured items')
                    ->itemListElement([
                        //...
                    ]);
    }
}

自定义生成器

要创建自定义生成器,只需实现Honeystone\Seo\Contracts\MetadataGenerator合约并将其添加到您的配置文件中的生成器部分。您也可以在这里指定生成器的任何配置。

配置

以下是完整的配置文件

<?php

declare(strict_types=1);

use Honeystone\Seo\Generators;

return [

    'generators' => [
        Generators\MetaGenerator::class => [
            'title' => env('APP_NAME'),
            'titleTemplate' => '{title} - '.env('APP_NAME'),
            'description' => '',
            'keywords' => [],
            'canonicalEnabled' => true,
            'canonical' => null, //null to use current url
            'robots' => [],
            'custom' => [
                //[
                //    'greeting' => 'Hey, thanks for checking out the source code of our website. '.
                //        'Hopefully you find what you are looking for 👍'
                //],
                //[
                //    'google-site-verification' => 'xxx',
                //],
            ],
        ],
        Generators\TwitterGenerator::class => [
            'enabled' => true,
            'site' => '', //@twitterUsername
            'creator' => '',
            'title' => '',
            'description' => '',
            'image' => '',
        ],
        Generators\OpenGraphGenerator::class => [
            'enabled' => true,
            'site' => env('APP_NAME'),
            'type' => 'website',
            'title' => '',
            'description' => '',
            'images' => [],
            'audio' => [],
            'videos' => [],
            'determiner' => '',
            'url' => null, //null to use current url
            'locale' => '',
            'alternateLocales' => [],
            'custom' => [],
        ],
        Generators\JsonLdGenerator::class => [
            'enabled' => true,
            'pretty' => env('APP_DEBUG'),
            'type' => 'WebPage',
            'name' => '',
            'description' => '',
            'images' => [],
            'url' => null, //null to use current url
            'custom' => [],

            //determines if the configured json-ld is automatically placed on the graph
            'place-on-graph' => true,
        ],
        Generators\RealFaviconGenerator::class => [
            'enabled' => true,
            'apiKey' => env('REAL_FAVICON_KEY'),
            'image' => '', //the source image path, relative to /resources

            //see https://realfavicongenerator.net/api/non_interactive_api#favicon_design
            'design' => [
                'ios' => [
                    'picture_aspect' => 'no_change',
                    'app_name' => env('APP_NAME'),
                    'assets' => [
                        'ios6_and_prior_icons' => false,
                        'ios7_and_prior_icons' => false,
                        'precomposed_icons' => false,
                        'declare_only_default_icon' => true,
                    ],
                ],
                'windows' => [
                    'picture_aspect' => 'no_change',
                    'background_color' => '#222',
                    'app_name' => env('APP_NAME'),
                    'assets' => [
                        'windows_80_ie_10_tile' => false,
                        'windows_10_ie_11_edge_tiles' => [
                            'small' => false,
                            'medium' => true,
                            'big' => false,
                            'rectangle' => false,
                        ],
                    ],
                ],
                'firefox_app' => [
                    'picture_aspect' => 'no_change',
                    'manifest' => [
                        'app_name' => env('APP_NAME'),
                        'app_description' => '',
                        'developer_name' => '',
                        'developer_url' => '',
                    ],
                ],
                'android_chrome' => [
                    'picture_aspect' => 'no_change',
                    'manifest' => [
                        'name' => env('APP_NAME'),
                        'display' => 'browser',
                        'theme_color' => '#222',
                    ],
                    'assets' => [
                        'legacy_icon' => false,
                        'low_resolution_icons' => false,
                    ],
                ],
                'safari_pinned_tab' => [
                    'picture_aspect' => 'silhouette',
                    'theme_color' => '#222',
                ],
            ],

            //see https://realfavicongenerator.net/api/non_interactive_api#settings
            'settings' => [
                'compression' => 3,
                'scaling_algorithm' => 'Mitchell',
            ],
        ],
    ],

    'sync' => [
        'url-canonical' => true,
        'keywords-tags' => false,
    ],
];

变更日志

更改列表可以在CHANGELOG.md文件中找到。

许可证

MIT © Honeystone Consulting Ltd