Skip to content

Commit

Permalink
Merge pull request #20 from thefrosty/develop
Browse files Browse the repository at this point in the history
Version 1.7
  • Loading branch information
thefrosty authored Nov 5, 2019
2 parents 44d3353 + c384fa9 commit a92b1bc
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ env:
- WORDPRESS_DB_USER=wp
- WORDPRESS_DB_PASS=password
- WORDPRESS_DB_NAME=wp_tests
- WP_VERSION=4.9
- WP_VERSION=5.2
- WP_MULTISITE=0

matrix:
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased
PHP version >= 7.2 & WordPress version >= 5.1 will be required by version `2.0`.

## [1.7.0] - 2019-11-04
### Added
- `RestApi\PostTypeFilter`, `Api\WpCacheTrait` & `Api\WpQueryTrait`.
- `PostTypeFilter` can be initiated to add `filter[]` querying to the rest endpoints
(enhanced rest filter of [WP-API/rest-filter](https://github.com/WP-API/rest-filter))
- Default action/filter prefix constant in `TheFrosty\WpUtilities\Plugin\Plugin` called `TAG`
### Updated
- Add missing dependency "phpcompatibility" for require-dev.
- Bumped WordPress version to 5.2 in travis.

## [1.6.2] - 2019-10-28
### Updated
- In the `BaseModel` class update the get method call to use the `getMethod` helper in `toArray`.
Expand Down
2 changes: 1 addition & 1 deletion bin/phpcs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fi

commitFiles=`git diff --name-only $(git merge-base develop ${against})`

args="-s --colors --extensions=php --tab-width=4 --standard=phpcs.ruleset.xml --runtime-set testVersion 7.1-"
args="-s --colors --extensions=php --tab-width=4 --standard=phpcs-ruleset.xml --runtime-set testVersion 7.1-"
phpFiles="";
phpFilesCount=0;
for f in ${commitFiles}; do
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "thefrosty/wp-utilities",
"description": "A library containing my standard development resources",
"version": "1.6.2",
"version": "1.7.0",
"license": "MIT",
"authors": [
{
Expand All @@ -22,6 +22,7 @@
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.3",
"phpcompatibility/php-compatibility": "^9.3",
"phpunit/phpunit": "^7",
"phpmd/phpmd": "^2.6",
"squizlabs/php_codesniffer": "^3.2",
Expand Down
82 changes: 82 additions & 0 deletions src/Api/WpCacheTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php declare(strict_types=1);

namespace TheFrosty\WpUtilities\Api;

/**
* Trait WpCacheTrait
*
* @package TheFrosty\WpUtilities\Api
*/
trait WpCacheTrait
{

/**
* Cache group value.
*
* @var string $group
*/
private $group = __CLASS__;

/**
* Get a cache key.
*
* @param string $data
*
* @return string
*/
protected function getHashedKey(string $data): string
{
return \hash('sha256', $data);
}

/**
* Get the cache group.
*/
protected function getCacheGroup(): string
{
return $this->group;
}

/**
* Optional. Set the cache group.
*
* @param string $group
*/
protected function setCacheGroup(string $group): void
{
$this->group = $group;
}

/**
* Retrieve object from cache.
*
* @param string $key The key under which to store the value.
* @param string|null $group The group value appended to the $key.
* @param bool $force Optional. Whether to force an update of the local cache from the persistent
* cache. Default false.
* @param bool $found Optional. Whether the key was found in the cache. Disambiguates a return of false,
* a storable value. Passed by reference. Default null.
*
* @return mixed Cached object value.
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
*/
protected function getCache(string $key, ?string $group = null, bool $force = false, ?bool &$found = null)
{
return \wp_cache_get($key, $group ?? $this->group, $force, $found);
}

/**
* Sets a value in cache.
*
* @param string $key The key under which to store the value.
* @param mixed $value The value to store.
* @param string|null $group The group value appended to the $key.
* @param int $expiration The expiration time, defaults to 0.
*
* @return bool Returns TRUE on success or FALSE on failure.
*/
protected function setCache(string $key, $value, ?string $group = '', int $expiration = 0): bool
{
return \wp_cache_set($key, $value, $group ?? $this->group, $expiration);
}
}
98 changes: 98 additions & 0 deletions src/Api/WpQueryTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php declare(strict_types=1);

namespace TheFrosty\WpUtilities\Api;

use TheFrosty\WpUtilities\Plugin\Plugin;
use WP_Query;

/**
* Trait WpQueryTrait.
*
* @package TheFrosty\WpUtilities\Api
*/
trait WpQueryTrait
{

use WpCacheTrait;

/**
* Return a new WP_Query object.
*
* @param string $post_type The post type to query.
* @param array $args Additional WP_Query parameters
*
* @return WP_Query
*/
protected function wpQuery(string $post_type, array $args = []): WP_Query
{
$defaults = [
'post_type' => $post_type,
'posts_per_page' => 1000,
'post_status' => ['publish', 'pending', 'future', 'draft'],
'ignore_sticky_posts' => true,
'no_found_rows' => true,
];

return new WP_Query(\wp_parse_args($args, $defaults));
}

/**
* Return a cached WP_Query object.
*
* @param string $post_type
* @param array $args Additional WP_Query parameters.
* @param int|null $expiration The expiration time, defaults to `MINUTE_IN_SECONDS`.
*
* @return WP_Query
*/
protected function wpQueryCached(string $post_type, array $args = [], ?int $expiration = null): WP_Query
{
$cache_key = $this->getHashedKey(
\sprintf('%s/query_%s_by_%s', Plugin::TAG, $post_type, \md5(\json_encode($args)))
);
$query = $this->getCache($cache_key);
if ($query === false || !($query instanceof WP_Query)) {
$query = $this->wpQuery($post_type, $args);
if ($query->have_posts()) {
$this->setCache($cache_key, $query, $this->getCacheGroup(), $expiration ?? \MINUTE_IN_SECONDS);
\wp_reset_postdata();
}
}

return $query;
}

/**
* Return an array of cached WP_Query post ID's. This will do a large loop to get *all* posts within
* the `$post_type`. So when you are aware of thousands of posts, and might need them all use this method.
*
* @param string $post_type
* @param array $args Additional WP_Query parameters.
*
* @return array An array of all post type IDs
*/
protected function wpQueryGetAllIds(string $post_type, array $args = []): array
{
static $paged;
$post_ids = [];
do {
$paged++; // phpcs:ignore
$defaults = [
'fields' => 'ids',
'posts_per_page' => 100,
'no_found_rows' => false, // We need pagination & the count for all posts found.
'paged' => $paged,
'update_post_term_cache' => false,
'update_post_meta_cache' => false,
];
$query = $this->wpQueryCached($post_type, \wp_parse_args($args, $defaults));
if ($query->have_posts()) {
foreach ($query->posts as $id) {
$post_ids[] = $id;
}
}
} while ($query->max_num_pages > $paged);

return $post_ids;
}
}
2 changes: 2 additions & 0 deletions src/Plugin/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ class Plugin extends AbstractPlugin
{

use ContainerAwareTrait;

public const TAG = 'thefrosty/wp_utilities';
}
2 changes: 1 addition & 1 deletion src/Plugin/PluginFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ private static function setContainer(Plugin $plugin) : Plugin
$plugin->setContainer(new Container());
}
} catch (\InvalidArgumentException $exception) {
if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
if (\defined('WP_DEBUG_LOG') && \WP_DEBUG_LOG) {
\error_log(
\sprintf(
'[DEBUG] The `Psr\Container\ContainerInterface` couldn\'t initiate. message: %s',
Expand Down
95 changes: 95 additions & 0 deletions src/RestApi/PostTypeFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php declare(strict_types=1);

namespace TheFrosty\WpUtilities\RestApi;

use TheFrosty\WpUtilities\Plugin\HooksTrait;
use TheFrosty\WpUtilities\Plugin\Plugin;
use TheFrosty\WpUtilities\Plugin\WpHooksInterface;

/**
* Class PostTypeFilter
*
* @package TheFrosty\WpUtilities\RestApi
*/
class PostTypeFilter implements WpHooksInterface
{

use HooksTrait;

public const QUERY_PARAM = 'filter';

/**
* Post Type names.
*
* @var string[]
*/
private $post_types = [];

/**
* Filters constructor.
*/
public function __construct()
{
$this->post_types = \get_post_types(['show_in_rest' => true], 'names');
}

/**
* Add class hooks.
*/
public function addHooks(): void
{
$this->addAction('rest_api_init', [$this, 'addRestFilters']);
}

/**
* Allow the `filter[]` to REST responses for our post types.
* Taken from https://github.com/WP-API/rest-filter
*/
protected function addRestFilters(): void
{
$post_types = \array_filter(
\apply_filters(\sprintf('%/filter_post_types', Plugin::TAG), $this->post_types)
);
\array_walk($post_types, function (string $slug): void {
$post_type = \get_post_type_object($slug);
if ($post_type instanceof \WP_Post_Type) {
$this->addFilter("rest_{$post_type->name}_query", [$this, 'addFilterParam'], 10, 2);
}
});
}

/**
* Add the `filter` parameter to the REST call query which is then passed to WP_Query.
*
* @see https://developer.wordpress.org/reference/classes/wp_query/ For available params to pass to the filter.
*
* @param array $args The query arguments.
* @param \WP_REST_Request $request Full details about the request.
*
* @return array $args.
*/
protected function addFilterParam(array $args, \WP_REST_Request $request): array
{
// Bail out if no filter parameter is set.
if (empty($request->get_params()) || empty($request->get_params()[self::QUERY_PARAM])) {
return $args;
}
$filter = $request->get_params()[self::QUERY_PARAM];
if (isset($filter['posts_per_page']) &&
((int)$filter['posts_per_page'] >= 1 &&
(int)$filter['posts_per_page'] <= 100)
) {
$args['posts_per_page'] = $filter['posts_per_page'];
}
$vars = \apply_filters('rest_query_vars', $GLOBALS['wp']->public_query_vars);
// Allow valid meta query vars.
$vars = \array_unique(\array_merge($vars, ['meta_query', 'meta_key', 'meta_value', 'meta_compare']));
foreach ($vars as $var) {
if (isset($filter[$var])) {
$args[$var] = $filter[$var];
}
}

return $args;
}
}

0 comments on commit a92b1bc

Please sign in to comment.