Passed
Push — master ( 7ac5db...ad4b0f )
by Alexander
11:06
created

LoaderBehavior::load()   D

Complexity

Conditions 9
Paths 30

Size

Total Lines 40
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 47.1143

Importance

Changes 0
Metric Value
dl 0
loc 40
ccs 6
cts 27
cp 0.2222
rs 4.909
c 0
b 0
f 0
cc 9
eloc 26
nc 30
nop 0
crap 47.1143
1
<?php
2
3
namespace Horat1us\Yii\Behaviors;
4
5
use yii\base\Behavior;
6
use yii\base\InvalidConfigException;
7
use yii\base\Model;
8
9
use yii\db\ActiveQuery;
10
use yii\db\ActiveRecord;
11
12
use yii\helpers\Inflector;
13
use yii\web\NotFoundHttpException;
14
15
/**
16
 * Class LoaderBehavior
17
 * @package Horat1us\Yii\Behaviors
18
 *
19
 * @todo tests
20
 */
21
class LoaderBehavior extends Behavior
22
{
23
    /**
24
     * Callback, returns identifier value (will be used in filtering)
25
     * @see targetAttribute
26
     * @see queryFilter
27
     *
28
     * ```php
29
     * <?php
30
     * $id = function(): string {
31
     *  return \Yii::$app->request->post('some-post-value');
32
     * };
33
     * ```
34
     *
35
     * Or string - name of get parameter
36
     *
37
     * @var string|callable
38
     */
39
    public $id = 'id';
40
41
    /**
42
     * ActiveRecord class
43
     * will be used for creating query
44
     *
45
     * @var string
46
     */
47
    public $targetClass;
48
49
    /**
50
     * ActiveRecord attribute that will be used for auto filtering
51
     * @var string
52
     */
53
    public $targetAttribute = 'id';
54
55
    /**
56
     * Custom query filter.
57
     * Receives query as first argument and id value as second
58
     * Have to return ActiveQuery:
59
     *
60
     * ```php
61
     * <?php
62
     * $queryFilter = function(\yii\db\ActiveQuery $query, string $id) {
63
     *  return $query->andWhere(['=', 'some_attribute', $id + 1]);
64
     * };
65
     * ```
66
     *
67
     * @var callable
68
     */
69
    public $queryFilter;
70
71
    /**
72
     * Model attribute to load record to
73
     * Default value will be counted from targetClass::tableName method
74
     *
75
     * For example: if your active record table name is `post_comment_author`
76
     * this value will be set to `postCommentAuthor`
77
     *
78
     * @see ActiveRecord::tableName()
79
     * @see Inflector::camelize()
80
     *
81
     * @var string
82
     */
83
    public $attribute;
84
85
    /**
86
     * Callable that receives record if it found
87
     *
88
     * ```php
89
     * <?php
90
     * $load = function(\yii\db\ActiveRecord $record) use($model) {
91
     *  $model->dependency = $record;
92
     * };
93
     * ```
94
     *
95
     * @var callable
96
     */
97
    public $load;
98
99
    /** @var callable */
100
    public $notFoundCallback;
101
102 1
    public function events()
103
    {
104
        return [
105 1
            Model::EVENT_BEFORE_VALIDATE => [$this, 'load'],
106
        ];
107
    }
108
109 1
    public function init()
110
    {
111 1
        parent::init();
112 1
        if (!empty($this->targetClass) && empty($this->attribute)) {
113
            /** @see ActiveRecord::tableName() */
114
            $this->attribute = lcfirst(Inflector::camelize(call_user_func([$this->targetClass, 'tableName'])));
115
        }
116 1
    }
117
118
    /**
119
     * @throws NotFoundHttpException
120
     * @throws InvalidConfigException
121
     */
122 1
    public function load(): void
123
    {
124 1
        $id = null;
125
126 1
        if (is_string($this->id)) {
127 1
            $id = \Yii::$app->request->get($this->id);
128
        } elseif (is_callable($this->id)) {
129
            $id = call_user_func($this->id, $this);
130
        }
131
132 1
        if (empty($id)) {
133 1
            $this->notFound($id);
134
            return;
135
        }
136
137
        /** @var ActiveQuery $query */
138
        $query = call_user_func([$this->targetClass, 'find']);
139
        if (is_callable($this->queryFilter)) {
140
            $query = call_user_func($this->queryFilter, $query, $id);
141
        } elseif (!empty($this->targetAttribute)) {
142
            $query->andWhere(['=', $this->targetAttribute, $id]);
143
        } else {
144
            throw new InvalidConfigException("Query filter or target attribute have to be configured.");
145
        }
146
147
        $record = $query->one();
148
        if (!$record instanceof $this->targetClass) {
149
            $this->notFound();
150
            return;
151
        }
152
153
        if (is_callable($this->load)) {
154
            call_user_func($this->load, $record);
155
        } elseif (!empty($this->attribute)) {
156
            $closure = function (ActiveRecord $value, string $attribute) {
157
                $this->{$attribute} = $value;
158
            };
159
            $closure->call($this->owner, $record, $this->attribute);
160
        }
161
    }
162
163
    /**
164
     * @param int $id
165
     * @throws NotFoundHttpException
166
     */
167 1
    protected function notFound(int $id = null): void
168
    {
169 1
        if (is_callable($this->notFoundCallback)) {
170
            call_user_func($this->notFoundCallback, $this);
171
            return;
172
        }
173
174 1
        throw new NotFoundHttpException($id ? "Resource {$id} not found" : "Resource id was not specified.");
175
    }
176
}
177