ArtisanPack UI
DocsSEOSchema.org / JSON-LD

Schema.org / JSON-LD

SEO v1.3.0

Structured data helps search engines understand your content and can enable rich search results. This guide covers implementing Schema.org markup using...

Structured data helps search engines understand your content and can enable rich search results. This guide covers implementing Schema.org markup using JSON-LD format.

Overview

The package provides 14 built-in schema types with customizable JSON-LD generation. Schema markup is stored with your SEO metadata and rendered automatically.

Setting Schema Type

Basic Usage

$post->updateSeoMeta([
    'schema_type' => 'Article',
]);

With Custom Data

$post->updateSeoMeta([
    'schema_type' => 'Article',
    'schema_data' => [
        'author' => [
            '@type' => 'Person',
            'name' => 'John Doe',
            'url' => 'https://example.com/authors/john',
        ],
        'publisher' => [
            '@type' => 'Organization',
            'name' => 'My Site',
            'logo' => [
                '@type' => 'ImageObject',
                'url' => 'https://example.com/logo.png',
            ],
        ],
        'datePublished' => $post->created_at->toIso8601String(),
        'dateModified' => $post->updated_at->toIso8601String(),
    ],
]);

Available Schema Types

Article

For blog posts and news articles:

$post->updateSeoMeta([
    'schema_type' => 'Article',
    'schema_data' => [
        'headline' => 'Article Headline',
        'author' => ['@type' => 'Person', 'name' => 'Author Name'],
        'datePublished' => '2024-01-15T10:00:00Z',
        'dateModified' => '2024-01-16T14:30:00Z',
        'image' => 'https://example.com/article-image.jpg',
        'articleSection' => 'Technology',
        'wordCount' => 1500,
    ],
]);

BlogPosting

Specifically for blog posts:

$post->updateSeoMeta([
    'schema_type' => 'BlogPosting',
    'schema_data' => [
        'headline' => 'My Blog Post Title',
        'author' => ['@type' => 'Person', 'name' => 'Blogger Name'],
        'datePublished' => '2024-01-15',
        'mainEntityOfPage' => $post->url,
    ],
]);

Product

For e-commerce products:

$product->updateSeoMeta([
    'schema_type' => 'Product',
    'schema_data' => [
        'name' => 'Amazing Product',
        'description' => 'Product description here.',
        'image' => 'https://example.com/product.jpg',
        'brand' => ['@type' => 'Brand', 'name' => 'Brand Name'],
        'sku' => 'SKU123',
        'offers' => [
            '@type' => 'Offer',
            'price' => '99.99',
            'priceCurrency' => 'USD',
            'availability' => 'https://schema.org/InStock',
            'url' => $product->url,
        ],
        'aggregateRating' => [
            '@type' => 'AggregateRating',
            'ratingValue' => '4.5',
            'reviewCount' => '89',
        ],
    ],
]);

Event

For events and happenings:

$event->updateSeoMeta([
    'schema_type' => 'Event',
    'schema_data' => [
        'name' => 'Tech Conference 2024',
        'startDate' => '2024-06-15T09:00:00-05:00',
        'endDate' => '2024-06-17T18:00:00-05:00',
        'location' => [
            '@type' => 'Place',
            'name' => 'Convention Center',
            'address' => [
                '@type' => 'PostalAddress',
                'streetAddress' => '123 Main St',
                'addressLocality' => 'Chicago',
                'addressRegion' => 'IL',
                'postalCode' => '60601',
                'addressCountry' => 'US',
            ],
        ],
        'offers' => [
            '@type' => 'Offer',
            'price' => '299',
            'priceCurrency' => 'USD',
            'availability' => 'https://schema.org/InStock',
            'validFrom' => '2024-01-01',
        ],
        'performer' => [
            '@type' => 'Person',
            'name' => 'Keynote Speaker',
        ],
    ],
]);

Organization

For company/organization pages:

$page->updateSeoMeta([
    'schema_type' => 'Organization',
    'schema_data' => [
        'name' => 'My Company',
        'url' => 'https://example.com',
        'logo' => 'https://example.com/logo.png',
        'contactPoint' => [
            '@type' => 'ContactPoint',
            'telephone' => '+1-800-555-1234',
            'contactType' => 'customer service',
        ],
        'sameAs' => [
            'https://facebook.com/mycompany',
            'https://twitter.com/mycompany',
            'https://linkedin.com/company/mycompany',
        ],
    ],
]);

