WP Framework – myšlenka presenterů


Stejně jako v minulém díle, kde jsme se snažili vytvořit přehlednou a jasnou strukturu WordPress šablony, jsme nyní stáli před podobný problémem. Chtěli jsme mít i přehledné layouty samotných stránek pro front-end. Občas se stane, že potřebujete něco změnit – ano, souhlasím, pokud máte detail příspěvku, kde vypisuje 4 parametry : Titulek, Obsah, Autor a datum publikace, není asi potřeba psát a vytvářet presenter a vůbec se něčím takovým zabývat – také na co stavět kanón, když by vám na vrabce stačila vzduchovka. Ale přeci jenom, co když opravdu budete mít velký projekt.

Podívejme se třeba na základní soubor  pro zobrazování obsahu příspěvku – single.php

Jak vidíte z komentáře, jedná se o základní šablonu Twenty Elven. Chybí zde také header, footer a celá loopa. Zde autor šablony použil content část, kterou vola v rámci loopy pomocí get_template_part(); Nicméně, je Vám vše hned jasné nebo se musíte opravdu začíst a pátrat, co jaká část kódu dělá? Popravdě bych i já tápal, co která funkce asi dělá a jak.

<?php
/**
 * The template for displaying content in the single.php template
 *
 * @package WordPress
 * @subpackage Twenty_Eleven
 * @since Twenty Eleven 1.0
 */
?>

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
	<header class="entry-header">
		<h1 class="entry-title"><?php the_title(); ?></h1>

		<?php if ( 'post' == get_post_type() ) : ?>
		<div class="entry-meta">
			<?php twentyeleven_posted_on(); ?>
		</div><!-- .entry-meta -->
		<?php endif; ?>
	</header><!-- .entry-header -->

	<div class="entry-content">
		<?php the_content(); ?>
		<?php wp_link_pages( array( 'before' => '<div class="page-link"><span>' . __( 'Pages:', 'twentyeleven' ) . '</span>', 'after' => '</div>' ) ); ?>
	</div><!-- .entry-content -->

	<footer class="entry-meta">
		<?php
			/* translators: used between list items, there is a space after the comma */
			$categories_list = get_the_category_list( __( ', ', 'twentyeleven' ) );

			/* translators: used between list items, there is a space after the comma */
			$tag_list = get_the_tag_list( '', __( ', ', 'twentyeleven' ) );
			if ( '' != $tag_list ) {
				$utility_text = __( 'This entry was posted in %1$s and tagged %2$s by <a href="%6$s">%5$s</a>. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>.', 'twentyeleven' );
			} elseif ( '' != $categories_list ) {
				$utility_text = __( 'This entry was posted in %1$s by <a href="%6$s">%5$s</a>. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>.', 'twentyeleven' );
			} else {
				$utility_text = __( 'This entry was posted by <a href="%6$s">%5$s</a>. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>.', 'twentyeleven' );
			}

			printf(
				$utility_text,
				$categories_list,
				$tag_list,
				esc_url( get_permalink() ),
				the_title_attribute( 'echo=0' ),
				get_the_author(),
				esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) )
			);
		?>
		<?php edit_post_link( __( 'Edit', 'twentyeleven' ), '<span class="edit-link">', '</span>' ); ?>

		<?php if ( get_the_author_meta( 'description' ) && ( ! function_exists( 'is_multi_author' ) || is_multi_author() ) ) : // If a user has filled out their description and this is a multi-author blog, show a bio on their entries ?>
		<div id="author-info">
			<div id="author-avatar">
				<?php echo get_avatar( get_the_author_meta( 'user_email' ), apply_filters( 'twentyeleven_author_bio_avatar_size', 68 ) ); ?>
			</div><!-- #author-avatar -->
			<div id="author-description">
				<h2><?php printf( __( 'About %s', 'twentyeleven' ), get_the_author() ); ?></h2>
				<?php the_author_meta( 'description' ); ?>
				<div id="author-link">
					<a href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>" rel="author">
						<?php printf( __( 'View all posts by %s <span class="meta-nav">&rarr;</span>', 'twentyeleven' ), get_the_author() ); ?>
					</a>
				</div><!-- #author-link	-->
			</div><!-- #author-description -->
		</div><!-- #author-info -->
		<?php endif; ?>
	</footer><!-- .entry-meta -->
