sybrew / the-seo-framework

The SEO Framework WordPress plugin.
https://theseoframework.com/
GNU General Public License v3.0
416 stars 47 forks source link

Make The SEO Framework JSON easier to access #682

Open jdevalk opened 2 months ago

jdevalk commented 2 months ago

I currently have a site that is using the SEO Framework and I need access to its JSON output so I can use the data in there in Google Tag Manager to enrich the data we're measuring. Unfortunately, those JSON blocks have no class or id attributes, they're just plain, for example, from your own front page:

<script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https://theseoframework.com/#/schema/WebSite","url":"https://theseoframework.com/","name":"The SEO Framework","description":"Plugins that deliver results","inLanguage":"en-US","potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://theseoframework.com/search/{search_term_string}/"},"query-input":"required name=search_term_string"},"publisher":{"@id":"https://theseoframework.com/#/schema/Organization"}},{"@type":"WebPage","@id":"https://theseoframework.com/","url":"https://theseoframework.com/","name":"The SEO Framework &bull; Plugins that deliver results","description":"You don’t need more clicks that bounce. You need more clicks that pay. Join over 190,000 businesses worldwide and grow using proven SEO tactics.","inLanguage":"en-US","isPartOf":{"@id":"https://theseoframework.com/#/schema/WebSite"},"breadcrumb":{"@type":"BreadcrumbList","@id":"https://theseoframework.com/#/schema/BreadcrumbList","itemListElement":{"@type":"ListItem","position":1,"name":"The SEO Framework"}},"potentialAction":{"@type":"ReadAction","target":"https://theseoframework.com/"},"about":{"@id":"https://theseoframework.com/#/schema/Organization"},"primaryImageOfPage":{"@id":"https://theseoframework.com/#/schema/ImageObject/PrimaryImage"},"image":{"@id":"https://theseoframework.com/#/schema/ImageObject/PrimaryImage","@type":"ImageObject","url":"https://o.theseoframework.com/wp-content/uploads/2019/09/banner-2-4096x2409.png","width":4096,"height":2151}},{"@type":"Organization","@id":"https://theseoframework.com/#/schema/Organization","name":"The SEO Framework","url":"https://theseoframework.com/","sameAs":["https://twitter.com/TheSEOFramework","https://wordpress.org/plugins/autodescription/","https://github.com/sybrew/the-seo-framework","https://woo.com/products/the-seo-framework-premium/"],"logo":{"@type":"ImageObject","url":"https://o.theseoframework.com/wp-content/uploads/2019/11/tsf-space-full.png","contentUrl":"https://o.theseoframework.com/wp-content/uploads/2019/11/tsf-space-full.png","width":512,"height":512}}]}</script>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"LocalBusiness","image":"https://o.theseoframework.com/wp-content/uploads/2017/08/cbcalphen.jpg","name":"The SEO Framework","address":{"@type":"PostalAddress","streetAddress":"Leidse Schouw 2","addressLocality":"Alphen aan den Rijn","addressRegion":"ZH","postalCode":"2408 AE","addressCountry":"NL"},"geo":{"@type":"GeoCoordinates","latitude":52.1281883,"longitude":4.627419},"url":"https://theseoframework.com/","priceRange":"$0-$324","openingHoursSpecification":[{"@type":"OpeningHoursSpecification","dayOfWeek":["Monday","Tuesday","Wednesday","Thursday","Friday"],"opens":"08:30","closes":"17:00"},{"@type":"OpeningHoursSpecification","dayOfWeek":["Saturday","Sunday"],"opens":"0:00","closes":"0:00"}]}</script>

Could you give those script tags an identifiable class like tsf-website-schema and tsf-localbusiness-schema or something similar? That way I could grab them with JavaScript a lot more easily.

sybrew commented 2 months ago

Howdy!

I don't think setting an ID to the script is helpful other than for targeting TSF's performance specifically. So, it's a "maybe later" for me.

How do Schema.org's Validator, Google, Pinterest, etc. pull the scripts? I think it's better to mimic their behavior. A script like this ought to do the trick, and it'll also grab anything from other plugins, like WooCommerce, as well:

function getJsonFromPage() {
    const data = {};

    document.querySelectorAll( 'script[type="application/ld+json"]' ).forEach(
        script => {
            try {
                const json = JSON.parse( script.innerText );

                if ( json ) {
                    // Assign to 'WebSite' it hasn't a type or ID.
                    const key = json['@id'] || json['@type'] || 'WebSite';

                    data[ key ] ||= [];
                    data[ key ].push( json );
                }
            } catch ( e ) {
                console.error( 'JSON-LD parsing error:', e );
            }
        }
    );

    return data;
}

Call getJsonFromPage() to get all the data sorted by type. I chose to put everything under 'WebSite' for when an identifier or type is missing, but your use case might require an alteration herein.

But if you genuinely require to query the script specifically, you can use this filter to add an id-attribute like tsf-website-schema to the script element already:

add_filter(
    'the_seo_framework_meta_render_data',
    function ( $data ) {
        if ( isset( $data['schema:graph'] ) ) {
            $data['schema:graph']['attributes']['id'] = 'tsf-website-schema';
        }
        return $data;
    },
);

Setting attributes like this is new with TSF v5.0+. It won't work for Extension Manager's scripts (like Article and LocalBusiness), as that is still in a transitionary phase between TSF v4.x and v5.x. But in a future update, I'll merge them with TSF's Schema.org graph output.

Let me know if this is helpful. Have a lovely day 😄