BoxLang 🚀 A New JVM Dynamic Language Learn More...
Schema-org is a ColdBox module for building structured JSON-LD data using Schema.org in a flexible, fluent, and framework-friendly way.
Schema.org is a collaborative vocabulary standard (initiated by Google, Microsoft, Yahoo, and Yandex) that helps search engines understand webpage content.
JSON-LD (JavaScript Object Notation for Linked Data) is a data format that allows you to express structured data in a way that is easy for both humans and machines to read. It is ideal for embedding structured information in web pages.
Using Schema.org structured data can improve your website's visibility in search results by allowing search engines to understand your content more effectively. It also enables rich snippets, which can display additional information like ratings, reviews, and product details directly in search results. Bottom line: it's an essential part of modern SEO and web development.
@context
and @graph
wrappingInstall via CommandBox:
box install schema-org
Woah, what? Nothing to configure? That's awesome!
SchemaBuilder
into your
handler/layout/component or alternatively, instantiate an instance
of SchemaBuilder
via wirebox.
Important: SchemaBuilder
is a
transient, so make sure to use the provider:
method
when using property inection.// automatic injetcion
property name="sb" inject="provider:SchemaBuilder@schema-org";
// or manual instantiation
var sb = getInstance( "SchemaBuilder@schema-org" );
// In your handler method
prc.schema = sb
// Organization Schema
.organization( function( o ) {
o.id( "https://startrek.com/##organization" ) // must be unique
.name( "United Federation of Planets" )
.url( "https://startrek.com/" )
// ... rest of schema properties
} )
// Webpage Schema
.webpage( function( o ) {
o.id( https://startrek.com/##webpage" ) // must be unique
.name( "Starfleet Command" )
.description( "Official website of Starfleet Command" )
.url( "https://startrek.com/" )
// ... rest of schema properties
} )
<cfoutput>
#prc.schema.render()#
</cfoutput>
SchemaBuilder
Methods
get()
: Returns a struct representation of the full
schema graph.
render()
: Returns the serialized JSON-LD string
wrapped in <script
type="application/ld+json">
tags for easy
embedding in HTML.
toArray()
: Returns an array of all schema objects created.
toJsonLd()
: Returns serialized JSON-LD schema graph
(without the script tags).
[type]()
methods: Create specific schema types like
organization()
, webpage()
,
product()
, etc. The method accepts a closure or a
struct to define properties (see below).
Each supported type is accessible via method call on the builder. Examples include:
organization()
webpage()
product()
localBusiness()
etc...All core Schema.org types are supported. If you discover a missing one, please open an issue.
Each schema object type has methods to set properties and instantiate new objects. Common methods include:
id(value)
: Sets the unique identifier for the schema
object. Note: The actual property name is @id
, but
you can use id()
or setId()
for convenience.
[property](value)
: Sets a property for the schema
object. The method name corresponds to the Schema.org property
name (e.g., name(value)
, url(value)
,
description(value)
, etc.).
new(type, closure or struct)
: Creates a new nested
schema object of the specified type. The type
is the
Schema.org type name (e.g., "PostalAddress"), and the
closure
is a function or a struct to define its properties.
You can define properties using a closure or a struct. The closure approach allows for fluent chaining, while the struct approach is more explicit. The methods available for each type are based on the properties available for that particular type.
The below examples assume sb
is an instance of
SchemaBuilder
. The closure receives a single argument,
which is the schema object being built. You can chain methods to set properties.
sb.organization( function( o ) {
o.id( "https://example.com/##organization" )
.name( "Example Organization" )
.url( "https://example.com/" )
// ... other properties
} )
sb.organization( {
id: "https://example.com/##organization",
name: "Example Organization",
url: "https://example.com/"
// ... other properties
} )
You can nest schema objects within each other. You can quickly create
a new instance of a schema type by calling new()
on the
closure argument. For example, if you have a
localBusiness
that has an address
, you can
define it like this:
sb.localBusiness( function( lb ) {
lb.id( "https://example.com/##localBusiness" )
.name( "Example Local Business" )
// Address needs a nested schema object
.address(
// Create a new PostalAddress instance by calling new() on the schema object
lb.new( "PostalAddress", function( a ) {
a.streetAddress( "123 Example St" )
.addressLocality( "Example City" )
.addressRegion( "EX" )
.postalCode( "12345" )
.addressCountry( "US" );
} )
);
} )
Here's a complete example of how you might use the
SchemaBuilder
in a ColdBox app. You can also check out
the test-harness folder in this repo for the more fun.
// /layouts/Main.cfc
<script type="application/ld+json">
#runEvent(
event = "schemas.index",
eventArguments = {
<!--- Pass along important event arguments for schema generation --->
<!--- Tip: If you use event caching, the cache key is determined by these arguments, so make them unique per page/schema --->
eventName = event.getCurrentEvent(), <!--- example: "posts.show" --->
routedUrl = event.getCurrentRoutedUrl() <!--- example: "/posts/why-captain-picard-is-the-goat" --->
}
)#
</script>
Note that when building the webpage
schema, we recommend
using variables like prc.canonicalUrl
,
prc.pageTitle
, and prc.pageDescription
to
dynamically set the schema properties based on the current page
context. This allows for flexible schema generation across different
pages without hardcoding values. All your pages should have these
properties which is a best-practice for any modern website where SEO
is a priority.
// /handlers/Schemas.cfc
// Inject the SchemaBuilder into your handler
property name="sb" inject="provider:SchemaBuilder@schema-org";
function index( event, rc, prc, eventName, routedUrl ) {
// Build the schema objects
prc.schema = sb
// add the organization schema based on Coldbox config settings
.organization( getSetting( "schema" ).organization )
// add the webpage schema based on Coldbox config settings
.website( getSetting( "schema" ).website )
// Dynamically build the webpage schema based on the current event and routed URL
.webpage( function( o ) {
o.id( prc.canonicalUrl & "$##webpage" )
.name( prc.pageTitle )
.description( prc.pageDescription )
.url( prc.canonicalUrl );
} );
// Return the schema as JSON-LD
return prc.schema.toJsonLd();
}
Organization
on every main page (for branding/trust).@id
values (especially for shared entities
like authors or logos).WebPage
on all routable pages.If you're using Adobe ColdFusion (ACF), you may encounter issues when
setting a property that is a reserved word in ACF, such as
abstract
, which is found in the Schema.org
CreativeWork
type. To work around this limitation,
you can populate the property using a struct or prepend an underscore
_
to the method call, like this:
// MyHandler.cfc
// Closure workaround 1
prc.schema = schemaBuilder.creativeWork( function( cw ) {
cw.id( "https://example.com/##creativeWork" )
.name( "Example Creative Work" )
._abstract( "This is an example abstract." );
} );
// Closure workaround 2
prc.schema = schemaBuilder.creativeWork( function( cw ) {
cw.id( "https://example.com/##creativeWork" )
.name( "Example Creative Work" )
.set_abstract( "This is an example abstract." );
} );
// Struct workaround
prc.schema = schemaBuilder.creativeWork( {
id: "https://example.com/##creativeWork",
name: "Example Creative Work",
abstract: "This is an example abstract." // no need for underscore
} );
Ideas? Bugs? Pull requests welcome! Please include tests when submitting PRs.
This module was forged by Angry Sam Productions, a California-based web development company. We love ColdBox and believe structured data is critical for modern SEO.
To run the tests, simply run the following command from the root of
the project in Commandbox: start [email protected]
(or
whichever server JSON you want to use) server open
(to
open the server in your browser) navigate to
/tests/runner.cfm
in your browser.
This module is provided "as is" without warranty of any kind. Use at your own risk. The author is not responsible for any issues that may arise from using this module in your applications.
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.
$
box install schema-org