Widget classes dependency injection 02

Getting hacky.

Context

In my earlier post I had outlined a possible alternative for the construction and development of WordPress widget classes.
The effort is to allow for an easier dependency injection and thus allow for a test like this one to be written

public function tests_not_connected_user_get_connect_cta_and_url(){
    $user = $this->prophesize('myplugin_User');
    $user->is_connected()->willReturn(false);

    $messages = $this->prophesize('myplugin_UserMessages');
    $messages->get_register_cta()->shouldBeCalled();

    $urls = $this->prophesize('myplugin_UserUrls');
    $urls->get_register_url()->shouldBeCalled();

    $sut = new myplugin_CTAWidget($user->reveal(), $messages->reveal(), $urls->reveal());
    $sut->widget();
}

for this method

/**
 * Outputs the content of the widget
 *
 * @param array $args
 * @param array $instance
 */
public function widget( $args, $instance ) {

    if $this->user->is_connected(){
        $user_type = $this->user->get_type();
        $message = $this->user_messages->get_cta_for($user_type);
        $url = $this->user_urls->get_for($user_type);
    } elseif ($this->user->is_pending_registration()) {
        $message = $this->user_messages->get_pending_registration_cta();
        $url = $this->user_urls->get_pending_registration_url();
    } else {
        $message = $this->user_messages->get_register_cta();
        $url = $this->user_urls->get_register_url();
    }

    echo sprint_f('<a href="%s">%s</a>', $url, $message);
}

defined in the myplugin_CTAWidget class.

Starting from the constructor

I start by modifying the widget class constructor

public function __construct(
    myplugin_UserInterface $user,
    myplugin_UserMessagesInterface $user_messages,
    myplugin_UserUrlsInterface $user_urls
) {
    $this->user = $user;
    $this->user_messages = $user_messages;
    $this->user_urls = $user_urls;

    $widget_ops = array( 
        'class_name' => 'my_widget',
        'description' => 'My Widget is awesome',
    );
    parent::__construct( 'my_widget', 'My Widget', $widget_ops );
}

Leaving things as they are the standard WordPress widget instantiation flow will fail in an error generated by the call to the WP_Widget_Factory::register method

public function register( $widget_class ) {

    $this->widgets[$widget_class] = new $widget_class();

}

The cause being dependencies the class needs not being supplied.

Different flow

In place of registering the widget the usual way

add_action('widgets_init', 'add_my_widgets');
function add_my_widgets(){
    register_widget('myplugin_CTAWidget');
}

I will take an approach that will allow me to instantiate the widget the way I need to

add_action('widgets_init', 'add_my_widget');
function add_my_widget(){
    global $wp_widget_factory;

    $user = new myplugin_User();
    $user_messages = new myplugin_UserMessages());
    $user_urls = new myplugin_UserUrls();

    $widget = new myplugin_CTAWidget($user, $user_messages, $user_urls);

    $wp_widget_factory->widgets['myplugin_CTAWidget'] = $widget;
}

This will instantiate the widget injecting dependencies the way I want and will complete the cycle using a dependency action container.
In the example below I’m using DI52 to manage the flow; see the sudo code below.

// some plugin or bootstrap file

$container = new tad_DI52_Container();

$container['container'] = $container;
$container['user'] = 'myplugin_User';
$container['user-messages'] = 'myplugin_UserMessages';
$container['user-urls'] = 'myplugin_UserUrls';

$container['cta-widget'] = array('myplugin_CTAWidget', [email protected]', [email protected]', [email protected]');

$container['widgets-register'] = array('myplugin_WidgetsRegister', [email protected]');

$widget_register = $container['widget-register'];
$widget_register->hook();

// src/WidgetsRegister.php

class myplugin_WidgetsRegister {

    protected $cta_widget;

    public function __construct($cta_widget){
        $this->cta_widget = $cta_widget;
    }

    public function hook(){
        add_action('widgets_init', array($this, 'register_widgets'));
    }

    public function register_widgets(){
        global $wp_widget_factory;

        $wp_widget_factory->widgets[get_class($this->cta-widget)] = $this->cta-widget;
    }
}

Not a great difference but great one for me that have come to appreciate the use of a DI container.

4 thoughts on “Widget classes dependency injection 02

    1. Dependency injection in the constructor method is usually widely supported by dependency injection containers and, personal preference, makes sure that by calling new Something(A $a, B $b, C $c) you will get hold of a fully functional instance of the object in place of something half-baked.
      This said any good DI container will support something like $container->make('MyObject')->setDependencyA($container->make('A'))->setDependencyB($container->make('B'));.
      Plus sometimes circular dependency does not allow for straight DI.

      1. But loading injection when it is not needed is it correct? For example I have a class for validating data that it is required only during widget update in admin widget page, is it correct DI that class in constructor instead of have a method that inject the instance of that class in a member variable and then call it only when the widget data are updating?

        1. A good DI container will be lazy and will not instantiate anything before it’s actually needed. Binding is usually a DI container set up phase and it means “if someone needs an implementation of this interface/class use this”

I appreciate your input