references_listFilter   F
last analyzed

Complexity

Total Complexity 119

Size/Duplication

Total Lines 929
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 929
rs 1.671
c 0
b 0
f 0
wmc 119
lcom 1
cbo 1

38 Methods

Rating   Name   Duplication   Size   Complexity  
A setDefaultValues() 0 17 1
A __construct() 0 20 3
A setSortFields() 0 4 1
A getSortPlaceHolderNames() 0 4 1
A getSortPlaceHolderHtmlCode() 0 8 1
A setOption() 0 6 1
A addDefaultCriteria() 0 4 1
A getArrayValue() 0 8 5
B autoComplete() 0 25 7
A getJavascriptInitCode() 0 35 5
A initFilter() 0 12 2
A getSessionName() 0 4 1
A getStartSessionName() 0 4 1
A reinitializeFieldsValue() 0 11 3
A addDefaultCriterias() 0 10 4
A areThereSortFields() 0 9 3
A isInSortFieldsList() 0 4 1
A isInSortOrderList() 0 4 1
B setSortFieldsFromRequest() 0 30 10
A setupFilter() 0 9 1
A isSetCleanFilter() 0 7 2
A getCriteria() 0 4 1
C filter() 0 67 16
A getCount() 0 8 2
A setStartInSession() 0 7 2
A getStartValue() 0 16 4
A addAdditionnalParameterToPager() 0 6 1
A addAdditionnalParameterToClearButton() 0 6 1
A addAditionnalArrayParametersToPager() 0 10 3
B getPager() 0 30 7
A getObjects() 0 20 4
B getFilterField() 0 35 8
A assignFilterFieldsToTemplate() 0 11 3
A getClearFilterbutton() 0 14 3
A getGoButton() 0 15 5
A getSortField() 0 4 1
A getSortOrder() 0 4 1
A getFieldValue() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like references_listFilter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use references_listFilter, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * ****************************************************************************
4
 * references - MODULE FOR XOOPS