</article><!-- #post-<?php the_ID(); ?> -->

Nevím jak vy, já osobně tyhle typy souborů nemám rád – a to je to jenom single.php, kde je minimální logika a moc tam toho není. Teď si představte, když budete muset prezentovat hotelový pokoj – Těch parametrů, podmínek, cyklů, výpisů, formulářů k poptávce a já nevím co všechno. Na spoustu těchto věcí nebudeme mít ze strany WordPressu připravené funkce, jako je tomu zde u single.php. Takže si je musíte napsat a už tak rozsáhlý layout osadíte další sadou vlastních funkcí. Nebo to horší, místo funkce zapíšete logiku přímo do souboru.

Co když by ale layout vypadal třeba takto?

<?php
/**
 * The template for displaying content in the single.php template
 *
 * @package WordPress
 * @subpackage Twenty_Eleven
 * @since Twenty Eleven 1.0
 */
?>

<? $singlePresenter = new KT_Single_Content_Presenter(); ?>

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
	<header class="entry-header">
		<h1 class="entry-title"><?php the_title(); ?></h1>

		<?php echo $singlePresenter->getPostsEntryMeta(); ?>
	</header><!-- .entry-header -->

	<div class="entry-content">
		<?php the_content(); ?>
		<?php echo $singlePresenter->getPostContentPagination(); ?>
	</div><!-- .entry-content -->

	<footer class="entry-meta">
		<?php echo $singlePresenter->getPostCategoryAndTagList(); ?>

		<?php echo $singlePresenter->editLinkForLoggedUsers(); ?>

		<?php echo $singlePresenter->getAuthorInfo(); ?>
	</footer><!-- .entry-meta -->
</article><!-- #post-<?php the_ID(); ?> -->

Layout je 1:1 s tím, co vidíte nahoře. Pouze jsme založili nějaký objekt, kam jsme část logiky přesunuli. Ano, v těch funkcí, které volám nad prezenterem budou ty obdobná data jako v layoutu nahoře – nic vynechat ani opomenout pochopitelně nemůžeme. Není teď ale hned jasné, co se kde děje a proč?

Pokud používáte IDE – pokud to ještě neděláte, tak co nejdříve začněte – pomůže Vám nad presenterem také našeptávač, který hned nadhodí funkce, co potřebujete a které jsou v rámci layoutu pro single.php dostupné. Stejně tak stačí v IDE kliknout na funkci a jste hned v jejím těle a díváte se, co se v rámci ní děje.

Proč presentery používat?

Tímto způsobem je velmi jednoduché se v kódu snadno orientovat. Jedna funcke (pokud jí dobře pojmenujete) vyjádří víc, než 20 řádků kódu, kterým na první pohled nikdo nerozumí. Ano, pro psaní presenterů je potřeba znát aspoň základy objektového programování v PhP. Pokud ale řešíte například i výkon a rychlost Vaší presentace, velmi často se stává, že některé podmínky, výpisy nebo dotazy v rámci layotů opakujete nebo máte mnoho nepřehledných proměnných, v kterých se už nevyzná vůbec nikdo, protože jsou velmi často zakládány a načítány v průběhu celého layoutu.

Jak tedy takový presenter může vypadat?

<?php

final class KT_Single_Content_Presenter{

	private $post = null;
	private $thumbnailId = null;
	private $cateogryCollection = null;
	private $tagCollection = null;
	private $postAuthor = null;

	public function __construct(){
		global $post;

		if(isset($post) && !empty($post) && is_single() && $post->post_type = "post"){
			$this->setPost($post)->inicializace();
			return $this;
		}

		throw new InvalidArgumentException("post")
	}


