Schema-related post meta controls JSON-LD output for individual posts.
| Meta Key | Type | Description |
|---|---|---|
_rankforge_schema_type | string | Manual schema type override (e.g. 'Article', 'FAQ', 'Product'). When set, generates the corresponding JSON-LD automatically. |
_rankforge_manual_schema | string (JSON) | Full manual JSON-LD schema object as a JSON string. Bypasses all auto-generation. |
_rankforge_ai_schema | array (serialized) | AI-generated schema cache containing detected_types, json_ld, generated_at, and source keys. |
// ----- Reading schema meta -----
$post_id = 42;
// Read the manual schema type override
$schema_type = get_post_meta( $post_id, '_rankforge_schema_type', true );
// Read the full manual schema JSON
$manual_json = get_post_meta( $post_id, '_rankforge_manual_schema', true );
if ( $manual_json ) {
$schema_array = json_decode( $manual_json, true );
}
// Read AI-detected schema (returns unserialized array)
$ai_schema = get_post_meta( $post_id, '_rankforge_ai_schema', true );
if ( $ai_schema ) {
$types = $ai_schema['detected_types']; // e.g. ['Article', 'FAQ']
}// ----- Writing schema meta -----
$post_id = 42;
// Set a simple schema type override (Rank Forge generates the JSON-LD)
update_post_meta( $post_id, '_rankforge_schema_type', 'FAQ' );
// Set a complete manual schema (takes full control of JSON-LD output)
$faq_schema = json_encode( [
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => [
[
'@type' => 'Question',
'name' => 'What is Rank Forge?',
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => 'Rank Forge is a WordPress SEO plugin providing comprehensive on-page optimization.',
],
],
],
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
update_post_meta( $post_id, '_rankforge_manual_schema', $faq_schema );
// Clear AI schema cache (forces re-detection on next analysis)
delete_post_meta( $post_id, '_rankforge_ai_schema' );
// Remove manual schema type to fall back to auto-detection
delete_post_meta( $post_id, '_rankforge_schema_type' );
delete_post_meta( $post_id, '_rankforge_manual_schema' );Additive emit cascade (April 2026 / R3 F4)
RANKFORGE_Schema::output_schema() is additive, not priority-wins. Setting _rankforge_schema_type to 'FAQ' on a post does not suppress the post-type-default Article schema — instead the post emits [Article, FAQPage, BreadcrumbList]. The cascade is:
- Auto-detected default for the post type —
Articlefor posts,Productforproduct,WebPageeverywhere else (auto_detect_type()). Skipped when_rankforge_schema_typeresolves to the same@type, since step (3) would generate it anyway. - AI-cached blocks from
_rankforge_ai_schema['json_ld'](a multi-block array — each entry is emitted independently, deduplicated by@type). - Manual
_rankforge_schema_typeselection. If the type-specific generator returnsnull(e.g. HowTo without imperative steps — seeextract_howto_steps()filter), the block is silently dropped without affecting other layers. - Auto-FAQ — emitted when
generate_faq($post_id, $post, only_questions=true)finds 2+?-terminated H2/H3 + paragraph pairs. Skipped ifFAQPagewas already emitted by an earlier layer. BreadcrumbListon every page except the front page, when the trail has at least two items, andOrganizationon the front page, unless Local SEO is turned on and has a business name: then Local SEO emits the business entity instead.
The whole list is deduplicated by @type. Full manual JSON-LD via _rankforge_manual_schema is the one “exclusive” path: when it is set (a stub that names nothing but a type, or the JSON the Schema Builder form writes, takes the cascade above), none of the cascade is emitted for the post. The last item of the list is written after that branch, so it still follows: BreadcrumbList on every page except the front page, when the trail has at least two items, and Organization on the front page, unless Local SEO emits the business entity.
#### Filters and actions
// Opt out of auto-FAQ (returns null to suppress the FAQPage block).
add_filter( 'rankforge_auto_faq_schema', function( $faq, $post_id ) {
if ( get_post_type( $post_id ) === 'tutorial' ) {
return null; // tutorials shouldn't auto-trigger FAQPage
}
return $faq;
}, 10, 2 );
// Inspect the final list of emitted @types per request — useful for
// debug / analytics / extra dedup. Fires AFTER all schemas are echoed.
add_action( 'rankforge_schemas_emitted', function( $emitted_types, $post_id ) {
error_log( "Schemas on post {$post_id}: " . implode( ',', $emitted_types ) );
}, 10, 2 );The rankforge_schemas_emitted action is informational only — schemas are already on the page when it fires. To pre-empt or modify a block, use the per-type generator filters (e.g. rankforge_auto_faq_schema) or hook earlier in wp_head (priority < 5) and call remove_action( 'wp_head', [ RANKFORGE_Schema::instance(), 'output_schema' ] ) if you need to fully replace the emit.
—