artesaos/seotools
SEOTools adds SEO helpers to Laravel and Lumen: set page titles and meta tags, manage Open Graph and Twitter Card data, and generate JSON-LD structured data. Simple API, easy configuration, works with Laravel package discovery.
Installation:
composer require artesaos/seotools
Laravel 5.5+ auto-discovers the package; for older versions, register the SEOToolsServiceProvider in config/app.php.
Publish Config:
php artisan vendor:publish --provider="Artesaos\SEOTools\Providers\SEOToolsServiceProvider"
This generates config/seotools.php for customization.
First Use Case: In a controller, set basic meta tags for a page:
use Artesaos\SEOTools\Facades\SEOMeta;
public function show()
{
SEOMeta::setTitle('My Page Title');
SEOMeta::setDescription('A brief description of the page.');
return view('page');
}
Verify Output:
Inspect the rendered HTML (<head> section) to confirm tags like <title> and <meta name="description"> are present.
Use SEOMeta for standard SEO tags:
SEOMeta::setTitle('Blog Post');
SEOMeta::setDescription('Detailed summary of the post.');
SEOMeta::setCanonical(url('/posts/123'));
SEOMeta::addKeyword(['laravel', 'seo', 'tips']);
OpenGraph (Facebook, LinkedIn):
OpenGraph::setTitle('Post Title')
->setUrl(url('/posts/123'))
->addImage(url('images/post-123.jpg'))
->addProperty('article:published_time', now()->toW3CString());
Twitter Cards:
TwitterCard::setTitle('Post Title')
->setSite('@myhandle')
->setDescription('Tweet-friendly summary')
->addImage(url('images/twitter-card.jpg'));
For rich snippets (e.g., articles, products):
JsonLd::setTitle('Product Name')
->setDescription('Product details.')
->setType('Product')
->addProperty('price', 19.99)
->addProperty('availability', 'https://schema.org/InStock');
Multi-Type JSON-LD (e.g., combining Article and Breadcrumb):
JsonLdMulti::setTitle('Article Title')
->setType('Article')
->addProperty('headline', 'Article Headline');
if (!JsonLdMulti::isEmpty()) {
JsonLdMulti::newJsonLd();
JsonLdMulti::setType('Breadcrumb');
JsonLdMulti::addProperty('itemListElement', [
['position' => 1, 'name' => 'Home'],
['position' => 2, 'name' => 'Blog']
]);
}
Use view composers or middleware to set SEO tags dynamically:
// In a composer
public function compose(View $view)
{
$post = Post::find(1);
SEOMeta::setTitle($post->title);
OpenGraph::setImage($post->featured_image_url);
}
Middleware Example:
public function handle($request, Closure $next)
{
SEOMeta::setTitle(config('app.name') . ' | ' . $request->route()->getName());
return $next($request);
}
Create a base controller or trait for common SEO logic:
trait SEOable
{
protected function setSEO($model)
{
SEOMeta::setTitle($model->title);
SEOMeta::setDescription($model->excerpt);
OpenGraph::setImage($model->image_url);
JsonLd::setType('Article')->setAuthor($model->author);
}
}
Render SEO tags directly in Blade:
@seotools
For SPAs or APIs, return SEO data in JSON:
return response()->json([
'seo' => [
'title' => 'API Response',
'description' => 'Data fetched via API',
'og_image' => url('images/api-logo.png')
]
]);
Use SEOTools::generate() to test rendered tags in PHPUnit:
$html = SEOTools::generate();
$this->assertStringContainsString('<title>Test Page</title>', $html);
Facades vs. Direct Service Binding
app('seotools') directly.Overriding Defaults
seotools.php config values do not override dynamically set values. Use SEOMeta::setTitle() to force updates.JSON-LD Validation
JsonLd::generate() before rendering.Duplicate Meta Tags
SEOMeta::setDescription() twice) will overwrite previous values.SEOMeta::addMeta() for non-overwriting additions (e.g., custom properties).URL Generation
url() or route() helpers for canonical/og:url to avoid hardcoding paths.SEOMeta::setCanonical(url()->current()); // Dynamic canonical
Image Optimization
Storage::url() for local files:
OpenGraph::addImage(Storage::url('images/post.jpg'));
Inspect Rendered HTML Use browser dev tools to verify tags:
<!-- Check for -->
<meta property="og:title" content="Expected Title">
<script type="application/ld+json">...</script>
Log Generated Tags Temporarily log output in a controller:
dd(SEOTools::generate());
Validate Structured Data
<script> output into Google’s Structured Data Testing Tool.Clear Cached Views If changes aren’t reflected, clear Laravel’s view cache:
php artisan view:clear
Custom Meta Tags
Extend SEOMeta for project-specific tags:
SEOMeta::addMeta('custom:property', 'value', 'property');
Dynamic OpenGraph Types Create a helper for common OG types (e.g., articles, products):
OpenGraph::setArticleType($post)
->setTitle($post->title)
->setPublishedTime($post->published_at)
->addAuthor($post->author);
Middleware for SEO Auto-set SEO tags based on request:
public function handle($request, Closure $next)
{
if ($request->route()->getName() === 'blog.post') {
SEOMeta::setTitle($request->post->title);
}
return $next($request);
}
Service Provider Extensions Bind custom SEO services:
$this->app->bind('seotools.custom', function () {
return new CustomSEOService();
});
Localization Use Laravel’s localization features to switch SEO content:
SEOMeta::setTitle(__('seo.title.home'));
OpenGraph::setDescription(__('seo.description.home'));
seotools.php
meta.defaults to false to disable default tags entirely.'meta' => [
'defaults' => false, // Disables all default meta tags
'
How can I help you explore Laravel packages today?