Maison >développement back-end >tutoriel php >Refactor : améliorez la méta-boîte WordPress pour une maintenance à long terme

Refactor : améliorez la méta-boîte WordPress pour une maintenance à long terme

王林
王林original
2023-08-30 20:37:02919parcourir

重构:增强 WordPress 元框以实现长期维护

Dans cette série, nous nous concentrons sur la création d'une méta-boîte WordPress maintenable. Ce que je veux dire, c'est que nous avons travaillé dur pour créer un plugin WordPress bien organisé, conforme aux normes de codage de WordPress et pouvant être facilement ajusté et maintenu à mesure que le projet continue de progresser.

Bien que nous ayons mis en œuvre quelques bonnes pratiques, il reste encore de la place pour une refactorisation. Pour cette série, c'est par conception. Chaque fois que vous développez un projet pour un client ou une grande entreprise, il y a de fortes chances que vous deviez maintenir une base de code existante. J'espère donc que nous pourrons revenir à notre base de code pour améliorer une partie du code que nous avons écrit.

Veuillez noter que cet article ne sera pas rédigé dans le format d'autres articles - c'est-à-dire qu'il ne nécessitera pas une approche de développement « d'abord, nous faisons ceci, puis nous faisons ceci ». Au lieu de cela, nous mettrons en évidence quelques domaines qui nécessitent une refactorisation, puis nous les aborderons indépendamment des autres changements que nous apportons.

Refactor

Pour être clair, l'acte de refactoring (tel que défini par Wikipédia) est :

Le refactoring améliore les propriétés non fonctionnelles du logiciel. Les avantages incluent une meilleure lisibilité du code et une complexité réduite pour améliorer la maintenabilité du code source, ainsi que la création d'un schéma interne ou d'un modèle objet plus expressif pour améliorer l'évolutivité.

En bref, cela rend le code plus lisible, plus simple et plus facile à suivre, le tout sans modifier le comportement du code du point de vue de l'utilisateur final.

Cela peut être réalisé de différentes manières, chacune étant propre au projet donné. Dans notre cas, nous envisagerons de refactoriser notre constructeur, certaines méthodes de sauvegarde, certaines méthodes d'assistance, etc.

En fin de compte, notre objectif est de présenter quelques stratégies que vous pouvez utiliser dans vos futurs efforts WordPress. Mon objectif est d'être aussi détaillé que possible dans cet article ; cependant, veuillez noter qu'il peut y avoir d'autres opportunités de refactoring qui ne sont pas couvertes.

Si c’est le cas, c’est génial ! N'hésitez pas à les créer sur votre propre instance de base de code. Cela dit, commençons.

Constructeur

Si vous regardez notre constructeur :

<?php 
    	
public function __construct( $name, $version ) {

	$this->name = $name;
	$this->version = $version;

	$this->meta_box = new Authors_Commentary_Meta_Box();

	add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
	add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );

}

Veuillez noter qu'il fait actuellement deux choses :

  1. Initialisez les attributs tels que le nom et la version
  2. Utilisez le crochet d'inscription WordPress

Il est courant d’examiner les paramètres du hook dans le contexte du constructeur d’un plugin WordPress, mais ce n’est pas un bon point de départ.

Les constructeurs doivent être utilisés pour initialiser toutes les propriétés liées à une classe donnée afin que lorsqu'un utilisateur instancie une classe, il dispose de tout le nécessaire pour l'utiliser.

Comme ils ne voudront peut-être pas enregistrer le hook lors de l'initialisation de la classe, nous devons l'abstraire dans sa propre méthode initialize_hooks. Notre code devrait maintenant ressembler à ceci :

<?php

public function __construct( $name, $version ) {

    $this->name = $name;
    $this->version = $version;

    $this->meta_box = new Authors_Commentary_Meta_Box();

}

public function initialize_hooks() {
	
	add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
	add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
	
}

Après cela, nous devons nous assurer de mettre à jour le code principal de auteurs-commentary.php afin qu'il instancie et enregistre correctement le hook.

<?php

function run_author_commentary() {
    
	$author_commentary = new Author_Commentary_Admin( 'author-commentary', '1.0.0' );
	$author_commentary->initialize_hooks();
	
}
run_author_commentary();

