236d37976c
DbCompat (app/Support/DbCompat.php) : - like() → ilike (pgsql) ou like (mysql) - jsonRegexRaw() → data::text ~* ? (pgsql) ou CAST(data AS CHAR) REGEXP ? (mysql) - ftsRaw() → to_tsvector/plainto_tsquery (pgsql) ou null/fallback LIKE (mysql) - generatedJsonCol() → syntaxe colonne générée JSON selon le SGBD - generatedJsonNestedCol() → idem pour champs imbriqués Migrations : - create_releves_table : JSON/JSONB selon SGBD, colonnes générées adaptées, index GIN uniquement pour PostgreSQL Controllers : - LieuController (search + index) : ilike → DbCompat::like() - Admin\UserController (index) : ilike → DbCompat::like() - RechercheController : FTS + regex → DbCompat, fallback LIKE MySQL - ExportController : regex → DbCompat::jsonRegexRaw() UpdateService : - backupDatabase() : pg_dump (pgsql) ou mysqldump (mysql) - restoreBackup() : psql (pgsql) ou mysql (mysql) Docker : - docker-compose.yml : suppression Redis (plus requis) - docker-compose.mysql.yml : nouveau fichier pour dev MySQL - docker-compose.prod.yml : suppression Redis, DB_IMAGE configurable, CACHE_STORE/SESSION_DRIVER/QUEUE_CONNECTION → database par défaut .env.example : - DB_PORT commenté avec les deux valeurs (5432/3306) - CACHE_STORE et QUEUE_CONNECTION commentés (database par défaut) - Redis marqué optionnel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
75 lines
2.2 KiB
PHP
75 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/**
|
|
* Helpers pour les différences de syntaxe SQL entre PostgreSQL et MySQL.
|
|
*/
|
|
class DbCompat
|
|
{
|
|
public static function isPgsql(): bool
|
|
{
|
|
return DB::connection()->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'))";
|
|
}
|
|
|
|
/** 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}'))";
|
|
}
|
|
}
|