-
Notifications
You must be signed in to change notification settings - Fork 206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhance/product category rest api #2510
base: develop
Are you sure you want to change the base?
Changes from all commits
c277a20
bd6867f
a6001c6
068ac08
20b746a
a797ee3
60b9be2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,208 @@ | ||||||||||||||||||||||||
<?php | ||||||||||||||||||||||||
namespace WeDevs\Dokan\REST; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
use WeDevs\Dokan\Abstracts\DokanRESTVendorController; | ||||||||||||||||||||||||
use WP_Error; | ||||||||||||||||||||||||
use WP_REST_Request; | ||||||||||||||||||||||||
use WP_REST_Server; | ||||||||||||||||||||||||
use WP_Term; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
class ProductCategoriesVendorController extends DokanRESTVendorController { | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
protected $rest_base = 'product-categories'; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
public function register_routes() { | ||||||||||||||||||||||||
register_rest_route( | ||||||||||||||||||||||||
$this->namespace, '/' . $this->rest_base, [ | ||||||||||||||||||||||||
[ | ||||||||||||||||||||||||
'methods' => 'GET', | ||||||||||||||||||||||||
'callback' => array( $this, 'get_product_categories' ), | ||||||||||||||||||||||||
'permission_callback' => [ $this, 'check_permission' ], | ||||||||||||||||||||||||
'args' => $this->get_collection_params(), | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
], | ||||||||||||||||||||||||
] | ||||||||||||||||||||||||
); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/** | ||||||||||||||||||||||||
* Get the product categories | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* @param WP_REST_Request $request Full details about the request. | ||||||||||||||||||||||||
* @return \WP_REST_Response | \WP_Error Response object on success, or WP_Error object on failure. | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
public function get_product_categories( $request ) { | ||||||||||||||||||||||||
// Get parameters from request | ||||||||||||||||||||||||
$per_page = $request->get_param( 'per_page' ) ? $request->get_param( 'per_page' ) : 10; | ||||||||||||||||||||||||
$page = $request->get_param( 'page' ) ? $request->get_param( 'page' ) : 1; | ||||||||||||||||||||||||
$search = $request->get_param( 'search' ); | ||||||||||||||||||||||||
$exclude = $request->get_param( 'exclude' ); | ||||||||||||||||||||||||
$include = $request->get_param( 'include' ); | ||||||||||||||||||||||||
$order = $request->get_param( 'order' ); | ||||||||||||||||||||||||
$orderby = $request->get_param( 'orderby' ); | ||||||||||||||||||||||||
$hide_empty = $request->get_param( 'hide_empty' ); | ||||||||||||||||||||||||
$parent = $request->get_param( 'parent' ); | ||||||||||||||||||||||||
$fields = $request->get_param( '_fields' ); | ||||||||||||||||||||||||
Comment on lines
+36
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Sanitize and validate request parameters The request parameters such as 'order', 'orderby', 'parent', and others are not sanitized or validated. This could lead to unexpected behavior or security issues if invalid data is passed. It's important to add 'sanitize_callback' and 'validate_callback' functions to these parameters in your For example, update the 'order' parameter: 'order' => array(
'description' => 'Order sort attribute ascending or descending.',
'type' => 'string',
'default' => 'ASC',
'enum' => array( 'ASC', 'DESC' ),
+ 'sanitize_callback' => 'sanitize_text_field',
+ 'validate_callback' => 'rest_validate_request_arg',
), Similarly, add appropriate callbacks for 'orderby', 'parent', and other parameters to ensure they're properly sanitized and validated.
|
||||||||||||||||||||||||
// Set up query arguments | ||||||||||||||||||||||||
$args = array( | ||||||||||||||||||||||||
'taxonomy' => 'product_cat', | ||||||||||||||||||||||||
'number' => $per_page, | ||||||||||||||||||||||||
'offset' => ( $page - 1 ) * $per_page, | ||||||||||||||||||||||||
'hide_empty' => $hide_empty === 'true', | ||||||||||||||||||||||||
'orderby' => $orderby ? $orderby : 'name', | ||||||||||||||||||||||||
'order' => $order ? $order : 'ASC', | ||||||||||||||||||||||||
); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Add conditional parameters | ||||||||||||||||||||||||
if ( $search ) { | ||||||||||||||||||||||||
$args['search'] = $search; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
if ( $exclude ) { | ||||||||||||||||||||||||
$args['exclude'] = array_map( 'absint', explode( ',', $exclude ) ); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
if ( $include ) { | ||||||||||||||||||||||||
$args['include'] = array_map( 'absint', explode( ',', $include ) ); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
if ( $parent ) { | ||||||||||||||||||||||||
$args['parent'] = absint( $parent ); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Get categories | ||||||||||||||||||||||||
$categories = get_terms( $args ); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
if ( is_wp_error( $categories ) ) { | ||||||||||||||||||||||||
return new WP_Error( | ||||||||||||||||||||||||
'rest_category_error', | ||||||||||||||||||||||||
__( 'Error retrieving product categories.', 'dokan-lite' ), | ||||||||||||||||||||||||
array( 'status' => 400 ) | ||||||||||||||||||||||||
); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Get total count for pagination | ||||||||||||||||||||||||
unset( $args['number'] ); | ||||||||||||||||||||||||
unset( $args['offset'] ); | ||||||||||||||||||||||||
$total_categories = wp_count_terms( $args ); | ||||||||||||||||||||||||
osmansufy marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Format the response data | ||||||||||||||||||||||||
$data = array(); | ||||||||||||||||||||||||
foreach ( $categories as $category ) { | ||||||||||||||||||||||||
$response = $this->prepare_category_for_response( $category, $request ); | ||||||||||||||||||||||||
if ( $fields ) { | ||||||||||||||||||||||||
$response = $this->filter_response_by_fields( $response, $fields ); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
$data[] = $response; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Create response with pagination headers | ||||||||||||||||||||||||
$response = new \WP_REST_Response( $data ); | ||||||||||||||||||||||||
$response->header( 'X-WP-Total', (int) $total_categories ); | ||||||||||||||||||||||||
$response->header( 'X-WP-TotalPages', ceil( $total_categories / $per_page ) ); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return $response; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/** | ||||||||||||||||||||||||
* Prepare category data for REST response | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* @param WP_Term $category The category object. | ||||||||||||||||||||||||
* @param WP_REST_Request $request Request object. | ||||||||||||||||||||||||
* @return array Formatted category data. | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
protected function prepare_category_for_response( $category, $request ): array { | ||||||||||||||||||||||||
$thumbnail_id = get_term_meta( $category->term_id, 'thumbnail_id', true ); | ||||||||||||||||||||||||
$thumbnail_url = $thumbnail_id ? wp_get_attachment_url( $thumbnail_id ) : ''; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return array( | ||||||||||||||||||||||||
'id' => (int) $category->term_id, | ||||||||||||||||||||||||
'name' => $category->name, | ||||||||||||||||||||||||
'slug' => $category->slug, | ||||||||||||||||||||||||
'parent' => (int) $category->parent, | ||||||||||||||||||||||||
'description' => $category->description, | ||||||||||||||||||||||||
'count' => (int) $category->count, | ||||||||||||||||||||||||
'thumbnail' => $thumbnail_url, | ||||||||||||||||||||||||
'link' => get_term_link( $category ), | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling for term link generation The - 'link' => get_term_link( $category ),
+ 'link' => is_wp_error( $term_link = get_term_link( $category ) ) ? '' : $term_link, 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||
); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/** | ||||||||||||||||||||||||
* Filter response data by requested fields | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* @param array $response Response data. | ||||||||||||||||||||||||
* @param string $fields Requested fields. | ||||||||||||||||||||||||
* @return array Filtered response data. | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
protected function filter_response_by_fields( $response, $fields ) { | ||||||||||||||||||||||||
$fields = explode( ',', $fields ); | ||||||||||||||||||||||||
return array_intersect_key( $response, array_flip( $fields ) ); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
/** | ||||||||||||||||||||||||
* Get collection parameters for the REST API | ||||||||||||||||||||||||
* | ||||||||||||||||||||||||
* @return array Collection parameters. | ||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||
public function get_collection_params() { | ||||||||||||||||||||||||
return array( | ||||||||||||||||||||||||
'page' => array( | ||||||||||||||||||||||||
'description' => 'Current page of the collection.', | ||||||||||||||||||||||||
'type' => 'integer', | ||||||||||||||||||||||||
'default' => 1, | ||||||||||||||||||||||||
'minimum' => 1, | ||||||||||||||||||||||||
'sanitize_callback' => 'absint', | ||||||||||||||||||||||||
'validate_callback' => 'rest_validate_request_arg', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
'per_page' => array( | ||||||||||||||||||||||||
'description' => 'Maximum number of items to be returned in result set.', | ||||||||||||||||||||||||
'type' => 'integer', | ||||||||||||||||||||||||
'default' => 10, | ||||||||||||||||||||||||
'minimum' => 1, | ||||||||||||||||||||||||
'maximum' => 100, | ||||||||||||||||||||||||
'sanitize_callback' => 'absint', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
'search' => array( | ||||||||||||||||||||||||
'description' => 'Limit results to those matching a string.', | ||||||||||||||||||||||||
'type' => 'string', | ||||||||||||||||||||||||
'sanitize_callback' => 'sanitize_text_field', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
'exclude' => array( | ||||||||||||||||||||||||
'description' => 'Ensure result set excludes specific IDs.', | ||||||||||||||||||||||||
'type' => 'string', | ||||||||||||||||||||||||
'sanitize_callback' => 'sanitize_text_field', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
'include' => array( | ||||||||||||||||||||||||
'description' => 'Limit result set to specific IDs.', | ||||||||||||||||||||||||
'type' => 'string', | ||||||||||||||||||||||||
'sanitize_callback' => 'sanitize_text_field', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
'order' => array( | ||||||||||||||||||||||||
'description' => 'Order sort attribute ascending or descending.', | ||||||||||||||||||||||||
'type' => 'string', | ||||||||||||||||||||||||
'default' => 'ASC', | ||||||||||||||||||||||||
'enum' => array( 'ASC', 'DESC' ), | ||||||||||||||||||||||||
'sanitize_callback' => 'sanitize_text_field', | ||||||||||||||||||||||||
'validate_callback' => 'rest_validate_request_arg', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
'orderby' => array( | ||||||||||||||||||||||||
'description' => 'Sort collection by term attribute.', | ||||||||||||||||||||||||
'type' => 'string', | ||||||||||||||||||||||||
'default' => 'name', | ||||||||||||||||||||||||
'enum' => array( 'name', 'id', 'slug', 'count' ), | ||||||||||||||||||||||||
'sanitize_callback' => 'sanitize_text_field', | ||||||||||||||||||||||||
'validate_callback' => 'rest_validate_request_arg', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
'hide_empty' => array( | ||||||||||||||||||||||||
'description' => 'Whether to hide terms not assigned to any posts.', | ||||||||||||||||||||||||
'type' => 'boolean', | ||||||||||||||||||||||||
'default' => false, | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
'parent' => array( | ||||||||||||||||||||||||
'description' => 'Limit result set to terms assigned to a specific parent.', | ||||||||||||||||||||||||
'type' => 'integer', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
Comment on lines
+198
to
+201
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add validation for parent parameter The parent parameter should be validated to ensure it's a positive integer. 'parent' => array(
'description' => 'Limit result set to terms assigned to a specific parent.',
'type' => 'integer',
+ 'minimum' => 0,
+ 'sanitize_callback' => 'absint',
+ 'validate_callback' => 'rest_validate_request_arg',
), 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||
'_fields' => array( | ||||||||||||||||||||||||
'description' => 'Limit response to specific fields.', | ||||||||||||||||||||||||
'type' => 'string', | ||||||||||||||||||||||||
), | ||||||||||||||||||||||||
Comment on lines
+202
to
+205
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add validation for _fields parameter The _fields parameter should validate that only existing fields are requested. '_fields' => array(
'description' => 'Limit response to specific fields.',
'type' => 'string',
+ 'validate_callback' => array( $this, 'validate_fields' ),
), Add this validation method to your class: /**
* Validate requested fields
*
* @param string $value Comma-separated list of fields
* @return bool|WP_Error
*/
protected function validate_fields( $value ) {
$valid_fields = array( 'id', 'name', 'slug', 'parent', 'description', 'count', 'thumbnail', 'link' );
$requested_fields = explode( ',', $value );
$invalid_fields = array_diff( $requested_fields, $valid_fields );
if ( ! empty( $invalid_fields ) ) {
return new WP_Error(
'rest_invalid_fields',
sprintf(
__( 'Invalid field(s): %s. Valid fields are: %s', 'dokan-lite' ),
implode( ', ', $invalid_fields ),
implode( ', ', $valid_fields )
)
);
}
return true;
} |
||||||||||||||||||||||||
); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate maximum value for per_page parameter
The per_page parameter should be capped at 100 as defined in get_collection_params().
📝 Committable suggestion