source: spip-zone/_plugins_/polyhierarchie/trunk/polyhier_fonctions.php @ 107574

Last change on this file since 107574 was 107574, checked in by rastapopoulos@…, 3 years ago

Amélioration de [107571] : comme proposé par cerdic, quand on trouve une jointure ET qu'on détecte que l'objet peut être classé avec polyhier, alors on cherche les deux liaisons avec un OR. Sinon c'est que l'un ou que l'autre.

File size: 13.4 KB
Line 
1<?php
2/*
3 * Plugin Polyhierarchie
4 * (c) 2009-2010 Cedric Morin
5 * Distribue sous licence GPL
6 *
7 */
8
9if (!defined("_ECRIRE_INC_VERSION")) return;
10
11/**
12 * {enfants} ou {enfants #ID_RUBRIQUE}
13 * renvoit tous les enfants d'une rubrique ou article
14 * directs (liens descendants) ou indirects (liens transverses)
15 *
16 * @global <type> $exceptions_des_tables
17 * @param <type> $idb
18 * @param <type> $boucles
19 * @param <type> $crit
20 * @param <type> $tous
21 */
22function critere_enfants($idb, &$boucles, $crit, $tous=true) {
23        global $exceptions_des_tables;
24        $boucle = &$boucles[$idb];
25
26        if (isset($crit->param[0])) {
27                $arg = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
28        }
29        else {
30                $arg = kwote(calculer_argument_precedent($idb, 'id_rubrique', $boucles));
31        }
32
33        if ($boucle->type_requete == 'rubriques' or isset($exceptions_des_tables[$boucle->id_table]['id_parent'])) {
34                $id_parent = isset($exceptions_des_tables[$boucle->id_table]['id_parent']) ?
35                        $exceptions_des_tables[$boucle->id_table]['id_parent'] :
36                        'id_parent';
37                $mparent = $boucle->id_table . '.' . $id_parent;
38        }
39        else {
40                $mparent = $boucle->id_table . '.id_rubrique';
41        }
42
43        $where = array();
44
45        if ($tous!=='indirects') {
46                $where[] = "is_array(\$r=$arg)?sql_in('$mparent',\$r):array('=', '$mparent', \$r)";
47        }
48
49        if ($tous !== 'directs'
50          AND in_array(table_objet_sql($boucle->type_requete),array_keys(lister_tables_objets_sql()))){
51                $type = objet_type($boucle->type_requete);
52                $cond = "is_array(\$r=$arg)?sql_in('rl.id_parent',\$r):'rl.id_parent='.\$r";
53                $sous = "sql_get_select('rl.id_objet','spip_rubriques_liens as rl',$cond.' AND rl.objet=\'$type\'')";
54                $where[] = "array('IN', '" . $boucle->id_table . "." . $boucle->primary . "', '(SELECT * FROM('.$sous.') AS subquery)')";
55        }
56       
57        if (count($where) == 2) {
58                $where = array("'OR'", $where[0], $where[1]);
59        }
60        else {
61                $where = reset($where);
62        }
63
64        $boucle->where[]= $where;
65}
66
67function critere_enfants_directs_dist($idb, &$boucles, $crit) {
68        critere_enfants($idb, $boucles, $crit, 'directs');
69}
70
71function critere_enfants_indirects_dist($idb, &$boucles, $crit) {
72        critere_enfants($idb, $boucles, $crit, 'indirects');
73}
74
75/**
76 * {parents}
77 * renvoit tous les parents d'une rubrique ou article
78 * {parents #ID_RUBRIQUE}
79 * renvoit tous les parents d'une rubrique
80 * directs (liens ascendants) ou indirects (liens transverses)
81 *
82 * @global <type> $exceptions_des_tables
83 * @param <type> $idb
84 * @param <type> $boucles
85 * @param <type> $crit
86 * @param <type> $tous
87 */
88function critere_parents($idb, &$boucles, $crit, $tous=true) {
89        global $exceptions_des_tables;
90       
91        $boucle = &$boucles[$idb];
92        $boucle_parent = $boucles[$boucle->id_parent];
93
94        $primary = $boucle->id_table.".".$boucle->primary;
95
96        $where = array();
97
98        if ($tous !== 'indirects') {
99                $argp = kwote(calculer_argument_precedent($idb, $boucle_parent->type_requete == 'rubriques' ? 'id_parent' : 'id_rubrique', $boucles));
100                $where[] = "is_array(\$r=$argp)?sql_in('$primary',\$r):array('=', '$primary', \$r)";
101        }
102
103        if ($tous !== 'directs'
104          AND in_array(table_objet_sql($boucle_parent->type_requete),array_keys(lister_tables_objets_sql()))){
105                $arg = kwote(calculer_argument_precedent($idb, id_table_objet(objet_type($boucle_parent->type_requete)), $boucles));
106                $type = objet_type($boucle_parent->type_requete);
107                $sous = "sql_get_select('rl.id_parent','spip_rubriques_liens as rl','rl.id_objet='.$arg.' AND rl.objet=\'$type\'')";
108                $where[] = array("'IN'", "'$primary'", "'(SELECT * FROM('.$sous.') AS subquery)'");
109        }
110       
111        if (count($where) == 2) {
112                $where = array("'OR'", $where[0], $where[1]);
113        }
114        else {
115                $where = reset($where);
116        }
117
118        $boucle->where[] = $where;
119}
120
121function critere_parents_directs_dist($idb, &$boucles, $crit) {
122        critere_parents($idb, $boucles, $crit, 'directs');
123}
124function critere_parent($idb, &$boucles, $crit) {
125        critere_parents($idb, $boucles, $crit, 'directs');
126}
127function critere_parents_indirects_dist($idb, &$boucles, $crit) {
128        critere_parents($idb, $boucles, $crit, 'indirects');
129}
130
131
132/**
133 * Calcul d'une branche
134 * (liste des id_rubrique contenues dans une rubrique donnee)
135 * pour le critere {branche}
136 *
137 * @param <type> $id
138 * @return <type>
139 */
140function calcul_branche_polyhier_in($id, $tous=true) {
141        // normaliser $id qui a pu arriver comme un array, comme un entier, ou comme une chaine NN,NN,NN
142        if (!is_array($id)) {
143                $id = explode(',', $id);
144        }
145        $id = array_map('intval', $id);
146       
147        // Notre branche commence par la rubrique de depart
148        $branche = $id;
149       
150        // On ajoute une generation (les filles de la generation precedente)
151        // jusqu'a epuisement
152        while (
153                $id = array_merge(
154                        $filles_directes = ($tous!=='indirects' ? array_map('reset', sql_allfetsel('id_rubrique', 'spip_rubriques', sql_in('id_parent', $id))) : array()),
155                        $filles_indirectes = ($tous!=='directs' ? array_map('reset', sql_allfetsel('id_objet', 'spip_rubriques_liens', "objet='rubrique' AND " . sql_in('id_parent', $id))) : array())
156                )
157        ) {
158                // enlever les rubriques deja trouvee, sinon on risque de tourner en rond a l'infini en cas
159                // de polyhierarchie bouclee
160                $id = array_diff($id, $branche);
161                $branche = array_merge($branche, $id);
162        }
163       
164        return implode(',', $branche);
165}
166
167
168
169/**
170 * {branche ?} ou {branche #ID_RUBRIQUE}
171 * {branche_directe ?} ou {branche_directe #ID_RUBRIQUE}
172 * {branche_principale ?} ou {branche_principale #ID_RUBRIQUE}
173 * {branche_complete ?} ou {branche_complete #ID_RUBRIQUE}
174 *
175 *
176 * @param <type> $idb
177 * @param <type> $boucles
178 * @param <type> $crit
179 */
180function critere_branche($idb, &$boucles, $crit, $tous='elargie') {
181        $not = $crit->not;
182        $boucle = &$boucles[$idb];
183       
184        // On cherche la ou les rubriques dont on demande la branche
185        if (isset($crit->param[0])) {
186                $arg = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
187        }
188        else {
189                $arg = kwote(calculer_argument_precedent($idb, 'id_rubrique', $boucles));
190        }
191       
192
193        $type = objet_type($boucle->type_requete);
194        $primary = $boucle->id_table.".".$boucle->primary;
195
196        //Trouver une jointure
197        $champ = "id_rubrique";
198        $desc = $boucle->show;
199        //Seulement si necessaire
200        if (!array_key_exists($champ, $desc['field'])){
201                $trouver_table = charger_fonction("trouver_table", "base");
202                $cle = "";
203                // peut-etre deja une jointure qui fournit id_rubrique ?
204                foreach($boucle->from as $k=>$t){
205                        $desc = $trouver_table($t);
206                        if (isset($desc['field']['id_rubrique'])){
207                                $cle = $k;
208                                break;
209                        }
210                }
211                if (!$cle){
212                        $cle = trouver_jointure_champ($champ, $boucle);
213                        $desc = $trouver_table($boucle->from[$cle]);
214                        if (count(trouver_champs_decomposes($champ, $desc))>1){
215                                $decompose = decompose_champ_id_objet($champ);
216                                $champ = array_shift($decompose);
217                                $boucle->where[] = array("'='", _q($cle.".".reset($decompose)), '"'.sql_quote(end($decompose)).'"');
218                        }
219                }
220                // si le champ id_rubrique est recuperer par jointure, c'est le type et la primary de la table jointe
221                // qu'il faut chercher dans la table spip_rubriques_liens (ie cas des evenements)
222                if ($cle AND $desc) {
223                        $type_jointure = objet_type($boucle->from[$cle]);
224                        $primary_jointure = $cle . "." . id_table_objet($boucle->from[$cle]);
225                }
226        }
227        else {
228                $cle = $boucle->id_table;
229        }
230
231        $where = array();
232
233        // On construit en avance la liste des rubriques
234        $boucle->hash .= "
235        // {branche}
236        if ($arg) {
237                \$in_rub = calcul_branche_polyhier_in($arg," . ($tous === true ? 'true' : "'directs'") . ");
238        }
239        ";
240       
241        // Si c'est tout ou que directs ET qu'on a trouvé un "id_rubrique" quelque part
242        // on ajoute le critère de branche principale, avec le champ id_rubrique
243        if ($tous !== 'indirects' and $cle) {
244                $where[] = "sql_in(
245                        '{$cle}.{$champ}',
246                        \$in_rub"
247                        . ($not ? ", 'NOT'" : '')
248                . ")";
249        }
250       
251        // Si c'est tout ou que indirects, on ajoute le critère de branche secondaire, avec la table de liens
252        if ($tous !== 'directs') {
253                // S'il y a une jointure, on cherche toujours les liaisons avec celle-ci
254                if (isset($type_jointure)) {
255                        $sous_jointure = "sql_get_select('rl.id_objet','spip_rubriques_liens as rl',sql_in('rl.id_parent',\$in_rub" . ($not ? ", 'NOT'" : '') . ").' AND rl.objet=\'$type_jointure\'')";
256                        $where_jointure = "array('IN', '$primary_jointure', '(SELECT * FROM('.$sous_jointure.') AS subquery)')";
257                }
258               
259                // S'il n'y a pas de jointure (cas par défaut) ou que l'objet est explicitement configuré pour être classé avec polyhier
260                // on cherche les liaisons sur l'objet
261                if (
262                        !isset($type_jointure)
263                        or (include_spip('inc/config') and in_array(table_objet_sql($type), lire_config('polyhier/lier_objets', array())))
264                ) {
265                        $sous_objet = "sql_get_select('rl.id_objet','spip_rubriques_liens as rl',sql_in('rl.id_parent',\$in_rub" . ($not ? ", 'NOT'" : '') . ").' AND rl.objet=\'$type\'')";
266                        $where_objet = "array('IN', '$primary', '(SELECT * FROM('.$sous_objet.') AS subquery)')";
267                }
268               
269                // S'il y a les deux, on fait un OR
270                if (isset($where_jointure) and isset($where_objet)) {
271                        $where[] = "array('OR', $where_jointure, $where_objet)";
272                }
273                // Sinon s'il n'y a que jointure
274                elseif (isset($where_jointure)) {
275                        $where[] = $where_jointure;
276                }
277                // Sinon que sur l'objet
278                else {
279                        $where[] = $where_objet;
280                }
281        }
282       
283        // S'il y a les deux critères, c'est l'un ou l'autre
284        if (count($where) == 2) {
285                $where = "array('OR'," . $where[0] . "," . $where[1] . ")";
286        }
287        else {
288                $where = reset($where);
289        }
290       
291        $boucle->where[] = !$crit->cond ? $where : ("($arg ? $where : " . ($not ? "'0=1'" : "'1=1'") . ')');
292}
293
294function critere_branche_principale_dist($idb, &$boucles, $crit) {
295        critere_branche($idb, $boucles, $crit, 'directs');
296}
297// un alias
298function critere_branche_directe_dist($idb, &$boucles, $crit) {
299        critere_branche($idb, $boucles, $crit, 'directs');
300}
301function critere_branche_indirecte_dist($idb, &$boucles, $crit) {
302        critere_branche($idb, $boucles, $crit, 'indirects');
303}
304function critere_branche_complete_dist($idb, &$boucles, $crit) {
305        critere_branche($idb, $boucles, $crit, true);
306}
307
308/*
309 * Déclarer un fonction générique pour pouvoir chercher dans les champs des rubriques liées
310 *
311 */
312function inc_rechercher_joints_objet_rubrique_dist($table, $table_liee, $ids_trouves, $serveur) {
313        $cle_depart = id_table_objet($table);
314       
315        $s = sql_select(
316                "id_objet as $cle_depart, id_parent as id_rubrique",
317                'spip_rubriques_liens',
318                array("objet='$table'", sql_in('id_parent', $ids_trouves)),
319                '', '', '', '', $serveur
320        );
321       
322        return array($cle_depart, 'id_rubrique', $s);
323}
324
325/**
326 * Filtre pour appeler polyhier_get_enfants depuis un squelette
327 * @param $id_parent
328 * @param string $objet
329 * @param string $serveur
330 * @return array
331 */
332function filtre_polyhier_get_enfants_dist($id_parent, $objet='', $serveur='') {
333        include_spip('inc/polyhier');
334        return polyhier_get_enfants($id_parent, $objet, $serveur);
335}
336
337function filtre_polyhier_lister_enfants($objet,$ids){
338        $fond = "prive/objets/liste/".table_objet($objet);
339        if (trouver_fond($fond)){
340                $primary = id_table_objet($objet);
341                return recuperer_fond($fond,array($primary=>$ids));
342        }
343}
344
345
346/**
347 * Compile la balise `#URL_POLYHIER{#ID_RUBRIQUE,article,#ID_ARTICLE}`
348 * qui génère l'URL d'un article contextualisee a l'une de ses rubriques parentes
349 *
350 * Si la rubrique passee en argument n'est pas une rubrique parente elle est ignoree
351 * Si les URLs ne contiennent pas l'URL de la rubrique parente (URL arbo), la rubrique contextuelle est ajoutee en query string
352 *
353 * @balise
354 * @example
355 *     ```
356 *     #URL_POLYHIER{#ENV{id_rubrique,#ID_RUBRIQUE},article,#ID_ARTICLE}
357 *     ```
358 * @param Champ $p
359 *     Pile au niveau de la balise
360 * @return Champ
361 *     Pile complétée par le code à générer
362 */
363function balise_URL_POLYHIER_dist($p) {
364
365        $_id_rubrique = interprete_argument_balise(1, $p);
366        $_type = interprete_argument_balise(2, $p);
367        $_id = interprete_argument_balise(3, $p);
368
369        $code = "urlencode_1738(generer_url_polyhier_entite($_id, $_type, $_id_rubrique))";
370        $p->code = $code;
371        if (!$p->etoile) {
372                $p->code = "vider_url($code)";
373        }
374        $p->interdire_scripts = false;
375
376        return $p;
377}
378
379/**
380 * Generer l'URL polyhierarchique d'un objet relativement a une rubrique parente secondaire
381 * on cherche le parent secondaire qui est dans la branche de la rubrique fournie et on le passe en argument
382 * de l'URL. Si le module URL le gere (arbopoly) l'URL reposera sur l'URL de ce parent secondaire,
383 * sinon elle restera en argument
384 *
385 * a charge pour les squelettes de gerer la presence du id_rubrique=xxx dans le contexte de la page de l'objet concerne
386 *
387 * Ne fonctionne que si l'objet est lui même rattache en polyhierarchie,
388 * pas si il est enfant direct d'une rubrique enfant secondaire
389 *
390 * @param int $id_objet
391 * @param string $objet
392 * @param int $id_rubrique
393 * @param string $args
394 * @param string $ancre
395 * @return string
396 */
397function generer_url_polyhier_entite($id_objet, $objet, $id_rubrique=null, $args='', $ancre='') {
398
399        // si id_rubrique contextuel passe en argument et si c'est bien un parent polyhierarchique
400        $parents_poly = sql_allfetsel('id_parent','spip_rubriques_liens', 'objet='.sql_quote($objet). ' AND id_objet='.intval($id_objet));
401        $parents_poly = array_map('reset',$parents_poly);
402
403        $maxiter = 100;
404        $branche = $r = array($id_rubrique);
405        while (!($id_parent = array_intersect($parents_poly, $r))
406          and $maxiter--
407          and $filles = sql_allfetsel('id_rubrique','spip_rubriques',sql_in('id_parent', $r) . " AND " . sql_in('id_rubrique', $branche, 'NOT'))) {
408                $r = array_map('reset', $filles);
409                $branche = array_merge($branche, $r);
410        }
411
412        if ($id_parent = reset($id_parent)) {
413                // le vrai parent
414                $champ_parent = ($objet == 'rubrique' ? 'id_parent' : 'id_rubrique');
415                $args .= ($args?'&':'')."$champ_parent=$id_parent";
416        }
417        $url = generer_url_entite($id_objet, $objet, $args, $ancre, true);
418        return $url;
419}
Note: See TracBrowser for help on using the repository browser.