WordPress-widgets: Refactoring, del 3
När det gäller att uppdatera WordPress Widget Boilerplate (som alla spåras i utvecklingsgrenen) har vi kommit långt när det gäller att omstrukturera hur den är organiserad.
Hittills har vi:
- tittat på hur WordPress Widget API ger ett exempel på objektorienterad programmering,
- hur vi kan använda detta API för att bestämma objektorienterad programmering i andra delar av WordPress,
- installerade verktyg för att hjälpa oss att utvärdera kodens kvalitet,
- hittade fel som för närvarande finns i koden när det gäller moderna programmeringsstandarder,
- och började omorganisera kodbasen så att den passar med mer modern praxis.
Nu är vi redo att börja omfaktorisera den här koden på ett mycket mer objektorienterat sätt.
Så om du ännu inte har hunnit ikapp de tidigare inläggen (några av dem, verkligen), rekommenderar jag att du gör det eftersom det kommer att ta lite tid att uppdatera detta. Det finns mycket kod för att skriva en förklara.
Låt oss börja.
WordPress Widget Boilerplate: Refactoring, del 3
Det största problemet med Boilerplate är utan tvekan att allt är inkapslat i en enda klass.
Visst, det finns några trevliga saker som att hålla våra åsikter åtskilda från logiken på serversidan, men det är ungefär så långt det går.
Andra problem som uppstår bara från att titta på koden inkluderar:
- lägga till åtgärder och filter i konstruktorn,
- att ha metoder som gör mer än en sak,
- inte ha klasser ansvariga för att implementera saker som att registrera beroenden,
- och så vidare.
I det här inlägget kommer vi att börja processen att skapa abstraktioner som vi så småningom kommer att implementera för att bryta upp den gudaklassliknande karaktären hos Boilerplate som den ser ut.
Det här kommer att delas upp i flera inlägg så att jag kan ge en solid förklaring till varför vi gör vissa saker som vi gör samt förklara exemplen bakom det.
Om jag gör det på något annat sätt, utelämnar serien för mycket värdefull information som är tillämplig på andra objektorienterade programmeringsmetoder.
Vad är en prenumerant?
WordPress hook-systemet – det vill säga de åtgärder och filter vi har tillgängliga – bygger på ett händelsedrivet designmönster. Detta betyder att när något händer, en händelse, kommer WordPress att avfyra all annan kod som har prenumererat på händelsen.
Så när vi registrerar en funktion med en hook, prenumererar vi på eventet. För det ändamålet är jag ett fan av att skapa prenumeranter för varje given hook som vi kommer att behöva.
Utöver det följer prenumeranter i allmänhet ett konsekvent format. Det betyder att det är väldigt enkelt att skapa en abstrakt klass som implementerar en del av den konsekventa funktionaliteten och sedan låter klassen som implementerar den abstrakta klassen fokusera enbart på affärslogik.
Ett av de enklaste sätten att visa detta är genom att prenumerera på CSS-filer och JavaScript-filer eftersom de är två av de vanligaste sakerna som vi använder när vi bygger plugins.
Skapa en abstrakt klass
Innan vi implementerar den abstrakta klassen, låt oss lägga ut exakt vad det är vi ska göra för att skapa detta.
- Vi behöver en fastighet som representerar evenemanget som vi prenumererar på.
- Vi behöver en funktion för att avfyra när kroken avfyras av WordPress. Ett annat sätt att tänka på detta är att vi behöver en funktion att implementera närhelst en given åtgärd eller filter avfyras av WordPress.
- Vi behöver klasser för att implementera abstraktionen.
Låt oss först definiera abstrakta klasser. Direkt från PHP-manualen läser vi:
Klasser som definieras som abstrakt får inte instansieras, och varje klass som innehåller minst en abstrakt metod måste också vara abstrakt. Metoder som definieras som abstrakt deklarerar helt enkelt metodens signatur – de kan inte definiera implementeringen.
Kort sagt betyder detta att vi faktiskt inte kan skapa en instans av en abstrakt klass. Vi kan bara instansiera klasser som definierar implementeringen.
Detta betyder dock inte att den abstrakta klassen inte kan implementera konkreta saker (som definitionen av en krok). Men det betyder att det finns vissa metoder som inte har någon implementering.
Annars har vi bara en grundklass kvar.
Vettigt? Låt oss ta en titt.
Skapa en abstrakt klass
För det här inlägget kommer vi att skapa en abstrakt klass specifikt för CSS-filer och JavaScript.
Kom ihåg, eftersom detta är en abstrakt klass, kan de konkreta prenumeranterna kallas något som identifierar vad de gör (det vill säga de kan kalla sig vad som helst som representerar deras syfte). Och det ska vi komma till.
Men först den abstrakta klassen. Jag delar koden och förklarar sedan exakt vad som händer med den:
<?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 WordPressWidgetBoilerplateSubscriber;
/**
* An abstract implementation of a subscriber that requires a hook and the ability to
* start the class.
*/
abstract class AbstractSubscriber
{
/**
* @var string a reference to the hook to which the subscriber should be registered
*/
protected $hook;
/**
* @param string $hook the hook to which the subscriber is registered
*/
public function __construct(string $hook)
{
$this->hook = $hook;
}
/**
* @return string the hook to which the subscriber is registered
*/
public function getHook(): string
{
return $this->hook;
}
/**
* Implements the domain logic for the concrete class implementating this subcriber.
*/
abstract public function load();
}
- Observera att egendomen vi har skapat är skyddad. Detta är så att implementerande klasser kan komma åt det, men allt utanför det kan inte.
- Jag har skapat en funktion för att hämta kroken som kommer att visa sig senare. Vanligtvis hatar jag att implementera funktionalitet som inte är direkt uppenbar, men det här är något som är viktigt med tanke på vart vi är på väg.
- Det finns en abstrakt funktion som kallas load. Det är här varje klass som implementerar den här funktionen kommer att hysa sin affärslogik, som vi kommer att se för en stund.
- Jag gillar att dokumentera syftet med funktionerna när det behövs så att de finns på ett ställe och låter sedan de implementerande klasserna tillhandahålla den dokumentation de behöver tillhandahålla i sin implementering. Du kommer att se detta för en stund också.
Nu när vi har fått den abstrakta klassen på plats är det sista vi behöver göra att se till att den är placerad i rätt katalog och med namnavstånd. Om du har följt med från och med föregående inlägg har du troligen kunnat gissa var den kommer att ligga baserat på namnutrymmet i koden.
Och om inte, oroa dig inte. Att ta reda på namnrymder och vad som inte kan ta lite tid. Så genom dessa medlemsinlägg och dessa exempel är det min förhoppning att det blir tydligt med tiden.
Skapa betongklasser
Låt oss nu implementera denna speciella klass för att lägga till både stilmallarna och JavaScript-källorna vi har. Vad du dock kommer att märka är att de är väldigt lika.
Det enda som skiljer sig är implementeringen av laddningsfunktionaliteten vilket är exakt hur detta ska fungera.
Stilmallar
Med tanke på den abstrakta klassen ovan måste vi nu skapa en klass för att registrera stilmallar. Eftersom vi har två stilmallar kommer vi att skapa två klasser:
- den första klassen kommer att ansvara för att registrera stilarket för instrumentpanelen och specifikt för WordPress-widgetens sida,
- den andra klassen kommer att ansvara för att registrera plugin för den faktiska bloggen.
Låt oss kalla var och en av dessa AdminStylesheetSubscriber respektive PublicStylesheetSubscriber.
Först, admin stylesheet-prenumeranten :
<?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 WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the stylesheet on the Widget administration page.
*/
class AdminStyleAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the administrative stylesheet to the widget administration page.
*/
public function load()
{
if ('widgets' !== get_current_screen()->id) {
return;
}
wp_enqueue_style(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/css/admin.css'
);
}
}
Lägg märke till att detta använder funktionen get_current_screen() som jag har använt i tidigare inlägg för att se till att vi bara lägger till beroenden där det är nödvändigt.
Nu, den offentliga JavaScript-prenumeranten. Detta använder funktionen is_admin() för att se till att vi inte är i det administrativa området för WordPress.
<?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 WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the stylesheet on the blog.
*/
class PublicStyleAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the stylesheet to the public-facing side of the site.
*/
public function load()
{
if (is_admin()) {
return;
}
wp_enqueue_style(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/css/widget.css'
);
}
}
Uppenbarligen har vi ännu inte instansierat dessa klasser. Det kommer senare i serien.
JavaScript
JavaScript-prenumeranterna är inte mycket annorlunda, som du kanske har gissat. Vi kommer att separera dem baserat på det område av applikationen som de fokuserar på och vi kommer att namnge dem på lämpligt sätt.
Först, admin JavaScript-prenumeranten :
<?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 WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the JavaScript on the Widget's adminsitration page.
*/
class AdminScriptAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the administrative JavaScript to the widget administration page.
*/
public function load()
{
if ('widgets' !== get_current_screen()->id) {
return;
}
wp_enqueue_script(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/js/admin.js'
);
}
}
Och den offentliga JavaScript-prenumeranten:
<?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 WordPressWidgetBoilerplateSubscriber;
/**
* The subscriber responsible for loading the JavaScript on the blog.
*/
class PublicScriptAssetSubscriber extends AbstractSubscriber
{
/**
* {@inheritdoc}
*/
public function __construct(string $hook)
{
parent::__construct($hook);
}
/**
* Adds the JavaScript to the public-facing side of the site.
*/
public function load()
{
if (is_admin()) {
return;
}
wp_enqueue_script(
'wordpress-widget-boilerplate',
plugin_dir_url(dirname(__DIR__)).'assets/js/widget.js'
);
}
}
Återigen, dessa klasser kan ännu inte instansieras men vi kommer att fokusera på det i ett kommande inlägg.
Abstraktioner och gränssnitt
Kom ihåg att abstraktioner och gränssnitt är olika men är lätta att förväxla. Gränssnitt innehåller absolut noll implementering. Istället ger de en garanti för att alla klasser som implementerar gränssnittet kommer att implementera alla metoder.
Abstrakta klasser, å andra sidan, kan ha viss funktionalitet implementerad i den abstrakta klassen samtidigt som den domänspecifika koden – som laddning av stilmallar och JavaScript – lämnas till lämplig metod.
Detta kommer att bli uppenbart, om det inte redan har gjort det, ju längre vi kommer in i den här serien. Under tiden – och som vanligt – glöm inte att kolla in utveckla grenen för att se var vi står med koden.


