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', '@user', '@user-messages', '@user-urls');
$container['widgets-register'] = array('myplugin_WidgetsRegister', '@cta-widget');
$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.