SearchModelTrait::search()   B
last analyzed

Complexity

Conditions 7
Paths 5

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 8.2826
c 0
b 0
f 0
ccs 0
cts 21
cp 0
cc 7
nc 5
nop 2
crap 56
1
<?php
2
/**
3
 * HiPanel core package
4
 *
5
 * @link      https://hipanel.com/
6
 * @package   hipanel-core
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2014-2019, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hipanel\base;
12
13
use hiqdev\hiart\ActiveQuery;
14
use hiqdev\hiart\ActiveRecord;
15
use hiqdev\hiart\ActiveDataProvider;
16
use Yii;
17
use yii\helpers\ArrayHelper;
18
19
/**
20
 * Trait SearchModelTrait
21
 * Basic behavior for search models.
22
 */
23
trait SearchModelTrait
24
{
25
    public static $filterConditions = ['in', 'ne', 'like', 'ilike', 'gt', 'ge', 'lt', 'le', 'ni'];
26
27
    public function attributes()
28
    {
29
        return $this->searchAttributes();
30
    }
31
32
    protected function searchAttributes()
33
    {
34
        static $attributes = [];
35
36
        if ($attributes === []) {
37
            foreach (parent::attributes() as $attribute) {
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (attributes() instead of searchAttributes()). Are you sure this is correct? If so, you might want to change this to $this->attributes().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
38
                $attributes = array_merge($attributes, $this->buildAttributeConditions($attribute));
39
            }
40
        }
41
42
        return $attributes;
43
    }
44
45
    /**
46
     * Builds all possible $attribute names using [[$filterConditions]].
47
     *
48
     * @param $attribute
49
     * @return array
50
     */
51
    protected function buildAttributeConditions($attribute)
52
    {
53
        $attributes = [];
54
        foreach (array_merge([''], static::$filterConditions) as $condition) {
55
            $attributes[] = $attribute . ($condition === '' ? '' : "_$condition");
56
        }
57
58
        return $attributes;
59
    }
60
61
    public function rules()
62
    {
63
        $rules = parent::rules();
64
        $rules[] = [$this->searchAttributes(), 'safe'];
65
66
        return $rules;
67
    }
68
69
    public function scenarios()
70
    {
71
        // bypass scenarios() implementation in the parent class
72
        return \yii\base\Model::scenarios();
73
    }
74
75
    /**
76
     * Creates data provider instance with search query applied.
77
     * @param $params
78
     * @param array $dataProviderConfig
79
     * @throws \yii\base\InvalidConfigException
80
     * @return ActiveDataProvider
81
     */
82
    public function search($params, $dataProviderConfig = [])
83
    {
84
        /**
85
         * @var ActiveRecord
86
         * @var ActiveRecord $class
87
         * @var ActiveQuery $query
88
         */
89
        $class = get_parent_class();
90
        $query = $class::find(); // $class::find()->orderBy($sort->orders)->all(); if $sort is Sort
91
92
        $dataProvider = Yii::createObject(ArrayHelper::merge([
93
            'class' => ActiveDataProvider::class,
94
            'query' => $query
95
        ], $dataProviderConfig));
96
97
        if (!($this->load($params) && $this->validate())) {
0 ignored issues
show
Bug introduced by
It seems like load() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
Bug introduced by
It seems like validate() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
98
            return $dataProvider;
99
        }
100
101
        foreach ($this->attributes() as $attribute) {
102
            if (($value = $this->{$attribute}) === null) {
103
                continue;
104
            }
105
            /*
106
             * Extracts underscore suffix from the key.
107
             *
108
             * Examples:
109
             * client_id -> 0 - client_id, 1 - client, 2 - _id, 3 - id
110
             * server_owner_like -> 0 - server_owner_like, 1 - server_owner, 2 - _like, 3 - like
111
             */
112
            preg_match('/^(.*?)(_((?:.(?!_))+))?$/', $attribute, $matches);
113
114
            /// If the suffix is in the list of acceptable suffix filer conditions
115
            if ($matches[3] && in_array($matches[3], static::$filterConditions, true)) {
116
                $cmp = $matches[3];
117
                $attribute = $matches[1];
118
            } else {
119
                $cmp = 'eq';
120
            }
121
            $query->andFilterWhere([$cmp, $attribute, $value]);
122
        }
123
124
        return $dataProvider;
125
    }
126
127
    /**
128
     * Returns default labels.
129
     * Solves infinite recursion problem.
130
     * @return array
131
     */
132
    public function defaultAttributeLabels()
133
    {
134
        /// XXX parent::attributeLabels() creates infinite recursion
135
        $class = parent::class;
136
137
        return (new $class())->attributeLabels();
138
    }
139
}
140