Parte 5 – WordPress e Programação Orientada a Objetos: Um Exemplo WordPress – Implementação: O Menu de Administração

Publicados: 2022-02-04

Em nosso artigo anterior sobre Programação Orientada a Objetos, discutimos o design que eventualmente criamos para nosso plugin orientado a objetos.

Agora, entraremos na parte mais empolgante, onde nos aprofundaremos em como o implementamos!

Vamos orientá-lo através de algumas partes da implementação, um passo de cada vez, falando sobre os fundamentos da programação orientada a objetos, sintaxe PHP, alguns conceitos básicos, e vamos até mesmo dar uma olhada nos princípios SOLID.

Ao final deste artigo, esperamos que você tenha uma melhor compreensão da POO e esteja animado para escrever seus próprios plugins orientados a objetos!

Começando

Presumimos que você esteja familiarizado com o desenvolvimento de plugins WordPress em geral, então vamos nos concentrar nos aspectos orientados a objetos do nosso plugin. Se você é novo no desenvolvimento de plugins ou precisa de uma atualização, você deve aprender como construir seu primeiro plugin WordPress primeiro.

Vamos começar como sempre fazemos, criando um novo arquivo prsdm-limit-login-attempts.php, em nosso diretório de plugins (ou seja, /wp-content/plugins/prsdm-limit-login-attempts).

O arquivo de plug-in principal incluirá o cabeçalho do plug-in com o qual você já está familiarizado:

 /** * Plugin Name: PRSDM Limit Login Attempts * Plugin URI: https://pressidium.com * Description: Limit rate of login attempts, including by way of cookies, for each IP. * Author: Pressidium * Author URI: https://pressidium.com * Text Domain: prsdm-limit-login-attempts * License: GPL-2.0+ * Version: 1.0.0 */

E uma instrução if simples para impedir o acesso direto a ele.

 if ( ! defined( 'ABSPATH' ) ) { exit; }

Isso é tudo que precisamos por enquanto. Revisitaremos este arquivo mais tarde!

Construindo um Menu de Administração

Quando você está desenvolvendo um plug-in, geralmente precisa fornecer aos usuários uma maneira de configurá-lo. É aí que entra uma página de configurações. Para construir uma, vamos adicionar um menu de administração que utiliza a API de configurações do WordPress.

Então, vamos começar a pensar em como nossa API orientada a objetos ficaria.

Idealmente, gostaríamos de instanciar nossa Pressidium_LLA_Settings_Page e terminar com ela. Para criar uma instância de uma classe, a palavra-chave new deve ser usada.

 new Pressidium_LLA_Settings_Page();

Agora, vamos pensar em como nossa classe Pressidium_LLA_Settings_Page ficaria.

Começaremos criando uma nova classe, usando a palavra-chave class :

 class Pressidium_LLA_Settings_Page {}

Nosso nome de classe deve ser prefixado com um identificador exclusivo, Pressidium_LLA_ para evitar colisões de nomenclatura com outros plugins do WordPress. Prefixos impedem que outros plugins sobrescrevam e/ou chamem acidentalmente nossas classes. Contanto que nossos nomes de classe sejam únicos - ou usemos namespaces - não haverá conflitos com outros plugins.

O Construtor

