Setting query flags.
Part of a series
This post is part of a series of posts where I attempt to implement the Action-Domain-Responder pattern in the context of WordPress and in the form of a WordPress plugin; the first post in the series might prove a better starting point than this one.
The starting code for this post can be found on GitHub under the dev-3
tag.
Thinking of other plugins
In the previous post I wrote some tests, and the code to pass them, to make sure that when the plugin is handling requests in its own way support for WordPress basic actions and filters, vie the wp_head()
, wp_footer()
and body_class()
functions, is still available.
A further step in that direction is to make sure the “context” of those requests, properties on the global wp_query
object, are set to allow themes and plugins to “know what is happening”; any call to contextual functions like is_home()
or is_single()
relies on those parameters to be set and correct.
To test for this support I add a functional test:
codecept generate:cest functional "WPQuery\BasicSupport"
And write the following test hitting, again, the /posts
route:
<?php
namespace WPQuery;
use FunctionalTester;
class BasicSupportCest {
public function _before( FunctionalTester $I ) {
$I->useTheme( 'twentyseventeen' );
add_filter( 'adr.debug', '__return_true' );
add_filter( 'klein_die_handler', function () {
return 'echo';
} );
}
/**
* It should correctly set global wp_query flags for route
*
* @test
*/
public function should_correctly_set_global_wp_query_flags_for_route( FunctionalTester $I ) {
$I->amOnPage( '/posts' );
$expected_flags = [
'is_single' => false,
'is_preview' => false,
'is_page' => false,
'is_archive' => true, // the page is an archive of posts
'is_date' => false,
'is_year' => false,
'is_month' => false,
'is_day' => false,
'is_time' => false,
'is_author' => false,
'is_category' => false,
'is_tag' => false,
'is_tax' => false,
'is_search' => false,
'is_feed' => false,
'is_comment_feed' => false,
'is_trackback' => false,
'is_home' => false,
'is_404' => false,
'is_paged' => false,
'is_admin' => false,
'is_attachment' => false,
'is_singular' => false,
'is_robots' => false,
'is_posts_page' => true, // the page is the one dedicated to posts
'is_post_type_archive' => 'post', // the page is the "post" post type archive
];
/** @var \WP_Query $wp_query */
global $wp_query;
foreach ( $expected_flags as $key => $value ) {
$I->assertEquals( $wp_query->{$key}, $value, "Global wp_query {$key} does not match expected {$value}" );
}
}
}
Running the test confirms there is something the plugin is not currently doing:
Passing the test requires, as a first step, deciding which part of the Action-Domain-Responder pattern will be in charge of setting the global query flags; the Responder part can be excluded immediately as responsible for handling the response.
The candidates remaining are, then, the Action component and the Domain component; I’ve tried to give a personal and concise interpretation of what each might be in charge of in WordPress terms in the first post of the series: it’s worth getting back to those definitions to make a decision:
- the Action component is the code equivalent of what the HTTP client is trying to do: it could be described as “view the posts archive page”, “search for something and see the results” or “create a post”; for sake of simplicity I will try to implement the first example action: “view the site posts archive”.
- the Domain is the context in which the request is happening and all the logic dealing with CRUD operations: it represents things like “who is the user”, “are there posts matching a query” or “insert this post in the database”, back-end and session stuff.
In light of this definition I chose the Domain component as the one responsible for it.
Setting the query flags
Choosing the /posts
page as a first route to test was not casual: there are a number of non intuitive things to fix in how the built-in post type is set up by default and how I’d like it to behave; the flags set in the tests above are a testimony to it.
By default for the post
post type:
- there is no archive dedicated to it; the
is_archive
flag would befalse
is_posts_page
istrue
only when visiting the page set to be the “Posts page” from the Admin UI- since it does not have an archive
is_post_type_archive
too would befalse
I update the adr_Domains_Domain
class to take care of the task:
// file src/adr/Domains/Domain.php
class adr_Domains_Domain {
/**
* @var \WP_Query
*/
protected $query;
public function __construct( WP_Query $query ) {
$this->query = $query;
}
public function get( array $args ) {
$this->query->parse_query( $args );
$posts = $this->query->get_posts();
/** @var WP_Query $wp_query */
global $wp_query;
$this->set_query_flags( $args );
$wp_query = $this->query;
return $this->query->have_posts() ? $posts : array();
}
protected function set_query_flags( array $args = null ) {
if ( empty( $args ) ) {
return;
}
if ( ! empty( $args['post_type'] ) && $args['post_type'] === 'post' ) {
$this->query->is_archive = true;
$this->query->is_post_type_archive = 'post';
$this->query->is_posts_page = true;
}
$this->query->is_home = false;
}
}
Running the tests again yields the expected success:
From the code it’s clear the implementation tailor-made to pass the test at hand and will require more testing and refinements to handle other requests in a flexible way.
Next
The code I’m showing in this post is on GitHub tagged dev-04
for anyone to see.
I will refactor the code to generalize the plugin: the main objective of this will be to make broader testing possible and, collaterally, making the plugin re-usable outside of a specific implementation.