LocalBusiness

For local businesses:

$business->updateSeoMeta([
    'schema_type' => 'LocalBusiness',
    'schema_data' => [
        'name' => 'My Local Shop',
        'image' => 'https://example.com/shop.jpg',
        'telephone' => '+1-555-123-4567',
        'address' => [
            '@type' => 'PostalAddress',
            'streetAddress' => '456 Oak Street',
            'addressLocality' => 'Springfield',
            'addressRegion' => 'IL',
            'postalCode' => '62701',
            'addressCountry' => 'US',
        ],
        'geo' => [
            '@type' => 'GeoCoordinates',
            'latitude' => '39.7817',
            'longitude' => '-89.6501',
        ],
        'openingHoursSpecification' => [
            [
                '@type' => 'OpeningHoursSpecification',
                'dayOfWeek' => ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
                'opens' => '09:00',
                'closes' => '17:00',
            ],
        ],
        'priceRange' => '$$',
    ],
]);

WebSite

For the main website (usually homepage):

$homepage->updateSeoMeta([
    'schema_type' => 'WebSite',
    'schema_data' => [
        'name' => 'My Website',
        'url' => 'https://example.com',
        'potentialAction' => [
            '@type' => 'SearchAction',
            'target' => 'https://example.com/search?q={search_term_string}',
            'query-input' => 'required name=search_term_string',
        ],
    ],
]);

WebPage

For general web pages:

$page->updateSeoMeta([
    'schema_type' => 'WebPage',
    'schema_data' => [
        'name' => 'About Us',
        'description' => 'Learn about our company.',
        'isPartOf' => ['@type' => 'WebSite', 'name' => 'My Website'],
    ],
]);

Service

For service offerings:

$service->updateSeoMeta([
    'schema_type' => 'Service',
    'schema_data' => [
        'name' => 'Web Development',
        'description' => 'Professional web development services.',
        'provider' => [
            '@type' => 'Organization',
            'name' => 'My Agency',
        ],
        'serviceType' => 'Web Development',
        'areaServed' => 'United States',
    ],
]);

FAQPage

For FAQ sections:

$faqPage->updateSeoMeta([
    'schema_type' => 'FAQPage',
    'schema_data' => [
        'mainEntity' => [
            [
                '@type' => 'Question',
                'name' => 'What is your return policy?',
                'acceptedAnswer' => [
                    '@type' => 'Answer',
                    'text' => 'We offer 30-day returns on all items.',
                ],
            ],
            [
                '@type' => 'Question',
                'name' => 'Do you ship internationally?',
                'acceptedAnswer' => [
                    '@type' => 'Answer',
                    'text' => 'Yes, we ship to over 50 countries.',
                ],
            ],
        ],
    ],
]);

For breadcrumb navigation:

$page->updateSeoMeta([
    'schema_type' => 'BreadcrumbList',
    'schema_data' => [
        'itemListElement' => [
            ['@type' => 'ListItem', 'position' => 1, 'name' => 'Home', 'item' => 'https://example.com'],
            ['@type' => 'ListItem', 'position' => 2, 'name' => 'Products', 'item' => 'https://example.com/products'],
            ['@type' => 'ListItem', 'position' => 3, 'name' => 'Widget', 'item' => 'https://example.com/products/widget'],
        ],
    ],
]);

Review

For review content:

$review->updateSeoMeta([
    'schema_type' => 'Review',
    'schema_data' => [
        'itemReviewed' => ['@type' => 'Product', 'name' => 'Product Name'],
        'reviewRating' => ['@type' => 'Rating', 'ratingValue' => '4', 'bestRating' => '5'],
        'author' => ['@type' => 'Person', 'name' => 'Reviewer Name'],
        'reviewBody' => 'Great product, highly recommended!',
    ],
]);

AggregateRating

For aggregate ratings:

