Skip to content

Instantly share code, notes, and snippets.

@tcharlss
Last active November 28, 2023 21:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tcharlss/ff8d7de690dbcf5ab3e8eb48ef86b423 to your computer and use it in GitHub Desktop.
Save tcharlss/ff8d7de690dbcf5ab3e8eb48ef86b423 to your computer and use it in GitHub Desktop.
SPIP : critère {initiale}
<?php
/**
* Calcul du critère `initiale`
*
* NE FONCTIONNE QU'AVEC MYSQL
*
* Extrait l'initiale d'un champ ou d'une expression.
* Ajoute l'alias `initiale` dans la boucle, ce qui permet de faire un GROUP BY `{fusion initiale}` ou un ORDER BY `{par initiale}`
* Voir aussi le pipeline pre_boucle à ce sujet.
*
* @example
* ```
* {initiale}
* {initiale?}
* {initiale a}
* {initiale a, prenom}
* {initiale a, CONCAT(prenom,nom)}
* {initiale, CONCAT(prenom,nom)}
* ```
*
* @critere
*
* @param string $idb Identifiant de la boucle
* @param array $boucles AST du squelette
* @param Critere $crit Paramètres du critère dans cette boucle
* @return void
*/
function critere_initiale_dist($idb, &$boucles, $crit) {
$boucle = &$boucles[$idb];
$id_table = $boucle->id_table;
$_id_table = "'$id_table'";
$not = ($crit->not ? 'NOT ' : '');
$condition = $crit->cond;
// Initiale : celle passée en param, sinon celle dans l'env si opérateur conditionnel
if (isset($crit->param[0])) {
$_initiale = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
} else {
$_initiale = ($condition ? "(isset(\$Pile[0]['initiale']) ? \$Pile[0]['initiale'] : '')" : "''");
}
// Nom du ou des champs d'où extraire l'initiale : celui passé en param, sinon `nom`
// Possible de mettre `CONCAT(prenom,nom)` si on veut
if (isset($crit->param[1])) {
$_champ = calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent);
} else {
$_champ = "'nom'";
}
// SELECT : ajout de l'alias `initiale` pour permettre de trier ou de grouper dessus
$boucle->hash .= '$champ_initiale = ' . $_champ . ';';
$boucle->select[] = '" . calculer_sql_initiale($champ_initiale, ' . $_id_table . ') . " AS initiale" . "';
// WHERE : kamoulox
$boucle->where[] = "(($_initiale) ? '$not' . '(' . calculer_sql_initiale($_champ, $_id_table) . ' = ' . sql_quote(" . $_initiale . ") . ')' : '')";
}
/**
* Retourne l'expression SQL qui permet d'extraire une initiale
*
* @param string $champ
* Nom ou expression où extraire l'initiale :
* - nom
* - CONCAT(prenom,nom)
* - etc.
* @param string $id_table
* Nom de la table dans la requête
*/
function calculer_sql_initiale(string $champ, ? string $id_table = null) {
// Injecter le nom de la table
if ($id_table) {
$champ = preg_replace('/([^\.])(prenom|nom)/', "$1$id_table.$2", $champ);
}
// La formule : 1ère lettre du 1er mot trouvé (ce qui exclut les tirets, les points, les apostrophes, etc)
$sql = "REGEXP_SUBSTR(LOWER($champ), '[:word:]')";
return $sql;
}
/**
* Calcul de la balise #INITIALE
*
* @balise
*
* @param unknown_type $p
*/
function balise_INITIALE_dist($p) {
return rindex_pile($p, 'initiale', 'initiale');
}
/**
* Modifier les boucles
*
* Éviter une erreur sql en supprimant le champ `initiale` inexistant dans le select.
* Il est ajouté automatiquement en présence des critères {par initiale} ou {fusion initiale}.
*
* @pipeline pre_boucle
* @param [type] $boucle
* @return void
*/
function plugin_pre_boucle($boucle) {
foreach ($boucle->criteres as $critere) {
if (
// il y le critère {initiale}
$critere->type === 'critere'
and $critere->op === 'initiale'
// et il y a 'initiale' dans le select (à cause de {par initiale} ou {fusion initiale})
and ($k = array_search('initiale', $boucle->select)) !== false
) {
unset($boucle->select[$k]);
}
}
return $boucle;
}
@JLuc
Copy link

JLuc commented Nov 28, 2023

Chouette, mais j'ai un doute
C'est présenté comme polyvalent alors qu'il y a prenom et nom en dur dans le code.
Dans critere_initiale_dist c'est seulement une valeur par défaut (donc pas bloquant, juste ça pourrait être documenté)
mais calculer_sql_initiale limite sa regexp à prenom ou nom et donc le critère aussi en l'état.
Non ?
Au passage je pige pas ce que fait ce replace : avec $1 il transforme un préfixe de champ xxx_nom ou xxx_prénom en préfixe SPIP de table genre xxx_profils ??

@tcharlss
Copy link
Author

mais calculer_sql_initiale limite sa regexp à prenom ou nom et donc le critère aussi en l'état.

Ah oui en effet, cette partie n'était pas finie. Il faudrait récupérer la liste des champs de la table de l'objet.

Au passage je pige pas ce que fait ce replace : avec $1 il transforme un préfixe de champ xxx_nom ou xxx_prénom en préfixe SPIP de table genre xxx_profils ??

Ça ajoute l'alias de la table pour éviter les ambiguités en cas de jointure : titre → articles.titre

@JLuc
Copy link

JLuc commented Nov 28, 2023

wè mais et le ([^\.]) qui précède ?

@tcharlss
Copy link
Author

On ne veut pas préfixer les champs déjà préfixés : articles.titre → articles.articles.titre

@JLuc
Copy link

JLuc commented Nov 28, 2023

Aaaah ok merci :-) ... et ça marche car il y a une quote dans le cas contraire (ici ça serait plus simple d'exiger une quote direct, mais faudra voir dans le cas général...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment