/** * Permet de faire un comptage par table liee * * @syntaxe `{compteur table[, champ]}` * @link http://www.spip-contrib.net/Classer-les-articles-par-nombre-de#forum409210 * * @example * Pour avoir les auteurs classes par articles et * le nombre d'article de chacun : * * ``` * <BOUCLE1(AUTEURS){compteur articles}{par compteur_articles}> * #ID_AUTEUR : #COMPTEUR{articles} * </BOUCLE1> * ``` * * @note * Avec un seul argument {compteur autre_table} le groupby est fait * implicitement sur la cle primaire de la boucle en cours. * Avec un second argument {compteur autre_table,champ_fusion} * le groupby est fait sur le champ_fusion" * * @param string $idb * Identifiant de la boucle * @param Boucle[] $boucles * AST du squelette * @param Critere $crit * Paramètres du critère dans cette boucle * @param bool $left * true pour utiliser un left join plutôt qu'un inner join. * @return void */ function critere_compteur($idb, &$boucles, $crit, $left = false) { $boucle =& $boucles[$idb]; if (isset($crit->param[1])) { $_fusion = calculer_liste($crit->param[1], array(), $boucles, $boucle->id_parent); } else { $_fusion = "''"; } $params = $crit->param; $table = reset($params); $table = $table[0]->texte; $op = false; if (preg_match(',^(\\w+)([<>=])([0-9]+)$,', $table, $r)) { $table = $r[1]; if (count($r) >= 3) { $op = $r[2]; } if (count($r) >= 4) { $op_val = $r[3]; } } $type = objet_type($table); $type_id = id_table_objet($type); /** * Si la clé primaire est une clé multiple, on prend la première partie * Utile pour compter les versions de spip_versions par exemple */ if (count($types = explode(',', $type_id)) > 1) { $type_id = $types[0]; } $table_sql = table_objet_sql($type); $trouver_table = charger_fonction('trouver_table', 'base'); $arrivee = array($table, $trouver_table($table, $boucle->sql_serveur)); $depart = array($boucle->id_table, $trouver_table($boucle->id_table, $boucle->sql_serveur)); // noter les jointures deja installees $joins = array_keys($boucle->from); if ($compt = calculer_jointure($boucle, $depart, $arrivee)) { if ($_fusion != "''") { // en cas de jointure, on ne veut pas du group_by sur la cle primaire ! // cela casse le compteur ! foreach ($boucle->group as $k => $group) { if ($group == $boucle->id_table . '.' . $boucle->primary) { unset($boucle->group[$k]); } } $boucle->group[] = '".($gb=' . $_fusion . ')."'; } $boucle->select[] = "COUNT({$compt}.{$type_id}) AS compteur_{$table}"; if ($op) { $boucle->having[] = array("'" . $op . "'", "'compteur_" . $table . "'", $op_val); } if ($left) { foreach ($boucle->from as $k => $val) { if (!in_array($k, $joins)) { $boucle->from_type[$k] = 'left'; } } } } }
/** * http://www.spip-contrib.net/Classer-les-articles-par-nombre-de#forum409210 * Permet de faire un comptage par table liee * exemple * <BOUCLE1(AUTEURS){compteur articles}{par compteur_articles}> * #ID_AUTEUR : #COMPTEUR{articles} * </BOUCLE1> * pour avoir les auteurs classes par articles et le nombre d'article de chacun * * @param unknown_type $idb * @param unknown_type $boucles * @param unknown_type $crit */ function critere_compteur($idb, &$boucles, $crit, $left=false){ $boucle = &$boucles[$idb]; $_fusion = calculer_liste($crit->param[1], array(), $boucles, $boucle->id_parent); $params = $crit->param; $table = reset($params); $table = $table[0]->texte; $op = false; if(preg_match(',^(\w+)([<>=])([0-9]+)$,',$table,$r)){ $table=$r[1]; if (count($r)>=3) $op=$r[2]; if (count($r)>=4) $op_val=$r[3]; } $type = objet_type($table); $type_id = id_table_objet($type); $table_sql = table_objet_sql($type); $trouver_table = charger_fonction('trouver_table','base'); $arrivee = array($table, $trouver_table($table, $boucle->sql_serveur)); $depart = array($boucle->id_table,$trouver_table($boucle->id_table, $boucle->sql_serveur)); // noter les jointures deja installees $joins = array_keys($boucle->from); if ($compt = calculer_jointure($boucle,$depart,$arrivee)){ if ($_fusion!="''"){ // en cas de jointure, on ne veut pas du group_by sur la cle primaire ! // cela casse le compteur ! foreach($boucle->group as $k=>$group) if ($group == $boucle->id_table.'.'.$boucle->primary) unset($boucle->group[$k]); $boucle->group[] = '".($gb='.$_fusion.')."'; } $boucle->select[]= "COUNT($compt.$type_id) AS compteur_$table"; if ($op) $boucle->having[]= array("'".$op."'", "'compteur_".$table."'",$op_val); if ($left){ foreach($boucle->from as $k=>$val){ if (!in_array($k, $joins)){ $boucle->from_type[$k] = 'left'; } } } } }
function trouver_jointure_champ($champ, &$boucle) { $cle = trouver_champ_exterieur($champ, $boucle->jointures, $boucle); if ($cle) { $desc = $boucle->show; $cle = calculer_jointure($boucle, array($desc['id_table'], $desc), $cle, false); } if ($cle) return $cle; spip_log("trouver_jointure_champ: $champ inconnu"); return ''; }
/** * Champ hors table, ça ne peut être qu'une jointure. * * On cherche la table du champ et on regarde si elle est déjà jointe * Si oui et qu'on y cherche un champ nouveau, pas de jointure supplementaire * Exemple: criteres {titre_mot=...}{type_mot=...} * Dans les 2 autres cas ==> jointure * (Exemple: criteres {type_mot=...}{type_mot=...} donne 2 jointures * pour selectioner ce qui a exactement ces 2 mots-cles. * * @param Boucle $boucle * Description de la boucle * @param array $joints * Liste de jointures possibles (ex: $boucle->jointures ou $boucle->jointures_explicites) * @param string $col * Colonne cible de la jointure * @param array $desc * Description de la table * @param bool $cond * Flag pour savoir si le critère est conditionnel ou non * @param bool|string $checkarrivee * string : nom de la table jointe où on veut trouver le champ. * n'a normalement pas d'appel sans $checkarrivee. * @return string * Alias de la table de jointure (Lx) * Vide sinon. */ function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $cond, $checkarrivee = false) { // si on demande un truc du genre spip_mots // avec aussi spip_mots_liens dans les jointures dispo // et qu'on est la // il faut privilegier la jointure directe en 2 etapes spip_mots_liens, spip_mots if ($checkarrivee and is_string($checkarrivee) and $a = table_objet($checkarrivee) and in_array($a . '_liens', $joints)) { if ($res = calculer_lien_externe_init($boucle, $joints, $col, $desc, $cond, $checkarrivee)) { return $res; } } foreach ($joints as $joint) { if ($arrivee = trouver_champ_exterieur($col, array($joint), $boucle, $checkarrivee)) { // alias de table dans le from $t = array_search($arrivee[0], $boucle->from); // recuperer la cle id_xx eventuellement decomposee en (id_objet,objet) $cols = $arrivee[2]; // mais on ignore la 3eme cle si presente qui correspond alors au point de depart if (count($cols) > 2) { array_pop($cols); } if ($t) { // la table est déjà dans le FROM, on vérifie si le champ est utilisé. $joindre = false; foreach ($cols as $col) { $c = '/\\b' . $t . ".{$col}" . '\\b/'; if (trouver_champ($c, $boucle->where)) { $joindre = true; } else { // mais ca peut etre dans le FIELD pour le Having $c = "/FIELD.{$t}" . ".{$col},/"; if (trouver_champ($c, $boucle->select)) { $joindre = true; } } } if (!$joindre) { return $t; } } array_pop($arrivee); if ($res = calculer_jointure($boucle, array($boucle->id_table, $desc), $arrivee, $cols, $cond, 1)) { return $res; } } } return ''; }
function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $eg, $checkarrivee = false) { $cle = trouver_champ_exterieur($col, $joints, $boucle, $checkarrivee); if (!$cle) return ''; $t = array_search($cle[0], $boucle->from); // transformer eventuellement id_xx en (id_objet,objet) $cols = trouver_champs_decomposes($col,$cle[1]); if ($t) { $joindre = false; foreach($cols as $col){ $c = '/\b' . $t . ".$col" . '\b/'; if (trouver_champ($c, $boucle->where)) $joindre = true; else { // mais ca peut etre dans le FIELD pour le Having $c = "/FIELD.$t" .".$col,/"; if (trouver_champ($c, $boucle->select)) $joindre = true; } } if (!$joindre) return $t; } return calculer_jointure($boucle, array($boucle->id_table, $desc), $cle, $cols, $eg); }
/** * Champ hors table, ca ne peut etre qu'une jointure. * On cherche la table du champ et on regarde si elle est deja jointe * Si oui et qu'on y cherche un champ nouveau, pas de jointure supplementaire * Exemple: criteres {titre_mot=...}{type_mot=...} * Dans les 2 autres cas ==> jointure * (Exemple: criteres {type_mot=...}{type_mot=...} donne 2 jointures * pour selectioner ce qui a exactement ces 2 mots-cles. * * http://doc.spip.org/@calculer_critere_externe_init * * @param $boucle * @param $joints * @param $col * @param $desc * @param $cond * @param bool|string $checkarrivee * @return mixed|string */ function calculer_critere_externe_init(&$boucle, $joints, $col, $desc, $cond, $checkarrivee = false) { // si on demande un truc du genre spip_mots // avec aussi spip_mots_liens dans les jointures dispo // et qu'on est la // il faut privilegier la jointure directe en 2 etapes spip_mots_liens, spip_mots if ($checkarrivee and is_string($checkarrivee) and $a = table_objet($checkarrivee) and in_array($a . '_liens', $joints)) { if ($res = calculer_lien_externe_init($boucle, $joints, $col, $desc, $cond, $checkarrivee)) { return $res; } } foreach ($joints as $joint) { if ($arrivee = trouver_champ_exterieur($col, array($joint), $boucle, $checkarrivee)) { $t = array_search($arrivee[0], $boucle->from); // transformer eventuellement id_xx en (id_objet,objet) $cols = trouver_champs_decomposes($col, $arrivee[1]); if ($t) { $joindre = false; foreach ($cols as $col) { $c = '/\\b' . $t . ".{$col}" . '\\b/'; if (trouver_champ($c, $boucle->where)) { $joindre = true; } else { // mais ca peut etre dans le FIELD pour le Having $c = "/FIELD.{$t}" . ".{$col},/"; if (trouver_champ($c, $boucle->select)) { $joindre = true; } } } if (!$joindre) { return $t; } } if ($res = calculer_jointure($boucle, array($boucle->id_table, $desc), $arrivee, $cols, $cond, 1)) { return $res; } } } return ''; }
/** * Cherche a ajouter la possibilite d'interroger un champ sql * dans une boucle. Cela construira les jointures necessaires * si une possibilite est trouve et retournera le nom de * l'alias de la table contenant ce champ * (L2 par exemple pour 'spip_mots AS L2' dans le FROM), * * * @param string $champ * Nom du champ cherche (exemple id_article) * @param object $boucle * Informations connues de la boucle * @param array $jointures * Liste des tables parcourues (articles, mots) pour retrouver le champ sql * et calculer la jointure correspondante. * En son absence et par defaut, on utilise la liste des jointures connues * par SPIP pour la table en question ($boucle->jointures) * @param bool $cond * flag pour savoir si le critere est conditionnel ou non * * @return string */ function trouver_jointure_champ($champ, &$boucle, $jointures = false, $cond = false) { if ($jointures === false) { $jointures = $boucle->jointures; } $cle = trouver_champ_exterieur($champ, $jointures, $boucle); if ($cle) { $desc = $boucle->show; $cle = calculer_jointure($boucle, array($desc['id_table'], $desc), $cle, '', $cond); } if ($cle) { return $cle; } spip_log("trouver_jointure_champ: {$champ} inconnu"); return ''; }
/** * Cherche a ajouter la possibilite d'interroger un champ sql * dans une boucle. Cela construira les jointures necessaires * si une possibilite est trouve et retournera le nom de * l'alias de la table contenant ce champ * (L2 par exemple pour 'spip_mots AS L2' dans le FROM), * * * @param string $champ * Nom du champ cherche (exemple id_article) * @param object $boucle * Informations connues de la boucle * @param array $jointures * Liste des tables parcourues (articles, mots) pour retrouver le champ sql * et calculer la jointure correspondante. * En son absence et par defaut, on utilise la liste des jointures connues * par SPIP pour la table en question ($boucle->jointures) * @param bool $cond * flag pour savoir si le critere est conditionnel ou non * * @return string */ function trouver_jointure_champ($champ, &$boucle, $jointures = false, $cond = false) { if ($jointures === false) { $jointures = $boucle->jointures; } // TODO : aberration, on utilise $jointures pour trouver le champ // mais pas poour construire la jointure ensuite $arrivee = trouver_champ_exterieur($champ, $jointures, $boucle); if ($arrivee) { $desc = $boucle->show; array_pop($arrivee); // enlever la cle en 3eme argument $cle = calculer_jointure($boucle, array($desc['id_table'], $desc), $arrivee, '', $cond); if ($cle) { return $cle; } } spip_log("trouver_jointure_champ: {$champ} inconnu"); return ''; }