Completed
Push — master ( b25177...fd4d50 )
by Basil
06:35
created

RestBehaviorsTrait::sendArrayError()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
c 0
b 0
f 0
rs 9.4285
cc 3
eloc 8
nc 3
nop 1
1
<?php
2
3
namespace luya\traits;
4
5
use Yii;
6
use yii\web\Response;
7
use yii\filters\auth\CompositeAuth;
8
use yii\filters\auth\QueryParamAuth;
9
use yii\filters\auth\HttpBearerAuth;
10
use yii\filters\ContentNegotiator;
11
use yii\filters\Cors;
12
use yii\base\Model;
13
use yii\base\InvalidParamException;
14
use luya\rest\UserBehaviorInterface;
15
16
/**
17
 * Rest Behaviors Trait.
18
 *
19
 * This class overrides the default behaviors method of {{yii\rest\Controller}} controllers.
20
 *
21
 * The following changes are differ to the base implementation:
22
 *
23
 * + If {{luya\rest\UserBehaviorInterface}} is **not** implemented, the `authenticator` behavior ({{yii\filters\auth\CompositeAuth}}) is removed.
24
 * + If {{luya\rest\UserBehaviorInterface}} **is** implemented, the `authenticator` behavior ({{yii\filters\auth\CompositeAuth}}) is enabled.
25
 * + If {{luya\rest\UserBehaviorInterface}} **is** implemented, the `contentNegotiator` behavior ({{yii\filters\ContentNegotiator}}) is enabled.
26
 * + The `rateLimiter` behavior filter is **removed** by default.
27
 *
28
 * Read the {{luya\rest\UserBehaviorInterface}} about the configuration ability to protect the controller.
29
 *
30
 * @author Basil Suter <[email protected]>
31
 * @since 1.0.0
32
 */
33
trait RestBehaviorsTrait
34
{
35
    /**
36
     * @var boolean Whether CORS is enabled or not.
37
     */
38
    public $enableCors = false;
39
    
40
    /**
41
     * Whether the rest controller is protected or not.
42
     *
43
     * @return boolean|\yii\web\User
44
     */
45
    private function getUserAuthClass()
46
    {
47
        if ($this instanceof UserBehaviorInterface) {
48
            $class = $this->userAuthClass();
49
            
50
            if (!$class) { // return false;
51
                return false;
52
            }
53
            
54
            if (!is_object($class)) {
55
                return Yii::createObject($class);
0 ignored issues
show
Bug introduced by
It seems like $class defined by $this->userAuthClass() on line 48 can also be of type boolean; however, yii\BaseYii::createObject() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
56
            }
57
    
58
            return $class;
59
        }
60
        
61
        return false;
62
    }
63
64
    /**
65
     * Override the default {{yii\rest\Controller::behaviors()}} method.
66
     * The following changes are differ to the base implementation:
67
     *
68
     * + If {{luya\rest\UserBehaviorInterface}} is **not** implemented, the `authenticator` behavior ({{yii\filters\auth\CompositeAuth}}) is removed.
69
     * + If {{luya\rest\UserBehaviorInterface}} **is** implemented, the `authenticator` behavior ({{yii\filters\auth\CompositeAuth}}) is enabled.
70
     * + If {{luya\rest\UserBehaviorInterface}} **is** implemented, the `contentNegotiator` behavior ({{yii\filters\ContentNegotiator}}) is enabled.
71
     * + The `rateLimiter` behavior filter is **removed** by default.
72
     *
73
     * @return array Returns an array with registered behavior filters based on the implementation type.
74
     */
75
    public function behaviors()
76
    {
77
        $behaviors = parent::behaviors();
78
79
        if (!$this->getUserAuthClass()) {
80
            unset($behaviors['authenticator']);
81
        } else {
82
            // change to admin user auth class
83
            $behaviors['authenticator'] = [
84
                'class' => CompositeAuth::className(),
85
                'user' => $this->getUserAuthClass(),
86
                'authMethods' => [
87
                    QueryParamAuth::className(),
88
                    HttpBearerAuth::className(),
89
                ],
90
            ];
91
            
92
            if ($this->enableCors) {
93
                $behaviors['authenticator']['except'] = ['options'];
94
            }
95
        }
96
        
97
        if ($this->enableCors) {
98
            $behaviors['cors'] = Cors::class;
99
        }
100
101
        $behaviors['contentNegotiator'] = [
102
            'class' => ContentNegotiator::className(),
103
            'formats' => [
104
                'application/json' => Response::FORMAT_JSON,
105
                'application/xml' => Response::FORMAT_XML,
106
            ],
107
        ];
108
        
109
        // by default rate limiter behavior is removed as it requires a database
110
        // user given from the admin module.
111
        if (isset($behaviors['rateLimiter'])) {
112
            unset($behaviors['rateLimiter']);
113
        }
114
115
        return $behaviors;
116
    }
117
    
118
    /**
119
     * Send Model errors with correct headers.
120
     *
121
     * Helper method to correctly send model errors with the correct response headers.
122
     *
123
     * Example return value:
124
     *
125
     * ```php
126
     * Array
127
     * (
128
     *     [0] => Array
129
     *         (
130
     *             [field] => firstname
131
     *             [message] => Firstname cannot be blank.
132
     *         )
133
     *     [1] => Array
134
     *         (
135
     *             [field] => email
136
     *             [message] => Email cannot be blank.
137
     *         )
138
     * )
139
     * ```
140
     *
141
     * @param \yii\base\Model $model The model to find the first error.
142
     * @throws \yii\base\InvalidParamException
143
     * @return array If the model has errors InvalidParamException will be thrown, otherwise an array with message and field key.
144
     */
145
    public function sendModelError(Model $model)
146
    {
147
        if (!$model->hasErrors()) {
148
            throw new InvalidParamException('The model as thrown an uknown Error.');
149
        }
150
        
151
        Yii::$app->response->setStatusCode(422, 'Data Validation Failed.');
152
        $result = [];
153
        foreach ($model->getFirstErrors() as $name => $message) {
154
            $result[] = [
155
                'field' => $name,
156
                'message' => $message,
157
            ];
158
        }
159
        
160
        return $result;
161
    }
162
    
163
    /**
164
     * Send Array validation error.
165
     *
166
     * Example input:
167
     *
168
     * ```php
169
     * return $this->sendArrayError(['firstname' => 'Firstname cannot be blank']);
170
     * ```
171
     *
172
     * Example return value:
173
     *
174
     * ```php
175
     * Array
176
     * (
177
     *     [0] => Array
178
     *         (
179
     *             [field] => firstname
180
     *             [message] => Firstname cannot be blank.
181
     *         )
182
     * )
183
     * ```
184
     * @param array $errors Provide an array with messages. Where key is the field and value the message.
185
     * @return array Returns an array with field and message keys for each item.
186
     * @since 1.0.3
187
     */
188
    public function sendArrayError(array $errors)
189
    {
190
        Yii::$app->response->setStatusCode(422, 'Data Validation Failed.');
191
        $result = [];
192
        foreach ($errors as $key => $value) {
193
            $messages = (array) $value;
194
            
195
            foreach ($messages as $msg) {
196
                $result[] = ['field' => $key, 'message' => $msg];
197
            }
198
        }
199
        
200
        return $result;
201
    }
202
}
203