The wrong solution to query content restriction.
The restriction framework I’m trying to implement was born out of the need to have a common base to reuse every time I’m tasked with the creation of a content restriction system.
Keeping in mind the past ones and the next one I’ve been tasked to build I need it to provide me a way to restrict user access to single posts and post queries alike.
While the first task was quiet easy to develop the latter poses a small challenge.
Restricted and unrestricted post types
I might be asked to register some post types for a website and not have all of them restrictable; to make an example posts (
post post type) might be restricted while pages (
page post type) are not.
When querying for posts (of any post type) a discriminating function is in place to make sure only queries that should be restricted will, in fact, be restricted.
But what should happen when a single query will come in querying for posts and pages (hence querying for the
post and the
page post type)?
On a natural language level things should go like this:
Return all the posts of the
pagepost type and only the posts of the
postpost type that the user can access.
As easy as it might sound the restriction system being based on the use of taxonomy terms will not allow for that to be done in a single query. At least I’m not aware of it.
WP_Query class allows for post results to be filtered after those have been fetched from the database and each result has been transformed into a
/** * Filter the raw post results array, prior to status checks. * * @since 2.3.0 * * @param array $posts The post results array. * @param WP_Query &$this The WP_Query instance (passed by reference). */ $this->posts = apply_filters_ref_array( 'posts_results', array( $this->posts, &$this ) );
I’ve currently hooked into that when dealing with queries querying restricted and unrestricted post types to remove all of the posts the user cannot access from the results.
My perplexity with this solution lies in the fact that the work made to build the post objects is essentially wasted when those are removed from the results; this will happen only when querying for restricted and unrestricted post types but performance will be an issue on sites with much content.
Room for improvement
Instead of pruning the post results after each one of those has been cast to a
WP_Post object the query could be split using the
split_query filter to have
WP_Query fetch the resulting
IDs first and then fetch the post fields with a single query. My speculation is that removing the inaccessible post
IDs at that time would be less wasteful.
That implementation poses a challenge where the
posts_request_ids is not passing an array of
IDs but a SQL request string
/** * Filter the Post IDs SQL request before sending. * * @since 3.4.0 * * @param string $request The post ID request. * @param WP_Query $this The WP_Query instance. */ $this->request = apply_filters( 'posts_request_ids', $this->request, $this );
so manipulating it, taking into account all of the possible query vars settings, is not a pleasure ride.
Simpler and probably right solution
As often happens I’ve fell into the rabbit hole working head down and missing an easier way to do the same thing. I’ve ran out of fuel for the day and will try to accomplish the same result using the
post__not_in query var.