Coder Social home page Coder Social logo

stevegrunwell / schemify Goto Github PK

View Code? Open in Web Editor NEW
58.0 5.0 11.0 186 KB

Automatically generate Schema.org JSON-LD markup for WordPress content.

Home Page: https://wordpress.org/plugins/schemify/

License: MIT License

PHP 92.48% JavaScript 0.91% Shell 6.61%
schema json-ld structured-data wordpress-plugin

schemify's Introduction

Schemify

Build Status Code Climate Test Coverage

Structured data allows developers and publishers to mark up content in a way that it's easy for machines to understand. Proper use of structured data enables third-parties like Google to parse reviews, event data, and contact information in meaningful ways, ensuring you're getting the most "bang" out of your publishing buck.

Fortunately, the major players in the Search game, including Google, Microsoft, Yahoo!, and Yandex) came together in the early 2010s to form Schema.org, a collaborative, community-driven standard for structured data.

With the major search engines and communities behind it, we're all marking up everything with appropriate structured data now, right?

Unfortunately, the process of implementing Schema.org in a site – especially one driven by dynamic content – is less than straightforward. WordPress, for instance, powers roughly a quarter of the web, but implementing structured data across hundreds of thousands of themes would be a nightmare.

Or, at least it would be, if it weren't for Schemify.

What does Schemify do?

There are two approaches to adding structured data to a website: via the markup or JSON for Linking Data (JSON-LD).

Consider the following author information, which might appear at the bottom of a blog post:

<div class="author">
	<img src="http://en.gravatar.com/avatar/88ea4e10ed968136228545d7112d82cb?s=200" alt="Steve Grunwell" />
	<h2><a href="https://stevegrunwell.com" rel="author">Steve Grunwell</a></h2>
	<p>Steve is the Director of Technology at Growella. When he's not working, you can find him speaking at conferences, roasting coffee, or spending time with his wife and daughter</p>
</div>

If I wanted this information to use Schema.org markup, it would look something like this:

<div class="author" itemscope itemtype="http://schema.org/Person">
	<img src="http://en.gravatar.com/avatar/88ea4e10ed968136228545d7112d82cb?s=200" alt="Steve Grunwell" itemprop="image" />
	<h2 itemprop="name"><a href="https://stevegrunwell.com" rel="author" itemprop="url">Steve Grunwell</a></h2>
	<p itemprop="description">Steve is the Director of Technology at Growella. When he's not working, you can find him speaking at conferences, roasting coffee, or spending time with his wife and daughter</p>
</div>

While that may not seem like a lot of work, that's still a lot of extra markup being added. Even worse, a lot of that markup might normally be generated by WordPress helper functions like get_avatar(), which means extra work to get the necessary itemprop attribute in place.

Fortunately, the other approach for adding structured data is much more straight-forward in a theme, as it's simply a JSON-LD object:

<script type="application/ld+json">
{
	"@context": "http://schema.org",
	"@type": "Person",
	"name": "Steve Grunwell",
	"url": "https://stevegrunwell.com",
	"image": "http://en.gravatar.com/avatar/88ea4e10ed968136228545d7112d82cb?s=200",
	"description": "Steve is the Director of Technology at Growella. When he's not working, you can find him speaking at conferences, roasting coffee, or spending time with his wife and daughter"
}
</script>

We simply generate this JSON-LD object and append it to our document's <body>. When Google or anyone else who supports JSON-LD structured data parses our page, they can immediately understand that Steve Grunwell is a person and determine his website, avatar, and biography.

The best part? There's no need to change the markup within our theme!

Schemify aims to automate the generation of JSON-LD objects for content within WordPress. Its flexible structure and reasonable defaults enables drop-in support for structured data, regardless of the WordPress theme.

Installation

After uploading Schemify to your WordPress instance, activate it via the Plugins screen in wp-admin.

Registering post type support

Out of the box, Schemify has support for the "post", "page", and "attachment" post types, but you can easily enable it on your own custom post types via add_post_type_support():

/**
 * Add Schemify support to the "book" custom post type.
 */
function mytheme_add_schemify_support() {
	add_post_type_support( 'book', 'schemify' );
}
add_action( 'init', 'mytheme_add_schemify_support' );

