Developing a plugin using DI and TDD 16

Post control, the front-end part.

What’s this all about?

This post moves on the step by step development of a simple votes plugin using dependency injection and test-driven development.
The posts alone might not be as interesting without a peek at the plugin code and probably a skim over the first post in the series and the following ones.
Tools of the trade are DI52 to handle PHP 5.2 compatible dependency injection, Codeception and wp-browser to handle the testing.

Votes control

The plugin will add two meta boxes in the back-end: one to display the votes and another to control the votes.
Controlling, in this context and plugin iteration, will mean the post editor will be given the possibility to:

  • reset all votes on a post
  • consolidate all votes on a post

The first one will imply, in the back-end, deleting all the plugin generated comments for a post while the latter will mean deleting all the comments as well but store a count somewhere for a post to have a record of the votes.
This will be the UI result Display and control meta boxes

Test, provide and use

The process is now a known one where I’ve scaffolded a test for the control meta box class

wpcept generate:wpunit wpunit "idlikethis\MetaBoxes\PostControlMetaBox"

and have gone through the usual “red light, green light” cycle of the TDD flow. The test code for the idlikethis_MetaBoxes_PostControlMetaBox class can be seen here.
The meta box class __construct method will specify the dependencies needed by it

    public function __construct(
        idlikethis_Repositories_CommentsRepositoryInterface $comments_repository,
        idlikethis_Templates_RenderEngineInterface $rendering_engine,
        idlikethis_Texts_PostControlMetaBoxTextsProviderInterface $texts_provider,
        idlikethis_Contexts_MetaBoxContextInterface $context
        );

Again a class modelling the context the meta box will render into will allow me to control the class input and its interactions with its surroundings.
Once the meta box class and its dependencies are defined and tested (where applicable, I’m not losing my sanity to test adapters) it’s time to iron out the JavaScript side of things.
Before that I’ve updated the MetaBoxes and the Scripts service providers to set up the show.

WebPack to the rescue

I will enqueue a single script on the site backend, idlikethis-admin.js, and will produce it using the code splitting powers of webpack.
In essence I will associate each button to an extended Backbone view and delegate the back-end communication and syncing to an extended Backbone model.
The main file tells almost all the story

(function () {
    require("../css/modules/idlikethis-post-control-meta-box.scss");

    var app = require('admin-app'),
        $ = require('jquery');

    function bootstrap() {
        var nonce = app.data.endpoints.nonce,
            resetAllUrl = app.data.endpoints.reset_all.url,
            consolidateAllUrl = app.data.endpoints.consolidate_all.url,
            resetAllButton = new app.Views.AdminControlButton({
                el: '#idlikethis-reset-all',
                model: new app.Models.ResetAllCommand({nonce: nonce, url: resetAllUrl})
            }),
            consolidateAllButton = new app.Views.AdminControlButton({
                el: '#idlikethis-consolidate-all',
                model: new app.Models.ConsolidateAllCommand({nonce: nonce, url: consolidateAllUrl})
            });
    };

    $(bootstrap);
})();

A look at the plugin assets folder can make the dependency web easier to grasp.
Of note here, once again, the cookie based WP REST API authentication in the base ControlCommand object

var Backbone = require('backbone');

module.exports = Backbone.Model.extend({

    sync: function () {
            Backbone.sync('create', this, {
                beforeSend: function (xhr) {
                    xhr.setRequestHeader('X-WP-NONCE', this.nonce);
                },
            });

    }
});

This is the same modification to the sync method I was making for the vote casting object.

Next

The admin UI the plugin defines is now sending signals to missing endpoints and getting 404 responses in return: I will implement the route handlers next. REST API endpoints 404