Desacoplamento da lógica de domínio no WordPress
Lembre-se de que o WordPress usa o padrão de design orientado a eventos e, embora muitas vezes nos refiramos a ações e filtros, o conceito se resume a ganchos. O fluxo de controle através do programa é mais ou menos assim:
- Executar o programa,
- Sempre que o programa encontrar um gancho (no WordPress, veremos
do_actionouapply_filters), faça uma iteração por todos os ganchos registrados, - Retorne o controle de volta ao programa,
- Executar até o final.
Isso não é completamente diferente do padrão Publisher/Subscriber (ou PubSub, para abreviar), mas há uma diferença fundamental: o padrão orientado a eventos simplesmente sinaliza que algo aconteceu e, se houver ganchos, eles serão acionados. O padrão PubSub dirá a um assinante registrado para fazer algo.
De qualquer forma, de volta aos ganchos no WordPress. Manter os dois conceitos de ganchos que temos pode ser feito mais facilmente pensando neles assim:
- Ações são para fazer algo,
- Os filtros são para processar dados.
Se você deseja abordar o desenvolvimento do WordPress de maneira orientada a objetos, não é uma boa ideia acoplar seu código ao núcleo do WordPress registrando suas classes por meio de ganchos no aplicativo principal.
Em outras palavras, não registre sua lógica de negócios no WordPress. Mantenha-os separados. Aqui está um teste decisivo para saber se seu trabalho está fortemente acoplado ao WordPress: Se você não pode executar um teste de unidade em sua classe sem carregar o WordPress, ele está fortemente acoplado.
Então, qual é a solução? Delegação.
Lógica de domínio no WordPress
A lógica de domínio e a lógica de negócios são intercambiáveis no que me diz respeito, então se você leu posts anteriores sobre isso e eu falei sobre eles de maneiras diferentes, você sabe por quê.
Em seguida, a ideia de delegar lógica do WordPress para uma classe de lógica de domínio no WordPress é feita por uma classe intermediária que é responsável pelo seguinte:
- Inscrevendo-se em um gancho,
- Delegar o trabalho a uma classe.
Eu sei que as classes devem fazer “uma coisa bem", mas e se essa coisa for delegação?
comprometer (poderes, funções, etc.) a outro como agente
E para comprometer a funcionalidade adequadamente com outro agente ou, no nosso caso, uma classe, você precisa saber o que está delegando. Às vezes, para fazer uma coisa, você precisa saber várias informações.
Então, como isso se parece praticamente falando? Imagine que você tenha um [AbstractSubscriber](https://github.com/tommcfarlin/remove-empty-shortcodes/blob/master/src/Subscriber/AbstractSubscriber.php)to para levar o nome de um gancho em seu construtor:
<?php
/*
* This file is part of Remove Empty Shortcodes.
*
* (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 TomMcFarlinRESCSubscriber;
use TomMcFarlinUtilitiesRegistry;
/**
* 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;
/**
* @var Registry a reference to the simple container used to maintain plugin objects
*/
protected $registry;
/**
* @param string $hook the hook to which the subscriber is registered
*/
public function __construct(string $hook)
{
$this->hook = $hook;
$this->registry = apply_filters('rescRegistry', null);
}
/**
* @return string the hook to which the subscriber is registered
*/
public function getHook(): string
{
return $this->hook;
}
abstract public function load();
}
E então, uma vez feito, a loadfunção enviará o trabalho para uma classe responsável por realmente fazer o processamento.
Veja, por exemplo, este código de Remove Empty Shortcodes :
<?php
/*
* This file is part of Remove Empty Shortcodes.
*
* (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 TomMcFarlinRESCWordPress;
/**
* Processes the post content by looking to see if any orphaned shortcode
* exists and then removes it from displaying it from the user.
*/
class PostContentProcessor
{
/**
* A reference to the Shortcode Manager for processing orphaned shortcodes.
*/
private $shortcodeManager;
/**
* Initializes the class by setting up a reference to the Registry and the
* Shortcode Manager.
*/
public function __construct()
{
$registry = apply_filters('rescRegistry', null);
$this->shortcodeManager = $registry->get('shortcodeManager');
}
/**
* @param string $content the filtered post content
*
* @return string $content the filtered post content without the shortcode
*/
public function run(string $content): string
{
return $this->shortcodeManager->processShortcodes($content);
}
}
Uma classe se inscreve em um evento específico, como [the_content](https://developer.wordpress.org/reference/functions/the_content/), e depois delega o trabalho para a classe de processamento de conteúdo posterior.
Isso literalmente permite que o arquivo bootstrap do plugin instancie o delegado. O delegado então se conecta ao WordPress e quando o WordPress chega ao ponto adequado de execução, o delegado envia a responsabilidade para a classe responsável por processá-lo.
Toda essa arquitetura não é apenas completamente reutilizável (veja o uso de uma classe abstrata acima), mas nos permite desacoplar a lógica de domínio do WordPress e testá-la isoladamente.
Mais sobre separação de interesses
Eu escrevi alguns outros posts sobre separação de interesses:
- Separação de preocupações com modelos WordPress
- Separação de preocupações com consultas e funções auxiliares
- Programação WordPress: Separando Preocupações
