get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fs_tickets WHERE id = %d", (int) $m[1] ) ); } if ( $surface === 'fluent-boards' && preg_match( '/^task#(\d+)$/', $ref, $m ) ) { return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fbs_tasks WHERE id = %d", (int) $m[1] ) ); } if ( $surface === 'fluentcrm' && preg_match( '/^subscriber#(\d+)$/', $ref, $m ) ) { return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}fc_subscribers WHERE id = %d", (int) $m[1] ) ); } return null; } /** * True if the surface/ref pair points to a real row OR is the * 'derived' surface (legitimately rowless). False otherwise. */ public static function validate( $surface, $ref ) { if ( $surface === 'derived' ) { return $ref !== '' && preg_match( '/^derived#[A-Za-z0-9_\-]+$/', $ref ); } return self::resolve( $surface, $ref ) !== null; } /** Recognized surface vocabulary (closed set). */ public static function known_surfaces() { return [ 'fluent-support', 'fluent-boards', 'fluentcrm', 'derived' ]; } /** * Surface adapter factory. * * Returns the adapter for a given surface, or null if no adapter is * registered yet. Adapters are the safe path to surface data — * external schema knowledge is bounded to the adapter file. Direct * $wpdb access to surface tables in callers is the legacy path; new * code should route through the adapter. * * v0.7.0 shipped one adapter (fluent-boards). Board #18 added * fluent-support to the contract. fluentcrm and derived still resolve * via the legacy paths in resolve() / validate() above; that carryover * stays until those surfaces are ported (deferred Board #18 scope). */ public static function adapter_for( string $surface ): ?SA_Orch_Surface_Adapter { switch ( $surface ) { case 'fluent-boards': return new SA_Orch_Adapter_FluentBoards(); case 'fluent-support': return new SA_Orch_Adapter_FluentSupport(); // Deferred (later Board): // case 'fluentcrm': return new SA_Orch_Adapter_FluentCRM(); // case 'derived': return new SA_Orch_Adapter_Derived(); } return null; } }