Ici, la principale différence est que nous mettons à jour le numéro de version transmis à la classe principale, et nous appelons également explicitement la initialize_hooks 函数“内联”>run_author_commentary fonction "inline" >run_author_commentary dans le contexte de <p>Si vous exécutez le code maintenant, tout devrait être exactement le même qu'avant le refactoring. </p> <p>Je voudrais également ajouter que vous pouvez avoir une classe distincte chargée de coordonner les hooks et les rappels afin que la responsabilité incombe à une classe distincte. Même si j'aime cette approche, elle dépasse le cadre de cet article. </p> <p>Ensuite, faisons la même chose avec <code class="inline">class-authors-commentary-meta-box.php. Au lieu de créer une nouvelle fonction, nous pouvons simplement renommer le constructeur car le constructeur ne fait rien. Cela signifie que notre code devrait ressembler à ceci :

<?php 

public function __construct() {

    add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
    add_action( 'save_post', array( $this, 'save_post' ) );

}

À ceci :

<?php

public function initialize_hooks() {

    add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
    add_action( 'save_post', array( $this, 'save_post' ) );

}

Le dernier changement que nous devons apporter est de mettre à jour le constructeur dans la classe principale afin qu'il lise désormais l'intérieur de la fonction initialize_hooks que nous avons créée dans la classe principale du plugin.

<?php

public function initialize_hooks() {

    $this->meta_box->initialize_hooks();

    add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
    add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );

}

Actualisez à nouveau la page et votre plugin devrait toujours fonctionner comme avant la refactorisation.

Méthodes d'aide

Il y a beaucoup de conditions très redondantes dans la fonction Authors_Commentary_Meta_Box 类中,我们在 save_post. Lorsque cela se produit, cela signifie généralement qu'une grande partie des fonctionnalités peuvent être abstraites dans des fonctions d'assistance, puis appelées depuis la fonction où elles ont été initialement placées.

Jetons un œil au code maintenant :

<?php
    
public function save_post( $post_id ) {

	/* If we're not working with a 'post' post type or the user doesn't have permission to save,
	 * then we exit the function.
	 */
	if ( ! $this->is_valid_post_type() || ! $this->user_can_save( $post_id, 'authors_commentary_nonce', 'authors_commentary_save' ) ) {
		return;
	}

	// If the 'Drafts' textarea has been populated, then we sanitize the information.
	if ( ! empty( $_POST['authors-commentary-drafts'] ) ) {

		// We'll remove all white space, HTML tags, and encode the information to be saved
		$drafts = trim( $_POST['authors-commentary-drafts'] );
		$drafts = esc_textarea( strip_tags( $drafts ) );

		update_post_meta( $post_id, 'authors-commentary-drafts', $drafts );

	} else {

		if ( '' !== get_post_meta( $post_id, 'authors-commentary-drafts', true ) ) {
			delete_post_meta( $post_id, 'authors-commentary-drafts' );
		}

	}

	// If the 'Resources' inputs exist, iterate through them and sanitize them
	if ( ! empty( $_POST['authors-commentary-resources'] ) ) {

		$resources = $_POST['authors-commentary-resources'];
		$sanitized_resources = array();
		foreach ( $resources as $resource ) {

			$resource = esc_url( strip_tags( $resource ) );
			if ( ! empty( $resource ) ) {
				$sanitized_resources[] = $resource;
			}

		}

		update_post_meta( $post_id, 'authors-commentary-resources', $sanitized_resources );

	} else {

		if ( '' !== get_post_meta( $post_id, 'authors-commentary-resources', true ) ) {
			delete_post_meta( $post_id, 'authors-commentary-resources' );
		}

	}

	// If there are any values saved in the 'Published' input, save them
	if ( ! empty( $_POST['authors-commentary-comments'] ) ) {
		update_post_meta( $post_id, 'authors-commentary-comments', $_POST['authors-commentary-comments'] );
	} else {

		if ( '' !== get_post_meta( $post_id, 'authors-commentary-comments', true ) ) {
			delete_post_meta( $post_id, 'authors-commentary-comments' );
		}

	}

}

Outre la méthode de démarrage qui est trop longue, il y a quelques points que nous pouvons nettoyer :

  1. Conditions initiales utilisant des not 和逻辑 OR opérateurs logiques
  2. Condition pour vérifier $_POST si des informations existent dans le tableau
  3. Fonctions de nettoyage, de mise à jour et/ou de suppression des métadonnées associées

Examinons donc chacun d’eux individuellement et travaillons à la refactorisation de cette fonction.

1。初始条件

