Passed
Pull Request — master (#14)
by Alexander
02:11
created

LoaderBehavior::notFound()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3.1406

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 3
cts 4
cp 0.75
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 2
nop 1
crap 3.1406
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 = 'dependency';
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
        }
135
136
        /** @var ActiveQuery $query */
137
        $query = call_user_func([$this->targetClass, 'find']);
138
        if (is_callable($this->queryFilter)) {
139
            $query = call_user_func($this->queryFilter, $query, $id);
140
        } elseif (!empty($this->targetAttribute)) {
141
            $query->andWhere(['=', $this->targetAttribute, $id]);
142
        } else {
143
            throw new InvalidConfigException("Query filter or target attribute have to be configured.");
144
        }
145
146
        $record = $query->one();
147
        if (!$record instanceof $this->targetClass) {
148
            $this->notFound();
149
        }
150
151
        if (is_callable($this->load)) {
152
            call_user_func($this->load, $record);
153
        } elseif (!empty($this->attribute)) {
154
            $closure = function (ActiveRecord $value, string $attribute) {
155
                $this->{$attribute} = $value;
156
            };
157
            $closure->call($this->owner, $record, $this->attribute);
158
        }
159
    }
160
161
    /**
162
     * @param int $id
163
     * @throws NotFoundHttpException
164
     */
165 1
    protected function notFound(int $id = null): void
166
    {
167 1
        if (is_callable($this->notFoundCallback)) {
168
            call_user_func($this->notFoundCallback, $this);
169
        }
170
171 1
        throw new NotFoundHttpException($id ? "Resource {$id} not found" : "Resource id was not specified.");
172
    }
173
}
174