Bonjour,
Je me présente, Théo, je suis un jeune développeur dans une entreprise qui travaille sur Wordpress, et au fur et a mesure du temps, je me suis rendu compte que je faisais très souvent la même chose sur la pluspart des sites, j'avais envie d'arrêter de regarder ce que j'avais fait sur les autres sites pour le refaire. Donc je me suis dit que j'allais me crée des classes. Je ne suis pas ultra fort en POO, je me débrouille, mais j'ai besoin de savoir si les classes sont bien faites, bien construites, j'y ai passé pas mal de temps pour essayer de bien les réfléchir, mais j'aurais besoin d'un avis extérieur pour savoir si je suis dans la bonne voie.
Voici les problématiques rencontrées :
Sur les pages category.php et single.php, souvent, j'avais besoin d'afficher beaucoup d'articles et les catégories correspondantes. Cependant, ça me gonflait de mettre toute la logique dans category.php ou single.php et de répéter du code ou de faire une fonction pour afficher les catégories.
Et aussi, sur tous les sites ça me saoulait de faire des (while have_posts) tout ça pour afficher des articles je voulais faire quelque chose de plus simple (pour moi ça l'est en tout cas). Voici les classes en question :
Posts.php
<?php
namespace App\Posts;
use App\Pagination;
use App\Posts\Post;
class Posts {
/**
* Numbers of posts you want to display
*
* @var int
*/
private $numbers = -1;
/**
* Number of characters in excerpt
*
* @var int
*/
private $excerptCharacterNumber = 120;
/**
* The string after the excerpt
*
* @var string
*/
private $excerptOverflow = '...';
/**
* Excludes some Posts
*
* @var array
*/
private $excludes = [];
/**
* Posts need to be paginate ?
*
* @var bool
*/
private $pagination = false;
/**
* Date format
*
* @var string
*/
private $dateFormat = 'd F Y';
/**
* Category ID
*
* @var int
*/
private $categoryID;
/**
* Query of the page
*
* @var \WP_Query
*/
private $wpQuery;
/**
* Template use to display posts
* (Refer to setTemplate())
*
* @var string
*/
private $template;
/**
* Set up category ID
* Get the term ID if this is a category page
* else, from the parameter
*
* @param string $categoryID
*
* @throws \Exception
*/
public function __construct($categoryID = '') {
global $wp_query;
$this->wpQuery = $wp_query;
if (empty($categoryID) && !$this->categoryPage()) throw new \Exception('You need to set a category ID.');
if ($categoryID && !$this->categoryPage()) $this->categoryID = $categoryID;
if ($this->categoryPage()) $this->categoryID = $this->getQueriedTermID();
}
/**
* Get the queried term ID
*
* @return mixed
*/
public function getQueriedTermID() {
return $this->wpQuery->get_queried_object()->term_id;
}
/**
* Is this a category page ?
*
* @return bool
*/
private function categoryPage() {
return ($this->wpQuery->get_queried_object()->taxonomy === "category") ? true : false;
}
/**
* Get top category
*
* @return mixed
*/
private function getTopCategoryID() {
$category = get_category($this->categoryID);
if ($category->parent) {
$ancestors = get_ancestors($this->categoryID, 'category');
return $ancestors[count($ancestors) - 1];
} else {
return $category->term_id;
}
}
/**
* Get an array with all the categories
*
* @return \WP_Term[]
*/
private function getCategories() {
$topCategoryID = $this->getTopCategoryID();
$categories = get_categories(['child_of' => $topCategoryID]);
array_unshift($categories, get_category($topCategoryID));
return $categories;
}
/**
* Get all the posts
*
* If it's category page, get the posts from this category ID
*
* @return int[]|\WP_Post[]|\WP_Query
*/
private function getAll() {
if ($this->pagination) {
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
return new \WP_Query([
'post_type' => 'post',
'post__not_in' => $this->excludes,
'posts_per_page' => $this->numbers,
'paged' => $paged,
'tax_query' => [
[
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $this->categoryID
]
]
]);
} else {
return new \WP_Query([
'posts_per_page' => $this->numbers,
'post__not_in' => $this->excludes,
'cat' => $this->categoryID
]);
}
}
/**
* List all categories, return an array with
* ID of the category, if the category is active or not, it name
*
* @return array
*/
public function categories() {
$categories = [];
foreach ($this->getCategories() as $category) {
$categories[$category->term_id]['top_category'] = ($this->getTopCategoryID() === $category->term_id) ? 1 : 0;
$categories[$category->term_id]['active'] = ($this->getQueriedTermID() === $category->term_id) ? 1 : 0;
$categories[$category->term_id]['name'] = $category->name;
}
return $categories;
}
/**
* Exclude one or multiple IDs from display
*
* @param $excludesIDs
*
* @return $this
*/
public function exclude($excludesIDs) {
if (!is_array($excludesIDs)) $excludes[] = $excludesIDs;
$this->excludes = $excludes;
return $this;
}
/**
* Set the numbers of posts you want to display
*
* @param $numbers
*
* @return $this
* @throws \Exception
*/
public function setNumbers($numbers) {
if (!is_int($numbers)) throw new \Exception('Numbers have to be an integer.');
$this->numbers = $numbers;
return $this;
}
/**
* Set the number of character you want in excerpt
*
* @param $excerptCharacterNumber
*
* @return $this
* @throws \Exception
*/
public function setExcerptCharacterNumber($excerptCharacterNumber) {
if (empty($excerptCharacterNumber)) throw new \Exception('Excerpt number parameter can\'t be null.');
if (!is_int($excerptCharacterNumber)) throw new \Exception('Excerpt number have to be an integer.');
$this->excerptCharacterNumber = $excerptCharacterNumber;
return $this;
}
/**
* Set the string after the excerpt
*
* @param $excerptOverflow
*
* @return $this
* @throws \Exception
*/
public function setExcerptOverflow($excerptOverflow) {
if (empty($excerptOverflow)) throw new \Exception('Excerpt Overflow parameter can\'t be null.');
if (!is_string($excerptOverflow)) throw new \Exception('Excerpt Overflow parameter have to be a string.');
$this->excerptOverflow = $excerptOverflow;
return $this;
}
/**
* Paginate posts
*
* @return $this
*/
public function paginate() {
$this->pagination = true;
return $this;
}
/**
* Set up the date format
*
* @param $dateFormat
*
* @return $this
* @throws \Exception
*/
public function setDateFormat($dateFormat) {
if (empty($dateFormat)) throw new \Exception('Date format parameter can\'t be null.');
if (!is_string($dateFormat)) throw new \Exception('Date format parameter need to be a string.');
$this->dateFormat = $dateFormat;
return $this;
}
/**
* Set the template for a post with some variables
*
* {%TITLE%} for the title of the post
* {%LINK%} for the link of the post
* {%DATE%} for the date of the post
* {%TOP_CATEGORY%} for the top category of the post
* {%EXCERPT%} for the excerpt
* {%THUMBNAIL%} for the thumbnail
* {%THUMBNAIL_URL%} for the thumbnail URL
*
* @param string $template
*
* @return $this
*
* @throws \Exception
*/
public function setTemplate($template) {
if (empty($template)) throw new \Exception('Template parameter can\'t be null.');
$this->template = $template;
return $this;
}
/**
* Get pagination
*
* @return Pagination
* @throws \Exception
*/
public function getPagination() {
if (!$this->pagination) throw new \Exception('You need to activate the pagination with the "paginate" method to get a pagination.');
return new Pagination($this->wpQuery);
}
/**
* Get HTML of all posts, you can also wrap everything
*
* @param string $startWrap
* @param string $endWrap
*
* @return string
* @throws \Exception
*/
public function all($startWrap = '', $endWrap = '') {
$html = '';
$allPosts = $this->getAll();
if (!empty($startWrap)) $html .= $startWrap;
while ($allPosts->have_posts()) {
$allPosts->the_post();
$post = new Post(get_the_ID(), $this->excerptCharacterNumber, $this->excerptOverflow, $this->dateFormat, $this->template);
$html .= $post->display();
}
if (!empty($endWrap)) $html .= $endWrap;
return $html;
}
}
Post.php
<?php
namespace App\Posts;
class Post {
/**
* ID of the post
*
* @var int
*/
private $postID;
/**
* Number of characters in excerpt
*
* @var int
*/
private $excerptCharacterNumber;
/**
* The string after the excerpt
*
* @var string
*/
private $excerptOverflow;
/**
* Date format
*
* @var string
*/
private $dateFormat;
/**
* Template use to display posts
* (Refer to setTemplate()) of Posts class
*
* @var string
*/
private $template;
/**
* Create a post
*
* @param $postID
* @param $excerptCharacterNumber
* @param $excerptOverflow
* @param $dateFormat
* @param $template
*/
public function __construct($postID, $excerptCharacterNumber, $excerptOverflow, $dateFormat, $template) {
$this->postID = $postID;
$this->excerptCharacterNumber = $excerptCharacterNumber;
$this->excerptOverflow = $excerptOverflow;
$this->dateFormat = $dateFormat;
$this->template = $template;
}
/**
* Get the title with the tag we need
*
* @return string
*/
private function getTitle() {
return get_the_title($this->postID);
}
/**
* Get the link
*
* @return false|string
*/
private function getLink() {
return get_the_permalink($this->postID);
}
/**
* Get the date
*
* @return false|string
*/
private function getDate() {
return get_the_date($this->dateFormat, $this->postID);
}
/**
* Get top category name
*
* @return string
*/
private function getTopCategory() {
return get_the_category($this->postID)[0]->name;
}
/**
* Get excerpt
*
* @return string
*/
private function getExcerpt() {
if (!has_excerpt()) {
return substr(strip_tags(strip_shortcodes(preg_replace('#\[[^\]]+\]#', '', get_the_content($this->postID)))), 0, $this->excerptCharacterNumber) . $this->excerptOverflow;
}
return substr(strip_tags(strip_shortcodes(get_the_excerpt($this->postID))), 0, $this->excerptCharacterNumber) . $this->excerptOverflow;
}
/**
* Get the thumbnail
*
* @return string
*/
private function getThumbnail() {
return get_the_post_thumbnail($this->postID);
}
/**
* Get the thumbnail URL
*
* @return false|string
*/
private function getThumbnailUrl() {
return get_the_post_thumbnail_url($this->postID);
}
/**
* Replace variables in the template
*
* @return mixed|string
* @throws \Exception
*/
private function replaceVariables() {
$pattern = '/^.*{%(.*)%}.*$/m';
preg_match_all($pattern, $this->template, $matches);
array_shift($matches);
foreach ($matches[0] as $match) {
$method = 'get';
$strings = explode('_', $match);
foreach ($strings as $string) {
$string = ucfirst(strtolower($string));
$method .= $string;
}
if (!method_exists(Post::class, $method)) throw new \Exception("The method $method doesn't exist in the Post class. Create this method to use {%$match%} variable in your template.");
$this->template = str_replace("{%$match%}", $this->$method(), $this->template);
}
return $this->template;
}
/**
* Display the post
*
* @return mixed|string
* @throws \Exception
*/
public function display() {
return $this->replaceVariables();
}
}
Il y a aussi une class Pagination (que j'appelle dans Posts), la voici :
Pagination.php
<?php
namespace App;
/**
* Get Pagination
*
* Class Pagination
* @package App
*/
class Pagination {
/**
* @var \WP_Query
*/
private $query;
/**
* Total pages
*
* @var float
*/
private $totalPages;
/**
* Current page
*
* @var int
*/
private $currentPage;
/**
* Create a pagination
*
* @param $query
*/
public function __construct($query) {
$this->query = $query;
$this->init();
}
/**
* Init total pages & current pages variables
*/
private function init() {
$this->totalPages = $this->query->max_num_pages;
$this->currentPage = max(1, ( get_query_var( 'paged' ) ) ? absint( get_query_var( 'paged' ) ) : 1);
}
/**
* Pagination exist ?
*
* @return bool
*/
public function exist() {
if ($this->totalPages > 1) return true;
return false;
}
/**
* Get pages numbers with links, and if it's the current one
*
* @return array
*/
public function getPages() {
if (!$this->exist()) return [];
$pagination = [];
// Get all pages numbers and links
for ($i = 1 ; $i <= $this->totalPages ; $i++) {
$pagination[$i]['link'] = get_pagenum_link($i);
$pagination[$i]['current'] = ($this->currentPage == $i) ? 1 : 0;
}
return $pagination;
}
/**
* Get previous link
*
* @return array|string
*/
public function getPrevLink() {
if (!$this->exist()) return [];
foreach ($this->getPages() as $pageNumber => $page) {
if ($page['current'] && $pageNumber > 1) return get_pagenum_link($pageNumber - 1);
}
return '';
}
/**
* Get next link
*
* @return array|string
*/
public function getNextLink() {
if (!$this->exist()) return [];
foreach ($this->getPages() as $pageNumber => $page) {
if ($page['current'] && $pageNumber != $this->totalPages) return get_pagenum_link($pageNumber + 1);
}
return '';
}
}
J'ai fait la classe Pagination, car avant j'utilisais paginate_links, et ça me saoulait de devoir avoir une pagination uniforme sur toutes les pages et aller dans la fonction pour la faire, je voulais la faire sur chaque page différemment si j'en avais envie.
Voici un cas d'usage de Posts par exemple :
$posts = new \App\Posts\Posts(4);
$posts
->setNumbers(4)
->setExcerptCharacterNumber(211)
->setExcerptOverflow('.')
->setTemplate('
<article class="actuality">
<a href="{%LINK%}">
<div class="actuality__thumbnail">
{%THUMBNAIL%}
</div>
<div class="actuality__content">
<h2 class="actuality__content__title">{%TITLE%}</h2>
<div class="actuality__content__meta">
<p class="actuality__content__meta__date">{%DATE%}</p>
<p class="actuality__content__meta__divider">.</p>
<p class="actuality__content__meta__tagorcategory">{%TOP_CATEGORY%}</p>
</div>
<p class="actuality__content__excerpt">{%EXCERPT%}</p>
</div>
</a>
</article>
');
return $posts->all('<div class="news">', '</div>');
Merci!
Je ne sais pas si ce genre de chose se fais souvent, je ne sais pas si quelqu'un se donnera la peine de lire ce que j'ai fais, mais ce serais top! Histoire que je sache si je vais dans la bonne direction ou pas ? Ou peut-être que ce que j'ai fais c'est d'avoir perdu du temps ? J'ai l'impression d'en gagner, mais peut-être pas ? Est-ce que c'est plus simple ? Facilement évoluable, maintenable ?
Cordialement,
Théo