# Spatie Schema.org spatie/schema-org is a PHP library that provides a fluent builder interface for creating Schema.org structured data in JSON-LD format. The library auto-generates classes from the official Schema.org vocabulary, offering type-safe construction of all Schema.org types including LocalBusiness, Product, Article, Event, Person, Organization, and over 800 other types. Each type can be instantiated using either a factory method or direct construction, configured through chainable method calls, and rendered as JSON-LD script tags for embedding in HTML pages. The package supports advanced features like conditional property setting, array access for properties, nested types, multi-typed entities (combining multiple types into one), and graph structures (organizing multiple related schema objects). It handles automatic serialization of complex data types including DateTime objects, maintains proper JSON-LD context formatting, and provides utilities for hiding specific nodes in graphs. The library is particularly useful for improving SEO by adding rich snippets to websites, enabling search engines to better understand page content. ## Schema Factory Create Schema.org type instances using factory methods. ```php use Spatie\SchemaOrg\Schema; // Create a LocalBusiness with properties $business = Schema::localBusiness() ->name('Spatie') ->email('info@spatie.be') ->url('https://spatie.be') ->telephone('+32-3-292-56-79') ->priceRange('$$$') ->address(Schema::postalAddress() ->streetAddress('Kruikstraat 22') ->addressLocality('Antwerp') ->postalCode('2018') ->addressCountry('BE') ); echo $business->toScript(); ``` ## Product with Offers Create product schemas with nested offer information. ```php use Spatie\SchemaOrg\Schema; $product = Schema::product() ->name('Executive Anvil') ->image('https://example.com/photos/anvil.jpg') ->description('Sleeker than ACME\'s Classic Anvil') ->sku('0446310786') ->brand(Schema::brand()->name('ACME')) ->offers(Schema::offer() ->url('https://example.com/anvil') ->priceCurrency('USD') ->price('119.99') ->priceValidUntil('2025-12-31') ->itemCondition('https://schema.org/NewCondition') ->availability('https://schema.org/InStock') ) ->aggregateRating(Schema::aggregateRating() ->ratingValue('4.4') ->reviewCount('89') ); echo $product->toScript(); // Outputs: ``` ## Article Schema Build article schemas for blog posts and news content. ```php use Spatie\SchemaOrg\Schema; $article = Schema::article() ->headline('My Awesome Article') ->datePublished(new DateTime('2025-01-15')) ->dateModified(new DateTime('2025-01-16')) ->author(Schema::person() ->name('John Doe') ->url('https://example.com/authors/john') ) ->publisher(Schema::organization() ->name('Example Publisher') ->logo(Schema::imageObject() ->url('https://example.com/logo.png') ->width(600) ->height(60) ) ) ->image('https://example.com/article-image.jpg') ->articleBody('Full text of the article...'); echo json_encode($article, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); ``` ## Array Access and Property Methods Access and manipulate schema properties using array notation or methods. ```php use Spatie\SchemaOrg\Schema; $business = Schema::localBusiness(); // Set properties using methods $business->name('Spatie'); // Set properties using array access $business['email'] = 'info@spatie.be'; // Check if property exists if (isset($business['name'])) { echo $business['name']; // 'Spatie' } // Get property with default echo $business->getProperty('phone', 'Not provided'); // Get all properties $properties = $business->getProperties(); // Unset a property unset($business['email']); // Add multiple properties at once $business->addProperties([ 'telephone' => '+32-3-292-56-79', 'address' => 'Kruikstraat 22, Antwerp' ]); ``` ## Conditional Property Setting Conditionally set properties without breaking method chains. ```php use Spatie\SchemaOrg\Schema; use Spatie\SchemaOrg\LocalBusiness; $businessData = [ 'name' => 'Spatie', 'email' => 'info@spatie.be', // 'phone' is optional ]; $business = Schema::localBusiness() ->name($businessData['name']) ->email($businessData['email']) ->if(isset($businessData['phone']), function (LocalBusiness $schema) use ($businessData) { $schema->telephone($businessData['phone']); }) ->if(isset($businessData['priceRange']), function (LocalBusiness $schema) use ($businessData) { $schema->priceRange($businessData['priceRange']); }); echo $business->toScript(); ``` ## Event Schema Create event schemas with dates and locations. ```php use Spatie\SchemaOrg\Schema; $event = Schema::event() ->name('Laravel Conference 2025') ->startDate(new DateTime('2025-03-15T09:00:00')) ->endDate(new DateTime('2025-03-17T17:00:00')) ->eventStatus('https://schema.org/EventScheduled') ->eventAttendanceMode('https://schema.org/OfflineEventAttendanceMode') ->location(Schema::place() ->name('Conference Center') ->address(Schema::postalAddress() ->streetAddress('123 Main St') ->addressLocality('Amsterdam') ->postalCode('1000') ->addressCountry('NL') ) ) ->offers(Schema::offer() ->url('https://example.com/tickets') ->price('299.00') ->priceCurrency('EUR') ->availability('https://schema.org/InStock') ->validFrom(new DateTime('2025-01-01')) ) ->performer(Schema::person() ->name('Taylor Otwell') ); echo $event->toScript(); ``` ## Breadcrumb List Create breadcrumb navigation schemas for SEO. ```php use Spatie\SchemaOrg\Schema; $breadcrumbs = Schema::breadcrumbList() ->itemListElement([ Schema::listItem() ->position(1) ->name('Home') ->item('https://example.com'), Schema::listItem() ->position(2) ->name('Products') ->item('https://example.com/products'), Schema::listItem() ->position(3) ->name('Anvils') ->item('https://example.com/products/anvils'), Schema::listItem() ->position(4) ->name('Executive Anvil') ]); echo $breadcrumbs->toScript(); ``` ## Enumeration Types Use enumeration constants for type-safe property values. ```php use Spatie\SchemaOrg\Schema; use Spatie\SchemaOrg\BookFormatType; use Spatie\SchemaOrg\ItemAvailability; $book = Schema::book() ->name('Laravel: Up & Running') ->bookFormat(BookFormatType::EBook) ->isbn('978-1491936122') ->author(Schema::person()->name('Matt Stauffer')) ->offers(Schema::offer() ->price('34.99') ->priceCurrency('USD') ->availability(ItemAvailability::InStock) ); echo $book->toScript(); ``` ## Multi-Typed Entity Combine multiple Schema.org types into a single entity. ```php use Spatie\SchemaOrg\Schema; use Spatie\SchemaOrg\MultiTypedEntity; use Spatie\SchemaOrg\Product; $mte = new MultiTypedEntity(); // Add HotelRoom type $mte->hotelRoom() ->name('The Presidential Suite') ->occupancy(Schema::quantitativeValue()->value(4)); // Add Product type with offer $mte->product() ->offers(Schema::offer() ->name('One Night Stay') ->price(100000.00) ->priceCurrency('USD') ); // Add additional Product properties via closure $mte->product(function (Product $product) { $product->aggregateRating( Schema::aggregateRating() ->bestRating(5) ->worstRating(4) ->ratingValue(4.7) ); }); echo json_encode($mte, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); // Outputs: {"@context":"https://schema.org","@type":["HotelRoom","Product"],...} ``` ## Graph with Multiple Entities Create a graph containing multiple related schema objects. ```php use Spatie\SchemaOrg\Graph; use Spatie\SchemaOrg\Schema; use Spatie\SchemaOrg\Organization; $graph = new Graph(); // Create a product and reference an organization $graph->product() ->name('Executive Anvil') ->brand($graph->organization()) ->manufacturer($graph->organization()); // Fill out the organization details (referenced above) $graph->organization() ->name('ACME Corporation') ->url('https://acme.example.com') ->logo('https://acme.example.com/logo.png'); // Add another product $graph->product('anvil-lite') ->name('Anvil Lite') ->brand($graph->organization()); echo $graph->toScript(); // Outputs: ``` ## Graph with Node Identifiers Manage multiple nodes of the same type using identifiers. ```php use Spatie\SchemaOrg\Graph; use Spatie\SchemaOrg\Schema; use Spatie\SchemaOrg\Person; $graph = new Graph(); // Add team members with identifiers $graph->person('freek') ->givenName('Freek') ->familyName('Van der Herten') ->alternateName('freekmurze') ->jobTitle('Developer'); $graph->person('sebastian', function(Person $person, Graph $graph): void { $person ->givenName('Sebastian') ->familyName('De Deyne') ->alternateName('sebastiandedeyne') ->jobTitle('Developer'); }); $graph->person('tom') ->givenName('Tom') ->familyName('Witkowski') ->alternateName('gummibeer') ->jobTitle('Developer'); // Add organization with team members $graph->organization() ->name('Spatie') ->employee([ $graph->person('freek'), $graph->person('sebastian'), $graph->person('tom') ]); // Optionally hide specific nodes from output $graph->hide(Person::class, 'tom'); echo json_encode($graph, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); ``` ## Custom Properties and Identifiers Set custom properties and identifiers on schema types. ```php use Spatie\SchemaOrg\Schema; $organization = Schema::organization() ->name('Spatie') ->setProperty('identifier', 'https://spatie.be/#organization') ->setProperty('customProperty', 'custom value'); // Identifiers are converted to @id in JSON-LD echo $organization->toArray()['@id']; // 'https://spatie.be/#organization' // Get properties $name = $organization->getProperty('name'); $all = $organization->getProperties(); ``` ## Recipe Schema Create detailed recipe schemas for food content. ```php use Spatie\SchemaOrg\Schema; $recipe = Schema::recipe() ->name('Chocolate Chip Cookies') ->image('https://example.com/cookies.jpg') ->author(Schema::person()->name('Chef John')) ->datePublished(new DateTime('2025-01-10')) ->description('The best chocolate chip cookies recipe') ->prepTime('PT15M') ->cookTime('PT10M') ->totalTime('PT25M') ->recipeYield('24 cookies') ->recipeCategory('Dessert') ->recipeCuisine('American') ->keywords(['cookies', 'chocolate', 'baking']) ->recipeIngredient([ '2 cups all-purpose flour', '1 cup butter, softened', '1 cup white sugar', '2 eggs', '2 cups chocolate chips' ]) ->recipeInstructions([ Schema::howToStep()->text('Preheat oven to 350°F (175°C)'), Schema::howToStep()->text('Mix butter and sugar until fluffy'), Schema::howToStep()->text('Beat in eggs one at a time'), Schema::howToStep()->text('Stir in flour and chocolate chips'), Schema::howToStep()->text('Drop spoonfuls onto baking sheet'), Schema::howToStep()->text('Bake for 10 minutes until golden') ]) ->nutrition(Schema::nutritionInformation() ->calories('180 calories') ->fatContent('9g') ->sugarContent('12g') ); echo $recipe->toScript(); ``` ## FAQ Page Schema Create FAQ schemas for question-and-answer content. ```php use Spatie\SchemaOrg\Schema; $faq = Schema::fAQPage() ->mainEntity([ Schema::question() ->name('How do I install the package?') ->acceptedAnswer(Schema::answer() ->text('Run: composer require spatie/schema-org') ), Schema::question() ->name('What PHP versions are supported?') ->acceptedAnswer(Schema::answer() ->text('PHP 8.0 and higher is required.') ), Schema::question() ->name('Is it free to use?') ->acceptedAnswer(Schema::answer() ->text('Yes, the package is open source under the MIT license.') ) ]); echo $faq->toScript(); ``` ## Video Object Schema Create video schemas for video content SEO. ```php use Spatie\SchemaOrg\Schema; $video = Schema::videoObject() ->name('How to Build Schema.org Structured Data') ->description('A tutorial on implementing Schema.org in PHP') ->thumbnailUrl('https://example.com/video-thumbnail.jpg') ->uploadDate(new DateTime('2025-01-15')) ->duration('PT15M33S') ->contentUrl('https://example.com/video.mp4') ->embedUrl('https://example.com/embed/video') ->interactionStatistic(Schema::interactionCounter() ->interactionType('https://schema.org/WatchAction') ->userInteractionCount(5420) ); echo $video->toScript(); ``` ## Array Serialization Convert schemas to arrays for custom processing. ```php use Spatie\SchemaOrg\Schema; $person = Schema::person() ->name('Jane Smith') ->jobTitle('Software Engineer') ->email('jane@example.com') ->worksFor(Schema::organization() ->name('Tech Corp') ); // Get as array $array = $person->toArray(); /* [ '@context' => 'https://schema.org', '@type' => 'Person', 'name' => 'Jane Smith', 'jobTitle' => 'Software Engineer', 'email' => 'jane@example.com', 'worksFor' => [ '@type' => 'Organization', 'name' => 'Tech Corp' ] ] */ // Get as JSON string $json = json_encode($person, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); ``` ## CSP Nonce Support Add nonce attributes for Content Security Policy compliance. ```php use Spatie\SchemaOrg\Schema; $nonce = 'random-nonce-value-123'; $business = Schema::localBusiness() ->name('Spatie') ->setNonce($nonce); echo $business->toScript(); // Outputs: // Also works with Graph $graph = new Graph(); $graph->setNonce($nonce); $graph->organization()->name('Spatie'); echo $graph->toScript(); ``` ## Summary The spatie/schema-org library simplifies the creation of Schema.org structured data for PHP applications, offering comprehensive coverage of the entire Schema.org vocabulary through auto-generated, type-safe classes. Primary use cases include enhancing e-commerce sites with Product schemas, improving local business SEO with LocalBusiness schemas, optimizing content discoverability with Article and BlogPosting schemas, adding rich event information with Event schemas, and creating structured FAQ and HowTo content. The library handles complex nested structures, supports conditional property setting for dynamic content, and provides proper JSON-LD serialization with context management. Integration patterns include embedding schemas directly in HTML views using the toScript() method, building complex multi-entity relationships with Graph objects, combining multiple types for hybrid entities using MultiTypedEntity, and exporting schemas as arrays or JSON for API responses or custom processing. The fluent API design makes it easy to construct schemas programmatically from database records or CMS content, while built-in support for DateTime serialization, enumeration types, and CSP nonces ensures production-ready output. The library is particularly valuable for Laravel applications but works with any PHP framework or standalone application requiring SEO-optimized structured data.