Jake Vanderwerf
2026-02-09 2d0b98416804d8a132895720c9c33e6061bd6752
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<?php
namespace JVBase\managers\SEO\schemas\resolvers;
 
use JVBase\managers\SEO\schemas\SchemaDefinition;
use JVBase\managers\SEO\SchemaReferenceBuilder;
 
if (!defined('ABSPATH')) {
    exit;
}
 
/**
 * Resolver for archive/collection page schemas.
 *
 * Handles: CollectionPage, FAQPage, DefinedTermSet, ItemList
 *
 * Auto-enrichment:
 * - Builds `mainEntity` from archive posts or term's associated posts
 * - Adds `numberOfItems` for ItemList
 */
class CollectionPageResolver extends BaseResolver
{
    /** Schema types that should get mainEntity from their posts */
    private const ENTITY_TYPES = ['FAQPage', 'CollectionPage', 'ItemList', 'DefinedTermSet'];
 
    public function getAutoFields(SchemaDefinition $definition): array
    {
        $fields = [];
 
        if (!in_array($definition->schemaType, self::ENTITY_TYPES)) {
            return $fields;
        }
 
        $mainEntity = $this->buildMainEntity($definition);
 
        if (!empty($mainEntity)) {
            $fields['mainEntity'] = $mainEntity;
        }
 
        return $fields;
    }
 
    /**
     * Build mainEntity from the archive's posts.
     */
    private function buildMainEntity(SchemaDefinition $definition): ?array
    {
        // Term archive (e.g., /tattoo-style/american-traditional/)
        if ($definition->objectType === 'term' && $definition->objectId) {
            return $this->buildFromTerm($definition);
        }
 
        // Post type archive (e.g., /tattoos/)
        if ($definition->objectType === 'archive' && $definition->contentType) {
            return SchemaReferenceBuilder::buildFromArchive($definition->contentType);
        }
 
        return null;
    }
 
    /**
     * Build mainEntity from a taxonomy term's associated posts.
     */
    private function buildFromTerm(SchemaDefinition $definition): ?array
    {
        if (!defined('JVB_TAXONOMY') || !$definition->contentType) {
            return null;
        }
 
        $slug = jvbNoBase($definition->contentType);
        $taxConfig = JVB_TAXONOMY[$slug] ?? [];
 
        if (empty($taxConfig['for_content'])) {
            return null;
        }
 
        // Use the first associated post type
        $postType = $taxConfig['for_content'][0];
        $fullType = str_starts_with($postType, BASE) ? $postType : BASE . $postType;
 
        return SchemaReferenceBuilder::buildFromTerm(
            $definition->objectId,
            $fullType,
            10,
            null,
            true
        );
    }
}