function calculer_chaine_jointures(&$boucle, $depart, $arrivee, $vu=array(), $milieu_exclus = array(), $max_liens = 5) { static $trouver_table; if (!$trouver_table) $trouver_table = charger_fonction('trouver_table', 'base'); if (is_string($milieu_exclus)) $milieu_exclus = array($milieu_exclus); list($dnom,$ddesc) = $depart; list($anom,$adesc) = $arrivee; if (!count($vu)) $vu[] = $dnom; // ne pas oublier la table de depart $akeys = $adesc['key']; if ($v = $akeys['PRIMARY KEY']) { unset($akeys['PRIMARY KEY']); $akeys = array_merge(preg_split('/,\s*/', $v), $akeys); } // enlever les cles d'arrivee exclues par l'appel $akeys = array_diff($akeys,$milieu_exclus); // cles candidates au depart $keys = liste_champs_jointures($dnom,$ddesc); // enlever les cles dde depart exclues par l'appel $keys = array_diff($keys,$milieu_exclus); $v = !$keys ? false : array_intersect(array_values($keys), $akeys); if ($v) return array(array($dnom, array($adesc['table'],$adesc), array_shift($v))); // regarder si l'on a (id_objet,objet) au depart et si on peut le mapper sur un id_xx if (count(array_intersect(array('id_objet','objet'),$keys))==2){ // regarder si l'une des cles d'arrivee peut se decomposer en // id_objet,objet // si oui on la prend foreach($akeys as $key){ $v = decompose_champ_id_objet($key); if (is_array($v)){ $objet = array_shift($v);// objet,'article' array_unshift($v,$key); // id_article,objet,'article' array_unshift($v,$objet); // id_objet,id_article,objet,'article' return array(array($dnom, array($adesc['table'],$adesc), $v)); } } } else { // regarder si l'une des cles de depart peut se decomposer en // id_objet,objet a l'arrivee // si oui on la prend foreach($keys as $key){ if (count($v = trouver_champs_decomposes($key,$adesc))>1){ if (count($v)==count(array_intersect($v, $akeys))) $v = decompose_champ_id_objet($key); // id_objet,objet,'article' array_unshift($v,$key); // id_article,id_objet,objet,'article' return array(array($dnom, array($adesc['table'],$adesc), $v)); } } } // si l'on voulait une jointure direct, c'est rate ! if ($max_liens<=1) return array(); // sinon essayer de passer par une autre table $new = $vu; foreach($boucle->jointures as $v) { if ($v && (!in_array($v,$vu)) && ($def = $trouver_table($v, $boucle->sql_serveur))) { // ne pas tester les cles qui sont exclues a l'appel // ie la cle de la jointure precedente $test_cles = $milieu_exclus; $new[] = $v; $max_iter = 50; // securite while (count($jointure_directe_possible = calculer_chaine_jointures($boucle,$depart,array($v, $def),$vu,$test_cles,1)) AND $max_iter--) { $jointure_directe_possible = reset($jointure_directe_possible); $milieu = end($jointure_directe_possible); if (is_string($milieu)) $test_cles[] = $milieu; else $test_cles = array_merge($test_cles,$milieu); // essayer de rejoindre l'arrivee a partir de cette etape intermediaire // sans repasser par la meme cle milieu $r = calculer_chaine_jointures($boucle, array($v, $def), $arrivee, $new, $milieu,$max_liens-1); if ($r) { array_unshift($r, $jointure_directe_possible); return $r; } } } } return array(); }
/** * Constuire la chaine de jointures, de proche en proche * * http://doc.spip.org/@calculer_chaine_jointures * * @param objetc $boucle * @param array $depart * sous la forme array(nom de la table, description) * @param array $arrivee * sous la forme array(nom de la table, description) * @param array $vu * tables deja vues dans la jointure, pour ne pas y repasser * @param array $milieu_exclus * cles deja utilisees, pour ne pas les reutiliser * @param int $max_liens * nombre maxi d'etapes * @return array */ function calculer_chaine_jointures(&$boucle, $depart, $arrivee, $vu = array(), $milieu_exclus = array(), $max_liens = 5) { static $trouver_table; if (!$trouver_table) { $trouver_table = charger_fonction('trouver_table', 'base'); } if (is_string($milieu_exclus)) { $milieu_exclus = array($milieu_exclus); } // quand on a exclus id_objet comme cle de jointure, il faut aussi exclure objet // faire une jointure sur objet tout seul n'a pas de sens if (in_array('id_objet', $milieu_exclus) and !in_array('objet', $milieu_exclus)) { $milieu_exclus[] = 'objet'; } list($dnom, $ddesc) = $depart; list($anom, $adesc) = $arrivee; if (!count($vu)) { $vu[] = $dnom; // ne pas oublier la table de depart $vu[] = $anom; // ne pas oublier la table d'arrivee } $akeys = array(); foreach ($adesc['key'] as $k) { // respecter l'ordre de $adesc['key'] pour ne pas avoir id_trad en premier entre autres... $akeys = array_merge($akeys, preg_split('/,\\s*/', $k)); } // enlever les cles d'arrivee exclues par l'appel $akeys = array_diff($akeys, $milieu_exclus); // cles candidates au depart $keys = liste_champs_jointures($dnom, $ddesc); // enlever les cles dde depart exclues par l'appel $keys = array_diff($keys, $milieu_exclus); $v = !$keys ? false : array_intersect(array_values($keys), $akeys); if ($v) { return array(array($dnom, array($adesc['table'], $adesc), array_shift($v))); } // regarder si l'on a (id_objet,objet) au depart et si on peut le mapper sur un id_xx if (count(array_intersect(array('id_objet', 'objet'), $keys)) == 2) { // regarder si l'une des cles d'arrivee peut se decomposer en // id_objet,objet // si oui on la prend foreach ($akeys as $key) { $v = decompose_champ_id_objet($key); if (is_array($v)) { $objet = array_shift($v); // objet,'article' array_unshift($v, $key); // id_article,objet,'article' array_unshift($v, $objet); // id_objet,id_article,objet,'article' return array(array($dnom, array($adesc['table'], $adesc), $v)); } } } else { // regarder si l'une des cles de depart peut se decomposer en // id_objet,objet a l'arrivee // si oui on la prend foreach ($keys as $key) { if (count($v = trouver_champs_decomposes($key, $adesc)) > 1) { if (count($v) == count(array_intersect($v, $akeys))) { $v = decompose_champ_id_objet($key); // id_objet,objet,'article' array_unshift($v, $key); // id_article,id_objet,objet,'article' return array(array($dnom, array($adesc['table'], $adesc), $v)); } } } } // si l'on voulait une jointure direct, c'est rate ! if ($max_liens <= 1) { return array(); } // sinon essayer de passer par une autre table $new = $vu; foreach ($boucle->jointures as $v) { if ($v and !in_array($v, $vu) and $def = $trouver_table($v, $boucle->sql_serveur) and !in_array($def['table_sql'], $vu)) { // ne pas tester les cles qui sont exclues a l'appel // ie la cle de la jointure precedente $test_cles = $milieu_exclus; $new[] = $v; $max_iter = 50; // securite while (count($jointure_directe_possible = calculer_chaine_jointures($boucle, $depart, array($v, $def), $vu, $test_cles, 1)) and $max_iter--) { $jointure_directe_possible = reset($jointure_directe_possible); $milieu = end($jointure_directe_possible); $exclure_fin = $milieu_exclus; if (is_string($milieu)) { $exclure_fin[] = $milieu; $test_cles[] = $milieu; } else { $exclure_fin = array_merge($exclure_fin, $milieu); $test_cles = array_merge($test_cles, $milieu); } // essayer de rejoindre l'arrivee a partir de cette etape intermediaire // sans repasser par la meme cle milieu, ni une cle deja vue ! $r = calculer_chaine_jointures($boucle, array($v, $def), $arrivee, $new, $exclure_fin, $max_liens - 1); if ($r) { array_unshift($r, $jointure_directe_possible); return $r; } } } } return array(); }