Skip to content
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

New: Enhance API endpoint to support additional filters #7285

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@
"/Tests/"
]
},
"exclude_from_prefix": {
"namespaces": [
"session"
]
},
"delete_vendor_packages": true,
"override_autoload": {
"symfony/polyfill-ctype": {},
Expand Down
281 changes: 164 additions & 117 deletions src/Donations/Endpoints/ListDonations.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ class ListDonations extends Endpoint
protected $endpoint = 'admin/donations';

/**
* @unreleased becomes public to be usable in hooks
* @var WP_REST_Request
*/
protected $request;
public $request;

/**
* @unreleased becomes public to be usable in hooks
* @var DonationsListTable
*/
protected $listTable;
public $listTable;

/**
* @since 3.4.0
Expand All @@ -46,6 +48,77 @@ public function __construct(DonationsListTable $listTable)
*/
public function registerRoute()
{
$args = [
'page' => [
'type' => 'integer',
'required' => false,
'default' => 1,
'minimum' => 1
],
'perPage' => [
'type' => 'integer',
'required' => false,
'default' => 30,
'minimum' => 1
],
'form' => [
'type' => 'integer',
'required' => false,
'default' => 0
],
'search' => [
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
],
'start' => [
'type' => 'string',
'required' => false,
'validate_callback' => [$this, 'validateDate']
],
'end' => [
'type' => 'string',
'required' => false,
'validate_callback' => [$this, 'validateDate']
],
'donor' => [
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
],
'sortColumn' => [
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
],
'sortDirection' => [
'type' => 'string',
'required' => false,
'enum' => [
'asc',
'desc',
],
],
'locale' => [
'type' => 'string',
'required' => false,
'default' => get_locale(),
],
'testMode' => [
'type' => 'boolean',
'required' => false,
'default' => give_is_test_mode(),
],
'return' => [
'type' => 'string',
'required' => false,
'default' => 'columns',
'enum' => [
'model',
'columns',
],
],
];
register_rest_route(
'give-api/v2',
$this->endpoint,
Expand All @@ -55,77 +128,22 @@ public function registerRoute()
'callback' => [$this, 'handleRequest'],
'permission_callback' => [$this, 'permissionsCheck'],
],
'args' => [
'page' => [
'type' => 'integer',
'required' => false,
'default' => 1,
'minimum' => 1
],
'perPage' => [
'type' => 'integer',
'required' => false,
'default' => 30,
'minimum' => 1
],
'form' => [
'type' => 'integer',
'required' => false,
'default' => 0
],
'search' => [
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
],
'start' => [
'type' => 'string',
'required' => false,
'validate_callback' => [$this, 'validateDate']
],
'end' => [
'type' => 'string',
'required' => false,
'validate_callback' => [$this, 'validateDate']
],
'donor' => [
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
],
'sortColumn' => [
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_text_field',
],
'sortDirection' => [
'type' => 'string',
'required' => false,
'enum' => [
'asc',
'desc',
],
],
'locale' => [
'type' => 'string',
'required' => false,
'default' => get_locale(),
],
'testMode' => [
'type' => 'boolean',
'required' => false,
'default' => give_is_test_mode(),
],
'return' => [
'type' => 'string',
'required' => false,
'default' => 'columns',
'enum' => [
'model',
'columns',
],
],
],
/**
* Allow adding API endpoint args
*
* @unreleased
* @param array $args Array of api args {
* Arg details
*
* @type string $type Type of value
* @type boolean $required Is this arg required for each request
* @type mixed $default Optional - Default value for this arg
* @type callable $validate_callback Optional
* @type callable $sanitize_callback Optional
* @type mixed[] $enum Optional - Array of allowed values
* }[]
*/
'args' => apply_filters('give_list-donation_api_args', $args),
]
);
}
Expand Down Expand Up @@ -171,20 +189,22 @@ public function handleRequest(WP_REST_Request $request): WP_REST_Response
*/
public function getDonations(): array
{
$query = give()->donations->prepareQuery();

// Pagination
$page = $this->request->get_param('page');
$perPage = $this->request->get_param('perPage');
$query->limit($perPage)->offset(($page - 1) * $perPage);

// Sort
$sortColumns = $this->listTable->getSortColumnById($this->request->get_param('sortColumn') ?: 'id');
$sortDirection = $this->request->get_param('sortDirection') ?: 'desc';

$query = give()->donations->prepareQuery();
list($query) = $this->getWhereConditions($query);

foreach ($sortColumns as $sortColumn) {
$query->orderBy($sortColumn, $sortDirection);
}

$query->limit($perPage)
->offset(($page - 1) * $perPage);
// Where
list($query) = $this->getWhereConditions($query);

$donations = $query->getAll();

Expand All @@ -203,12 +223,11 @@ public function getDonations(): array
*/
public function getTotalDonationsCount(): int
{
$query = DB::table('posts')
->where('post_type', 'give_payment')
->groupBy('mode');
$query = DB::table('posts')->where('post_type', 'give_payment');

list($query, $dependencies) = $this->getWhereConditions($query);

$dependencies = array_unique($dependencies);
$query->attachMeta(
'give_donationmeta',
'ID',
Expand All @@ -231,35 +250,49 @@ public function getTotalDonationsCount(): int
*/
protected function getWhereConditions(QueryBuilder $query): array
{
$search = $this->request->get_param('search');
$start = $this->request->get_param('start');
$end = $this->request->get_param('end');
$form = $this->request->get_param('form');
$donor = $this->request->get_param('donor');
$testMode = $this->request->get_param('testMode');
$dependencies = [];
list($query, $dependencies) = $this->getSearchWhereCondition($query, $dependencies);
list($query, $dependencies) = $this->getDonorWhereCondition($query, $dependencies);
list($query, $dependencies) = $this->getFormWhereCondition($query, $dependencies);
list($query, $dependencies) = $this->getDateWhereCondition($query, $dependencies);
list($query, $dependencies) = $this->getModeWhereCondition($query, $dependencies);

$dependencies = [
DonationMetaKeys::MODE(),
];

$hasWhereConditions = $search || $start || $end || $form || $donor;
/**
* Allow adding request clauses
*
* @unreleased
* @param array $value {
* @type ModelQueryBuilder $query Donation query builder
* @type DonationMetaKeys[] $dependencies List of meta dependencies for added where clauses
* }
* @param ListDonations $endpoint API Endpoint instance
*/
return apply_filters('give_list-donation_where_conditions', [$query, $dependencies], $this);
}

if ($search) {
if (ctype_digit($search)) {
$query->where('id', $search);
} elseif (strpos($search, '@') !== false) {
$query
->whereLike('give_donationmeta_attach_meta_email.meta_value', $search);
$dependencies[] = DonationMetaKeys::EMAIL();
} else {
$query
->whereLike('give_donationmeta_attach_meta_firstName.meta_value', $search)
->orWhereLike('give_donationmeta_attach_meta_lastName.meta_value', $search);
$dependencies[] = DonationMetaKeys::FIRST_NAME();
$dependencies[] = DonationMetaKeys::LAST_NAME();
}
private function getSearchWhereCondition (QueryBuilder $query, array $dependencies)
{
$search = $this->request->get_param('search');
if (!$search) return [$query, $dependencies];
if (ctype_digit($search)) {
$query->where('id', $search);
} elseif (strpos($search, '@') !== false) {
$query
->whereLike('give_donationmeta_attach_meta_email.meta_value', $search);
$dependencies[] = DonationMetaKeys::EMAIL();
} else {
$query
->whereLike('give_donationmeta_attach_meta_firstName.meta_value', $search)
->orWhereLike('give_donationmeta_attach_meta_lastName.meta_value', $search);
$dependencies[] = DonationMetaKeys::FIRST_NAME();
$dependencies[] = DonationMetaKeys::LAST_NAME();
}
return [$query, $dependencies];
}

private function getDonorWhereCondition (QueryBuilder $query, array $dependencies)
{
$donor = $this->request->get_param('donor');
if ($donor) {
if (ctype_digit($donor)) {
$query
Expand All @@ -273,33 +306,47 @@ protected function getWhereConditions(QueryBuilder $query): array
$dependencies[] = DonationMetaKeys::LAST_NAME();
}
}
return [$query, $dependencies];
}

private function getFormWhereCondition (QueryBuilder $query, array $dependencies)
{
$form = $this->request->get_param('form');
if ($form) {
$query
->where('give_donationmeta_attach_meta_formId.meta_value', $form);
$dependencies[] = DonationMetaKeys::FORM_ID();
}
return [$query, $dependencies];
}

private function getDateWhereCondition (QueryBuilder $query, array $dependencies)
{
$start = $this->request->get_param('start');
$end = $this->request->get_param('end');
if ($start && $end) {
$query->whereBetween('post_date', $start, $end);
} elseif ($start) {
$query->where('post_date', $start, '>=');
} elseif ($end) {
$query->where('post_date', $end, '<=');
}
return [$query, $dependencies];
}

if ($hasWhereConditions) {
$query->havingRaw('HAVING COALESCE(give_donationmeta_attach_meta_mode.meta_value, %s) = %s', DonationMode::LIVE, $testMode ? DonationMode::TEST : DonationMode::LIVE);
} elseif ($testMode) {
private function getModeWhereCondition (QueryBuilder $query, array $dependencies)
{
$dependencies[] = DonationMetaKeys::MODE();
$testMode = $this->request->get_param('testMode');
if ($testMode) {
$query->where('give_donationmeta_attach_meta_mode.meta_value', DonationMode::TEST);
} else {
$query->whereIsNull('give_donationmeta_attach_meta_mode.meta_value')
->orWhere('give_donationmeta_attach_meta_mode.meta_value', DonationMode::TEST, '<>');
$query->where(function ($whereBuilder) {
$whereBuilder
->whereIsNull('give_donationmeta_attach_meta_mode.meta_value')
->orWhere('give_donationmeta_attach_meta_mode.meta_value', DonationMode::TEST, '<>');
});
}

return [
$query,
$dependencies,
];
return [$query, $dependencies];
}
}
5 changes: 4 additions & 1 deletion src/Views/Components/ListTable/Filters/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ export const Filter = ({filter, value = null, onChange, debouncedOnChange}) => {
};

// figure out what the initial filter state should be based on the filter configuration
export const getInitialFilterState = (filters) => {
export const getInitialFilterState = (filters, apiSettings) => {
const state = {};
const urlParams = new URLSearchParams(window.location.search);

// Allow third party extends filters
filters = wp.hooks.applyFilters(`give-${apiSettings.table.id}-list-table-filters`, filters);
filters.map((filter) => {
// if the search parameters contained a value for the filter, use that
const filterQuery = decodeURI(urlParams.get(filter.name));
Expand Down
Loading