Prototypage rapide : Prototyper pour coder, partie 1
En ce qui concerne les prototypes rapides et WordPress, nous avons fait deux choses jusqu’à présent :
À ce stade, nous avons fait suffisamment de travail pour justifier de commencer à refactoriser notre code. Autrement dit, nous allons commencer à convertir le prototype en code. Mais c’est quelque chose qui va devoir être fait en deux phases.
Dans un premier temps, nous allons simplement introduire des classes qui représentent les diagrammes du post précédent et qui encapsulent la responsabilité de chaque projet.
Après cela, nous verrons comment organiser le code en espaces de noms et packages. Avant de pouvoir le faire, nous devons nous assurer que le code est orienté objet et reste fonctionnel. C’est donc ce qui va se passer dans ce post.
Prototyper à coder
Si vous avez lu les messages précédents, notez que je prévois de suivre l’organisation que j’ai esquissée dans le dernier message. Vous n’êtes pas obligé de suivre cette conception particulière, bien sûr.
Un mot sur le contrôle des sources
Si vous utilisez le contrôle de source, c’est là que je recommande de créer une branche hors de la branche master (si vous utilisez Git) afin que vous puissiez faire votre travail sans nuire à la version stable du code.
C’est un peu au-delà de la portée de la série, donc si vous n’utilisez pas le contrôle de source, pas de soucis. Si c’est le cas, j’opte pour développer comme nom de cette branche. Je le fusionnerai dans master une fois que je serai sûr qu’il est fonctionnel.
Code d’écriture
Selon le travail esquissé hier, je vais créer deux classes :
- la classe meta box,
- la classe d’affichage de la boîte méta.
Il y aura une certaine réutilisation du code à partir de ce que nous avons déjà vu, comme vous le verrez dans le code suivant.
Le code
Tout d’abord, notre meta box :
<?php
/**
* Registers the Meta Box with WordPress.
*
* @author Tom McFarlin
* @since 0.2.0
*/
/**
* Registers the Meta Box with WordPress. Defines the ID, title, display function,
* and the post type on which it will live.
*
* @author Tom McFarlin
* @since 0.2.0
*/
class Meta_Box {
/**
* A reference to the class that will display the contents in the meta box.
*
* @access private
* @var Meta_Box_Display
*/
private $meta_box_display;
/**
* Instantiates the class by setting its property equal to a reference to its display.
*/
public function __construct() {
$this->meta_box_display = new Meta_Box_Display();
}
/**
* The function responsible for hooking into the WordPress API.
*/
public function init() {
add_meta_box(
'three-recent-posts',
'Three Recent Posts',
array( $this->meta_box_display, 'display' ),
'post',
'side'
);
}
}
Et ensuite, notre affichage :
<?php
/**
* Defines the display for the meta box.
*
* @author Tom McFarlin
* @since 0.2.0
*/
/**
* Defines the display for the meta box that will render the content in the
* context of its meta box.
*
* @author Tom McFarlin
* @since 0.2.0
*/
class Meta_Box_Display {
/**
* A reference to the class that will display the contents in the meta box.
*
* @access private
* @var Post_Messenger
*/
private $messenger;
/**
* Instantiates the object by setting a property equal to that of the class
* responsible for rendering the messages from the post query.
*/
public function __construct() {
$this->messenger = new Post_Messenger( $this );
}
/**
* If there are posts to display, renders them in the metabox. Otherwise, displays
* a note that there are no posts to display.
*/
public function display( $message) {
$this->messenger->get_message();
}
}
Que dans le code de la méta-boîte, dans le code de la méta-boîte, nous instancions explicitement l’affichage afin que nous puissions appeler sa méthode d’affichage en cas de besoin.
Une autre alternative consisterait à instancier les deux objets séparément, puis à injecter l’affichage dans la méta-boîte via une injection de constructeur ou quelque chose de similaire. Cela devrait être fait dans une classe tierce.
Les avantages de cela viennent du découplage un peu plus des deux classes. Peut-être verrons-nous comment procéder dans le prochain article.
Après cela, nous devons continuer et définir la classe responsable de l’affichage des messages dans le contexte de l’affichage de la Meta Box. C’est ce que nous appellerons le Post Messenger :
<?php
/**
* Display content for the meta box when requested.
*
* @author Tom McFarlin
* @since 0.2.0
*/
/**
* Retrieves information from the class responsible for querying the database and
* renders it in the context of our meta box when called via the Meta Box Display.
*
* @author Tom McFarlin
* @since 0.2.0
*/
class Post_Messenger {
/**
* A reference to the query resonsible for retrieving post information from
* the database.
*
* @access private
* @var WP_Query
*/
private $query;
/**
* A reference to the message that's displayed in the view of the
* meta box.
*
* @access private
*/
private $message;
/**
* Instantiates the class by setting a reference to the query.
*/
public function __construct() {
$this->query = new Post_Query();
}
/**
* Retrieves the content to be displayed in the meta box.
*/
public function get_message() {
$this->get_description();
if ($this->query->has_posts()) {
$this->get_post_message();
} else {
$this->get_no_posts_message();
}
}
/**
* Displays the description of the content of the meta box.
*
* @access private
*/
private function get_post_message() {
include_once 'post-list.php';
}
/**
* Displays the description of the content of the meta box.
*
* @access private
*/
private function get_description() {
include_once 'message-description.php';
}
/**
* Displays a message of there are no recent posts.
*
* @access private
*/
private function get_no_posts_message() {
include_once 'no-post-list.php';
}
}
Notez ici que Post Messenger fait également référence à Post Query. Il s’agit de la classe où se produit la communication avec la base de données. J’ai également inclus quelques fonctions d’assistance pour rendre le code de vue un peu plus simple, comme nous le verrons dans un instant.
<?php
/**
* Queries the database for three most recent posts.
*
* @author Tom McFarlin
* @since 0.2.0
*/
/**
* Queries the database for three most recent posts. Returns the query to the
* caller so that it can be interrogates for posts or not.
*
* @author Tom McFarlin
* @since 0.2.0
*/
class Post_Query {
/**
* A reference to the WP_Query this class wraps.
*
* @access private
* @var WP_Query
*/
private $query;
/**
* Instantiates the class by preparing instance data and executing the
* query so the display can render the contents.
*/
public function __construct() {
$this->query = null;
$this->get_posts();
}
/**
* Executes the query for returning the post recent posts ordered by date.
*
* @access private
*/
private function get_posts() {
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'desc',
);
$this->query = new WP_Query( $args );
return $this->query;
}
/**
* A helper function to determine if the query has any posts.
*/
public function has_posts() {
return! $this->query->have_posts();
}
/**
* A helper function for retrieving the next post in the list of
* posts
*/
public function the_post() {
return $this->query->the_post();
}
}
Et c’est tout pour les classes de base. Bien sûr, nous devons encore parler des points de vue.
Les vues
Les vues sont responsables du rendu du HTML dans le contexte de la méta-boîte. Je n’aime pas écrire du HTML dans le contexte de PHP (je n’aime pas non plus mélanger PHP dans le contexte de HTML, mais c’est inévitable dans ce projet).
Il existe d’excellents projets de modèles pour faciliter cela, mais je m’éloigne du sujet. Quoi qu’il en soit, vous remarquerez que dans le fichier post-list.php, il y a des références aux fonctions d’assistance dans la classe Post Query. C’est pour m’assurer que je n’expose pas trop de propriétés et que je ne viole pas la loi de Déméter.
Voyons d’abord ce fichier car c’est le plus compliqué :
<ol>
<?php while ($this->query->has_posts()) { ?>
<?php $this->query->the_post(); ?>
<li>
<a href="<?php get_the_permalink(); ?>" target="_blank">
<?php echo get_the_title(); ?>
</a>
</li>
<?php } ?>
</ol>
Il ressemble à ce code WordPress standard, mais rappelez-vous que puisque ce fichier est appelé dans le Post Messenger, il va faire référence à la requête en tant que requête enveloppée par cette classe.
Les deux derniers fichiers sont assez simples. L’un d’eux fournit une description :
<p>
<span class="description">
Displays up to the three most recent posts.
</span><!-- .description -->
</p>
L’autre fournit un message lorsqu’il n’y a pas de messages :
<p>There are no recent posts.</p>
En dehors de cela, la fonctionnalité de base est terminée.
Amorcer le plugin
La dernière chose que nous devons faire est de démarrer le plugin. Pour ce faire, nous modifions le code dans le fichier principal du plugin pour qu’il ressemble à ceci :
<?php
/**
* Three Recent Posts
*
* @package TRP
* @author Tom McFarlin
* @copyright 2017 Tom McFarlin
* @license MIT
*
* @wordpress-plugin
* Plugin Name: Three Recent Posts
* Plugin URI: https://tommcfarlin.com/three-recent-posts/
* Description: Displays the three mot recent posts in your post editor screen.
* Version: 0.2.0
* Author: Tom McFarlin
* Author URI: https://tommcfarlin.com
* Text Domain: three-recent-posts
* License: GPL
* License URI: http://www.gnu.org/licenses/gpl-3.0.txt
*/
include 'class-meta-box.php';
include 'class-meta-box-display.php';
include 'class-post-messenger.php';
include 'class-post-query.php';
add_action( 'add_meta_boxes', 'trp_start' );
/**
* Starts the plugin.
*/
function trp_start() {
$meta_box = new Meta_Box();
$meta_box->init();
}
Cela s’accrochera à WordPress, instanciera notre plugin, puis le mettra en mouvement. Lorsque vous l’exécutez dans votre installation de WordPress, il devrait ressembler exactement à ce qu’il était lors de la première version.
La seule différence est que nous avons maintenant des choses organisées en classes plutôt qu’en fonctions individuelles.
Remarques
Tout d’abord, il existe ici des opportunités de refactorisation qui réduiraient encore plus le découplage (comme différents types d’ injection de dépendances, etc.), mais le but de cette série n’est pas de couvrir cela.
Au lieu de cela, il s’agit de prendre l’idée de voir des plugins écrits par de nombreuses fonctions procédurales, puis de les diviser en classes plus conceptuelles qui encapsulent leurs responsabilités.
Deuxièmement, si vous examinez le code source dans le référentiel de cette version du projet, vous verrez que j’ai également introduit composer.json. C’est pour que je puisse tirer parti de PHP CodeSniffer et des normes de codage WordPress lors de l’écriture de code.
Dans la dernière partie de la série, nous passerons en revue l’ espacement des noms et réorganiserons les fichiers. Si le temps le permet, nous inclurons un chargeur automatique afin que nous n’ayons pas à inclure manuellement des fichiers en haut de notre fichier de plugin.
Enfin, j’ai fusionné ce code dans master et l’ai marqué comme 0.2.1 (car j’ai dû faire un petit correctif) car c’est encore un travail en cours.
Messages de la série
- Prototypage rapide avec WordPress : du concept au plugin
- Prototypage rapide avec WordPress: analyse de concept
- Prototypage rapide: Prototyper pour coder, partie 1
- Prototypage rapide: Prototyper pour coder, partie 2
- Prototypage rapide : présentation du chargement automatique