Rapid Prototyping: Prototype To Code, del 1
När det gäller snabba prototyper och WordPress har vi hittills gjort två saker:
Vid det här laget har vi gjort tillräckligt mycket arbete för att motivera att vi börjar omstrukturera vår kod. Det vill säga, vi ska börja konvertera prototypen till kod. Men det här är något som kommer att behöva göras i två faser.
Först kommer vi helt enkelt att introducera klasser som representerar diagrammen från föregående inlägg och som kapslar in ansvaret för varje projekt.
Efter det kommer vi att titta på att organisera koden i namnutrymmen och paket. Innan vi kan göra det måste vi dock se till att koden är objektorienterad och förblir funktionell. Så det är vad som kommer att hända i det här inlägget.
Prototyp till kod
Om du har läst de tidigare inläggen, notera att jag planerar att följa organisationen som jag skissade på i förra inlägget. Du behöver naturligtvis inte följa den här designen.
Ett ord om källkontroll
Om du använder källkontroll, är det här jag rekommenderar att du skapar en förgrening av huvudgrenen (om du använder Git) så att du kan göra ditt arbete utan att försvåra den stabila versionen av koden.
Detta är lite utanför seriens omfattning, så om du inte använder källkontroll, oroa dig inte. Om du är det väljer jag att gå med utveckla som namnet på denna gren. Jag slår ihop den tillbaka till master när jag är säker på att den fungerar.
Att skriva kod
Enligt arbetet som skissades igår kommer jag att skapa två klasser:
- metabox-klassen,
- metabox-visningsklassen.
Det kommer att finnas en viss återanvändning av kod från det vi redan har sett som du kommer att se i följande kod.
Koden
Först vår metabox :
<?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'
);
}
}
Och nästa, vår display :
<?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();
}
}
Att i metaboxkoden, i metaboxkoden instansierar vi uttryckligen displayen så att vi kan kalla dens displaymetod när det behövs.
Ett annat alternativ skulle vara att instansiera de två objekten separat och sedan injicera displayen i metaboxen via konstruktorinjektion eller något liknande. Detta skulle behöva göras i en tredje parts klass.
Fördelarna med detta kommer från att koppla de två klasserna lite mer. Vi kanske ska granska hur man gör detta i nästa inlägg.
Efter det måste vi gå vidare och definiera klassen som är ansvarig för att visa meddelanden inom ramen för Meta Box Display. Det här är vad vi kommer att kalla 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';
}
}
Observera här att Post Messenger också refererar till Post-frågan. Det här är klassen där kommunikationen till databasen sker. Jag har också inkluderat några hjälpfunktioner för att göra visningskoden lite enklare som vi kommer att se ett ögonblick.
<?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();
}
}
Och det är det för kärnklasserna. Naturligtvis behöver vi fortfarande prata om synpunkterna.
Utsikterna
Vyerna är ansvariga för att rendera HTML i kontexten av metaboxen. Jag gillar inte att skriva HTML inom PHP-sammanhang (och jag gillar inte heller att blanda PHP i HTML-sammanhang, men detta är oundvikligt i det här projektet).
Det finns några bra mallprojekt för att göra det här enklare, men jag avviker. Hur som helst, du kommer att märka att det i post-list.php- filen finns referenser till hjälpfunktioner i klassen Post Query. Detta för att se till att jag inte exponerar för många egenskaper och bryter mot Demeterlagen.
Låt oss ta en titt på den filen först eftersom den är den mest komplicerade :
<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>
Det ser ut som den där vanliga WordPress-koden, men kom ihåg att eftersom den här filen anropas i Post Messenger kommer den att hänvisa till frågan som den fråga som lindas av den klassen.
De två sista filerna är ganska okomplicerade. En av dem ger en beskrivning :
<p>
<span class="description">
Displays up to the three most recent posts.
</span><!-- .description -->
</p>
Den andra ger ett meddelande när det inte finns några inlägg :
<p>There are no recent posts.</p>
Annat än det är den grundläggande funktionaliteten klar.
Bootstrapping Plugin
Det sista vi behöver göra är att starta insticksprogrammet. För att göra detta ändrar vi koden i huvudpluginfilen så att den ser ut så här :
<?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();
}
Det kommer att ansluta till WordPress, instansiera vårt plugin och sedan sätta igång det. När du kör det i din installation av WordPress bör det se ut precis som det gjorde under den första versionen.
Den enda skillnaden är att vi nu har saker organiserade i klasser snarare än individuella funktioner.
Anteckningar
För det första finns det möjligheter till refaktorisering här som skulle minska frikopplingen ytterligare (som olika typer av beroendeinjektion, etc.) men syftet med den här serien är inte att täcka det.
Istället är det att ta tanken på att se plugins skrivna av många procedurfunktioner och sedan dela upp dem i mer konceptuella klasser som kapslar in deras ansvar.
För det andra, om du granskar källkoden i arkivet för den här versionen av projektet, kommer du att se att jag också introducerade composer.json. Detta för att jag ska kunna dra nytta av PHP CodeSniffer och WordPress Coding Standards när jag skriver kod.
I den sista delen av serien går vi igenom namnavstånd och omorganiserar filerna. Om tiden tillåter kommer vi att inkludera en autoloader så att vi inte behöver inkludera filer manuellt överst i vår plugin-fil.
Slutligen har jag slagit ihop den här koden till master och taggat den som 0.2.1 (eftersom jag var tvungen att göra en liten snabbkorrigering) eftersom det fortfarande är ett mycket pågående arbete.
Serie inlägg
- Snabb prototyping med WordPress: från koncept till plugin
- Snabb prototyping med WordPress: konceptanalys
- Rapid Prototyping: Prototype To Code, del 1
- Rapid Prototyping: Prototype To Code, del 2
- Rapid Prototyping: Introduktion av Autoloading