Completed
Pull Request — trunk (#541)
by Justin
07:19
created

CMB2_REST_Controller_Fields::get_item()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 3
eloc 8
c 3
b 0
f 0
nc 3
nop 1
dl 0
loc 15
rs 9.4285
1
<?php
2
/**
3
 * CMB2 objects/fields endpoint for WordPres REST API.
4
 * Allows access to fields registered to a specific box.
5
 *
6
 * @todo  Add better documentation.
7
 * @todo  Research proper schema.
8
 *
9
 * @since 2.2.4
10
 *
11
 * @category  WordPress_Plugin
12
 * @package   CMB2
13
 * @author    WebDevStudios
14
 * @license   GPL-2.0+
15
 * @link      http://webdevstudios.com
16
 */
17
class CMB2_REST_Controller_Fields extends CMB2_REST_Controller_Boxes {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
18
19
	/**
20
	 * Register the routes for the objects of the controller.
21
	 *
22
	 * @since 2.2.4
23
	 */
24 View Code Duplication
	public function register_routes() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
25
26
		// Returns specific box's fields.
27
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<cmb_id>[\w-]+)/fields/', array(
28
			array(
29
				'methods'  => WP_REST_Server::READABLE,
30
				'callback' => array( $this, 'get_items' ),
31
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
32
			),
33
			'schema' => array( $this, 'get_item_schema' ),
34
		) );
35
36
		// Returns specific field data.
37
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<cmb_id>[\w-]+)/fields/(?P<field_id>[\w-]+)', array(
38
			array(
39
				'methods'  => WP_REST_Server::READABLE,
40
				'callback' => array( $this, 'get_item' ),
41
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
42
			),
43
			'schema' => array( $this, 'get_item_schema' ),
44
		) );
45
	}
46
47
	/**
48
	 * Get all public CMB2 box fields.
49
	 *
50
	 * @since 2.2.4
51
	 *
52
	 * @param  WP_REST_Request $request Full data about the request.
53
	 * @return WP_Error|WP_REST_Response
54
	 */
55
	public function get_items( $request ) {
56
		$this->initiate_rest_read_box( $request, 'fields_read' );
57
58
		if ( is_wp_error( $this->rest_box ) ) {
59
			return $this->prepare_item( array( 'error' => $this->rest_box->get_error_message() ) );
0 ignored issues
show
Bug introduced by
The method get_error_message() does not seem to exist on object<CMB2_REST>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
60
		}
61
62
		$fields = array();
63
		foreach ( $this->rest_box->cmb->prop( 'fields', array() ) as $field ) {
64
			$field_id = $field['id'];
65
			$rest_field = $this->get_rest_field( $field_id );
66
67
			if ( ! is_wp_error( $rest_field ) ) {
68
				$fields[ $field_id ] = $this->server->response_to_data( $rest_field, isset( $this->request['_embed'] ) );
69
			} else {
70
				$fields[ $field_id ] = array( 'error' => $rest_field->get_error_message() );
71
			}
72
		}
73
74
		return $this->prepare_item( $fields );
75
	}
76
77
	/**
78
	 * Get one CMB2 field from the collection.
79
	 *
80
	 * @since 2.2.4
81
	 *
82
	 * @param  WP_REST_Request $request Full data about the request.
83
	 * @return WP_Error|WP_REST_Response
84
	 */
85
	public function get_item( $request ) {
86
		$this->initiate_rest_read_box( $request, 'field_read' );
87
88
		if ( is_wp_error( $this->rest_box ) ) {
89
			return $this->prepare_item( array( 'error' => $this->rest_box->get_error_message() ) );
0 ignored issues
show
Bug introduced by
The method get_error_message() does not seem to exist on object<CMB2_REST>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
90
		}
91
92
		$field = $this->get_rest_field( $this->request->get_param( 'field_id' ) );
93
94
		if ( is_wp_error( $field ) ) {
95
			return $this->prepare_item( array( 'error' => $field->get_error_message() ) );
96
		}
97
98
		return $this->prepare_item( $field );
99
	}
100
101
	/**
102
	 * Get a specific field
103
	 *
104
	 * @since 2.2.4
105
	 *
106
	 * @param  string Field id
107
	 * @return array|WP_Error
108
	 */
