PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.10.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.10.0
2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 All 51 releases
thinkrank / includes / seo / class-schema-factory.php

class-schema-factory.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 2.10.0, at includes/seo/class-schema-factory.php

277 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Schema Factory Class
4 *
5 * Handles schema type instantiation, configuration, and registry management.
6 * Extracted from Schema_Generator to follow Single Responsibility Principle.
7 * Maintains exact same functionality and data structures as original implementation.
8 *
9 * @package ThinkRank\SEO
10 * @since 1.0.0
11 */
12
13 declare(strict_types=1);
14
15 namespace ThinkRank\SEO;
16
17 // Prevent direct access
18 if (!defined('ABSPATH')) {
19 exit;
20 }
21
22 /**
23 * Schema Factory Class
24 *
25 * Manages schema type registry, instantiation, and configuration.
26 * Preserves all existing schema type definitions and specifications.
27 *
28 * @since 1.0.0
29 */
30 class Schema_Factory {
31
32 /**
33 * Supported schema types with their Schema.org specifications
34 * PRESERVED: Exact same data structure from original Schema_Generator
35 *
36 * @since 1.0.0
37 * @var array
38 */
39 private array $supported_schema_types = [
40 'Article' => [
41 'required' => ['@type', 'headline', 'author', 'datePublished'],
42 'recommended' => ['description', 'image', 'dateModified', 'publisher', 'mainEntityOfPage'],
43 'optional' => ['wordCount', 'keywords', 'articleSection', 'articleBody']
44 ],
45 'BlogPosting' => [
46 'required' => ['@type', 'headline', 'author', 'datePublished'],
47 'recommended' => ['description', 'image', 'dateModified', 'publisher', 'mainEntityOfPage'],
48 'optional' => ['wordCount', 'keywords', 'blogPost', 'comment']
49 ],
50 'TechnicalArticle' => [
51 'required' => ['@type', 'headline', 'author', 'datePublished'],
52 'recommended' => ['description', 'image', 'dateModified', 'publisher', 'mainEntityOfPage'],
53 'optional' => ['wordCount', 'keywords', 'articleSection', 'articleBody', 'dependencies', 'proficiencyLevel']
54 ],
55 'NewsArticle' => [
56 'required' => ['@type', 'headline', 'author', 'datePublished'],
57 'recommended' => ['description', 'image', 'dateModified', 'publisher', 'mainEntityOfPage'],
58 'optional' => ['wordCount', 'keywords', 'articleSection', 'articleBody', 'dateline', 'printColumn', 'printEdition', 'printPage', 'printSection']
59 ],
60 'ScholarlyArticle' => [
61 'required' => ['@type', 'headline', 'author', 'datePublished'],
62 'recommended' => ['description', 'image', 'dateModified', 'publisher', 'mainEntityOfPage'],
63 'optional' => ['wordCount', 'keywords', 'articleSection', 'articleBody', 'citation', 'pageStart', 'pageEnd', 'pagination']
64 ],
65 'Report' => [
66 'required' => ['@type', 'headline', 'author', 'datePublished'],
67 'recommended' => ['description', 'image', 'dateModified', 'publisher', 'mainEntityOfPage'],
68 'optional' => ['wordCount', 'keywords', 'articleSection', 'articleBody', 'reportNumber']
69 ],
70 'Product' => [
71 'required' => ['@type', 'name', 'description'],
72 'recommended' => ['image', 'offers', 'brand', 'sku', 'gtin', 'review', 'aggregateRating'],
73 'optional' => ['category', 'manufacturer', 'mpn', 'productID']
74 ],
75 'Organization' => [
76 'required' => ['@type', 'name', 'url'],
77 'recommended' => ['logo', 'contactPoint', 'sameAs', 'address'],
78 'optional' => ['founder', 'foundingDate', 'description']
79 ],
80 'WebSite' => [
81 'required' => ['@type', 'name', 'url'],
82 'recommended' => ['description', 'author', 'publisher', 'potentialAction'],
83 'optional' => ['sameAs', 'inLanguage']
84 ],
85 'FAQPage' => [
86 'required' => ['@type', 'mainEntity'],
87 'recommended' => ['about', 'author'],
88 'optional' => ['name', 'description', 'url', 'dateModified']
89 ],
90 // The WebPage family. Schema_Builder has had a `case 'WebPage'` branch
91 // all along, but nothing here declared the type — so build_schema()
92 // rejected it before the switch was ever reached, and that branch was
93 // dead code. A customer asking for AboutPage and ContactPage is what
94 // surfaced it (#624).
95 //
96 // Only the four the output side can actually emit as themselves.
97 // ItemPage, QAPage and CollectionPage appear in
98 // Schema_Graph::PAGE_LEVEL_TYPES but have no branch in
99 // Global_SEO_Schema_Output::generate_schema(), so they fall to its
100 // `default` arm and publish plain WebPage — offering a type that
101 // silently emits a different one is worse than not offering it.
102 'WebPage' => [
103 'required' => ['@type', 'name'],
104 'recommended' => ['description', 'url', 'datePublished', 'dateModified'],
105 'optional' => ['image', 'breadcrumb', 'primaryImageOfPage', 'about', 'mainEntity']
106 ],
107 'AboutPage' => [
108 'required' => ['@type', 'name'],
109 'recommended' => ['description', 'url', 'datePublished', 'dateModified'],
110 'optional' => ['image', 'breadcrumb', 'primaryImageOfPage', 'about', 'mainEntity']
111 ],
112 'ContactPage' => [
113 'required' => ['@type', 'name'],
114 'recommended' => ['description', 'url', 'datePublished', 'dateModified'],
115 'optional' => ['image', 'breadcrumb', 'primaryImageOfPage', 'about', 'mainEntity']
116 ],
117 'ProfilePage' => [
118 'required' => ['@type', 'name'],
119 'recommended' => ['description', 'url', 'datePublished', 'dateModified'],
120 'optional' => ['image', 'breadcrumb', 'primaryImageOfPage', 'about', 'mainEntity']
121 ],
122 'LocalBusiness' => [
123 'required' => ['@type', 'name', 'address'],
124 'recommended' => ['telephone', 'url', 'openingHours', 'geo', 'priceRange'],
125 'optional' => ['description', 'logo', 'contactPoint', 'sameAs', 'paymentAccepted', 'currenciesAccepted']
126 ],
127 'Person' => [
128 'required' => ['@type', 'name'],
129 'recommended' => ['url', 'image'],
130 'optional' => ['jobTitle', 'worksFor', 'sameAs', 'description']
131 ],
132 'SoftwareApplication' => [
133 'required' => ['@type', 'name', 'applicationCategory'],
134 'recommended' => ['description', 'url', 'offers', 'creator', 'features', 'aggregateRating'],
135 'optional' => ['alternateName', 'version', 'operatingSystem', 'fileSize', 'license', 'downloadUrl', 'screenshot']
136 ],
137 'Event' => [
138 'required' => ['@type', 'name', 'startDate', 'location'],
139 'recommended' => ['endDate', 'description', 'image', 'offers', 'performer'],
140 'optional' => ['eventStatus', 'eventAttendanceMode', 'url', 'organizer']
141 ],
142 'Review' => [
143 'required' => ['@type', 'itemReviewed', 'reviewRating', 'author'],
144 'recommended' => ['reviewBody', 'datePublished', 'publisher'],
145 'optional' => ['name', 'url']
146 ],
147 'HowTo' => [
148 'required' => ['@type', 'name', 'step'],
149 'recommended' => ['image', 'totalTime', 'estimatedCost', 'tool', 'supply'],
150 'optional' => ['description', 'prepTime', 'difficulty', 'yield', 'video']
151 ],
152 'VideoObject' => [
153 'required' => ['@type', 'name', 'description', 'thumbnailUrl', 'uploadDate'],
154 'recommended' => ['contentUrl', 'embedUrl', 'duration'],
155 'optional' => ['interactionStatistic', 'expires', 'regionsAllowed']
156 ]
157 ];
158
159 /**
160 * Schema.org context URL
161 * PRESERVED: Exact same value from original Schema_Generator
162 *
163 * @since 1.0.0
164 * @var string
165 */
166 private string $schema_context = 'https://schema.org';
167
168 /**
169 * Get all supported schema types
170 * Simplified: Context filtering moved to config files
171 *
172 * @since 1.0.0
173 *
174 * @return array Array of all supported schema types
175 */
176 public function get_all_supported_types(): array {
177 return array_keys($this->supported_schema_types);
178 }
179
180 /**
181 * Check if schema type is supported
182 * PRESERVED: Exact same method logic from original Schema_Generator
183 *
184 * @since 1.0.0
185 *
186 * @param string $schema_type Schema type to check
187 * @return bool True if supported, false otherwise
188 */
189 public function is_supported_schema_type(string $schema_type): bool {
190 return isset($this->supported_schema_types[$schema_type]);
191 }
192
193 /**
194 * Get schema type specification
195 * PRESERVED: Returns exact same data structure as original Schema_Generator
196 *
197 * @since 1.0.0
198 *
199 * @param string $schema_type Schema type
200 * @return array Schema type specification
201 */
202 public function get_schema_specification(string $schema_type): array {
203 return $this->supported_schema_types[$schema_type] ?? [];
204 }
205
206 /**
207 * Normalize schema type name to proper Schema.org format
208 *
209 * @since 1.0.0
210 *
211 * @param string $schema_type Schema type to normalize
212 * @return string Normalized schema type
213 */
214 public function normalize_schema_type(string $schema_type): string {
215 // Handle Schema.org camelCase requirements
216 $schema_cases = [
217 'localbusiness' => 'LocalBusiness',
218 'softwareapplication' => 'SoftwareApplication',
219 'blogposting' => 'BlogPosting',
220 'technicalarticle' => 'TechnicalArticle',
221 'newsarticle' => 'NewsArticle',
222 'scholarlyarticle' => 'ScholarlyArticle',
223 'faqpage' => 'FAQPage',
224 'howto' => 'HowTo',
225 'videoobject' => 'VideoObject'
226 ];
227
228 $key = strtolower($schema_type);
229 return $schema_cases[$key] ?? ucfirst($schema_type);
230 }
231
232 /**
233 * Create base schema structure
234 * PRESERVED: Exact same method logic from original Schema_Generator
235 *
236 * @since 1.0.0
237 *
238 * @param string $schema_type Schema type
239 * @return array Base schema structure
240 */
241 public function create_base_schema(string $schema_type): array {
242 return [
243 '@context' => $this->schema_context,
244 '@type' => $schema_type
245 ];
246 }
247
248 // Removed error schema creation - moved to Schema_Builder where it belongs
249
250 // Removed auto-detection logic - over-engineered and rarely used
251 // Schema types are now explicitly selected by users
252
253 /**
254 * Get schema context URL
255 * PRESERVED: Returns exact same value as original Schema_Generator
256 *
257 * @since 1.0.0
258 *
259 * @return string Schema context URL
260 */
261 public function get_schema_context(): string {
262 return $this->schema_context;
263 }
264
265 /**
266 * Get all supported schema types
267 * PRESERVED: Returns exact same data structure as original Schema_Generator
268 *
269 * @since 1.0.0
270 *
271 * @return array All supported schema types with specifications
272 */
273 public function get_all_schema_types(): array {
274 return $this->supported_schema_types;
275 }
276 }
277