第一个条件检查的目的是确保当前用户能够将数据保存到给定的帖子。现在,我们实际上是在检查当前帖子类型是否是有效的帖子类型,以及用户是否有权保存给定 WordPress 传递的当前随机数值。

现在,代码如下:

如果这不是有效的帖子类型或用户没有保存权限,则退出此功能。

这并不是很糟糕,但绝对可以改进。让我们将其合并到单个评估中,而不是使用 OR ,使其显示为:

如果用户没有保存权限,则退出此功能。

幸运的是,这是一个相对容易的修复。由于保存的帖子类型有助于确定用户是否有权保存帖子,因此我们可以将该逻辑移至 user_can_save 函数中。

因此,让我们将 is_valid_post_type 函数移至 user_can_save 函数中:

<?php

private function user_can_save( $post_id, $nonce_action, $nonce_id ) {

    $is_autosave = wp_is_post_autosave( $post_id );
    $is_revision = wp_is_post_revision( $post_id );
    $is_valid_nonce = ( isset( $_POST[ $nonce_action ] ) && wp_verify_nonce( $_POST[ $nonce_action ], $nonce_id ) );

    // Return true if the user is able to save; otherwise, false.
    return ! ( $is_autosave || $is_revision ) && $this->is_valid_post_type() && $is_valid_nonce;

}

现在,负责确定用户是否可以保存帖子元数据的所有逻辑都封装在专门设计用于精确评估的函数中。

我们从这个开始:

<?php
    
if ( ! $this->is_valid_post_type() || ! $this->user_can_save( $post_id, 'authors_commentary_nonce', 'authors_commentary_save' ) ) {
	return;
}

现在我们有这个:

<?php
    
if ( ! $this->user_can_save( $post_id, 'authors_commentary_nonce', 'authors_commentary_save' ) ) {
	return;
}

阅读起来容易多了,不是吗?

2.检查 $_POST 数组

接下来,在开始清理、验证和保存(或删除)元数据之前,我们将检查 $_POST 集合以确保数据确实存在。

我们可以编写一个小的辅助函数来为我们处理这个评估。虽然我们本质上是编写了一些代码,使我们的评估更加冗长,但与我们直接保留它们相比,条件语句读起来会更清晰一些。

首先,引入以下函数(注意它接受一个参数):

<?php

/**
 * Determines whether or not a value exists in the $_POST collection
 * identified by the specified key.
 *
 * @since   1.0.0
 *
 * @param   string    $key    The key of the value in the $_POST collection.
 * @return  bool              True if the value exists; otherwise, false.
 */
private function value_exists( $key ) {
    return ! empty( $_POST[ $key ] );
}

接下来,重构最初调用 的所有调用!空( $_POST[ ... ] ) 以便他们利用此功能。

例如,函数调用应如下所示:

if ( $this->value_exists( 'authors-commentary-comments' ) ) {
    // ...
} else {
	// ...
}

2.删除元数据

请注意,在该函数中放置的整个条件中,如果值不存在,则每次删除帖子元数据的评估看起来都完全相同。

例如,我们每次都会看到这样的东西:

<?php

if ( '' !== get_post_meta( $post_id, 'authors-commentary-comments', true ) ) {
    delete_post_meta( $post_id, 'authors-commentary-comments' );
}

这显然是重构代码的机会。因此,让我们创建一个名为 delete_post_meta 的新函数,并让它封装所有这些信息:

<?php

/**
 * Deletes the specified meta data associated with the specified post ID 
 * based on the incoming key.
 *
 * @since    1.0.0
 * @access   private
 * @param    int    $post_id    The ID of the post containing the meta data
 * @param    string $meta_key   The ID of the meta data value
 */
private function delete_post_meta( $post_id, $meta_key ) {
    
	if ( '' !== get_post_meta( $post_id, $meta_key, true ) ) {
		delete_post_meta( $post_id, '$meta_key' );
	}
	
}

现在我们可以返回并替换所有 else 条件评估以调用此单个函数,使其读取如下内容:

<?php
    
// If the 'Drafts' textarea has been populated, then we sanitize the information.
if ( $this->value_exists( 'authirs-commentary-drafts' ) ) {

	// We'll remove all white space, HTML tags, and encode the information to be saved
	$drafts = trim( $_POST['authors-commentary-drafts'] );
	$drafts = esc_textarea( strip_tags( $drafts ) );

	update_post_meta( $post_id, 'authors-commentary-drafts', $drafts );

} else {
	$this->delete_post_meta( $post_id, 'authors-commentary-drafts' );
}