Agora, vamos conectar em admin_menu e admin_init. Para manter as coisas simples, vamos apenas chamar add_action() em nosso construtor (alerta de spoiler: vamos mudar isso mais tarde).

 class Pressidium_LLA_Settings_Page { /** * Settings_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } }

Classes que possuem um construtor, chamam esse método quando um objeto é instanciado. Portanto, o método __construct() é ótimo para qualquer inicialização que queiramos realizar.

Vamos dar uma olhada em nossas chamadas add_action() . Se você desenvolveu plugins do WordPress no passado, pode ter esperado algo assim:

 add_action( 'admin_menu', 'my_plugin_prefix_add_page' );

Mas em vez disso, temos:

 add_action( 'admin_menu', array( $this, 'add_page' ) );

Você pode estar confuso sobre o uso de uma matriz aqui. Sempre que queremos passar um método de um objeto instanciado como callback/callable, podemos usar um array contendo um objeto no índice 0 e um nome de método no índice 1.

O que é $isto?

É uma pseudo-variável que está disponível quando um método é chamado de dentro de um contexto de objeto. $this é o valor do objeto de chamada. Nesse caso, $this é uma instância de Pressidium_LLA_Settings_Page .

Além disso, todas as nossas “funções” agora são métodos, agrupados em uma classe, então não há necessidade de prefixar nossos nomes de métodos.

Namespaces

Namespaces em PHP nos permitem agrupar classes relacionadas, interfaces, funções, etc., evitando colisões de nomes entre nosso código e PHP interno ou classes/funções de terceiros.

Vamos em frente e usá-los, para que não precisemos prefixar nenhuma de nossas classes daqui para frente.

Declararemos um namespace usando a palavra-chave namespace .

 namespace Pressidium;

Os namespaces podem ser definidos com subníveis.

 namespace Pressidium\Limit_Login_Attempts;

Como estamos construindo uma página de configurações, vamos declarar um sub-namespace “pages” para agrupar qualquer coisa relacionada às páginas de administração.

 namespace Pressidium\Limit_Login_Attempts\Pages;

Finalmente podemos nos livrar do prefixo Pressidium_LLA_ !

 namespace Pressidium\Limit_Login_Attempts\Pages; class Settings_Page { // ...

Outro plugin WordPress contendo uma classe Settings_Page não é mais um problema, já que sua classe e nossa classe não estarão no mesmo namespace.

Ao instanciar nosso Settings_Page dentro do mesmo namespace, podemos omiti-lo:

 namespace Pressidium\Limit_Login_Attempts\Pages; $settings_page = new Settings_Page();

Ao instanciar nosso Settings_Page fora de seu namespace, temos que especificá-lo assim:

 namespace Another\Namespace; $settings_page = new \Pressidium\Limit_Login_Attempts\Pages\Settings_Page();

Alternativamente, poderíamos importar nossa classe com o operador use :

 use Pressidium\Limit_Login_Attempts\Pages\Settings_Page; $settings_page = new Settings_Page();

Adicionando retornos de chamada de gancho

Agora, vamos declarar esses add_page() e register_sections() .

 class Settings_Page { /** * Settings_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } /** * Add this page as a top-level menu page. */ public function add_page() { // TODO: Implement this method. } /** * Register sections. */ public function register_sections() { // TODO: Implement this method. } }

Nosso método add_page() irá apenas chamar a função add_menu_page() do WordPress.

 public function add_page() { add_menu_page( __( 'Limit Login Attempts Settings', 'prsdm-limit-login-attempts' ), __( 'Limit Login Attempts', 'prsdm-limit-login-attempts' ), 'manage_options', 'prsdm_limit_login_attempts_settings', array( $this, 'render' ), 'dashicons-shield-alt', null ); }

Isso parece uma maneira complicada de desenvolver plugins do WordPress. É simplesmente chamar funções do WordPress, com etapas extras.

Bem, isso não é exatamente “reutilizável”, ainda teríamos que escrever todo esse código extra para cada menu/página de administração que queremos adicionar.

Reestruturação

Vamos em frente e refatorar um pouco nosso código para tirar vantagem da programação orientada a objetos e tornar nosso código reutilizável . Começaremos substituindo nossos valores codificados em add_page() por alguns métodos, assim:

 public function add_page() { add_menu_page( $this->get_page_title(), // page_title $this->get_menu_title(), // menu_title $this->get_capability(), // capability $this->get_slug(), // menu_slug array( $this, 'render' ), // callback function $this->get_icon_url(), // icon_url $this->get_position() // position ); }

Vamos definir esses métodos como protected , para que possam ser acessados ​​apenas dentro da própria classe e por suas classes filha/pai.

 protected function get_page_title() { /* ... */ } protected function get_menu_title() { /* ... */ } protected function get_capability() { /* ... */ } protected function get_slug() { /* ... */ } protected function get_icon_url() { /* ... */ } protected function get_position() { /* ... */ }

Excelente! Agora podemos usar essa classe como uma classe genérica reutilizável para estender.

Redesenho

Nós dissemos que isso provavelmente iria acontecer eventualmente. Aqui estamos nós, repensando o design de nossa classe enquanto a construímos.

Como essa será nossa classe base , vamos renomeá-la para um nome mais genérico, como Admin_Page . Até agora, está assim:

 class Admin_Page { /** * Admin_Page constructor. */ public function __construct() { add_action( 'admin_menu', array( $this, 'add_page' ) ); add_action( 'admin_init', array( $this, 'register_sections' ) ); } /** * Add this page as a top-level menu page. */ public function add_page() { add_menu_page( $this->get_page_title(), // page_title $this->get_menu_title(), // menu_title $this->get_capability(), // capability $this->get_slug(), // menu_slug array( $this, 'render' ), // callback function $this->get_icon_url(), // icon_url $this->get_position() // position ); } /** * Register sections. */ public function register_sections() { // TODO: Implement this method. } protected function get_page_title() { /* ... */ } protected function get_menu_title() { /* ... */ } protected function get_capability() { /* ... */ } protected function get_slug() { /* ... */ } protected function get_icon_url() { /* ... */ } protected function get_position() { /* ... */ } }

Agora podemos criar uma Settings_Page separada que estende essa classe base Admin_Page .

 class Settings_Page extends Admin_Page { // ... }

Esse é um ótimo exemplo de herança , um dos conceitos centrais da programação orientada a objetos. Ao estender uma classe, a classe filha — Settings_Page , neste caso — herda todos os métodos, propriedades e constantes públicos e protegidos da classe pai.

Podemos fazer uso disso e definir alguns valores padrão. Por exemplo, definiremos um ícone genérico para todas as páginas do menu, definindo nosso método get_icon_url() assim:

 class Admin_Page { // ... /** * Return the menu icon to be used for this menu. * * @link https://developer.wordpress.org/resource/dashicons/ * * @return string */ protected function get_icon_url() { return 'dashicons-admin-generic'; } }

A menos que uma classe substitua esses métodos, eles manterão sua funcionalidade original. Portanto, por padrão, todas as classes filhas usarão esse ícone genérico.

No entanto, se quisermos definir outro ícone para uma página de menu específica, podemos simplesmente substituir o método get_icon_url() em nossa classe filha, assim:

 class Settings_Page extends Admin_Page { protected function get_icon_url() { return 'dashicons-shield-alt'; } }

Existem alguns valores, porém, que devem ser diferentes para cada classe filha. Por exemplo, o slug de menu - o quarto argumento de add_menu_page() - deve ser exclusivo para cada página de menu.

Se definissemos esse método em nossa classe base Admin_Page , precisaríamos de uma maneira de garantir que cada classe filha substitua esse método. Bem, podemos fazer algo ainda melhor. Podemos declarar a assinatura do método e pular completamente sua implementação.

Digite métodos abstratos!

Classes e métodos abstratos

Métodos definidos como abstratos simplesmente declaram a assinatura do método e não podem definir sua implementação.

 /** * Return page slug. * * @return string */ abstract protected function get_slug();

Qualquer classe que contenha pelo menos um método abstrato também deve ser abstrata. Isso significa que nossa classe Admin_Page também deve ser definida como abstrata.

 abstract class Admin_Page { // ...

Também é importante ressaltar aqui que as classes definidas como abstratas não podem ser instanciadas. Portanto, não podemos mais instanciar diretamente Admin_Page .

Aqui está também uma visualização da classe:

Ao herdar de uma classe abstrata, a classe filha deve definir todos os métodos marcados como abstratos na declaração de sua classe pai. Ou seja, nosso Settings_Page tem que implementar o método get_slug() .

 class Settings_Page extends Admin_Page { // ... protected function get_slug() { return 'prsdm_limit_login_attempts_settings'; } // ... }

Da mesma forma, devemos implementar o resto dos métodos protegidos que add_page() precisa.

Antes de continuar como vamos registrar as seções e campos da página de administração e renderizar seu conteúdo, vamos falar um pouco sobre configurações no WordPress.

A API de configurações

Vamos supor que você já esteja familiarizado com a API de configurações. Mas, apenas no caso, aqui está a essência:

  • settings_fields() — Gera campos nonce, action e option_page para uma página de configurações. Basicamente, os campos de formulário ocultos.
  • do_settings_sections() — Imprime todas as seções de configurações (e seus campos) adicionadas a uma página de configurações específica.
  • add_settings_section() — Adiciona uma nova seção a uma página de configurações.
  • add_settings_field() — Adiciona um novo campo a uma seção de uma página de configurações.
  • register_setting() — Registra uma configuração e seus dados.

Se você ainda não estiver familiarizado com isso, pode pausar a leitura deste artigo e verificar nosso artigo relacionado sobre como criar a página de configurações para um plug-in personalizado.

Agora que estamos na mesma página, vamos voltar ao nosso método register_sections() . Mais uma vez, temos que dar um passo atrás e pensar em nossa API.

Como definimos o método add_page() na classe Admin_Page , também definiremos o método render() lá. Passaremos os valores de retorno de nossos outros métodos como argumentos para as funções do WordPress.

 abstract class Admin_Page { // ... /** * Render this admin page. */ public function render() { ?> <div class="wrap"> <form action="options.php" method="post"> <h1><?php echo esc_html( $this->get_page_title() ); ?></h1> <?php settings_fields( $this->get_slug() ); do_settings_sections( $this->get_slug() ); submit_button( __( 'Change Options', 'prsdm-limit-login-attempts' ) ); ?> </form> </div> <?php } }

Dessa forma, nunca mais teremos que nos preocupar diretamente com essas funções do WordPress. Isso porque qualquer página de administração que possamos adicionar no futuro será construída por meio de uma classe filha assim como Settings_Page , e sua renderização será feita por meio do método render() herdado da classe pai Admin_Page .

Conclusão

Excelente! Criamos as classes responsáveis ​​por cadastrar um menu de administração e adicionar uma página de configurações.

No próximo artigo da série, continuaremos construindo nossa página de configurações e registrando suas seções, campos e elementos.

Clique aqui para ler a Parte 6 em nossa Série de Programação Orientada a Objetos

Veja também

  • WordPress e programação orientada a objetos – uma visão geral
  • Parte 2 – WordPress e Programação Orientada a Objetos: Um Exemplo do Mundo Real
  • Parte 3 – WordPress e Programação Orientada a Objetos: Α Exemplo WordPress – Definindo o Escopo
  • Parte 4 – WordPress e Programação Orientada a Objetos: um Exemplo WordPress – Design