Note: You may need to adjust the action ordering to ensure that your add_post_type_support() callback runs after your custom post type is registered. You may alternately choose to add "schemify" to the supports parameter within register_post_type() as the custom post type is registered.

Working with Schemify

Out of the box, Schemify will append a JSON-LD object to the footer of your site's homepage and any single posts that have registered post type support for Schemify.

While that might be plenty for most sites, more complex sites will likely want to tweak the way Schemify works to optimize the way content is represented.

Overriding Schemify properties

When constructing Schemify objects, it's important to remember that a single, top-level Schema can have any number of nested objects. For example, a single post may be represented with a BlogPosting object, which has properties like "author", "publisher", and "image", which are represented by Person, Organization, and ImageObject objects, respectively.

As each object is built, its values are passed through the schemify_get_properties_$schema filter, where $schema is the current object's declared Schema. The result is then passed through the schemify_get_properties filter, which receives the same arguments but is applied to every schema.

The filters are each passed up to four arguments:

(array) $data
The collection of properties assembled for this object.
(string) $schema
The current schema being filtered.
(int) $object_id
The object ID being constructed.
(bool) $is_main
Is this the top-level JSON-LD schema being constructed?

Example

Let's say you've added a custom twitter_url user meta property to user profiles on your site, and wish to inject this information into the "sameAs" property on a Person object:

/**
 * Add a user's "twitter_url" user meta.
 *
 * @param array  $data      The collection of properties assembled for this object.
 * @param string $schema    The current schema being filtered.
 * @param int    $object_id The object ID being constructed.
 */
function add_twitter_url( $data, $schema, $object_id ) {
	if ( $twitter = get_user_meta( $object_id, 'twitter_url', true ) ) {
		$data['sameAs'] = $twitter;
	}

	return $data;
}
add_filter( 'schemify_get_properties_Person', __NAMESPACE__ . '\add_twitter_url', 10, 3 );

Using the nested nature of Schema objects, you can easily use Schemify to manipulate your data objects.

Assigning schemas to custom post types

Unless told otherwise, Schemify will represent all custom post type objects using the Thing Schema, which is often undesirable. Fortunately, there are two ways to override this: via a filter or at post-type registration (via register_post_type()).

Overriding Schemas via filter

Before any object is built, Schemify needs to determine which Schema should be used. This value is then passed through the schemify_schema filter, where it can be modified.

(string) $schema
The schema to use for this object.
(string) $object_type
The type of object being constructed.
(string) $post_type
The object's post type. If $object_type is not equal to 'post' will be an empty string.
(int) $object_id
The post or object ID.

For example, if you wanted to overwrite the default WebPage schema used for the "page" post type to BlogPosting, you could do so with the following:

/**
 * Use "BlogPosting" instead of "WebPage" for single pages.
 *
 * @param string $schema      The schema to use for this post.
 * @param string $object_type The type of object being constructed.
 * @param string $post_type   The object's post type. If $object_type is not equal
 *                            to 'post' this will be an empty string.
 */
function mytheme_treat_pages_as_blog_posts( $schema, $object_type, $post_type ) {
	if ( 'page' === $post_type ) {
		$schema = 'BlogPosting';
	}

	return $schema;
}
add_filter( 'schemify_schema', 'mytheme_treat_pages_as_blog_posts', 10, 3 );

Setting Schemas at post-type registration

You may also set the default Schema for a custom post type at the time of registration, using the schemify_schema property:

register_post_type( 'my-custom-post-type', array(
	// ...
  'schemify_schema' => 'WebPage',
) );

This approach lets you skip having to write filters to override the schema type for custom post types.

schemify's People

Contributors

stevegrunwell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

schemify's Issues

Event Schema support

Even if it's not something that WordPress offers out of the box, Events are common custom post types for WordPress sites; Schemify should come with a default Event schema.

Recursive objects when building schemas for unattached media

When building a schema for attachment.php, the attachedArticle property is a copy of the main schema if the media is unattached (thus wp_get_post_parent_id() returns 0):

{
    "@context": "http:\/\/schema.org",
    "@type": "ImageObject",
    "associatedArticle": {
        "@type": "ImageObject"
    }
}

