getDriverName() === 'pgsql'; } /** Opérateur LIKE insensible à la casse */ public static function like(): string { return self::isPgsql() ? 'ilike' : 'like'; } /** Caste une colonne JSON en texte brut */ public static function jsonToText(string $column): string { return self::isPgsql() ? "{$column}::text" : "CAST({$column} AS CHAR)"; } /** * Condition de correspondance regex (insensible à la casse) sur une colonne JSON. * Retourne la chaîne à passer à whereRaw() avec un seul binding `?`. */ public static function jsonRegexRaw(string $column = 'data'): string { return self::isPgsql() ? "{$column}::text ~* ?" : "CAST({$column} AS CHAR) REGEXP ?"; } /** * Condition de recherche plein-texte. * Retourne null pour MySQL (le caller doit prévoir un fallback LIKE). */ public static function ftsRaw(): ?string { return self::isPgsql() ? "to_tsvector('french', data::text) @@ plainto_tsquery('french', ?)" : null; } /** Syntaxe de la colonne générée stockée pour extraire un champ JSON de premier niveau */ public static function generatedJsonCol(string $jsonKey): string { return self::isPgsql() ? "data->>'$jsonKey'" : "JSON_UNQUOTE(JSON_EXTRACT(data, '$.$jsonKey'))"; } /** * Fragment ORDER BY "colonne ASC, nulls en dernier". * Usage : ->orderByRaw(DbCompat::nullsLast('nom')) */ public static function nullsLast(string $column, string $direction = 'ASC'): string { $dir = strtoupper($direction); return self::isPgsql() ? "{$column} {$dir} NULLS LAST" : "({$column} IS NULL) ASC, {$column} {$dir}"; } /** Syntaxe de la colonne générée stockée pour un champ JSON imbriqué (ex: date_evenement.valeur) */ public static function generatedJsonNestedCol(string $jsonPath): string { if (self::isPgsql()) { // data->'date_evenement'->>'valeur' $parts = explode('.', $jsonPath); $last = array_pop($parts); $chain = implode('', array_map(fn ($p) => "->'{$p}'", $parts)); return "data{$chain}->>'{$last}'"; } // MySQL: $.date_evenement.valeur return "JSON_UNQUOTE(JSON_EXTRACT(data, '$.{$jsonPath}'))"; } }