ArtisanPack UI
DocsSEOChangelog

SEO Changelog

Latest: v1.8.1

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[1.8.1] - 2026-10-22

Fixed

  • Redirect lookups no longer take a global cache lock. Every uncached path sent through RedirectService::findMatch() (and so the HandleRedirects middleware) blocked for up to 5s on one shared lock to add itself to a tracked-paths array. With a database cache store, crawlers hitting unique URLs queued PHP workers behind that lock, and a LockTimeoutException turned the page view into a 500. The array also grew without limit, and clearCache() forgot each tracked key one by one. Match cache keys now include a unique generation token, and clearCache() replaces it in a single write; old entries expire through their TTL. A token is never reused, so overlapping clears or an evicted token can't bring back stale lookups. The old seo:redirects:cached_paths key is forgotten on the next clear (#111).
  • Sitemap cache clears can no longer bring back stale sitemaps. SitemapService::clearCache() read the generation counter and wrote it back plus one, so overlapping clears (SitemapObserver clears on every tracked save, force delete, and restore) could write an older generation and serve stale sitemap.xml, sitemap-*-N.xml, and llms.txt until the TTL expired. A missing counter also fell back to 1, reusing an old namespace if the store evicted the key. The generation is now a unique token that clearCache() replaces, and a missing token starts a fresh one. The protected SitemapService::getCacheGeneration() now returns string instead of int (#113).

[1.8.0] - 2026-10-20

Added

  • ap.seo.resolveMorphModel filter for morph types that aren't class names. A model whose getMorphClass() returns something like App\Models\Record:products (one model shared by several tables) stored a seoable_type / sitemapable_type that Eloquent tried to instantiate as a class, so SeoMeta::seoable and SitemapEntry::sitemapable threw Class "…" not found. Both relations now ask the filter for a configured model instance first, for lazy loading and for with( 'seoable' ) / with( 'sitemapable' ), through the new ResolvesMorphModels trait and ResolvableMorphTo relation. ArtisanPackUI\SEO\Support\MorphModelResolver wraps the filter. Types the filter doesn't resolve behave as before.

Fixed

  • HreflangService cache clearing works for morph-map aliases and resolver-backed types. It used to skip any seoable_type that wasn't a class name. It now checks MorphModelResolver::canResolve().
  • Meta and analysis cache keys include the model's table. Keys were meta:<class>:<id>, so records of one model class stored in several tables shared a cache entry and could serve each other's SEO data. They are now meta:<class>:<table>:<id> (and the same for analysis:). Existing entries simply miss once and are rebuilt.

[1.7.1] - 2026-10-19

Fixed

  • SeoMeta lookups now recognize relations bound with Model::resolveRelationUsing() (#104). Every lookup was gated on method_exists( $model, 'seoMeta' ), which misses dynamic relations served by Model::__call(). Models that couldn't take the HasSeo trait were treated as having no SEO meta. Content analysis reported "Meta description is missing." on every record, skipped the persisted focus_keyword, and never cached results. A new ArtisanPackUI\SEO\Support\SeoMetaResolver checks both declared methods and Model::isRelation(). All call sites now use it, including AnalysisService, SeoService, HreflangService, VisualEditorIntegration, AnalysisApiController, SeoObserver, the MetaTags/OpenGraph/TwitterCard components and the seoMeta() helper.
  • SeoService::updateSeoMeta() refreshes the loaded seoMeta relation (#105). A model whose seoMeta relation had already been loaded as null kept that cached null after updateSeoMeta() created a new row, so later lookups on the same instance missed the new metadata. The saved record is now set back onto the relation.

CI

  • Release workflow gains a sync-docs job that queues the documentation and changelog imports on docs.artisanpackui.dev after each release (#103). It skips with a notice when the DOCS_API_TOKEN secret is not set; the package slug can be overridden with a DOCS_PACKAGE_SLUG repository variable.

[1.7.0] - 2026-09-20

Added

  • Extension seam for AnalysisService::extractContent() (#100). SEO analyzers can now see template-provided markup (e.g. an H1 rendered by a page-title bar, hero component, or single-*.blade.php) instead of scoring only the model's content field. Two hooks land together: the ap.seo.analysisContent filter fires with the resolved content and the model so hosts can append template markup, and a new ArtisanPackUI\SEO\Contracts\SeoAnalyzableContent interface lets models return the exact HTML analyzers should reason over via getSeoAnalysisHtml(). Fixes the HeadingStructureAnalyzer false "No H1 heading found" flag on pages whose H1 lives outside the content body.
  • seo_analysis_cache.content_hash column + fingerprint-aware cache lookup (#100). The analysis cache now fingerprints the resolved-and-filtered HTML on write and compares it on read, so template-provided markup or ap.seo.analysisContent filter output changes invalidate the cached result even when the model's content field is untouched. Existing rows without a hash are treated as a miss so they refresh on next run.

[1.6.0] - 2026-09-17

Changed

  • Default OG card layout is now bottom-left anchored (#97). The title stack sits against the padded bottom of the canvas (above the subtitle when one is present, on the padded bottom edge when title-only), left-aligned at the template padding so it sits directly under the top-left logo. Matches the MightyShare-style layout the scrim was designed for and gives the title real breathing room over a background photo. Anchor points will be lifted onto OgImageTemplate in a follow-up so operators can toggle bottom / center / top layouts from config.

Added

  • GdOgImageRenderer now composites a semi-transparent scrim between the background image and the text so title / subtitle text has usable contrast on top of real photos (#97). Only applied when background_image_path is set — plain-colored backgrounds are unaffected.
  • New OgImageTemplate fields — backgroundScrimColor (hex), backgroundScrimOpacity (percentage 0-100, default 60), and backgroundScrimGradient (bool, default true) — plumbed through fromConfig(), cache signature, and config('seo.og_image.template') as background_scrim_color / background_scrim_opacity / background_scrim_gradient. Set opacity to 0 to disable the scrim.
  • With background_scrim_gradient = true (the default), the scrim fades from ~25% of the configured opacity at the top down to full opacity at the bottom so the darkening concentrates where the text sits and the top of the background image stays visible.

[1.5.0] - 2026-09-15

Added

  • MetaTitleSuggestionAgent and MetaDescriptionAgent now accept n (default 5, clamped to [1, 10]) and return that many deduplicated variants per call. Callers that want a single result read $variants[0] — the array shape is uniform across both agents (#94).
  • Both agents accept an optional h1 and drop any variant that restates the H1 verbatim, so the meta line adds context instead of echoing the visible headline (#94).
  • Golden-file test pins the rendered prompts for both agents (tests/Feature/Ai/MetaAgentPromptGoldenTest.php), so prompt regressions surface as a diff instead of silent quality drift.

Changed

  • MetaDescriptionAgent output is now { variants: [...] } (BREAKING). The old single-object shape (meta_description / character_count / rationale at the top level) is gone; take $variants[0] in downstream code. See docs/upgrade-1.5.0.md for the migration.
  • Meta prompts rewritten with a real quality pass — front-load the specific value, forbid brand-first titles unless the page is about the brand, ban filler openers ("Discover…", "Learn…", "Ultimate guide…", "Welcome to…"), require active voice, and include few-shot examples pulled from representative SERP snippets (#94).
  • MetaDescriptionAgent::MIN_LENGTH raised from 120 to 150 so the validator matches the prompt contract; variants outside the full 150–160 window are rejected rather than truncated.
  • MetaDescriptionSuggestor Livewire component + view render the variants list, matching MetaTitleSuggestor.

Fixed

  • Both agents now raise FeatureError when zero variants survive validation, so callers get an explicit failure instead of a silent empty list.
  • Variants that overshoot the length cap are rejected outright instead of being truncated — a truncated title/description loses the front-load discipline the prompt optimizes for.

[1.4.0] - 2026-09-05

Added

  • llms.txt AI-discovery manifest generator driven by the same indexable SitemapEntry source as the XML sitemap, with a new ap.seo.llmsTxtEntries filter hook and an opt-in GET /llms.txt route toggle (#68).
  • IndexNow submitter + sitemap-ping path for Bing/Yandex/Seznam/ Naver with a configurable key provider contract and an opt-in GET /{key}.txt key-verification route (#69).
  • Config-driven AI-crawler robots controls grouping related bots (OpenAI, Anthropic, Google-Extended, Perplexity, Common Crawl, ByteDance, Meta) with a working default_allow kill switch (#70).
  • RSS 2.0 and Atom 1.0 feed generator with sanitized XML output, an opt-in /feed.xml + /feed.atom route toggle backed by a FeedProviderContract binding, a seo.feeds config block, and the ap.seo.feedEntries filter hook (#72, #88).
  • Organization sameAs + logo on the schema graph, with sameAs URL validation and a width/height-carrying ImageObject for the publisher logo (#73).
  • OG image generator service (MightyShare-style social cards) rendered via GD, deterministically cached, alpha-preserving, and under a cache lock to coalesce concurrent misses (#74).
  • apSeoAddSchema() helper + SchemaCollector service (#77) and SchemaService render-pipeline centralization (#78).
  • Stable @id on every schema graph node (Organization, LocalBusiness, WebSite, WebPage, Article); Article.publisher cross-references the Organization by @id.

Changed

  • artisanpack-ui/ai constraint bumped to ^1.2. The SEO AI Livewire components now compose the ChecksFeatureToggle and InteractsWithAiFeature traits (#76 / #92). See docs/upgrade-1.4.0.md for the downstream extension BC note.
  • livewire/livewire require-dev + suggest constraint bumped to ^3.8.3 to pick up the CVE-2026-81887 patch.

Fixed

  • seo.robots.ai_crawlers.default_allow=false is now a real kill switch — previously a silent no-op that emitted the same robots.txt as default_allow=true.
  • RSS/Atom feeds strip XML 1.0 forbidden control characters and drop entries whose link uses a non-http(s) scheme (log-and-drop so a single bad row can't kill the feed).
  • Atom <id> reads seo.feeds.feed_id when set; when unset, falls back to the feed URL AND logs a Log::notice pointing at the config key.
  • LlmsTxtGenerator collapses newlines in titles and escapes [ / ] in descriptions so a Markdown list item can no longer shatter mid-line.
  • IndexNow surfaces per-URL rejections carried in an otherwise-200 body (code=UnverifiedHost, warnings[]) as success=false with a warning field populated; logged response bodies capped via Str::limit(..., 512).
  • FocusKeywordAnalyzer uses mb_strtolower('UTF-8') across all seven placement checks so multibyte keywords no longer under-match.
  • AiReadinessAnalyzer::countWords() uses preg_split('/\\s+/u') so Japanese and Cyrillic first paragraphs stop reporting zero words.
  • GD OG image renderer releases GD resources via try/finally around imagedestroy on the canvas + loaded PNG handles, preserves PNG alpha (no more black halos on transparent logos), and switches width measurement to mb_strlen; the bitmap fallback now logs a warning when asked to render non-ASCII text.
  • GdOgImageRenderer::hexToRgb() falls back to white and logs a warning on malformed color input rather than silently producing black-on-black cards.
  • LocalBusinessSchema drops the flat openingHours when a structured openingHoursSpecification is emitted, and normalizes validFrom/validThrough through a strict Y-m-d or DateTimeInterface normalizer.
  • OrganizationSchema.sameAs filters entries through FILTER_VALIDATE_URL.

Security

  • squizlabs/php_codesniffer updated to 3.13.6 (CVE-2026-67434, OS command injection).
  • livewire/livewire updated to 3.8.7 (CVE-2026-81887, DOM-based XSS).

Removed

  • Inline version field from composer.json — git tags drive versioning; the field had drifted from tags in past releases.

CI

  • PHPCS excludes Blade stubs; CI no longer swallows PHPCS errors (continue-on-error: true removed).
  • Test matrix varies Laravel version (11.x, 12.x, 13.x) alongside PHP.

[Unreleased-Beta History]

Entries below were accumulated pre-1.4.0 and are rolled into the 1.4.0 release above.

Added

  • AiReadinessAnalyzer (content category): New analyzer that scores content on the signals AI answer engines look for. Runs four checks: at least one question-style subheading (ends with ? or opens with what/why/how/…), a definition-style intro sentence (matches "X is/are/refers to/means Y" near the start), an FAQ section (detected via a heading or a JSON-LD FAQPage schema block), and a summary-friendly opening paragraph within ~60 words. Registered automatically in SEOServiceProvider and gated by config('seo.analysis.analyzers.ai_readiness') (#71).
  • First-paragraph placement check on FocusKeywordAnalyzer: The keyword analyzer now also verifies that the focus phrase appears in the opening paragraph (falls back to raw text when no <p> tag exists), on top of the existing title/description/URL/H1/subheadings/alt-text checks. Still no-ops when no focus phrase is set (#71).
  • LocalBusinessSchema dated/holiday OpeningHoursSpecification entries: buildOpeningHours() now accepts optional validFrom / validThrough on each entry so holiday overrides and other date-scoped hours are marked up alongside recurring dayOfWeek entries. A truthy closed flag emits opens and closes as "00:00" (per Google's guidance for all-day closures) and takes precedence over any explicit opens / closes values on the same entry. Consumed by the Keystone Modules/LocalSeo schema translator (#75).

Fixed

  • Sitemap XML cache is now invalidated on content changes: SitemapService::generate()/generateIndex() cached the rendered XML with a TTL (seo.sitemap.cache_ttl, default 3600s) but nothing ever forgot the cached entries when the underlying content changed — SitemapObserver kept the sitemap_entries table current on every Page/Post save, yet GET /sitemap.xml (and the sitemap-*-N.xml children) kept serving the pre-change snapshot until the TTL expired. SitemapObserver now calls SitemapService::clearCache() after a tracked model is saved, force deleted, or restored. SitemapService::clearCache() was also rewritten to bump a single generation counter that is baked into every cache key, so trailing pages for now-deleted content are invalidated as well (the previous per-page enumeration used the reduced page total and would leave a stale sitemap-{type}-N.xml for any page that no longer existed after a deletion). seo:generate-sitemap clears the cache before regenerating when writing to --output so cached generation primes fresh entries; the statistics-only invocation (no --output) no longer touches the cache, since it would wipe a warm cache with no priming to follow (#67).
  • Sitemap index child URLs now include the page number: SitemapIndexGenerator omitted the page segment for page 1, so the index pointed at sitemap-{type}.xml, which no route serves (only sitemap-{type}-{page}.xml is registered) — every typed child sitemap returned a 404. The index now emits sitemap-{type}-1.xml, provider sitemaps as sitemap-{provider}-1.xml, and the paginated main sitemap as sitemap-1.xml (instead of a self-referential sitemap.xml). Image, video, and news sitemap URLs are unchanged (#66).
  • Sitemap index now surfaces registered providers: SitemapService::needsIndex() did not consult $this->providers, so a site with a single database type plus one or more registered SitemapProviderContract providers served the plain <urlset> main sitemap at /sitemap.xml and never exposed provider URLs to crawlers. The service now passes providers to the generator, and SitemapIndexGenerator::needsIndex() returns true whenever any provider is registered (#66).

Removed

  • Laravel 10 support: Narrowed illuminate/support constraint to ^11.0|^12.0|^13.0 and orchestra/testbench to ^9.0|^10.0|^11.0. Models in this package declare casts with the Laravel 11+ casts() method, which Laravel 10's HasAttributes trait does not call — so Redirect, SeoAnalysisCache, SeoMeta, and SitemapEntry were silently returning raw column values (unparsed JSON, string datetimes, string enums) for anyone actually on Laravel 10. The advertised constraint was also unreachable in practice because artisanpack-ui/core: ^1.3 already requires illuminate/support: ^12.0|^13.0 (#65).

Documentation

  • Extensibility Hooks section in README.md: Replaced the previous "Filter Hooks" section with a dedicated "Extensibility Hooks" section that documents the ecosystem hook naming convention (ap. prefix + camelCase segments joined by ., with ✅/❌ examples), every filter and action hook the SEO package fires (ap.seo.metaTags, ap.seo.sitemapEntries, ap.seo.schemaGraph, ap.seo.schemaRendering, ap.seo.schemaRendered) with signatures and usage examples, and the apSeoAddSchema() helper with an example showing how a block's render() method contributes schema entries (#57, #61).

[1.3.0] - 2026-07-21

Added

  • ap.seo.metaTags filter hook: Fired in MetaTagService::generate() after tags are assembled and before the MetaTagsDTO is built. Payload: (array $tags, ?Model $subject, Request $request). Callbacks can add, remove, or rewrite title, description, canonical, robots, and additionalMeta before rendering (#62).
  • ap.seo.sitemapEntries filter hook: Fired inside SitemapGenerator::generate() and SitemapGenerator::generateFromProvider() so integrators can append, drop, or reorder sitemap entries per type. Payload: (array $entries, string $sitemapType) (#62).

Changed

  • artisanpack-ui/hooks: ^1.3 is now a required dependency (previously optional). The PackageDetector::hasHooks() guard has been removed and the hook helper functions are always available.
  • Renamed VE subscriber hook: VisualEditorIntegration::registerPrePublishChecks() now subscribes to ap.visualEditor.prePublishChecks (matching the visual-editor Wave 3 rename) instead of the old visual_editor.pre_publish_checks name.

Removed

  • PackageDetector::hasHooks() — no longer needed now that artisanpack-ui/hooks is required.

[1.2.0] - 2026-07-06

Added

  • AI Feature Suite: Five AI agents built on top of artisanpack-ui/ai for SEO workflows. Each ships with Livewire, React, and Vue trigger components plus a JSON API endpoint under /api/seo/ai/.
    • MetaTitleSuggestionAgent — 3-5 CTR-optimized title variants ≤60 chars (#50)
    • MetaDescriptionAgent — one 150-160 char meta description with keyword coverage (#51)
    • ContentAnalysisAgent — structured quality score across four dimensions with actionable recommendations (#52)
    • SchemaGenerationAgent — JSON-LD schema type suggestion + starter object, backed by the existing 14 supported schema types (#53)
    • HreflangSuggestionAgent — cross-references hreflang relationships and surfaces gaps/inconsistencies (#54)
  • Feature Registry Integration: SEOServiceProvider::aiFeatures() auto-registers all five features with labels, default models, and descriptions.
  • AI Trigger Components: <livewire:seo::ai-*> Livewire components + MetaTitleSuggestor/ContentAnalyzer/etc. React and Vue components with the useAiAgent hook/composable.

Changed

  • artisanpack-ui/ai: ^1.0 is now a suggested (optional) dependency, not required. The AI Feature Suite auto-activates when artisanpack-ui/ai is installed and silently disables otherwise, so PHP 8.2 / Laravel 10 / Laravel 11 users keep the rest of the package. AI features themselves require PHP 8.3+ and Laravel 12+, since those are artisanpack-ui/ai's own requirements. AI Livewire components, AI API endpoints (/api/seo/ai/*), and the AI feature registry entries are only registered when the ai package is present.

[1.1.1] - 2026-06-09

Added

  • Laravel 13 Support: Widened illuminate/support constraint to ^10.0|^11.0|^12.0|^13.0 and orchestra/testbench to include ^11.0. Existing Laravel 10/11/12 users are unaffected; Laravel 13 is selectable on PHP 8.3+ via L13's own requirements (#47)

[1.1.0] - 2026-04-08

Added

  • REST API Endpoints: Full API for SEO meta, analysis, redirects, and schema management under /api/seo/ (#29)
  • API Resources: Eloquent API Resources for SEO models and DTOs (SeoMetaResource, AnalysisResultResource, RedirectResource, SchemaResource, MetaTagsResource, OpenGraphResource, TwitterCardResource, HreflangResource, SeoPreviewResource) (#30)
  • TypeScript Type Definitions: Publishable TypeScript types for all SEO API responses via seo-types publish tag (#31)
  • React SEO Admin Components: 12 React components (SeoMetaEditor, BasicMetaTab, OpenGraphTab, TwitterCardTab, SchemaTab, HreflangTab, SitemapTab, MetaPreview, SocialPreview, SeoAnalysisPanel, RedirectManager, SeoDashboard) and 4 hooks (useApi, useSeoMeta, useSeoAnalysis, useRedirects) (#32)
  • Vue SEO Admin Components: 12 Vue components mirroring the React set, with 4 composables (useApi, useSeoMeta, useSeoAnalysis, useRedirects) (#33)
  • seo:install-frontend Artisan Command: Publishes React or Vue SEO components and TypeScript type definitions with --stack and --force options (#34)
  • Schema Type Field Definitions API: GET /api/seo/schema/types now returns rich metadata including descriptions and field definitions for each schema type, enabling dynamic form rendering in React/Vue editors (#35)
  • Publishable Asset Scaffolding: Publish tags seo-react, seo-vue, and seo-types for frontend component distribution
  • GitHub Actions CI/CD: Lint job (PHP-CS-Fixer + PHPCS), test matrix (PHP 8.2, 8.3, 8.4), and separate release workflow with Packagist notification
  • Comprehensive Documentation: Frontend scaffolding guide, schema type definitions API docs, v1.1.0 upgrade guide, and updated existing docs

Fixed

  • AnalysisService zero scores: Built-in analyzers are now properly registered with the AnalysisService in the service provider (#40)
  • SeoDashboard request guard: Added null check for request in fetchDashboardData to prevent errors
  • AnalyzerName typo: Corrected internal_link to internal_links in TypeScript type definitions
  • Schema numeric coercion: Deferred numeric coercion in schema fields until input is complete to prevent premature type conversion

Changed

  • Breaking: SchemaTypeContract interface gained two new methods — getDescription(): string and getFieldDefinitions(): array were added in 1.1.0. External classes that directly implement SchemaTypeContract must add these methods. Classes that extend AbstractSchema (the recommended approach) are unaffected, as AbstractSchema provides default implementations returning an empty string and empty array respectively. To migrate, either implement both methods in your custom class, or switch to extending AbstractSchema instead of implementing SchemaTypeContract directly.
  • CI migrated from GitLab to GitHub Actions: Replaced GitLab CI/CD with GitHub Actions workflows

[1.0.0] - 2026-01-23

First Stable Release

This is the first stable release of ArtisanPack UI SEO, a comprehensive SEO management package for Laravel applications built on Livewire 3.

Highlights

  • Complete SEO Management: Meta tags, Open Graph, Twitter Cards, and Schema.org markup
  • 14 Schema Types: Built-in JSON-LD structured data for rich search results
  • URL Redirect System: Exact, regex, and wildcard redirects with hit tracking
  • XML Sitemap Generation: Standard, image, video, and news sitemaps
  • SEO Content Analysis: 8 built-in analyzers for content quality scoring
  • Laravel 10, 11, and 12 Support: Compatible with current Laravel versions (illuminate/support ^10.0|^11.0|^12.0)
  • Livewire 3.6+ Compatible (Optional): Livewire components (SeoMetaEditor, RedirectManager, etc.) require Livewire 3.6+; Blade components work without Livewire
  • Comprehensive Documentation: Detailed guides and API reference

Added

Core Features

  • HasSeo Trait: Polymorphic SEO metadata attachment for any Eloquent model
  • Automatic Fallbacks: Smart fallback system using model properties for SEO values
  • SeoMeta Model: Store and manage SEO metadata with full field support
  • Feature Toggles: Enable/disable individual features via configuration

Meta Tags

  • Title Management: Custom titles with site name and separator
  • Description Management: Custom descriptions with automatic truncation
  • Robots Directives: Noindex, nofollow, and custom robots meta tags
  • Canonical URLs: Set canonical URLs to prevent duplicate content issues
  • Viewport Meta: Configurable viewport settings

Social Media

  • Open Graph Tags: Full Open Graph protocol support for Facebook/LinkedIn
  • Twitter Cards: Summary and summary_large_image card types
  • Image Support: Automatic image URL generation with fallbacks
  • Custom Social Titles: Separate titles/descriptions for social platforms

Schema.org / JSON-LD

  • 14 Built-in Schema Types: Article, BlogPosting, Product, Organization, Person, LocalBusiness, Event, Recipe, FAQPage, HowTo, BreadcrumbList, WebSite, WebPage, VideoObject
  • SchemaBuilderInterface: Contract for creating custom schema builders
  • Automatic Schema Generation: Generate schema from model data
  • Nested Schema Support: Support for complex nested schema structures

Multi-Language (Hreflang)

  • Hreflang Tags: Generate hreflang link tags for multi-language sites
  • HreflangService: Service for managing hreflang configurations
  • x-default Support: Support for default language fallback

URL Redirects

  • Redirect Model: Store redirect rules with source, destination, and type
  • Match Types: Exact match, regex patterns, and wildcard matching
  • Status Codes: Support for 301, 302, 307, and 308 redirects
  • Hit Tracking: Track redirect usage with hit counts and timestamps
  • Loop Prevention: Automatic detection and prevention of redirect loops
  • HandleRedirects Middleware: Automatic redirect handling via middleware

XML Sitemaps

  • Standard Sitemaps: Generate XML sitemaps with URLs, priorities, and frequencies
  • Image Sitemaps: Include image URLs in sitemaps for image search
  • Video Sitemaps: Include video metadata in sitemaps
  • News Sitemaps: Generate Google News-compatible sitemaps
  • Sitemap Index: Automatic sitemap index generation for large sites
  • SitemapProviderInterface: Contract for custom sitemap entry providers
  • Cache Support: Cache generated sitemaps for performance

Robots.txt

  • Dynamic Generation: Generate robots.txt dynamically from configuration
  • Global Rules: Set default allow/disallow rules for all bots
  • Bot-Specific Rules: Configure rules for specific user agents
  • Sitemap Inclusion: Automatically include sitemap URLs in robots.txt
  • RobotsService: Service for managing robots.txt configuration

SEO Analysis

  • 8 Built-in Analyzers: Title, description, headings, keyword density, readability, images, links, and meta robots
  • AnalyzerInterface: Contract for creating custom analyzers
  • Quality Scoring: Score content quality with good/warning/error status
  • Suggestions: Actionable suggestions for improving SEO
  • Caching: Cache analysis results for performance

Caching

  • Meta Tag Caching: Cache generated meta tags per model
  • Sitemap Caching: Cache generated sitemaps
  • Redirect Caching: Cache redirect lookups for performance
  • Analysis Caching: Cache SEO analysis results
  • CacheService: Centralized cache management service
  • Configurable TTL: Set cache duration per feature

Blade Components

  • x-seo-meta: All-in-one component rendering meta, OG, Twitter, and schema
  • x-seo-meta-tags: Basic meta tags component
  • x-seo-open-graph: Open Graph tags component
  • x-seo-twitter-card: Twitter Card tags component
  • x-seo-schema: Schema.org JSON-LD component
  • x-seo-hreflang: Hreflang link tags component

Livewire Components

  • SeoMetaEditor: Full SEO editing interface with tabbed sections
  • RedirectManager: URL redirect management with CRUD operations
  • SeoDashboard: Overview dashboard with SEO statistics
  • SeoAnalysisPanel: Content analysis results display
  • HreflangEditor: Multi-language URL editor
  • MetaPreview: Search result preview component
  • SocialPreview: Social share preview component

Services

  • SeoService: Main orchestrator for all SEO operations
  • MetaTagService: Generate and manage meta tags
  • SocialMetaService: Generate Open Graph and Twitter Card tags
  • SchemaService: Generate Schema.org JSON-LD markup
  • SitemapService: Generate XML sitemaps
  • RedirectService: Manage URL redirects
  • RobotsService: Generate robots.txt content
  • HreflangService: Manage hreflang configurations
  • AnalysisService: Run SEO content analysis
  • CacheService: Centralized cache management

Events

  • SeoMetaCreated: Dispatched when SEO meta is created
  • SeoMetaUpdated: Dispatched when SEO meta is updated
  • SitemapGenerated: Dispatched after sitemap generation
  • RedirectHit: Dispatched when a redirect is triggered

Helper Functions

  • seo(): Get the SeoService instance
  • seoMeta(): Get SeoMeta for a model
  • seoTitle(): Format page title with site name
  • seoDescription(): Truncate description to SEO length
  • seoIsEnabled(): Check if a feature is enabled
  • seoConfig(): Get configuration value

Artisan Commands

  • seo:generate-sitemap: Generate XML sitemap files
  • seo:submit-sitemap: Submit sitemap to search engines
  • seo:clear-cache: Clear all SEO caches

Facades

  • Seo: Facade for SeoService
  • Redirect: Facade for RedirectService

Contracts/Interfaces

  • SeoableInterface: Contract for SEO-enabled models
  • AnalyzerInterface: Contract for SEO analyzers
  • SchemaBuilderInterface: Contract for schema builders
  • SitemapProviderInterface: Contract for sitemap entry providers

Exceptions

  • RedirectLoopException: Thrown when redirect creates a loop
  • SitemapGenerationException: Thrown when sitemap generation fails
  • InvalidSchemaTypeException: Thrown when schema type is invalid
  • InvalidConfigurationException: Thrown when configuration is invalid

Extensibility

  • Filter Hooks: WordPress-style hooks via artisanpack-ui/hooks
  • Custom Schema Types: Register custom schema builders
  • Custom Analyzers: Register custom SEO analyzers
  • Custom Sitemap Providers: Register custom sitemap entry providers
  • Publishable Views: Customize all Blade views
  • Publishable Config: Full configuration customization

Database

  • seo_meta Migration: Polymorphic SEO metadata storage
  • redirects Migration: URL redirect rules storage
  • sitemap_entries Migration: Sitemap entry tracking
  • seo_analysis_cache Migration: Cached analysis results

Infrastructure

  • Code Style: PHP-CS-Fixer and PHPCS with ArtisanPackUI standards
  • GitLab CI/CD: Multi-PHP version testing (8.2, 8.3, 8.4)
  • Documentation: Comprehensive markdown documentation
  • WordPress-Style Documentation: Full PHPDoc blocks on all classes and methods