honeystone/laravel-seo
Composer 安装命令:
composer require honeystone/laravel-seo
包简介
SEO metadata and JSON-LD package for Laravel.
README 文档
README
The Honeystone SEO package makes configuring SEO metadata from anywhere within your Laravel application a breeze.
Included are metadata generators for general metadata, X (Formally Twitter) Cards, Open Graph and JSON-LD Schema.
This package was designed with extensibility in mind, so your own custom metadata generators can also be added with ease.
Support us
We are committed to delivering high-quality open source packages maintained by the team at Honeystone. If you would like to support our efforts, simply use our packages, recommend them and contribute.
If you need any help with your project, or require any custom development, please get in touch.
Installation
composer require honeystone/laravel-seo
Publish the configuration file with:
php artisan vendor:publish --tag=honeystone-seo-config
Usage
The package provides a helper function, seo(), and some Blade directives, @metadata and @openGraphPrefix. You
can also typehint the Honeystone\Seo\MetadataDirector if you prefer to use dependency injection.
Setting metadata is a simple as chaining methods:
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', );
Once you've set your metadata, you can render it using:
seo()->generate();
Alternatively, you can also use the @metadata Blade directive.
The rendered result will look something like this:
<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>
Exporting metadata as arrays
Every generator now implements the Honeystone\Seo\Contracts\ExportsArrayMetadata contract, which means the director
can provide a structured payload for each generator:
$seo = seo()->toArray(); // ['meta' => [...], 'twitter' => [...], ...] $metaOnly = seo()->toArray('meta'); // limit to a specific generator
This is particularly useful when sharing SEO data with Inertia.js:
return Inertia::render('Pages/Show', [ 'page' => $page, 'seo' => seo()->toArray(), ]);
On the client you can decide whether to render the tags yourself, hydrate a preview component, or simply inspect the payload for analytics/debugging purposes.
If you are using the bundled GenerateInertiaMetadata middleware, the structured payload is automatically shared as
$page.props.seoPayload (alongside the rendered HTML string that remains available under $page.props.seo for legacy
integrations).
Inertia React
When using Inertia with React, you can render the structured SEO payload directly in the head using the bundled React
component at resources/js/inertia/react/SeoHead.tsx.
- Register the React-specific middleware
\Honeystone\Seo\Http\Middleware\GenerateInertiaMetadataReact::classin your Inertia middleware stack so$page.props.seoPayloadis shared:// app/Http/Middleware/HandleInertiaRequests.php protected $middleware = [ // ... \Honeystone\Seo\Http\Middleware\GenerateInertiaMetadataReact::class, ];
- Render the component in your React layout so every page gets the tags:
// resources/js/Layouts/AppLayout.tsx import SeoHead from '@/inertia/react/SeoHead'; export default function AppLayout({ children }) { return ( <> <SeoHead defaultTitleSuffix="My App" /> {children} </> ); }
The component readsseoPayloadfromusePage()and outputs meta, Open Graph, Twitter and JSON-LD tags. PassforceNoIndexif you need to override robots for specific responses.
When using this method, Inertia itself will attempt to replace the meta tags for you so you will need to remove the @metadata blade directive from your layouts.
Default methods
Values provided to default methods will automatically propagate to all configured metadata generators.
The following default methods are available:
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('🤖', '🤖', '🤖');
The full baseline looks like this:
<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>
For your homepage you'll probably want to disable the title template:
seo()->title('My Awesome Website!', template: false);
Meta methods
The meta methods are provided by the Honeystone\Seo\Generators\MetaGenerator class.
Here's the full list:
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('🤖', '🤖', '🤖');
All of these are provided by the default methods and propagate through to the meta generator.
If you only want to render the meta generator, use seo()->generate('meta') or @metadata('meta')
Twitter methods
The meta methods are provided by the Honeystone\Seo\Generators\TwitterGenerator class.
Here's the full list:
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 methods
The meta methods are provided by the Honeystone\Seo\Generators\TwitterGenerator class.
Here's the full list:
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', '💀');
You can also use the following non-vertical supported types:
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', ));
You can provide more data for images, audio and videos using their respective properties classes:
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', ));
Here's an example using ArticleProperties and ImageProperties:
<!-- 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="💀" />
To set the prefix, you can use the @openGraphPrefix Blade directive or seo()->openGraphPrefix() like so:
<head prefix="@openGraphPrefix"> ... </head>
JSON-LD methods
The meta methods are provided by the Honeystone\Seo\Generators\JsonLdGenerator class.
Here's the full list:
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');
And the output:
<!-- 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>
But Wait, There's More!
Rather than reinventing the wheel, this package has support for the incredible
spatie/schema-org package. You can use the jsonLdImport() method to import an
exising schema, or build your schema using the fluent interface.
//graph seo()->jsonLdGraph() ->organization('honeystone') ->name('Honeystone') ->legalName('Honeystone Consulting Ltd.'); //or a MultiTypedEntity seo()->jsonLdMulti() ->organization('honeystone') ->name('Honeystone') ->legalName('Honeystone Consulting Ltd.');
Just don't forget to install the spatie/schema-org package to use this functionality.
Expectations / Check Ins
It's highly likely you'll be building your graph from many locations around your application, e.g. middleware, controllers, view composers, view components, etc.
This is where expectations come in. Simply specify your expectations, and then ensure the other parts of your application check in. If something failed to check in, an exception will be thrown. Conversely, if something unexpected checked in, an exception will also be thrown.
//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([ ... ]);
You'll be warned immediately if 'featured-tags' or 'contact' fail to check in.
This feature is entirely optional. Just don't set any expectations, or check in, and no exceptions will be thrown.
Model integration
This package doesn't include any specific functionality for integrating models. Ultimately, you'll always need to map
your model attributes to this package. For example, if your model has a meta_description attribute, you will need to
map it to description, otherwise this package would not know to consume it.
With this in mind, we have a simple pattern that should get you what you need.
Start by adding a new method to your model, and set your metadata using the model's attributes within:
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'); } }
Then in your controller, just call the method and chain any additional metadata:
use Illuminate\Contracts\View\View; class PageController { public function __invoke(Page $page): View { $page->seo() ->jsonLdCheckIn('featured-items') ->jsonLdGraph() ->itemList() ->name('Featured items') ->itemListElement([ //... ]); } }
Custom generators
To create a custom generator, simply implement the Honeystone\Seo\Contracts\MetadataGenerator contract and add it to
your config file in the generators section. You can specify any configuration for your generator here too.
Removing the source comments
The package includes comments in the generated HTML. If you would like to remove them, you can publish the views and remove the comments from the Blade files:
php artisan vendor:publish --tag=honeystone-seo-views
Publishing the package views is generally not recommended as these files will not be updated when the package is updated. If it is absolutely necessary to publish the views, we recommend only publishing the files you need to modify.
Configuration
Here's the full config file:
<?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, ], ], 'sync' => [ 'url-canonical' => true, 'keywords-tags' => false, ], ];
Changelog
A list of changes can be found in the CHANGELOG.md file.
License
MIT © George Palmer, Honeystone Consulting Ltd
honeystone/laravel-seo 适用场景与选型建议
honeystone/laravel-seo 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 84.28k 次下载、GitHub Stars 达 355, 最近一次更新时间为 2024 年 07 月 25 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「metadata」 「twitter」 「seo」 「JSON-LD」 「opengraph」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 honeystone/laravel-seo 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 honeystone/laravel-seo 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 honeystone/laravel-seo 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Handles basic OAuth/OAuth2 authentication along with classes for common services
Simple Twitter API wrapper
Simple Sharing generates social media share links within CP entry pages, allowing you to quickly & easily share entries.
Help to manage meta data on Laravel Eloquent model
Statamic Fine Seo addon
Yii2 Component to manage SEO data and metadata
统计信息
- 总下载量: 84.28k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 356
- 点击次数: 34
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2024-07-25