Widgets do WordPress: Refatoração, Parte 3
Em termos de atualização do WordPress Widget Boilerplate (todos os quais são rastreados na ramificação de desenvolvimento ), percorremos um longo caminho em termos de refatoração de como ele é organizado.
Até agora, temos:
- analisamos como a API do WordPress Widget fornece um exemplo de programação orientada a objetos,
- como podemos usar esta API para determinar a programação orientada a objetos em outras áreas do WordPress,
- ferramentas instaladas para nos ajudar a avaliar a qualidade do código,
- encontrou erros que existem atualmente no código em termos de padrões de programação modernos,
- e começou a reorganizar a base de código para se adequar às práticas mais modernas.
Agora estamos prontos para começar a refatorar esse código de uma maneira muito mais orientada a objetos.
Então, se você ainda não conseguiu acompanhar os posts anteriores (qualquer um deles, na verdade), recomendo fazê-lo porque vai demorar um pouco para atualizar isso. Há muito código para escrever uma explicação.
Vamos começar.
The WordPress Widget Boilerplate: Refatoração, Parte 3
Indiscutivelmente, o maior problema com o Boilerplate é que tudo é encapsulado em uma única classe.
Claro, existem algumas coisas legais, como manter nossas visualizações separadas da lógica do lado do servidor, mas isso é o máximo.
Outros problemas que existem apenas olhando o código incluem:
- adicionando ações e filtros no construtor,
- ter métodos fazendo mais de uma coisa,
- não ter classes responsáveis por implementar coisas como registrar dependências,
- e assim por diante.
Neste post, vamos iniciar o processo de criação de abstrações que eventualmente implementaremos para quebrar a natureza divina do Boilerplate como está.
Isso será dividido em vários posts para que eu possa fornecer uma explicação sólida sobre por que estamos fazendo certas coisas que estamos fazendo, bem como explicar os exemplos por trás disso.
Se eu fizer de outra forma, a série deixa de fora muitas informações valiosas que são aplicáveis a outras práticas de programação orientada a objetos.
O que é um Assinante?
O sistema de ganchos do WordPress – ou seja, as ações e filtros que temos disponíveis – são baseados em um padrão de design orientado a eventos. Isso significa que sempre que algo acontecer, um evento, o WordPress acionará qualquer outro código que tenha se inscrito no referido evento.
Então, quando registramos uma função com um gancho, estamos assinando o evento. Para esse fim, sou fã de criar assinantes para qualquer gancho que precisarmos.
Além disso, os assinantes geralmente seguem um formato consistente. Isso significa que é muito fácil criar uma classe abstrata que implementa algumas das funcionalidades consistentes e, em seguida, permite que a classe que implementa a classe abstrata se concentre apenas na lógica de negócios.
Uma das maneiras mais fáceis de demonstrar isso é através do assinante de arquivos CSS e JavaScript, porque são duas das coisas mais comuns que usamos ao construir plugins.
Criando uma classe abstrata
Antes de implementar a classe abstrata, vamos definir exatamente o que vamos fazer para criá-la.
- Precisamos de uma propriedade que represente o evento ao qual estamos assinando.
- Precisamos de uma função para disparar sempre que o gancho for disparado pelo WordPress. Outra maneira de pensar sobre isso é que precisamos de uma função para implementar sempre que uma determinada ação ou filtro for acionado pelo WordPress.
- Precisamos de classes para implementar a abstração.
Primeiro, vamos definir classes abstratas. Direto do manual do PHP, lemos:
Classes definidas como abstratas não podem ser instanciadas e qualquer classe que contenha pelo menos um método abstrato também deve ser abstrata. Métodos definidos como abstratos simplesmente declaram a assinatura do método – eles não podem definir a implementação.
Em resumo, isso significa que não podemos criar uma instância de uma classe abstrata. Podemos apenas instanciar classes que definem a implementação.
Isso não significa, porém, que a classe abstrata não possa implementar coisas concretas (como a definição de um gancho). Mas isso significa que existem certos métodos que não têm implementação.
Caso contrário, ficamos apenas com uma classe básica.
Faz sentido? Vamos dar uma olhada.
Criando uma classe abstrata
Para este post, vamos criar uma classe abstrata especificamente para arquivos CSS e JavaScript.
Lembre-se, porque esta é uma classe abstrata, os assinantes concretos podem ser chamados de algo que identifica o que eles fazem (ou seja, eles podem se chamar de qualquer coisa que represente seu propósito). E nós vamos chegar a isso.
Mas primeiro, a classe abstrata. Vou compartilhar o código e explicar exatamente o que está acontecendo com ele:
<?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();
}
- Observe que a propriedade que criamos está protegida. Isso é para que classes de implementação possam acessá-lo, mas qualquer coisa fora dele não pode.
- Eu criei uma função para recuperar o gancho que ficará aparente mais tarde. Normalmente, detesto implementar funcionalidades que não sejam imediatamente óbvias, mas isso é algo que é importante considerando para onde estamos indo.
- Existe uma função abstrata chamada load. É aqui que qualquer classe que implemente essa função abrigará sua lógica de negócios, como veremos em breve.
- Eu gosto de documentar o propósito das funções quando necessário para que elas fiquem em um só lugar e então deixar as classes de implementação fornecerem a documentação que elas precisam fornecer em sua implementação. Você verá isso momentaneamente, também.
Agora que temos a classe abstrata no lugar, a última coisa que precisamos fazer é garantir que ela seja colocada no diretório apropriado e com namespace. Se você acompanhou a partir da postagem anterior, provavelmente conseguiu adivinhar onde ele residirá com base no namespace no código.
E se não, não se preocupe. Descobrir namespaces e o que não pode levar um pouco de tempo. Então, por meio dessas postagens de membros e desses exemplos, espero que fique claro com o tempo.
Criando classes concretas
Agora vamos implementar essa classe específica para adicionar as folhas de estilo e as fontes JavaScript que temos. O que você vai notar, porém, é que eles são muito semelhantes.
A única coisa que difere é a implementação da funcionalidade de carregamento, que é exatamente como isso deve funcionar.
Folhas de estilo
Dada a classe abstrata acima, agora precisamos criar uma classe para registrar folhas de estilo. Como temos duas folhas de estilo, vamos criar duas classes:
- a primeira classe será responsável por registrar a folha de estilo para o dashboard e especificamente para a página do widget do WordPress,
- a segunda classe será responsável por registrar o plugin para o próprio blog.
Vamos chamar cada um desses AdminStylesheetSubscriber e PublicStylesheetSubscriber respectivamente.
Primeiro, o assinante da folha de estilo do administrador :
<?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'
);
}
}
Observe que isso usa a função get_current_screen() que usei em postagens anteriores para garantir que estamos adicionando dependências apenas quando necessário.
Agora, o assinante público de JavaScript. Isso usa a função is_admin() para garantir que não estamos na área administrativa do 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'
);
}
}
Obviamente, ainda temos que instanciar essas classes. Isso está vindo mais tarde na série.
JavaScript
Os assinantes de JavaScript não são muito diferentes, como você deve ter adivinhado. Vamos separá-los com base na área do aplicativo em que eles se concentram e os nomearemos adequadamente.
Primeiro, o assinante JavaScript do administrador :
<?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'
);
}
}
E o assinante público do JavaScript:
<?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'
);
}
}
Novamente, essas classes ainda não podem ser instanciadas, mas vamos nos concentrar nisso em um próximo post.
Abstrações e Interfaces
Lembre-se de que abstrações e interfaces são diferentes, mas são facilmente confundidas. As interfaces contêm implementação absolutamente zero. Em vez disso, eles fornecem uma garantia de que qualquer classe que implemente a interface implementará todos os métodos.
As classes abstratas, por outro lado, podem ter alguma funcionalidade implementada na classe abstrata, deixando o código específico do domínio – como carregar folhas de estilo e JavaScript – para o método apropriado.
Isso se tornará aparente, se já não ficou, quanto mais avançarmos nesta série. Enquanto isso – e como de costume – não se esqueça de verificar o branch de desenvolvimento para ver onde estamos com o código.