此时,我们实际上只有这部分代码的另一个方面需要重构。

3.消毒和保存

现在,保存帖子元数据的方式是通过评估 $_POST 集合中数据是否存在的过程来完成的,并根据信息类型对其进行清理,然后将其保存到帖子元数据中。

理想情况下,我们希望在自己的函数中清理数据,并将帖子元数据保存在自己的函数中。因此,我们需要引入新的功能。

首先,让我们进行消毒工作。因为我们正在处理 textareas 和数组,所以我们需要通过几种方法来处理清理调用。由于我们要么使用数组,要么不使用数组,所以我们可以创建一个函数,该函数接受一个可选参数,表示我们是否正在使用数组。

如果我们不使用数组,那么我们会将传入的数据视为文本;否则,我们会将其视为数组:

<?php
    
/**
 * Sanitizes the data in the $_POST collection identified by the specified key
 * based on whether or not the data is text or is an array.
 *
 * @since    1.0.0
 * @access   private
 * @param    string        $key                      The key used to retrieve the data from the $_POST collection.
 * @param    bool          $is_array    Optional.    True if the incoming data is an array.
 * @return   array|string                            The sanitized data.
 */
private function sanitize_data( $key, $is_array = false ) {

	$sanitized_data = null;

	if ( $is_array ) {

		$resources = $_POST[ $key ];
		$sanitized_data = array();

		foreach ( $resources as $resource ) {

			$resource = esc_url( strip_tags( $resource ) );
			if ( ! empty( $resource ) ) {
				$sanitized_data[] = $resource;
			}

		}

	} else {

		$sanitized_data = '';
		$sanitized_data = trim( $_POST[ $key ] );
		$sanitized_data = esc_textarea( strip_tags( $sanitized_data ) );

	}

	return $sanitized_data;

}

接下来,我们可以更新清理调用以使用此方法。但在此之前,我们还需要编写一个小助手,负责使用经过净化的输入更新帖子元数据:

<?php

private function update_post_meta( $post_id, $meta_key, $meta_value ) {
    
    if ( is_array( $_POST[ $meta_key ] ) ) {
        $meta_value = array_filter( $_POST[ $meta_key ] );
	}

    update_post_meta( $post_id, $meta_key, $meta_value );
}

现在我们可以更新之前在函数中使用的所有条件,如下所示:

<?php
    
public function save_post( $post_id ) {

	if ( ! $this->user_can_save( $post_id, 'authors_commentary_nonce', 'authors_commentary_save' ) ) {
		return;
	}

	if ( $this->value_exists( 'authors-commentary-drafts' ) ) {

		$this->update_post_meta(
			$post_id,
			'authors-commentary-drafts',
			$this->sanitize_data( 'authors-commentary-drafts' )
		);

	} else {
		$this->delete_post_meta( $post_id, 'authors-commentary-drafts' );
	}

	if ( $this->value_exists( 'authors-commentary-resources' ) ) {

		$this->update_post_meta(
			$post_id,
			'authors-commentary-resources',
			$this->sanitize_data( 'authors-commentary-resources', true )
		);

	} else {
		$this->delete_post_meta( $post_id, 'authors-commentary-resources' );
	}

	if ( $this->value_exists( 'authors-commentary-comments' ) ) {

		$this->update_post_meta(
			$post_id,
			'authors-commentary-comments',
			$_POST['authors-commentary-comments']
		);

	} else {
		$this->delete_post_meta( $post_id, 'authors-commentary-comments' );
	}

}

请注意,我们实际上可以进一步重构这个特定的部分,这样就没有那么多的条件,但是考虑到文章的长度、时间的长度,并且还尝试引入一些其他策略,这将是留作练习,在您自己的时间完成。

结论

到目前为止,我们已经完成了我们的插件。我们编写了一个插件,引入了一个元框,为撰写博客文章的作者提供选项。

此外,我们还采用了 WordPress 编码标准、一些强大的文件组织策略,并创建了许多辅助方法和抽象,这将帮助我们在未来的开发中维护这个特定的插件。

由于突出显示每一个重构机会并不容易,因此可能还需要进行其他更改。在您自己的时间里,请随意尝试自己实现其中一些。

总的来说,我希望您喜欢本系列并从中学到很多东西,并且我希望它能帮助您在未来基于 WordPress 的项目中编写更好、更易于维护的代码。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn