) * * @package Schema_AI * @since 1.1.5 */ if ( ! defined( 'ABSPATH' ) ) { exit; } class Schema_AI_Site_Type { /** * Full taxonomy: slug → [ label, homepage entity, is_local, group ] * * @var array */ private static $types = array( // ── Local & Physical ────────────────────────────────────────────── 'home_services' => array( 'label' => 'Home Services', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'restaurant_food' => array( 'label' => 'Restaurant & Food', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'health_medical' => array( 'label' => 'Health & Medical', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'legal_financial' => array( 'label' => 'Legal & Financial', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'beauty_wellness' => array( 'label' => 'Beauty & Wellness', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'automotive' => array( 'label' => 'Automotive', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'real_estate' => array( 'label' => 'Real Estate', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'retail_store' => array( 'label' => 'Retail Store', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'accommodation' => array( 'label' => 'Accommodation & Travel', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'entertainment' => array( 'label' => 'Entertainment', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'sports_recreation' => array( 'label' => 'Sports & Recreation', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'education_training' => array( 'label' => 'Education & Training', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'pet_services' => array( 'label' => 'Pet Services', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'professional_services' => array( 'label' => 'Professional Services', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), 'religious_org' => array( 'label' => 'Religious Organization', 'entity' => 'PlaceOfWorship', 'local' => true, 'group' => 'local' ), 'childcare' => array( 'label' => 'Childcare', 'entity' => 'ChildCare', 'local' => true, 'group' => 'local' ), 'general_local_business' => array( 'label' => 'General Local Business', 'entity' => 'LocalBusiness', 'local' => true, 'group' => 'local' ), // ── Online & Digital ────────────────────────────────────────────── 'online_store' => array( 'label' => 'Online Store / E-commerce', 'entity' => 'Organization', 'local' => false, 'group' => 'online' ), 'software_saas' => array( 'label' => 'Software / SaaS / App', 'entity' => 'Organization', 'local' => false, 'group' => 'online' ), 'blog_content' => array( 'label' => 'Blog / Content Site', 'entity' => 'Organization', 'local' => false, 'group' => 'online' ), 'news_media' => array( 'label' => 'News / Magazine / Media', 'entity' => 'NewsMediaOrganization', 'local' => false, 'group' => 'online' ), 'course_education' => array( 'label' => 'Course / Online Education', 'entity' => 'EducationalOrganization', 'local' => false, 'group' => 'online' ), 'podcast' => array( 'label' => 'Podcast', 'entity' => 'Organization', 'local' => false, 'group' => 'online' ), 'portfolio_agency' => array( 'label' => 'Portfolio / Creative Agency', 'entity' => 'Organization', 'local' => false, 'group' => 'online' ), // ── Other ───────────────────────────────────────────────────────── 'nonprofit' => array( 'label' => 'Nonprofit / Charity', 'entity' => 'Organization', 'local' => false, 'group' => 'other' ), 'community_forum' => array( 'label' => 'Community / Forum', 'entity' => 'Organization', 'local' => false, 'group' => 'other' ), 'government' => array( 'label' => 'Government', 'entity' => 'GovernmentOrganization', 'local' => false, 'group' => 'other' ), 'organizations_groups' => array( 'label' => 'Other Organization / Group', 'entity' => 'Organization', 'local' => false, 'group' => 'other' ), ); /** * Human labels for the three optgroup groups. * * @var array */ private static $groups = array( 'local' => 'Local & Physical Business', 'online' => 'Online & Digital', 'other' => 'Other', ); /** * Map from site type slug to the decision group key used by the engine. * * @var array */ private static $decision_group_map = array( 'home_services' => 'local_business', 'restaurant_food' => 'local_business', 'health_medical' => 'local_business', 'legal_financial' => 'local_business', 'beauty_wellness' => 'local_business', 'automotive' => 'local_business', 'real_estate' => 'local_business', 'retail_store' => 'local_business', 'accommodation' => 'local_business', 'entertainment' => 'local_business', 'sports_recreation' => 'local_business', 'education_training' => 'local_business', 'pet_services' => 'local_business', 'professional_services' => 'local_business', 'religious_org' => 'local_business', 'childcare' => 'local_business', 'general_local_business' => 'local_business', 'online_store' => 'non_local', 'software_saas' => 'software_saas', 'blog_content' => 'blog_content', 'news_media' => 'news_media', 'course_education' => 'course_education', 'podcast' => 'podcast', 'portfolio_agency' => 'portfolio_agency', 'nonprofit' => 'non_local', 'community_forum' => 'non_local', 'government' => 'government', 'organizations_groups' => 'non_local', ); /** * Get the current site type slug from settings. * * @return string Site type slug, or empty string if not configured. */ public static function get_site_type() { $settings = get_option( 'schema_ai_settings', array() ); return isset( $settings['site_type'] ) ? $settings['site_type'] : ''; } /** * Whether the current site is a local/physical business. * * Defaults to true when no site type is set — preserves existing behaviour * for users who update without configuring the new setting. * * @return bool */ public static function is_local_business() { $type = self::get_site_type(); if ( empty( $type ) || ! isset( self::$types[ $type ] ) ) { return true; } return (bool) self::$types[ $type ]['local']; } /** * Get the schema.org entity type for the homepage. * * For local business types this always returns 'LocalBusiness'. The actual * subtype (Plumber, Dentist, etc.) is resolved by the LocalBusiness schema * builder from the business_subtype setting. * * @return string Schema.org type string. */ public static function get_homepage_entity() { $type = self::get_site_type(); if ( empty( $type ) || ! isset( self::$types[ $type ] ) ) { return 'LocalBusiness'; } return self::$types[ $type ]['entity']; } /** * Get the URI fragment for the primary site-wide entity @id. * * Local businesses → '#localbusiness'; everything else → '#organization'. * * @return string URI fragment, e.g. '#localbusiness'. */ public static function get_primary_id_fragment() { return self::is_local_business() ? '#localbusiness' : '#organization'; } /** * Get the decision group key for the current site type. * * Used by Schema_AI_Schema_Decision_Engine to select the correct override * group from the 2D matrix. * * @return string Decision group key. */ public static function get_decision_group() { $type = self::get_site_type(); if ( ! empty( $type ) && isset( self::$decision_group_map[ $type ] ) ) { return self::$decision_group_map[ $type ]; } return 'local_business'; } /** * Check whether a site type slug is valid. * * @param string $slug The site type slug to check. * @return bool */ public static function is_valid( $slug ) { return isset( self::$types[ $slug ] ); } /** * Get all valid site type slugs. * * @return string[] */ public static function get_slugs() { return array_keys( self::$types ); } /** * Get grouped options for the settings UI