Completed
Branch FET-10766-extract-activation-d... (a650cc)
by
unknown
116:57 queued 106:13
created

Write::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace EventEspresso\core\libraries\rest_api\controllers\model;
3
4
use EE_DB_Only_Field_Base;
5
use \WP_REST_Request;
6
use \WP_REST_Response;
7
use EventEspresso\core\libraries\rest_api\Capabilities;
8
use EventEspresso\core\libraries\rest_api\ModelDataTranslator;
9
use EventEspresso\core\libraries\rest_api\RestException;
10
use \EEM_Base;
11
use \EE_Base_Class;
12
use \EE_Registry;
13
use \EE_Datetime_Field;
14
use \EEM_Soft_Delete_Base;
15
use EE_Restriction_Generator_Base;
16
use EED_Core_Rest_Api;
17
use EEH_Inflector;
18
use EE_Error;
19
20
if (! defined('EVENT_ESPRESSO_VERSION')) {
21
    exit('No direct script access allowed');
22
}
23
24
25
26
/**
27
 * Write controller for models
28
 * Handles requests relating to GET-ting model information
29
 *
30
 * @package               Event Espresso
31
 * @subpackage
32
 * @author                Mike Nelson
33
 */
34
class Write extends Base
35
{
36
37
38
39
    public function __construct()
40
    {
41
        parent::__construct();
42
        EE_Registry::instance()->load_helper('Inflector');
43
    }
44
45
46
47
    /**
48
     * Handles requests to get all (or a filtered subset) of entities for a particular model
49
     *
50
     * @param WP_REST_Request $request
51
     * @param string          $version
52
     * @param string          $model_name
53
     * @return WP_REST_Response|\WP_Error
54
     */
55 View Code Duplication
    public static function handleRequestInsert(WP_REST_Request $request, $version, $model_name)
56
    {
57
        $controller = new Write();
58
        try {
59
            $controller->setRequestedVersion($version);
60
            return $controller->sendResponse(
61
                $controller->insert(
62
                    $controller->getModelVersionInfo()->loadModel($model_name),
0 ignored issues
show
Bug introduced by
It seems like $controller->getModelVer...>loadModel($model_name) can be null; however, insert() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
63
                    $request
64
                )
65
            );
66
        } catch (\Exception $e) {
67
            return $controller->sendResponse($e);
68
        }
69
    }
70
71
72
73
    /**
74
     * Handles a request from \WP_REST_Server to update an EE model
75
     *
76
     * @param WP_REST_Request $request
77
     * @param string          $version
78
     * @param string          $model_name
79
     * @return WP_REST_Response|\WP_Error
80
     */
81 View Code Duplication
    public static function handleRequestUpdate(WP_REST_Request $request, $version, $model_name)
82
    {
83
        $controller = new Write();
84
        try {
85
            $controller->setRequestedVersion($version);
86
            return $controller->sendResponse(
87
                $controller->update(
88
                    $controller->getModelVersionInfo()->loadModel($model_name),
0 ignored issues
show
Bug introduced by
It seems like $controller->getModelVer...>loadModel($model_name) can be null; however, update() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
89
                    $request
90
                )
91
            );
92
        } catch (\Exception $e) {
93
            return $controller->sendResponse($e);
94
        }
95
    }
96
97
98
99
    /**
100
     * Deletes a single model object and returns it. Unless
101
     *
102
     * @param WP_REST_Request $request
103
     * @param string          $version
104
     * @param string          $model_name
105
     * @return WP_REST_Response|\WP_Error
106
     */
107 View Code Duplication
    public static function handleRequestDelete(WP_REST_Request $request, $version, $model_name)
108
    {
109
        $controller = new Write();
110
        try {
111
            $controller->setRequestedVersion($version);
112
            return $controller->sendResponse(
113
                $controller->delete(
114
                    $controller->getModelVersionInfo()->loadModel($model_name),
0 ignored issues
show
Bug introduced by
It seems like $controller->getModelVer...>loadModel($model_name) can be null; however, delete() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
115
                    $request
116
                )
117
            );
118
        } catch (\Exception $e) {
119
            return $controller->sendResponse($e);
120
        }
121
    }
122
123
124
125
    /**
126
     * Inserts a new model object according to the $request
127
     *
128
     * @param EEM_Base        $model
129
     * @param WP_REST_Request $request
130
     * @return array
131
     * @throws EE_Error
132
     * @throws RestException
133
     */
134
    public function insert(EEM_Base $model, WP_REST_Request $request)
135
    {
136
        Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_edit, 'create');
137
        $default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
138 View Code Duplication
        if (! current_user_can($default_cap_to_check_for)) {
139
            throw new RestException(
140
                'rest_cannot_create_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
141
                sprintf(
142
                    esc_html__(
143
                        // @codingStandardsIgnoreStart
144
                        'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to insert data into Event Espresso.',
145
                        // @codingStandardsIgnoreEnd
146
                        'event_espresso'
147
                    ),
148
                    $default_cap_to_check_for
149
                ),
150
                array('status' => 403)
151
            );
152
        }
153
        $submitted_json_data = array_merge((array)$request->get_body_params(), (array)$request->get_json_params());
154
        $model_data = ModelDataTranslator::prepareConditionsQueryParamsForModels(
155
            $submitted_json_data,
156
            $model,
157
            $this->getModelVersionInfo()->requestedVersion(),
158
            true
159
        );
160
        $model_obj = EE_Registry::instance()->load_class(
161
            $model->get_this_model_name(),
162
            array($model_data, $model->get_timezone()),
163
            false,
164
            false
165
        );
166
        $model_obj->save();
167
        $new_id = $model_obj->ID();
168
        if (! $new_id) {
169
            throw new RestException(
170
                'rest_insertion_failed',
171
                sprintf(__('Could not insert new %1$s', 'event_espresso'), $model->get_this_model_name())
172
            );
173
        }
174
        return $this->returnModelObjAsJsonResponse($model_obj, $request);
175
    }