109
	public function get_rest_field( $field_id ) {
110
		$field = $this->rest_box->field_can_read( $field_id, true );
111
112
		if ( ! $field ) {
113
			return new WP_Error( 'cmb2_rest_error', __( 'No field found by that id.', 'cmb2' ) );
114
		}
115
116
		$field_data = $this->prepare_field_data( $field );
0 ignored issues
show
Bug introduced by
It seems like $field defined by $this->rest_box->field_can_read($field_id, true) on line 110 can also be of type boolean; however, CMB2_REST_Controller_Fields::prepare_field_data() does only seem to accept object<CMB2_Field>, 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...
117
		$response = rest_ensure_response( $field_data );
118
119
		$response->add_links( $this->prepare_links( $field ) );
0 ignored issues
show
Bug introduced by
It seems like $field defined by $this->rest_box->field_can_read($field_id, true) on line 110 can also be of type boolean; however, CMB2_REST_Controller_Fields::prepare_links() does only seem to accept object<CMB2_Field>, 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...
120
121
		return $response;
122
	}
123
124
	/**
125
	 * Prepare the field data array for JSON.
126
	 *
127
	 * @since  2.2.4
128
	 *
129
	 * @param  CMB2_Field $field field object.
130
	 *
131
	 * @return array             Array of field data.
132
	 */
133
	protected function prepare_field_data( CMB2_Field $field ) {
134
		$field_data = array();
135
		$params_to_ignore = array( 'show_in_rest', 'options' );
136
		$params_to_rename = array(
137
			'label_cb' => 'label',
138
			'options_cb' => 'options',
139
		);
140
141
		// Run this first so the js_dependencies arg is populated.
142
		$rendered = ( $cb = $field->maybe_callback( 'render_row_cb' ) )
143
			// Ok, callback is good, let's run it.
144
			? $this->get_cb_results( $cb, $field->args(), $field )
145
			: false;
146
147
		$field_args = $field->args();
148
149
		foreach ( $field_args as $key => $value ) {
150
			if ( in_array( $key, $params_to_ignore, true ) ) {
151
				continue;
152
			}
153
154
			if ( 'options_cb' === $key ) {
155
				$value = $field->options();
156
			} elseif ( in_array( $key, CMB2_Field::$callable_fields, true ) ) {
157
158
				if ( isset( $this->request['_rendered'] ) ) {
159
					$value = $key === 'render_row_cb' ? $rendered : $field->get_param_callback_result( $key );
160
				} elseif ( is_array( $value ) ) {
161
					// We need to rewrite callbacks as string as they will cause
162
					// JSON recursion errors.
163
					$class = is_string( $value[0] ) ? $value[0] : get_class( $value[0] );
164
					$value = $class . '::' . $value[1];
165
				}
166
			}
167
168
			$key = isset( $params_to_rename[ $key ] ) ? $params_to_rename[ $key ] : $key;
169
170
			if ( empty( $value ) || is_scalar( $value ) || is_array( $value ) ) {
171
				$field_data[ $key ] = $value;
172
			} else {
173
				$field_data[ $key ] = sprintf( __( 'Value Error for %s', 'cmb2' ), $key );
174
			}
175
		}
176
177
		if ( $this->request['object_id'] && $this->request['object_type'] ) {
178
			$field_data['value'] = $field->get_data();
179
		}
180
181
		return $field_data;
182
	}
183
184
	/**
185
	 * Return an array of contextual links for field/fields.
186
	 *
187
	 * @since  2.2.4
188
	 *
189
	 * @param  CMB2_Field $field Field object to build links from.
190
	 *
191
	 * @return array             Array of links
192
	 */
193
	protected function prepare_links( $field ) {
194
		$boxbase      = $this->namespace_base . '/' . $this->rest_box->cmb->cmb_id;
195
		$query_string = $this->get_query_string();
196
197
		$links = array(
198
			'self' => array(
199
				'href' => rest_url( trailingslashit( $boxbase ) . 'fields/' . $field->_id() . $query_string ),
200
			),
201
			'collection' => array(
202
				'href' => rest_url( trailingslashit( $boxbase ) . 'fields' . $query_string ),
203
			),
204
			'up' => array(
205
				'href' => rest_url( $boxbase . $query_string ),
206
			),
207
		);
208
209
		// Don't embed boxes when looking at boxes route.
210
		if ( '/cmb2/v1/boxes' !== CMB2_REST_Controller::get_intial_route() ) {
211
			$links['up']['embeddable'] = true;
212
		}
213
214
		return $links;
215
	}
216
217
}
218