5
 * Copyright (c) Hervé Thouzard of Instant Zero (http://www.instant-zero.com)
6
 *
7
 * You may not change or alter any portion of this comment or credits
8
 * of supporting developers from this source code or any supporting source code
9
 * which is considered copyrighted (c) material of the original comment or credit authors.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
 *
14
 * @copyright       Hervé Thouzard of Instant Zero (http://www.instant-zero.com)
15
 * @license         http://www.fsf.org/copyleft/gpl.html GNU public license
16
 * @package         references
17
 * @author          Hervé Thouzard of Instant Zero
18
 * @link            http://xoops.instant-zero.com/
19
 *
20
 * ****************************************************************************
21
 */
22
23
/**
24
 *
25
 * A class to manage filters in data tables
26
 *
27
 * @copyright       Hervé Thouzard of Instant Zero (http://www.instant-zero.com)
28
 * @license         http://www.fsf.org/copyleft/gpl.html GNU public license
29
 * @author          Hervé Thouzard of Instant Zero
30
 * @link            http://xoops.instant-zero.com/
31
 *
32
 * Note : La classe dépend de la classe utilitaire et du fichier config.php pour l'option "REFERENCES_EXACT_SEARCH"
33
 *
34
 * @todo            Ajouter un paramètre pour spécifier une méthode à appeler (dans le handler de données) pour récupérer les listes de données (pour les champs de type select)
35
 */
36
class references_listFilter
37
{
38
    const FILTER_DATA_TEXT    = 1;
39
    const FILTER_DATA_NUMERIC = 2;
40
41
    const FILTER_FIELD_TEXT          = 1;
42
    const FILTER_FIELD_SELECT        = 2;
43
    const FILTER_FIELD_SELECT_YES_NO = 3;
44
45
    /**
46
     * Préfixe des variables de sélection
47
     */
48
    const PREFIX = 'filter_';
49
50
    /**
51
     * Nom du module (utilisé pour la session)
52
     */
53
    const MODULE_NAME = 'references';
54
55
    /**
56
     * Contient toutes les variables participant au filtre (avec leur type et description)
57
     *
58
     * @var array
59
     */
60
    private $vars = array();
61
62
    /**
63
     * Handler des données
64
     *
65
     * @var reference
66
     */
67
    private $handler = null;
68
69
    /**
70
     * Nom de la zone d'opération (par exemple op=products)
71
     *
72
     * @var string
73
     */
74
    private $op = '';
75
76
    /**
77
     * action de l'opération en cour (par exemple op=products)
78
     *
79
     * @var string
80
     */
81
    private $operationName = '';
82
83
    /**
84
     * Nombre maximum d'éléments pas page
85
     *
86
     * @var intger
87
     */
88
    private $limit = 10;
89
90
    /**
91
     * Nom de la variable start
92
     *
93
     * @var string
94
     */
95
    private $startName = '';
96
97
    /**
98
     * Critère {@link Criteria} servant à choisir les données
99
     *
100
     * @var object
101
     */
102
    private $criteria = null;
103
104
    /**
105
     * Indique s'il y a un nouveau critère
106
     *
107
     * @var boolean
108
     */
109
    private $newFilter = false;
110
111
    /**
112
     * Indique si la méthode filter() a été appelée
113
     *
114
     * @var boolean
115
     */
116
    private $isInitialized = false;
117
118
    /**
119
     * Zone de tri pour retourner les données (dans getObjects)
120
     *
121
     * @var string
122
     */
123
    private $sortField = '';
124
125
    /**
126
     * Sens de tri
127
     *
128
     * @var string
129
     */
130
    private $sortOrder = '';
131
132
    /**
133
     * Liste de critères par défaut
134
     *
135
     * @var array
136
     */
137
    private $defaultCriterias = array();
138
139
    /**
140
     * L'url complète du script qui appelle
141
     *
142
     * @var string
143
     */
144
    private $baseUrl = '';
145
146
    /**
147
     * Indique si on conserve en session la position de départ
148
     *
149
     * @var boolean
150
     */
151
    private $keepStart = true;
152
153
    /**
154
     * Indique si l'autocomplétion est activée
155
     *
156
     * @var boolean
157
     */
158
    private $hasAutoComplete = false;
159
160
    /**
161
     * Url du dossier js dans le module
162
     *
163
     * @var string
164
     */
165
    private $jsFolderUrl = '';
166
167
    /**
168
     * Paramètres additionnels à ajouter aux paramètres du pager
169
     * @var array
170
     */
171
    private $additionnalPagerParameters = array();
172
173
    /**
174
     * Paramètres additionnels à ajouter au bouton permettant de supprimer le filtre en cours
175
     * @var array
176
     */
177
    private $additionnalClearButtonParameters = array();
178
179
    /**
180
     * Tableau des champs triables [clé] = nom du champ dans la base, valeur = libellé
181
     * @var array
182
     */
183
    private $sortFields = array();
184
185
    /**
186
     * Initialise les paramètres avec des valeurs par défaut
187
     */
188
    private function setDefaultValues()
189
    {
190
        $this->handler                          = null;
191
        $this->op                               = '';
192
        $this->limit                            = 10;
0 ignored issues
show
Documentation Bug introduced by
It seems like 10 of type integer is incompatible with the declared type object<intger> of property $limit.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
193
        $this->startName                        = 'start';
194
        $this->operationName                    = 'op';
195
        $this->sortField                        = '';
196
        $this->sortOrder                        = 'asc';
197
        $this->baseUrl                          = '';
198
        $this->keepStart                        = true;
199
        $this->hasAutoComplete                  = false;
200
        $this->jsFolderUrl                      = '';
201
        $this->additionnalPagerParameters       = array();
202
        $this->additionnalClearButtonParameters = array();
203
        $this->sortFields                       = array();    // Les champs qui peuvent être utilisés pour trier
204
    }
205
206
    /**
207
     * Initialisation des données, handler et opération courante dans l'appelant
208
     *
209
     * @param mixed   $handler   Soit une référence au handler de données soit un tableau qui contient toutes les options (auxquel cas les autres paramètres sont inutiles)
210
     * @param string  $operationName
211
     * @param string  $operation Opération courante dans l'appelant
212
     * @param string  $startName Nom du paramètre start
213
     * @param integer $limit     Nombre maximum d'éléments par page
214
     * @param string  $baseUrl   L'url complète du script appelant
215
     * @param string  $sortField Zone de tri
216
     * @param string  $sortOrder Sens de tri
217
     * @param boolean $keepStart Indique si on conserve la position de départ
218
     * @param string  $jsFolderUrl
219
     * @package string $jsFolderUrl Url du répertoire qui contient les scripts Javascript
220
     */
221
    public function __construct($handler, $operationName = 'op', $operation = '', $startName = 'start', $limit = 10, $baseUrl = '', $sortField = '', $sortOrder = 'asc', $keepStart = true, $jsFolderUrl = '')
222
    {
223
        $this->setDefaultValues();
224
        if (!is_array($handler)) {
225
            $this->handler       = $handler;
226
            $this->op            = $operation;
227
            $this->limit         = $limit;
0 ignored issues
show
Documentation Bug introduced by
It seems like $limit of type integer is incompatible with the declared type object<intger> of property $limit.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
228
            $this->startName     = $startName;
229
            $this->operationName = $operationName;
230
            $this->sortField     = $sortField;
231
            $this->sortOrder     = $sortOrder;
232
            $this->baseUrl       = $baseUrl;
233
            $this->keepStart     = $keepStart;
234
            $this->jsFolderUrl   = $jsFolderUrl;
235
        } else {
236
            foreach ($handler as $key => $value) {
237
                $this->$key = $value;
238
            }
239
        }
240
    }
241
242
    /**
243
     * Donne à la classe le nom des champs sur lesquels on peut faire le tri
244
     *
245
     * @param  array $fields [clé] = nom du champ dans la base, valeur = libellé
246
     * @return object
0 ignored issues
show
Documentation introduced by
Should the return type not be object|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
247
     */
248
    public function setSortFields($fields)
249
    {
250
        $this->sortFields = $fields;
251
    }
252
253
    /**
254
     * Retourne les noms à utiliser pour les champs de tri (sélecteur de champ et ordre de tri)
255
     *
256
     * @return array [0] = Nom du sélecteur de champs, [1] = Nom du sélecteur pour le sens du tri
257
     */
258
    private function getSortPlaceHolderNames()
259
    {
260
        return array(self::PREFIX . 'sortFields', self::PREFIX . 'sortOrder');
261
    }
262
263
    /**
264
     * Retourne 2 sélecteurs html pour choisir la zone de tri et le sens du tri
265
     *
266
     * @return string
267
     */
268
    public function getSortPlaceHolderHtmlCode()
269
    {
270
        $sortNames      = $this->getSortPlaceHolderNames();
271
        $sortFieldsHtml = references_utils::htmlSelect($sortNames[0], $this->sortFields, $this->sortField, false);
272
        $sortOrderHtml  = references_utils::htmlSelect($sortNames[1], array('asc' => _MD_REFERENCES_ASC, 'desc' => _MD_REFERENCES_DESC), $this->sortOrder, false);
273
274
        return _MD_REFERENCES_SORT_BY . ' ' . $sortFieldsHtml . ' ' . $sortOrderHtml;
275
    }
276
277
    /**
278
     * Permet de valoriser une option directement tout en chainant
279
     *
280
     * @param  string $optionName
281
     * @param  mixed  $optionValue
282
     * @return object
283
     */
284
    public function setOption($optionName, $optionValue)
285
    {
286
        $this->$optionName = $optionValue;
287
288
        return $this;
289
    }
290
291
    /**
292
     * Ajoute un nouveau critère par défaut à la liste des critères par défaut
293
     *
294
     * @param Criteria $criteria
295
     */
296
    public function addDefaultCriteria(Criteria $criteria)
297
    {
298
        $this->defaultCriterias[] = $criteria;
299
    }
300
301
    /**
302
     * Retourne une valeur d'un tableau ou null si l'index n'existe pas
303
     *
304
     * @param array   $array        Le tableau à traiter
305
     * @param  string $index        L'index recherché
306
     * @param  mixed  $defaultValue La valeur par défaut
307
     * @return mixed
308
     */
309
    private function getArrayValue($array, $index, $defaultValue = false)
310
    {
311
        if ($index === 'autoComplete' && isset($array[$index]) && isset($array[$index]) == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
312
            $this->hasAutoComplete = true;    // On en profite pour vérifier si un champ utilise l'autocomplétion
313
        }
314
315
        return isset($array[$index]) ? $array[$index] : $defaultValue;
316
    }
317
318
    /**
319
     * Permet de faire l'autocomplétion d'un champ
320
     *
321
     * @param  string $query
322
     * @param         $limit
323
     * @param  string $fieldName
324
     * @return string
325
     */
326
    public function autoComplete($query, $limit, $fieldName)
327
    {
328
        $return = '';
329
        if (!$this->hasAutoComplete) {    // Si aucun champ n'est en autocomplétion, c'est pas la peine d'aller plus loin
330
331
            return $return;
332
        }
333
        if (isset($this->vars[$fieldName])) {    // On vérifie que le champ demandé est bien en autocomplétion
334
            if ($this->vars[$fieldName]['autoComplete'] == true) {
335
                if ($this->vars[$fieldName]['dataType'] == self::FILTER_DATA_TEXT) {
336
                    $criteria = new Criteria($fieldName, $query . '%', 'LIKE');
337
                }
338
                $criteria->setLimit((int)$limit);
0 ignored issues
show
Bug introduced by
The variable $criteria does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
339
                $ret = $this->handler->getObjects($criteria);
340
341
                if (count($ret) > 0) {
342
                    foreach ($ret as $object) {
343
                        $return .= $object->getVar($fieldName, 'n') . "\n";
344
                    }
345
                }
346
            }
347
        }
348
349
        return $return;
350
    }
351
352
    /**
353
     * Retourne le code Javascript à utiliser pour initialiser l'auto complétion (et donc à coller dans le code html)
354
     *
355
     * @param boolean $jqueryAlreadyLoaded Indique si jQuery est déjà chargé par l'appelant, auquel cas rien ne sert de le recharger
356
     * @return string
357
     */
358
    public function getJavascriptInitCode($jqueryAlreadyLoaded = false)
359
    {
360
        $return = '';
361
        if (!$this->hasAutoComplete) {
362
            return $return;
363
        }
364
        $return = '';
365
        $return .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" title=\"Style sheet\" href=\"" . $this->jsFolderUrl . "autocomplete/jquery.autocomplete.css\" />\n";
366
        if (!$jqueryAlreadyLoaded) {
367
            $return .= "<script type=\"text/javascript\" src=\"" . $this->jsFolderUrl . "jquery/jquery.js\"></script>\n";
368
        }
369
        $return .= "<script type=\"text/javascript\" src=\"" . $this->jsFolderUrl . "noconflict.js\"></script>\n";
370
        $return .= "<script type=\"text/javascript\" src=\"" . $this->jsFolderUrl . "autocomplete/jquery.autocomplete.min.js\"></script>\n";
371
        $return .= "<script type=\"text/javascript\">\n";
372
        $return .= "jQuery(function($) {\n";
373
        $return .= "var url='" . $this->baseUrl . "';\n";    // TODO: Supprimer "var" car cela limite sa portée !
374
        $return .= "var handlerName='" . $this->handler->className . "';\n";
375
376
        foreach ($this->vars as $fieldName => $parameters) {
377
            if ($parameters['autoComplete'] == true) {
378
                $field = self::PREFIX . $fieldName;
379
                $return .= "$('#" . $field . "').autocomplete(url, {\n";
380
                $return .= "extraParams: {\n";
381
                $return .= "    field: '" . $fieldName . "',\n";
382
                $return .= "    op: 'autocomplete',\n";
383
                $return .= "    handler: handlerName\n";
384
                $return .= "}\n";
385
                $return .= "});\n";
386
            }
387
        }
388
        $return .= "});\n";
389
        $return .= "</script>\n";
390
391
        return $return;
392
    }
393
394
    /**
395
     * Initialisation des données du filtre
396
     * Permet d'indiquer quelles sont les zones sur lesquelles on effectue des filtres ainsi que leur type
397
     *
398
     * @param  string $fieldName  Le nom du champs dans la table
399
     * @param  array  $parameters Les paramètres de la zone sous la forme d'un tableau :
400
     *                            [dataType]        Entier représentant le type de donnée (numérique ou chaine)
401
     *                            [fieldType]       Le type de zone de saisie (zone de texte, liste déroulante, liste déroulante Oui/Non)
402
     *                            [values]      La ou les valeurs de la zone de saisie (utilisé dans le cas d'un select)
403
     *                            [size]            Largeur d'affichage pour les textbox
404
     *                            [maxLength]       Largeur maximale pour les textbox
405
     *                            [withNull]        Dans le cas des listes déroulantes, indique s'il faut une valeur nulle
406
     *                            [minusOne]        Indique s'il faut retrancher 1 à la valeur saisie récupérée (cas classique des listes Oui/Non avec une valeur nulle)
407
     *                            [style]            Dans le cas des liste déroulante, le style à appliquer à la liste
408
     *                            [data]            A ne pas renseigner, contient la valeur saisie par l'utilisateur
409
     *                            [operator]        Opérateur de comparaison pour le Criteria
410
     *                            [autoComplete]  Indique si on active l'auto complétion sur le champs
411
     * @return object L'objet courant pour pouvoir chainer
412
     */
413
    public function initFilter($fieldName, $parameters)
414
    {
415
        // Tableau des valeurs attendues avec leur valeur par défaut
416
        $indexNames = array('dataType' => self::FILTER_DATA_TEXT, 'fieldType' => self::FILTER_FIELD_TEXT, 'values' => null, 'withNull' => false, 'size' => 5, 'maxLength' => 255, 'minusOne' => false, 'data' => null, 'style' => '', 'operator' => '=', 'autoComplete' => false);
417
        $data       = array();
418
        foreach ($indexNames as $indexName => $defaultValue) {
419
            $data[$indexName] = $this->getArrayValue($parameters, $indexName, $defaultValue);
420
        }
421
        $this->vars[$fieldName] = $data;
422
423
        return $this;
424
    }
425
426
    /**
427
     * Retourne le nom du tableau à utiliser pour la session
428
     * @note : Le nom de la session est composé de : nom du module_nom du handler, par exemple references_references_articles
429
     *
430
     * @return string
431
     */
432
    private function getSessionName()
433
    {
434
        return self::MODULE_NAME . '_' . $this->handler->table;
435
    }
436
437
    /**
438
     * Retourne le nom de la clé à utiliser pour la conservation du start en session
439
     *
440
     * @return string
441
     */
442
    private function getStartSessionName()
443
    {
444
        return $this->getSessionName() . '_start';
445
    }
446
447
    /**
448
     * Réinitialisation des données avant traitement
449
     *
450
     * @return void
451
     */
452
    private function reinitializeFieldsValue()
453
    {
454
        foreach ($this->vars as $fieldName => $fieldProperties) {
455
            if ($fieldProperties['dataType'] == self::FILTER_DATA_NUMERIC) {    // Zone numérique
456
                $fieldProperties['data'] = 0;
457
            } else {
458
                $fieldProperties['data'] = '';
459
            }
460
            $this->vars[$fieldName] = $fieldProperties;
461
        }
462
    }
463
464
    /**
465
     * Ajoute les critères par défaut au critère général
466
     *
467
     * @return void
0 ignored issues
show
Documentation introduced by
Should the return type not be references_listFilter?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
468
     */
469
    private function addDefaultCriterias()
470
    {
471
        if (is_array($this->defaultCriterias) && count($this->defaultCriterias) > 0) {
472
            foreach ($this->defaultCriterias as $criteria) {
473
                $this->criteria->add($criteria);
474
            }
475
        }
476
477
        return $this;
478
    }
479
480
    /**
481
     * Indique s'il y a des champs de tri
482
     *
483
     * @return boolean
484
     */
485
    private function areThereSortFields()
486
    {
487
        $return = false;
488
        if (is_array($this->sortFields) && count($this->sortFields) > 0) {
489
            $return = true;
490
        }
491
492
        return $return;
493
    }
494
495
    /**
496
     * Indique si le nom du champ passé en paramètre fait partie de la liste des champs "triables"
497
     *
498
     * @param  string $fieldName
499
     * @return boolean
500
     */
501
    private function isInSortFieldsList($fieldName)
502
    {
503
        return array_key_exists($fieldName, $this->sortFields);
504
    }
505
506
    /**
507
     * Indique si le sens de tri passé en paramètre fait partie de la liste autorisée
508
     *
509
     * @param  string $order
510
     * @return boolean
511
     */
512
    private function isInSortOrderList($order)
513
    {
514
        return in_array($order, array('asc', 'desc'));
515
    }
516
517
    /**
518
     * Récupère, depuis la requête d'entrée, la zone de tri à utiliser et le sens du tri et place l'information en cookie
519
     * pour que lorsque l'utilisateur se reconnecte, il retrouve ses informations de tri
520
     *
521
     * @return void
522
     */
523
    private function setSortFieldsFromRequest()
524
    {
525
        if (!$this->areThereSortFields()) {    // S'il n'y a pas de champs triables, on laisse tomber
526
527
            return;
528
        }
529
        $sortField        = $sortOrder = '';
530
        $cookieName       = $this->getSessionName();
531
        $orderFieldsNames = $this->getSortPlaceHolderNames();
532
        if (isset($_REQUEST[$orderFieldsNames[0]]) && isset($_REQUEST[$orderFieldsNames[1]])) {
533
            $sortField = $_REQUEST[$orderFieldsNames[0]];
534
            $sortOrder = $_REQUEST[$orderFieldsNames[1]];
535
        } else {
536
            if (isset($_SESSION[$cookieName . '_sortField'])) {
537
                $sortField = $_SESSION[$cookieName . '_sortField'];
538
            }
539
            if (isset($_SESSION[$cookieName . '_sortOrder'])) {
540
                $sortOrder = $_SESSION[$cookieName . '_sortOrder'];
541
            }
542
        }
543
544
        if ($this->isInSortFieldsList($sortField) && $this->isInSortOrderList($sortOrder)) {
545
            $this->sortField = $sortField;
546
            $this->sortOrder = $sortOrder;
547
        }
548
        if (trim($sortField) != '' && trim($sortOrder) != '') {
549
            $_SESSION[$cookieName . '_sortField'] = $sortField;
550
            $_SESSION[$cookieName . '_sortOrder'] = $sortOrder;
551
        }
552
    }
553
554
    /**
555
     * Réinitialisation des données avant traitement
556
     *
557
     * @return void
558
     */
559
    private function setupFilter()
560
    {
561
        $this->reinitializeFieldsValue();
562
        $this->newFilter     = false;
563
        $this->isInitialized = true;
564
        $this->criteria      = new CriteriaCompo();
565
        $this->criteria->add(new Criteria($this->handler->keyName, 0, '<>'));
566
        $this->addDefaultCriterias();
567
    }
568
569
    /**
570
     * RAZ des données du filtre si cela a été demandé dans la requête
571
     *
572
     * @return void
573
     */
574
    private function isSetCleanFilter()
575
    {
576
        if (isset($_REQUEST['cleanFilter'])) {
577
            $this->setStartInSession(0);
578
            unset($_SESSION[$this->getSessionName()]);
579
        }
580
    }
581
582
    /**
583
     * Retourne le critère de filtrage courant
584
     *
585
     * @return object
586
     */
587
    public function getCriteria()
588
    {
589
        return $this->criteria;
590
    }
591
592
    /**
593
     * Méthode à appeler juste après le constructeur pour qu'elle récupère les données saisies
594
     *
595
     * @return object L'objet courant pour pouvoir chainer
596
     */
597
    public function filter()
598
    {
599
        $this->setupFilter();                // Réinitialisations
600
        $ts = MyTextSanitizer::getInstance();
601
        $this->setSortFieldsFromRequest();    // On récupère la zone de tri éventuellement passée dans la requête
602
        $this->isSetCleanFilter();
603
604
        foreach ($this->vars as $fieldName => $fieldProperties) {
605
            // On commence par récupérer toutes les valeurs
606
            $formFieldName           = self::PREFIX . $fieldName;    // "filter_website_id" par exemple
607
            $fieldProperties['data'] = null;    // Valeur par défaut
608
            if (isset($_REQUEST[$formFieldName])) {
609
                if ($fieldProperties['dataType'] == self::FILTER_DATA_NUMERIC) {    // Zone numérique
610
                    if ((int)$_REQUEST[$formFieldName] != 0) {
611
                        $fieldProperties['data'] = (int)$_REQUEST[$formFieldName];
612
                        if (!$fieldProperties['minusOne']) {
613
                            $this->criteria->add(new Criteria($fieldName, $fieldProperties['data'], $fieldProperties['operator']));
614
                        } else {
615
                            $this->criteria->add(new Criteria($fieldName, $fieldProperties['data'] - 1, $fieldProperties['operator']));
616
                        }
617
                        $this->newFilter = true;
618
                    }
619
                } else {    // Zone texte
620
                    if (trim($_REQUEST[$formFieldName]) != '') {
621
                        $fieldProperties['data'] = $_REQUEST[$formFieldName];
622
                        if (!REFERENCES_EXACT_SEARCH) {
623
                            $this->criteria->add(new Criteria($fieldName, '%' . $ts->addSlashes($fieldProperties['data']) . '%', 'LIKE'));
624
                        } else {
625
                            $this->criteria->add(new Criteria($fieldName, $ts->addSlashes($fieldProperties['data']) . '%', 'LIKE'));
626
                        }
627
                        $this->newFilter = true;
628
                    }
629
                }
630
            }
631
            $this->vars[$fieldName] = $fieldProperties;
632
        }
633
634
        if ($this->newFilter) {
635
            $this->setStartInSession(0);
636
        }
637
638
        // Récupération des donées de la session s'il n'y a pas eu de filtre(s)
639
        if (!$this->newFilter && isset($_SESSION[$this->getSessionName()])) {
640
            $sessionFilterData = unserialize($_SESSION[$this->getSessionName()]);
641
            if (isset($sessionFilterData['criteria']) && is_object($sessionFilterData['criteria'])) {
642
                $this->criteria = $sessionFilterData['criteria'];
643
                unset($sessionFilterData['criteria']);
644
            }
645
            foreach ($this->vars as $fieldName => $fieldProperties) {
646
                if (isset($sessionFilterData[$fieldName])) {
647
                    $fieldProperties['data'] = $sessionFilterData[$fieldName];
648
                }
649
                $this->vars[$fieldName] = $fieldProperties;
650
            }
651
            unset($_SESSION[$this->getSessionName()]);
652
        }
653
654
        // Mise en place des données dans la session
655
        $dataForSession             = array();
656
        $dataForSession['criteria'] = $this->criteria;
657
        foreach ($this->vars as $fieldName => $fieldProperties) {
658
            $dataForSession[$fieldName] = $fieldProperties['data'];
659
        }
660
        $_SESSION[$this->getSessionName()] = serialize($dataForSession);
661
662
        return $this;
663
    }
664
665
    /**
666
     * Retourne le nombre d'enregistrement en fonction des critères courants
667
     *
668
     * @return integer
669
     */
670
    public function getCount()
671
    {
672
        if (!$this->isInitialized) {
673
            $this->filter();
674
        }
675
676
        return $this->handler->getCount($this->criteria);
677
    }
678
679
    /**
680
     * Conserve la valeur de start en session
681
     *
682
     * @param  integer $start
683
     * @return void
684
     */
685
    private function setStartInSession($start)
686
    {
687
        if ($this->keepStart) {
688
            $startSessionName            = $this->getStartSessionName();
689
            $_SESSION[$startSessionName] = (int)$start;
690
        }
691
    }
692
693
    /**
694
     * Retourne la valeur de ?start=x
695
     *
696
     * @return integer
697
     */
698
    private function getStartValue()
699
    {
700
        $start = 0;
701
        if (isset($_REQUEST[$this->startName])) {
702
            $start = (int)$_REQUEST[$this->startName];
703
        } elseif ($this->keepStart) {
704
            $startSessionName = $this->getStartSessionName();
705
            if (isset($_SESSION[$startSessionName])) {
706
                $start = (int)$_SESSION[$startSessionName];
707
            }
708
        }
709
        // Mise en session
710
        $this->setStartInSession($start);
711
712
        return $start;
713
    }
714
715
    /**
716
     * Permet d'ajouter un paramètre supplémentaire au pager
717
     *
718
     * @param  string $key
719
     * @param  string $value
720
     * @return object
721
     */
722
    public function addAdditionnalParameterToPager($key, $value = '')
723
    {
724
        $this->additionnalPagerParameters[$key] = $value;
725
726
        return $this;
727
    }
728
729
    /**
730
     * Permet d'ajouter un paramètre supplémentaire au bouton permettant de supprimer le filtre
731
     *
732
     * @param  string $key
733
     * @param  string $value
734
     * @return object
735
     */
736
    public function addAdditionnalParameterToClearButton($key, $value = '')
737
    {
738
        $this->additionnalClearButtonParameters[$key] = $value;
739
740
        return $this;
741
    }
742
743
    /**
744
     * Permet d'ajouter des paramètres supplémentaires au pager
745
     *
746
     * @param $array
747
     * @return object
748
     * @internal param string $key
749
     * @internal param string $value
750
     */
751
    public function addAditionnalArrayParametersToPager($array)
752
    {
753
        if (count($array) > 0) {
754
            foreach ($array as $key => $value) {
755
                $this->addAdditionnalParameterToPager($key, $value);
756
            }
757
        }
758
759
        return $this;
760
    }
761
762
    /**
763
     * Retourne le pager à utiliser
764
     *
765
     * @return mixed Null s'il n'y a pas lieu d'y avoir un pager, sinon un objet de type {@link XoopsPageNav}
766
     */
767
    public function getPager()
768
    {
769
        if (!$this->isInitialized) {
770
            $this->filter();
771
        }
772
        require_once XOOPS_ROOT_PATH . '/class/pagenav.php';
773
        $itemsCount  = $this->getCount();
774
        $queryString = array();
775
        if (trim($this->op) != '') {
776
            $queryString[$this->operationName] = $this->op;
777
        }
778
        $pagenav = null;
779
780
        if ($itemsCount > $this->limit) {
781
            foreach ($this->vars as $fieldName => $fieldProperties) {
782
                $formFieldName               = self::PREFIX . $fieldName;    // "filter_website_id" par exemple
783
                $queryString[$formFieldName] = $fieldProperties['data'];
784
            }
785
            // Ajout des paramètres supplémentaires éventuels
786
            if (count($this->additionnalPagerParameters) > 0) {
787
                foreach ($this->additionnalPagerParameters as $key => $value) {
788
                    $queryString[$key] = $value;
789
                }
790
            }
791
            $start   = $this->getStartValue();
792
            $pagenav = new XoopsPageNav($itemsCount, $this->limit, $start, $this->startName, http_build_query($queryString));
793
        }
794
795
        return $pagenav;
796
    }
797
798
    /**
799
     * Retourne une liste d'objets en fonction des critères définis
800
     *
801
     * @return array
802
     */
803
    public function getObjects()
804
    {
805
        if (!$this->isInitialized) {
806
            $this->filter();
807
        }
808
        $start    = $this->getStartValue();
809
        $limit    = $this->limit;
810
        $criteria = $this->criteria;
811
        $criteria->setStart($start);
812
        $criteria->setLimit($limit);
813
814
        $criteria->setOrder($this->sortOrder);
815
        if (trim($this->sortField) != '') {
816
            $criteria->setSort($this->sortField);
817
        } elseif (trim($this->handler->identifierName) != '') {
818
            $criteria->setSort($this->handler->identifierName);
819
        }
820
821
        return $this->handler->getObjects($this->criteria);
822
    }
823
824
    /**
825
     * Retourne la zone html à utiliser pour créer la zone de filtre (avec sa valeur saisie si c'est le cas)
826
     *
827
     * @param  string $fieldName La zone de saisie dont on veut récupérer le code html
828
     * @return string
829
     */
830
    public function getFilterField($fieldName)
831
    {
832
        $html = '';
833
        if (!$this->isInitialized) {
834
            $this->filter();
835
        }
836
        if (!isset($this->vars[$fieldName])) {
837
            trigger_error('Error, unknow field');
838
839
            return $html;
840
        }
841
        $fieldData     = $this->vars[$fieldName];
842
        $htmlFieldName = self::PREFIX . $fieldName;
843
844
        switch ($fieldData['fieldType']) {
845
            case self::FILTER_FIELD_TEXT:    // Zone de texte
846
                $ts   = MyTextSanitizer::getInstance();
847
                $html = "<input type='text' name='$htmlFieldName' id='$htmlFieldName' size='" . $fieldData['size'] . "' maxlength='" . $fieldData['maxLength'] . "' value='" . $ts->htmlSpecialChars($fieldData['data']) . "' />";
848
                break;
849
850
            case self::FILTER_FIELD_SELECT;     // Select
851
                $style = '';
852
                if (isset($fieldData['style']) && trim($fieldData['style']) != '') {
853
                    $style = $fieldData['style'];
854
                }
855
                $html = references_utils::htmlSelect($htmlFieldName, $fieldData['values'], $fieldData['data'], $fieldData['withNull'], $style);
856
                break;
857
858
            case self::FILTER_FIELD_SELECT_YES_NO:    // Select de type Oui/Non
859
                $html = references_utils::htmlSelect($htmlFieldName, array(2 => _YES, 1 => NO), $fieldData['data'], $fieldData['withNull']);
860
                break;
861
        }
862
863
        return $html;
864
    }
865
866
    /**
867
     * Assigne toutes les zones de filtre à un template
868
     *
869
     * @param  object  $xoopsTpl
870
     * @param  boolean $asArray   Est-ce qu'il faut placer le résultat dans un tableau ou assigner par nom de zone de filtre
871
     * @param string   $arrayName Le nom du tableau à utiliser
872
     * @return void
873
     */
874
    public function assignFilterFieldsToTemplate(&$xoopsTpl, $asArray = true, $arrayName = 'filterFields')
875
    {
876
        $fields = array_keys($this->vars);
877
        foreach ($fields as $field) {
878
            if (!$asArray) {
879
                $xoopsTpl->assign($field, $this->getFilterField($field));
880
            } else {
881
                $xoopsTpl->append($arrayName, $this->getFilterField($field));
882
            }
883
        }
884
    }
885
886
    /**
887
     * Retourne le bouton utilisé pour supprimer le filtre en cours
888
     *
889
     * @return string
890
     */
891
    public function getClearFilterbutton()
892
    {
893
        $queryString                       = array();
894
        $queryString[$this->operationName] = $this->op;
895
        if (count($this->additionnalClearButtonParameters) > 0) {
896
            foreach ($this->additionnalClearButtonParameters as $key => $value) {
897
                $queryString[$key] = $value;
898
            }
899
        }
900
        $queryString['cleanFilter'] = '1';
901
        $baseurl                    = $this->baseUrl;
902
903
        return "&nbsp;&nbsp;<a href='$baseurl?" . http_build_query($queryString) . "' title='" . _MD_REFERENCES_CLEAN_FILTER . "'><img align='top' src='../assets/images/clear_left.png' alt='" . _MD_REFERENCES_CLEAN_FILTER . "' /></a>";
904
    }
905
906
    /**
907
     * Retourne le bouton permettant de lancer le filtrage
908
     *
909
     * @param string $description  Texte à faire apparaître sur le bouton
910
     * @param array  $additionnals Champs supplémentaires à faire apparaître avec le bouton
0 ignored issues
show
Documentation introduced by
Should the type for parameter $additionnals not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
911
     * @return string
912
     */
913
    public function getGoButton($description = _GO, $additionnals = null)
914
    {
915
        $html = '';
916
        if (trim($this->operationName) != '' && trim($this->op) != '') {
917
            $html .= "<input type='hidden' name='" . $this->operationName . "' id='" . $this->operationName . "' value='" . $this->op . "' />";
918
        }
919
        if (!is_null($additionnals)) {
920
            foreach ($additionnals as $key => $value) {
921
                $html .= "<input type='hidden' name='" . $key . "' id='" . $key . "' value='" . $value . "' />";
922
            }
923
        }
924
        $html .= "<input type='submit' name='btngo' id='btngo' value='$description' />";
925
926
        return $html;
927
    }
928
929
    /**
930
     * Retourne le nom de la zone utilisée pour trier les données
931
     *
932
     * @return string
933
     */
934
    public function getSortField()
935
    {
936
        return $this->sortField;
937
    }
938
939
    /**
940
     * Retourne le sens de tri utilisé pour trier les données
941
     *
942
     * @return string
943
     */
944
    public function getSortOrder()
945
    {
946
        return $this->sortOrder();
0 ignored issues
show
Bug introduced by
The method sortOrder() does not exist on references_listFilter. Did you maybe mean isInSortOrderList()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
947
    }
948
949
    /**
950
     * Retourne la valeur d'un champ
951
     *
952
     * @param  string $fieldName
953
     * @return mixed  La valeur du champ ou null si on ne trouve pas la zone
954
     */
955
    public function getFieldValue($fieldName)
956
    {
957
        $ret = null;
958
        if (isset($this->vars[$fieldName])) {
959
            $ret = $this->vars[$fieldName]['data'];
960
        }
961
962
        return $ret;
963
    }
964
}
965