$product->updateSeoMeta([
    'schema_type' => 'AggregateRating',
    'schema_data' => [
        'itemReviewed' => ['@type' => 'Product', 'name' => 'Product Name'],
        'ratingValue' => '4.5',
        'bestRating' => '5',
        'ratingCount' => '127',
    ],
]);

Schema Type Definitions API

Added in v1.1.0

The package provides a Schema Type Definitions API that returns rich metadata for each schema type, including human-readable descriptions and field definitions. This is designed for building dynamic schema editing forms in React or Vue frontends.

API Endpoint

GET /api/seo/schema/types

Response Format

{
    "data": [
        {
            "name": "Article",
            "label": "Article",
            "description": "Represents a news or scholarly article.",
            "fields": [
                {
                    "name": "headline",
                    "type": "text",
                    "label": "Headline",
                    "required": true,
                    "description": "The headline of the article."
                },
                {
                    "name": "datePublished",
                    "type": "date",
                    "label": "Date Published",
                    "required": true,
                    "description": "The date the article was published."
                }
            ]
        }
    ]
}

Using the SchemaFactory Directly

use ArtisanPackUI\SEO\Schema\SchemaFactory;

$factory = app(SchemaFactory::class);

// Get all type definitions with descriptions and fields
$definitions = $factory->getTypeDefinitions();

// Each definition includes: name, label, description, fields[]
foreach ($definitions as $definition) {
    echo $definition['name'];        // e.g., "Article"
    echo $definition['description'];  // e.g., "Represents a news or scholarly article."

    foreach ($definition['fields'] as $field) {
        echo $field['name'];       // e.g., "headline"
        echo $field['type'];       // e.g., "text", "date", "url", "select", "textarea"
        echo $field['label'];      // e.g., "Headline"
        echo $field['required'];   // true or false
        echo $field['description'];
    }
}

Custom Schema Types with Field Definitions

When creating custom schema types, implement getDescription() and getFieldDefinitions() to have them appear in the API:

use ArtisanPackUI\SEO\Schema\Builders\AbstractSchema;
use Illuminate\Database\Eloquent\Model;

class RecipeSchema extends AbstractSchema
{
    public function getType(): string
    {
        return 'Recipe';
    }

    public function getDescription(): string
    {
        return 'A recipe for preparing a food dish.';
    }

    public function getFieldDefinitions(): array
    {
        return [
            [
                'name' => 'recipeName',
                'type' => 'text',
                'label' => 'Recipe Name',
                'required' => true,
                'description' => 'The name of the recipe.',
            ],
            [
                'name' => 'cookTime',
                'type' => 'text',
                'label' => 'Cook Time',
                'required' => false,
                'description' => 'The time it takes to cook the dish (ISO 8601 duration).',
            ],
        ];
    }

    public function generate(?Model $model = null): array
    {
        return $this->filterEmpty(array_merge($this->getBaseSchema(), [
            'name'     => $this->get('recipeName'),
            'cookTime' => $this->get('cookTime'),
        ]));
    }
}

Register your custom type:

$factory = app(\ArtisanPackUI\SEO\Schema\SchemaFactory::class);
$factory->register('Recipe', RecipeSchema::class);

Rendering Schema

Using Blade Component

<x-seo-schema :model="$post" />

Generated Output

<script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": "My Article Title",
    "author": {
        "@type": "Person",
        "name": "John Doe"
    },
    "datePublished": "2024-01-15T10:00:00Z",
    "image": "https://example.com/article.jpg"
}
</script>

Using the Schema Service

For programmatic schema generation:

use ArtisanPackUI\Seo\Services\SchemaService;

$schemaService = app(SchemaService::class);

// Generate schema for a model
$jsonLd = $schemaService->generate($post);

// Generate specific schema type
$articleSchema = $schemaService->buildArticle($post, [
    'author' => ['@type' => 'Person', 'name' => 'Author'],
]);

Testing Structured Data

Validate your schema markup using:

Best Practices

  1. Choose the most specific type - Use BlogPosting instead of Article for blogs
  2. Include required properties - Check Google's documentation for each type
  3. Use absolute URLs - Always use full URLs for images and links
  4. Keep data accurate - Schema should match visible page content
  5. Test regularly - Validate after any changes

Next Steps