Completed
Push — master ( e03c3a...46c0e5 )
by Vladimir
01:43
created

src/SearchComponent.php (3 issues)

Labels
Severity
1
<?php
2
/**
3
 * @link https://github.com/Vintage-web-production/yii2-search
4
 * @copyright Copyright (c) 2017 Vintage Web Production
5
 * @license BSD 3-Clause License
6
 */
7
8
namespace vintage\search;
9
10
use vintage\search\interfaces\CustomSearchInterface;
11
use Yii;
12
use yii\base\Component;
13
use yii\base\InvalidConfigException;
14
use yii\base\Model;
15
use yii\db\BaseActiveRecord;
16
use yii\helpers\ArrayHelper;
17
use vintage\search\data\SearchResult;
18
use vintage\search\interfaces\SearchInterface;
19
20
/**
21
 * Component for search in Active Record models.
22
 *
23
 * @author Vladimir Kuprienko <[email protected]>
24
 * @since 1.0
25
 */
26
class SearchComponent extends Component
27
{
28
    /**
29
     * @var array Array with configuration of models for search.
30
     * @example
31
     * ```php
32
     * 'models' => [
33
     *      'articles' => [
34
     *          'class' => Article::class,
35
     *          'label' => 'Articles',
36
     *      ],
37
     *      'products' => [
38
     *          'class' => Product::class,
39
     *          'label' => 'Shop products',
40
     *      ],
41
     *      // ...
42
     * ]
43
     * ```
44
     */
45
    public $models = [];
46
47
    /**
48
     * @var SearchResult[] Array of the search result.
49
     */
50
    protected $result = [];
51
52
53
    /**
54
     * Method for searching.
55
     *
56
     * @example
57
     * ```php
58
     * $result = Yii::$app->get('searcher')->search('query for searching');
59
     * ```
60
     *
61
     * @param string $query Keywords for search.
62
     * @return SearchResult[] Array of the result objects.
63
     * @throws \Exception
64
     * @throws InvalidConfigException
65
     */
66
    public function search($query)
67
    {
68
        foreach($this->models as $model) {
69
            /* @var BaseActiveRecord|SearchInterface $searchModel */
70
            $searchModel = Yii::createObject($model['class']);
71
72
            if(!$this->isSearchModel($searchModel)) {
73
                throw new InvalidConfigException(
74
                    $model['class'] . 'should be instance of `\vintage\search\interfaces\SearchInterface` and `\yii\db\ActiveRecordInterface`'
75
                );
76
            }
77
78
            $activeQuery = $searchModel::find();
0 ignored issues
show
The method find() does not exist on vintage\search\interfaces\SearchInterface. It seems like you code against a sub-type of said class. However, the method does not exist in vintage\search\interfaces\CustomSearchInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

78
            /** @scrutinizer ignore-call */ 
79
            $activeQuery = $searchModel::find();
Loading history...
79
            foreach($searchModel->getSearchFields() as $field) {
80
                if(!$searchModel->hasAttribute($field)) {
0 ignored issues
show
The method hasAttribute() does not exist on vintage\search\interfaces\CustomSearchInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to vintage\search\interfaces\CustomSearchInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

80
                if(!$searchModel->/** @scrutinizer ignore-call */ hasAttribute($field)) {
Loading history...
The method hasAttribute() does not exist on vintage\search\interfaces\SearchInterface. It seems like you code against a sub-type of said class. However, the method does not exist in vintage\search\interfaces\CustomSearchInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

80
                if(!$searchModel->/** @scrutinizer ignore-call */ hasAttribute($field)) {
Loading history...
81
                    throw new \Exception(sprintf("Field `%s` not found in `%s` model", $field, $model['class']));
82
                }
83
84
                $activeQuery = ($searchModel instanceof CustomSearchInterface)
85
                    ? $searchModel->getQuery($activeQuery, $field, $query)
86
                    : $activeQuery->orWhere(['like', $field, $query]);
87
            }
88
            $this->addToResult($activeQuery->all());
89
        }
90
        return $this->result;
91
    }
92
93
    /**
94
     * Getting model label by model name.
95
     *
96
     * @param string $modelName
97
     * @return null|string
98
     */
99
    public function getModelLabel($modelName)
100
    {
101
        foreach($this->models as $model) {
102
            if($model['class'] == $modelName) {
103
                return $model['label'];
104
            }
105
        }
106
        return null;
107
    }
108
109
    /**
110
     * Method for adding iteration result to final result.
111
     *
112
     * @param \yii\db\ActiveRecord[] $foundModels
113
     */
114
    protected function addToResult($foundModels)
115
    {
116
        if ($foundModels !== null) {
117
            $tmp = SearchResult::buildMultiply($foundModels);
118
            $this->result = ArrayHelper::merge($tmp, $this->result);
119
        }
120
    }
121
122
    /**
123
     * Check whether given model is search model.
124
     *
125
     * @param Model $model
126
     * @return bool `true` if given model is search model.
127
     * @since 1.1
128
     */
129
    protected function isSearchModel(Model $model)
130
    {
131
        return $model instanceof BaseActiveRecord
132
            && $model instanceof SearchInterface;
133
    }
134
}
135