	// --- setters

	private function setPost(WP_Post $post){
		$this->post = $post;

		return $this;
	}

	private function setThumbnailId($thumbnailId){
		$this->thumbnailId;

		return $this;
	}

	private function setCategoryCollection(array $cateogryCollection){
		$this->setCategoryCollection = $cateogryCollection;

		return $this;
	}

	private function setTagCollection(array $tagCollection){
		$this->setTagCollection($tagCollection);

		return $this;
	}

	private function setPostAuthor(WP_User $postAuthor){
		$this->postAuthor = $postAuthor;
	}

	// --- getters

	private function getPost(){
		return $this->post;
	}

	private function getThumbnailId(){
		return $this->thumbnailId;
	}	

	private function getCategoryCollection(){
		return $this->cateogryCollection;
	}

	private function getTagCollection(){
		return $this->tagCollection;
	}

	private function getPostAuthor(){
		return $this->postAuthor;
	}

	// --- public function

	public function getPostsEntryMeta(){
		// vrátí potřebné data
	}

	public function getPostContentPagination()
	{
		// vrátí potřebné data
	}

	public function getPostCategoryAndTagList(){
		// Zde použijeme categoryCollection a tagCollection
		// vrátí potřebné data
	}

	// ... atd dále bychom pokračovali v psání a definici dalších potřebných funkcí

	// --- private functions

	private function inicializace(){
		$this->thumbnailIdInit()
			->categoryCollectionInit()
			->tagCollectionInit()
			->postAuthorInit();
	}

	private function thumbnailIdInit(){
		// Zjištění zda post má náhledový obrázek, pokud ano, nastavíme jeho ID do proměnné objektu

		return $this;
	}

	private function categoryCollectionInit(){
		// Načteme všechny kategorie příspěvku a kolekci uložíme do proměnnné objektu

		return $this;
	}

	private function tagCollectionInit(){
		// Načteme všechny tagy příspěvku a kolekci uložíme do proměnné objektu

		return $this;
	}

	private function postAuthorInit(){
		// Založíme nový objekt WP_User na základě parametr post_author příspěvku

		return $this;
	}

}

Jak vidíte, většinu logiky jsem převedl do presenteru. V samotném souboru nemám žádné funkce, které by o něčem rozhodovaly, něco načítaly nebo iterovaly nějaké kolekce. Vše je úhledně, jasně a čistě zabalené do presenteru. Jednotlivé části funkcí jsem již programově nedoplňoval. Zde bych již tradičně použil funkce WordPressu, které ony data vracejí a jen bych je uzpůsobil své potřebě.

A přesně to je hlavní myšlenka presenterů. Je to velmi jednoduchý, účinný a přehledný „nástroj“. Každá funkce dělá svou jasnou dílčí část, každá má svůj důvod a smysl. Pokud v kombinaci s objekty budete používat i spl_autoloader_register, tak nemusíte při každém volání objektu includovat jeho soubor.

Ano, uznávám, že zde už je potřeba trochu znát php a určitě se na začátku budete chvíli trápit, než se celý proces „vychytá“. Na druhou stranu takováto struktura vám přináší další možnosti v dědičnosti dalších jiných presenterů nebo rozšířených logik.

Významné poděkování mému kolegovi Martinu Hlaváči, který do logiky presenterů, modelů a configů zavedl opravdu skvělý pořádek a smysl.

Tomáš Kocifaj, KTStudio.cz

, , ,

  1. #1 Musilda 21.8.2014 - 16:13

    Normálně mi mluvíš z duše :-).

    • #2 Tomáš Kocifaj 22.8.2014 - 07:14

      Zdravím, super, jsem rád, že někdo má podobný názor jako my. V další části bych se chtěl podívat na modely a configy. Celé si to pak sedne na vcelku dobře použitelný celek.

Komentáře jsou uzavřeny.