176
177
178
179
    /**
180
     * Updates an existing model object according to the $request
181
     *
182
     * @param EEM_Base        $model
183
     * @param WP_REST_Request $request
184
     * @return array
185
     * @throws EE_Error
186
     */
187
    public function update(EEM_Base $model, WP_REST_Request $request)
188
    {
189
        Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_edit, 'edit');
190
        $default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
191 View Code Duplication
        if (! current_user_can($default_cap_to_check_for)) {
192
            throw new RestException(
193
                'rest_cannot_edit_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
194
                sprintf(
195
                    esc_html__(
196
                        // @codingStandardsIgnoreStart
197
                        'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to update data into Event Espresso.',
198
                        // @codingStandardsIgnoreEnd
199
                        'event_espresso'
200
                    ),
201
                    $default_cap_to_check_for
202
                ),
203
                array('status' => 403)
204
            );
205
        }
206
        $obj_id = $request->get_param('id');
207
        if (! $obj_id) {
208
            throw new RestException(
209
                'rest_edit_failed',
210
                sprintf(__('Could not edit %1$s', 'event_espresso'), $model->get_this_model_name())
211
            );
212
        }
213
        $model_data = ModelDataTranslator::prepareConditionsQueryParamsForModels(
214
            $this->getBodyParams($request),
215
            $model,
216
            $this->getModelVersionInfo()->requestedVersion(),
217
            true
218
        );
219
        $model_obj = $model->get_one_by_ID($obj_id);
220 View Code Duplication
        if (! $model_obj instanceof EE_Base_Class) {
221
            $lowercase_model_name = strtolower($model->get_this_model_name());
222
            throw new RestException(
223
                sprintf('rest_%s_invalid_id', $lowercase_model_name),
224
                sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
225
                array('status' => 404)
226
            );
227
        }
228
        $model_obj->save($model_data);
229
        return $this->returnModelObjAsJsonResponse($model_obj, $request);
230
    }
231
232
233
234
    /**
235
     * Updates an existing model object according to the $request
236
     *
237
     * @param EEM_Base        $model
238
     * @param WP_REST_Request $request
239
     * @return array of either the soft-deleted item, or
240
     * @throws EE_Error
241
     */
242
    public function delete(EEM_Base $model, WP_REST_Request $request)
