WordPress-widgets: Refactoring, del 4
Vi har implementerat en betydande mängd ändringar i WordPress Widget Boilerplate. Om du inte har följt med rekommenderar jag att börja i början av serien och komma ikapp.
Om du däremot har följt med och du också kör några av kodkvalitetsverktygen som undersöker projektets tillstånd, kommer du att märka en handfull fel i konsolen.
Normalt är det här punkten där jag rekommenderar att du är uppmärksam på vad den delar och sedan fixar vad den rapporterar, men vi är inte där än.
Till exempel är några av de fel som våra verktyg visar just nu baserade på att vi har oanvända variabler. Naturligtvis är det dock så, eftersom vi inte har börjat bygga en widget.
Men det finns fortfarande några konkreta klasser vi behöver implementera.
WordPress Widget Boilerplate: Refactoring, del 4
Ett av problemen som fortfarande finns i koden som den ser ut just nu är att widgetens konstruktor registrerar funktioner och det är inte bra.
Syftet med en konstruktor är att ställa in värdena för klassens egenskaper, inte att implementera någon typ av logik. Detta är av några anledningar:
- det skapar stark koppling eller beroenden mellan klasser i projektet närhelst en given klass instansieras,
- det introducerar affärslogik i en funktion som inte är avsedd att innehålla nämnda funktionalitet,
- det gör det svårt att testa en klass isolerat.
Sättet att hantera detta är tvådelat:
- lägg till ett register som vi kan använda för att registrera klasser i applikationen (och skicka dem runt med så få beroenden som möjligt, vilket jag ska förklara senare),
- skapa abonnenter som kan hantera affärslogiken på en krokspecifik basis.
Under de kommande tre inläggen:
- vi ska titta på att skapa ett register,
- hur vi kan introducera det i bootstrap-filen,
- och titta sedan på att skapa prenumeranter för var och en av våra funktioner (vilket inte borde vara alltför svårt med tanke på vad vi gjorde i förra inlägget och vad vi gör i det här inlägget).
Skapa registret
Innan du skriver koden för registret är det viktigt att notera vad dess primära syfte är. Enkelt uttryckt är klassen menad att innehålla en referens till vilken klass som helst som är, ahem, registrerad med den.
Detta görs genom att skicka en referens till ett givet objekt till en funktion och även binda det till en nyckel så att vi enkelt kan hämta det senare.
Inledande överväganden
Men det finns några saker att tänka på. Till exempel:
om ett objekt redan finns i registret för en given nyckel, då måste vi kasta ett undantag – om en användare försöker ta ett objekt från registret med en given nyckel, så bör den kasta ett undantag
Naturligtvis behöver det inte nödvändigtvis ge undantag. Istället kan den också visa felmeddelanden, returnera null eller tomma värden, eller vad du än väljer.
För det andra måste registret kunna returnera en lista över alla prenumeranter som finns i det så att de kan registreras med WordPress (vilket är vad vi kommer att se i nästa inlägg).
För att göra detta måste vi dock se till att det stöder alla prenumeranter och det är här begreppet abstrakt prenumerant från det tidigare inlägget kommer in i bilden. Det vill säga, så länge en klass är en barnklass i den klassen, så är vi okej.
Skapar registret
Med det sagt, låt oss planera följande:
- Vi skapar en Registry-klass och placerar den i ett Utilities-namnområde (och därmed katalog) om den inte redan finns i ditt arbete.
- Vi kommer att låta registret upprätthålla en referens till alla dess objekt i en associativ array.
- Vi behöver metoder för att lägga till och få en enskild prenumerant och sedan en för att hämta listan över hela prenumeranter.
Stubben för klassen kommer att se ut ungefär så här:
<?php
class Registry extends AbstractSubscriber
{
public function __construct()
{
}
public function add($id, $obj)
{
}
public function get($id)
{
}
public function getRegisteredSubscribers()
{
}
}
Därefter kan vi definiera den grundläggande egenskapen, en array, och initiera den i konstruktorn:
<?php
class Registry
{
private $registry;
public function __construct()
{
$this->registry = [];
}
// ...
}
Efter det kan vi skapa add-metoden. Kom ihåg att i min implementering väljer jag att göra ett undantag om en nyckel redan är definierad, men du behöver inte göra det.
<?php
public function add($id, $obj)
{
if (isset($this->registry[$id])) {
throw new Exception('An object already exists for this given key.');
}
$this->registry[$id] = $obj;
}
På samma sätt kommer get-metoden att returnera en referens till instansen av ett objekt som identifieras med den nyckeln. Om nyckeln inte är inställd kommer det att skapa ett undantag. Om det är inställt, men inget objekt existerar, returnerar vi null.
<?php
public function get($id)
{
if (!isset($this->registry[$id])) {
throw new Exception('No object exists for the specified key.');
}
return $this->registry[$id] ?? null;
}
Slutligen behöver vi en metod för att returnera alla registrerade prenumeranter. I ett framtida inlägg kommer användningen av detta att bli mycket mer uppenbar, men för nu, notera att vi kommer att skapa en array av alla klasser som är en instans av klassen AbstractSubscriber och sedan returnerar vi den filtrerade arrayen.
<?php
public function getRegisteredSubscribers()
{
$subscribers = [];
foreach ($this->registry as $object) {
if ($object instanceof AbstractSubscriber) {
$subscribers[] = $object;
}
}
return array_filter($subscribers);
}
Vid det här laget har vi hela klassen (komplett med dokumentation):
<?php
/*
* This file is part of the WordPress Widget Boilerplate
*
* (c) Tom McFarlin <tom@tommcfarlin.com>
*
* This source file is subject to the GPL license that is bundled
* with this source code in the file LICENSE.
*/
namespace WordPressWidgetBoilerplateUtilities;
use Exception;
use WordPressWidgetBoilerplateSubscriberAbstractSubscriber;
/**
* This class services as a simple container that can be used to pass objects
* around the plugin.
*
* To use this class you'd make a call to the registry by saying Registry->get(),
* then making a class to `register()` and `retrieve()` on an instance of the object.
*/
class Registry
{
/**
* @var array an array used to maintain the objects registered with the plugin
*/
private $registry;
/**
* Initializes the class by setting up the registry.
*/
public function __construct()
{
$this->registry = [];
}
/**
* Registers an object with the registry with the specified ID; however, will throw an
* exception if the ID is already referencing an object.
*
* @param string $id an ID by which the specified object will be referenced
* @param mixed $obj an instance of an object to store in the registry
*
* @throws Exception if an object already exists for the specified key
*/
public function add($id, $obj)
{
if (isset($this->registry[$id])) {
throw new Exception('An object already exists for this given key.');
}
$this->registry[$id] = $obj;
}
/**
* @param string $id the ID for the object that we wish to retrieve
*
* @throws Exception if no object exists for the specified key
*
* @return mixed a reference to the object or null
*/
public function get($id)
{
if (!isset($this->registry[$id])) {
throw new Exception('No object exists for the specified key.');
}
return $this->registry[$id] ?? null;
}
/**
* @return array all of the the Subscribers that should be registered with WordPress
*/
public function getRegisteredSubscribers()
{
$subscribers = [];
foreach ($this->registry as $object) {
if ($object instanceof AbstractSubscriber) {
$subscribers[] = $object;
}
}
return array_filter($subscribers);
}
}
Nu måste vi lägga till det i bootstrap för vårt plugin.
Före Bootstrap, dock
Som nämnts tidigare i inlägget måste vi lägga till detta till pluginets bootstrap. För att göra detta måste vi dock definiera vårt eget filter så att vi enkelt kan skicka registret runt resten av pluginet (när det är dags att göra det).
Innan vi gör det är det dock viktigt att se till att du har ett bra grepp om registret vi just har skapat, att det passar in i pluginet och att du spårar med utvecklingsgrenen som vi har hittills.