| @@ -80,10 +80,19 @@ | ||
| 80 | 80 | return $validation; |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | 83 | $schema_type = $schema['@type']; |
| 84 | - $spec = $this->schema_factory->get_schema_specification($schema_type); | |
| 85 | 84 | |
| 85 | + // A LocalBusiness node carries the subtype the site chose ("Dentist"), | |
| 86 | + // and none of the ~150 subtypes has a spec of its own. They all share | |
| 87 | + // LocalBusiness's requirements, so validate against that rather than | |
| 88 | + // reporting the site's own business type as unsupported. | |
| 89 | + if (\ThinkRank\Config\Local_Business_Types_Config::is_local_business($schema_type)) { | |
| 90 | + $schema_type = 'LocalBusiness'; | |
| 91 | + } | |
| 92 | + | |
| 93 | + $spec =$this->schema_factory->get_schema_specification($schema_type); | |
| 94 | + | |
| 86 | 95 | if (empty($spec)) { |
| 87 | 96 | $validation['errors'][] = "Unsupported schema type: {$schema_type}"; |
| 88 | 97 | $validation['valid'] = false; |
| 89 | 98 | return $validation; |
| @@ -155,8 +164,11 @@ | ||
| 155 | 164 | break; |
| 156 | 165 | case 'FAQPage': |
| 157 | 166 | $validation = $this->validate_faq_schema($schema, $validation); |
| 158 | 167 | break; |
| 168 | + case 'VideoObject': | |
| 169 | + $validation = $this->validate_video_object_schema($schema, $validation); | |
| 170 | + break; | |
| 159 | 171 | } |
| 160 | 172 | |
| 161 | 173 | return $validation; |
| 162 | 174 | } |
| @@ -161,8 +173,35 @@ | ||
| 161 | 173 | return $validation; |
| 162 | 174 | } |
| 163 | 175 | |
| 164 | 176 | /** |
| 177 | + * Validate VideoObject schema specific requirements. | |
| 178 | + * | |
| 179 | + * Google requires name, description, thumbnailUrl and uploadDate. A video is | |
| 180 | + * only playable with a contentUrl or embedUrl, so warn when both are missing. | |
| 181 | + * | |
| 182 | + * @since 1.0.0 | |
| 183 | + * | |
| 184 | + * @param array $schema Schema markup | |
| 185 | + * @param array $validation Current validation results | |
| 186 | + * @return array Updated validation results | |
| 187 | + */ | |
| 188 | + private function validate_video_object_schema(array $schema, array $validation): array { | |
| 189 | + foreach (['name', 'description', 'thumbnailUrl', 'uploadDate'] as $field) { | |
| 190 | + if (empty($schema[$field])) { | |
| 191 | + $validation['errors'][] = sprintf('VideoObject schema requires a %s', $field); | |
| 192 | + $validation['valid'] = false; | |
| 193 | + } | |
| 194 | + } | |
| 195 | + | |
| 196 | + if (empty($schema['contentUrl']) && empty($schema['embedUrl'])) { | |
| 197 | + $validation['warnings'][] = 'VideoObject should include a contentUrl or embedUrl so the video is playable'; | |
| 198 | + } | |
| 199 | + | |
| 200 | + return $validation; | |
| 201 | + } | |
| 202 | + | |
| 203 | + /** | |
| 165 | 204 | * Validate Article schema specific requirements |
| 166 | 205 | * PRESERVED: Exact same method logic from original Schema_Generator |
| 167 | 206 | * |
| 168 | 207 | * @since 1.0.0 |
| @@ -172,9 +211,9 @@ | ||
| 172 | 211 | * @return array Updated validation results |
| 173 | 212 | */ |
| 174 | 213 | private function validate_article_schema(array $schema, array $validation): array { |
| 175 | 214 | // Check headline length (Google recommends under 110 characters) |
| 176 | - if (isset($schema['headline']) && strlen($schema['headline']) > 110) { | |
| 215 | + if (isset($schema['headline']) && is_string($schema['headline']) && strlen($schema['headline']) > 110) { | |
| 177 | 216 | $validation['warnings'][] = 'Headline is longer than 110 characters, may be truncated in search results'; |
| 178 | 217 | } |
| 179 | 218 | |
| 180 | 219 | // Check for image |
| @@ -341,9 +380,9 @@ | ||
| 341 | 380 | if (isset($schema['logo']) && !empty($schema['logo']) && |
| 342 | 381 | (is_string($schema['logo']) || (is_array($schema['logo']) && !empty($schema['logo']['url'])))) { |
| 343 | 382 | $has_logo = true; |
| 344 | 383 | } elseif (isset($schema['publisher']['logo']) && !empty($schema['publisher']['logo']) && |
| 345 | - (is_string($schema['publisher']['logo']) || (is_array($schema['publisher']['logo']) && !empty($schema['publisher']['logo']['url'])))) { | |
| 384 | + (is_string($schema['publisher']['logo']) || (is_array($schema['publisher']['logo']) && !empty($schema['publisher']['logo']['url'])))) { | |
| 346 | 385 | $has_logo = true; |
| 347 | 386 | } |
| 348 | 387 | |
| 349 | 388 | if (!$has_logo) { |
| @@ -376,9 +415,9 @@ | ||
| 376 | 415 | * @return array Updated validation results |
| 377 | 416 | */ |
| 378 | 417 | private function validate_faq_schema(array $schema, array $validation): array { |
| 379 | 418 | // Check minimum number of questions |
| 380 | - if (isset($schema['mainEntity']) && count($schema['mainEntity']) < 2) { | |
| 419 | + if (isset($schema['mainEntity']) && is_array($schema['mainEntity']) && count($schema['mainEntity']) < 2) { | |
| 381 | 420 | $validation['warnings'][] = 'FAQ pages should have at least 2 questions for optimal SEO'; |
| 382 | 421 | } |
| 383 | 422 | |
| 384 | 423 | // Validate question structure |