settings = $settings; $this->resolver = $resolver; $this->trail = $trail; } /** * The JSON-LD document of a page. * * @since 1.0.0 * * @param Context $context Page context. * @return array Empty when the page gets no markup. */ public function build( Context $context ): array { if ( in_array( $context->type(), array( Context::NOT_FOUND, Context::OTHER ), true ) ) { return array(); } $post = $context->post(); if ( null !== $post ) { /** * Filters whether a post gets structured data at all. * * @since 1.0.0 * * @param bool $enabled Whether markup is printed. Default true. * @param int $post_id Post id. */ if ( ! apply_filters( 'elevenrank_schema_enabled_for_post', true, $post->ID ) ) { return array(); } } $c = new Node_Context( $context, $this->settings, $this->resolver ); $graph = new Graph(); $this->emit( $graph, Entity_Node::build( $c ), $c ); $this->emit( $graph, Entity_Node::logo( $c ), $c ); $this->emit( $graph, Website_Node::build( $c ), $c ); $image = $this->primary_image( $c ); $breadcrumb = $this->settings->value( 'schema', 'include_breadcrumbs' ) ? Breadcrumb_Node::build( $c, $this->trail->items( $context ) ) : null; $this->emit( $graph, Webpage_Node::build( $c, null !== $breadcrumb, null !== $image ), $c ); $this->emit( $graph, $image, $c ); $this->emit( $graph, $breadcrumb, $c ); if ( null !== $context->user() ) { $this->emit( $graph, Author_Node::build( $context->user()->ID ), $c ); } if ( null !== $post && Context::SINGULAR === $context->type() ) { $this->add_content( $graph, $post, $c ); } $document = array( '@context' => 'https://schema.org', '@graph' => $graph->nodes(), ); /** * Filters the whole graph before it is printed. * * @since 1.0.0 * * @param array $document The document: `@context` and `@graph`. * @param Context $context Page context. */ $filtered = apply_filters( 'elevenrank_schema_graph', $document, $context ); return is_array( $filtered ) && ! empty( $filtered['@graph'] ) ? $filtered : array(); } /** * Adds the node of the post's chosen type. * * @since 1.0.0 * * @param Graph $graph Graph. * @param WP_Post $post Post. * @param Node_Context $c Page information. * @return void */ private function add_content( Graph $graph, WP_Post $post, Node_Context $c ): void { $type = Schema_Fields::type_for( $post, $this->settings ); if ( ! Type_Definitions::is_type( $type ) ) { return; } $builder = $this->content_builder( $type ); $node = $builder->build( $type, Schema_Fields::for_post( $post->ID )['fields'], $post, $c, $graph ); $this->emit( $graph, $node, $c ); } /** * The class that builds a content type. * * @since 1.0.0 * * @param string $type One of {@see Type_Definitions::TYPES}. * @return Content_Node */ private function content_builder( string $type ): Content_Node { switch ( $type ) { case 'Product': return new Product_Node(); case 'Recipe': return new Recipe_Node(); case 'Event': return new Event_Node(); case 'VideoObject': return new Video_Node(); case 'Review': return new Review_Node(); } return new Article_Node(); } /** * The primary image of a post: its featured image, or the image itself for an attachment page. * * @since 1.0.0 * * @param Node_Context $c Page information. * @return array|null */ private function primary_image( Node_Context $c ): ?array { $post = $c->post(); if ( null === $post ) { return null; } $id = Context::ATTACHMENT === $c->context->type() ? $post->ID : (int) get_post_thumbnail_id( $post ); return Image_Node::build( $id, $c->id( 'primaryimage' ) ); } /** * Lets the filter for the node's type change it, then adds it to the graph. * * @since 1.0.0 * * @param Graph $graph Graph. * @param array|null $node Node. * @param Node_Context $c Page information. * @return void */ private function emit( Graph $graph, ?array $node, Node_Context $c ): void { if ( null === $node ) { return; } $type = strtolower( preg_replace( '/[^A-Za-z]/', '', (string) ( $node['@type'] ?? '' ) ) ?? '' ); if ( '' !== $type ) { /** * Filters one node of the graph. The part after `elevenrank_schema_` is the node's `@type` in * lower case: `article`, `product`, `webpage`, `breadcrumblist`, `organization` and so on. Return * null to leave the node out. * * @since 1.0.0 * * @param array|null $node The node. * @param Context $context Page context. */ $node = apply_filters( "elevenrank_schema_{$type}", $node, $c->context ); } $graph->add( is_array( $node ) ? $node : null ); } }