This results in a "ImageObject is not a known valid target type for the associatedArticle property" validation error.

Feature Request

Ability to copy and past custom schema information in the structured data box on the post admin screen so it can be saved.

Useful in the event the plugin does not pick up all require data

More robust caching

As it stands, the caching in Schemify is pretty basic: the object assembled in Thing::build() (for top-level queries only) gets cached indefinitely. If, for instance, an attachment or a user profile were to be updated, these would not be automatically flushed from the cache.

Schemify needs a more robust system, wherein (for instance), an update to an attachment would flush the cache for any post that uses it as a post thumbnail, or a user updating his/her profile would cause the cache for any posts where he/she is the author to be rebuilt.

Naturally, we want to try to make this a non-blocking process that also won't cause a cache stampede on larger sites.

`get_the_excerpt()` getting called with null instead of post ID

When a search results page gets loaded with title and excerpt for each of the results, the debugger throws an error about a deprecated argument. This is caused by Thing::getDescription() passing false as the $post_id, which triggers a call to _deprecated_argument() inside get_the_excerpt(). I'm not sure what part is actually generating the post ID for the arg, but I've included the call stack here to see if someone else can help identify where it's happening.

Best I can tell, it all starts with append_to_footer(), which tries get_the_ID() but that fails and returns false. This just keeps getting passed down the line; even future calls to check it fail because they're using get_the_ID() which has already decided there isn't one to give.

( ! ) Notice: get_the_excerpt was called with an argument that is <strong>deprecated</strong> since version 2.3.0 with no alternative available. in /app/public/wp-includes/functions.php on line 4026
--
1 | 0.0043 | 364256 | {main}( ) | .../index.php:0
2 | 0.1128 | 365424 | require( '/app/public/wp-blog-header.php' ) | .../index.php:17
3 | 0.3119 | 3633768 | require_once( '/app/public/wp-includes/template-loader.php' ) | .../wp-blog-header.php:19
4 | 0.3129 | 3635152 | include( '/app/public/wp-content/themes/connect-base/search.php' ) | .../template-loader.php:74
5 | 0.3296 | 3697424 | get_footer( ??? ) | .../search.php:65
6 | 0.3296 | 3697800 | locate_template( ???, ???, ??? ) | .../general-template.php:84
7 | 0.3296 | 3697880 | load_template( ???, ??? ) | .../template.php:647
8 | 0.3299 | 3698112 | require_once( '/app/public/wp-content/themes/connect-base/footer.php' ) | .../template.php:688
9 | 0.3299 | 3698112 | wp_footer( ) | .../footer.php:9
10 | 0.3299 | 3698112 | do_action( ???, ??? ) | .../general-template.php:2605
11 | 0.3299 | 3698488 | WP_Hook->do_action( ??? ) | .../plugin.php:453
12 | 0.3299 | 3698488 | WP_Hook->apply_filters( ???, ??? ) | .../class-wp-hook.php:323
13 | 0.3299 | 3699616 | Schemify\Theme\append_to_footer( ??? ) | .../class-wp-hook.php:298
14 | 0.3300 | 3699616 | Schemify\Core\get_json( ???, ??? ) | .../theme.php:91
15 | 0.3300 | 3699616 | Schemify\Core\build_object( ???, ??? ) | .../core.php:132
16 | 0.3302 | 3700544 | Schemify\Schemas\Thing->getProperties( ) | .../core.php:46
17 | 0.3302 | 3700544 | Schemify\Schemas\Thing->build( ???, ??? ) | .../Thing.php:98
18 | 0.3302 | 3701936 | Schemify\Schemas\Thing->getProp( ??? ) | .../Thing.php:183
19 | 0.3302 | 3702256 | Schemify\Schemas\Thing->getDescription( ??? ) | .../Thing.php:135
20 | 102.5890 | 3708744 | get_the_excerpt( ??? ) | .../Thing.php:257
21 | 102.5890 | 3708744 | _deprecated_argument( ???, ???, ??? ) | .../post-template.php:376
22 | 102.5891 | 3709064 | trigger_error ( ??? ) | .../functions.php:4026

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.