243
    {
244
        Capabilities::verifyAtLeastPartialAccessTo($model, EEM_Base::caps_delete, 'delete');
245
        $default_cap_to_check_for = EE_Restriction_Generator_Base::get_default_restrictions_cap();
246 View Code Duplication
        if (! current_user_can($default_cap_to_check_for)) {
247
            throw new RestException(
248
                'rest_cannot_delete_' . EEH_Inflector::pluralize_and_lower(($model->get_this_model_name())),
249
                sprintf(
250
                    esc_html__(
251
                        // @codingStandardsIgnoreStart
252
                        'For now, only those with the admin capability to "%1$s" are allowed to use the REST API to delete data into Event Espresso.',
253
                        // @codingStandardsIgnoreEnd
254
                        'event_espresso'
255
                    ),
256
                    $default_cap_to_check_for
257
                ),
258
                array('status' => 403)
259
            );
260
        }
261
        $obj_id = $request->get_param('id');
262
        //this is where we would apply more fine-grained caps
263
        $model_obj = $model->get_one_by_ID($obj_id);
264 View Code Duplication
        if (! $model_obj instanceof EE_Base_Class) {
265
            $lowercase_model_name = strtolower($model->get_this_model_name());
266
            throw new RestException(
267
                sprintf('rest_%s_invalid_id', $lowercase_model_name),
268
                sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
269
                array('status' => 404)
270
            );
271
        }
272
        $requested_permanent_delete = filter_var($request->get_param('force'), FILTER_VALIDATE_BOOLEAN);
273
        $requested_allow_blocking = filter_var($request->get_param('allow_blocking'), FILTER_VALIDATE_BOOLEAN);
274
        if ($requested_permanent_delete) {
275
            $previous = $this->returnModelObjAsJsonResponse($model_obj, $request);
276
            $deleted = (bool)$model->delete_permanently_by_ID($obj_id, $requested_allow_blocking);
277
            return array(
278
                'deleted'  => $deleted,
279
                'previous' => $previous,
280
            );
281
        } else {
282
            if ($model instanceof EEM_Soft_Delete_Base) {
283
                $model->delete_by_ID($obj_id, $requested_allow_blocking);
284
                return $this->returnModelObjAsJsonResponse($model_obj, $request);
285
            } else {
286
                throw new RestException(
287
                    'rest_trash_not_supported',
288
                    501,
289
                    sprintf(
290
                        esc_html__('%1$s do not support trashing. Set force=1 to delete.', 'event_espresso'),
291
                        EEH_Inflector::pluralize($model->get_this_model_name())
292
                    )
293
                );
294
            }
295
        }
296
    }
297
298
299
300
    /**
301
     * Returns an array ready to be converted into a JSON response, based solely on the model object
302
     *
303
     * @param EE_Base_Class $model_obj
304
     * @param WP_REST_Request $request
305
     * @return array ready for a response
306
     */
307
    protected function returnModelObjAsJsonResponse(EE_Base_Class $model_obj, WP_REST_Request $request)
308
    {
309
        $model = $model_obj->get_model();
310
        //create an array exactly like the wpdb results row,
311
        // so we can pass it to controllers/model/Read::create_entity_from_wpdb_result()
312
        $simulated_db_row = array();
313
        foreach ($model->field_settings(true) as $field_name => $field_obj) {
314
            //we need to reconstruct the normal wpdb results, including the db-only fields
315
            //like a secondary table's primary key. The models expect those (but don't care what value they have)
316
            if( $field_obj instanceof EE_DB_Only_Field_Base){
317
                $raw_value = true;
318
            } elseif ($field_obj instanceof EE_Datetime_Field) {
319
                $raw_value = $model_obj->get_DateTime_object($field_name);
320
            } else {
321
                $raw_value = $model_obj->get_raw($field_name);
322
            }
323
            $simulated_db_row[$field_obj->get_qualified_column()] = $field_obj->prepare_for_use_in_db($raw_value);
324
        }
325
        $read_controller = new Read();
326
        $read_controller->setRequestedVersion($this->getRequestedVersion());
327
        //the simulates request really doesn't need any info downstream
328
        $simulated_request = new WP_REST_Request('GET');
329
        return $read_controller->createEntityFromWpdbResult(
330
            $model_obj->get_model(),
0 ignored issues
show
Bug introduced by
It seems like $model_obj->get_model() can be null; however, createEntityFromWpdbResult() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
331
            $simulated_db_row,
332
            $simulated_request
333
        );
334
    }
335
336
337
338
    /**
339
     * Gets the item affected by this request
340
     *
341
     * @param EEM_Base        $model
342
     * @param WP_REST_Request $request
343
     * @param  int|string     $obj_id
344
     * @return \WP_Error|array
345
     */
346
    protected function getOneBasedOnRequest(EEM_Base $model, WP_REST_Request $request, $obj_id)
347
    {
348
        $requested_version = $this->getRequestedVersion($request->get_route());
349
        $get_request = new WP_REST_Request(
350
            'GET',
351
            EED_Core_Rest_Api::ee_api_namespace
352
            . $requested_version
353
            . '/'
354
            . EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
355
            . '/'
356
            . $obj_id
357
        );
358
        $get_request->set_url_params(
359
            array(
360
                'id'      => $obj_id,
361
                'include' => $request->get_param('include'),
362
            )
363
        );
364
        $read_controller = new Read();
365
        $read_controller->setRequestedVersion($this->getRequestedVersion());
366
        return $read_controller->getEntityFromModel($model, $get_request);
367
    }
368
}
369
// End of file Read.php
370