Interface avancée: Éditeur de produit

Nous allons voir dans ce chapitre pour ajouter une ligne dans le tableau permettant de créer un produit avec un titre ainsi  que des métadonnées personnalisés telles que:

  • Prix TTC
  • Poids
  • Couleur

Nous allons rapidement rajouter deux entrées dans le tableau qui ne sont pas encore présentes: le poids et la couleur:

list.view.php

<table class="wpeo-table">
	<thead>
		<tr>
			<th><?php esc_html_e( 'Title', 'my-plugin' ); ?></th>
			<th><?php esc_html_e( 'Price TTC', 'my-plugin' ); ?></th>
			<th><?php esc_html_e( 'Weight', 'my-plugin' ); ?></th>
			<th><?php esc_html_e( 'Color', 'my-plugin' ); ?></th>
			<th><?php esc_html_e( 'Actions', 'my-plugin' ); ?></th>
		</tr>
	</thead>
	<tbody>
		<?php
		if ( ! empty( $products ) ) :
			foreach ( $products as $product ) :
				\eoxia\View_Util::exec( 'my-plugin', 'product', 'item', array(
					'product' => $product,
				) );
			endforeach;
		endif;
		\eoxia\View_Util::exec( 'my-plugin', 'product', 'edit' );
		?>
	</tbody>
</table>

Nous appelons également la vue edit.view.php à la fin du tableau.

item.view.php

<tr>
	<td><?php echo esc_html( $product->data['title'] ); ?></td>
	<td><?php echo esc_html( $product->data['price_ttc'] ); ?>$</td>
	<td><?php echo esc_html( $product->data['weight'] ); ?></td>
	<td><?php echo esc_html( $product->data['color'] ); ?></td>
	<td>
		<div class="wpeo-button button-square-40"><i class="button-icon fas fa-edit"></i></div>
		<div class="wpeo-button button-square-40"><i class="button-icon fas fa-trash"></i></div>
	</td>
</tr>

Dans cette vue, nous rajoutons les données weight et color pour l’affichage.

La vue edit.view.php

Cette vue contient un formulaire avec les champs nécessaires pour la création d’un produit.

<tr class="wpeo-form">
	<td>
		<div class="form-element">
			<label class="form-field-container">
				<input name="title" type="text" class="form-field" />
			</label>
		</div>
	</td>
	<td>
		<div class="form-element">
			<label class="form-field-container">
				<input name="price_ttc" type="text" class="form-field" />
				<span class="form-field-label-next"><i class="fal fa-euro-sign"></i></span>
			</label>
		</div>
	</td>
	<td>
		<div class="form-element">
			<label class="form-field-container">
				<input name="weight" type="text" class="form-field" />
				<span class="form-field-label-next">KG</span>
			</label>
		</div>
	</td>
	<td>
		<div class="form-element">
			<label class="form-field-container">
				<input name="color" type="text" class="form-field" />
			</label>
		</div>
	</td>
	<td>
		<input type="hidden" name="action" value="create_product" />
		<?php wp_nonce_field( 'create_product' ); ?>
		<div class="wpeo-button button-square-40 action-input"
			data-parent="wpeo-form"><i class="button-icon fas fa-plus"></i></div>
	</td>
</tr>
33,34,36

Ligne 33: L’action pour la requête AJAX

Ligne 34: Le nonce pour la requête AJAX

Ligne 35,36: Nous utilisons la classe « action-input » avec son attribut data-parent afin d’envoyer toutes les données englobé par le parent « .wpeo-form ». Je vous invites à lire l’exemple: « Actions AJAX« .

Nous avons maintenant nos vues qui sont prête à l’usage. Il faut maintenant gérer l’action du bouton « + » du nouveau formulaire.

Créer un nouveau produit avec notre nouveau formulaire

Nous allons modifier la méthode callback_create_product attaché à l’action « create_product ». Elle se trouve dans notre fichier « product.action.php », voici la méthode avec le nouveau code:

public function callback_create_product() {
	check_ajax_referer( 'create_product' );

	$title     = ! empty( $_POST['title'] ) ? sanitize_text_field( $_POST['title'] ) : '';
	$price_ttc = ! empty( $_POST['price_ttc'] ) ? floatval( str_replace( ',', '.', $_POST['price_ttc'] ) ) : 00.00;
	$weight    = ! empty( $_POST['weight'] ) ? floatval( str_replace( ',', '.', $_POST['weight'] ) ) : 00.00;
	$color     = ! empty( $_POST['color'] ) ? sanitize_text_field( $_POST['color'] ) : '';

	// Force le nombre flottant à être sans virgule, et 2 décimals.
	$price_ttc = str_replace( ',', '', number_format( $price_ttc, 2 ) );
	$weight    = str_replace( ',', '', number_format( $weight, 2 ) );

	Product_Class::g()->create( array(
		'title'     => $title,
		'price_ttc' => $price_ttc,
		'weight'    => $weight,
		'color'     => $color,
	) );

	wp_send_json_success();
}

Ligne 2: Vérification du nonce

Ligne 4,5,6,7: Protèges les données pour éviter des données non voulues.

Afficher le nouveau produit créé à la fin de la requête

Nous allons en JS, ajouter notre nouveau produit dans le tableau.

Attention, nous utilisons GULP pour compiler le JS principale lu par le plugin. Il est obligatoire de compiler le JS, je vous invites à lire « JavaScript avec EO Framework« .

Après l’installation de GULP, nous ouvrons une console à la racine du plugin, puis nous lançons la commande suivante:

npm start

Celle-ci s’occupe d’écouter tous nos fichiers finissant par backend.js pour ensuite les concaténé en une seul fichier, backend.min.js situé dans « core/assets/ ».

Si tout se passe bien, nous devons voir dans la console les messages suivant:

Si au contraire vous avez une erreur, je vous invites à lire « JavaScript avec EO Framework » ou d’écrire une Issue sur le GitHub de EO Framework.

Renvoyer la vue après la création du produit

Nous allons depuis notre méthode PHP appeler la vue « item.view.php » du module « product ».

Voici la méthode « callback_create_product » modifiée:

ublic function callback_create_product() {
	check_ajax_referer( 'create_product' );

	$title     = ! empty( $_POST['title'] ) ? sanitize_text_field( $_POST['title'] ) : '';
	$price_ttc = ! empty( $_POST['price_ttc'] ) ? floatval( str_replace( ',', '.', $_POST['price_ttc'] ) ) : 00.00;
	$weight    = ! empty( $_POST['weight'] ) ? floatval( str_replace( ',', '.', $_POST['weight'] ) ) : 00.00;
	$color     = ! empty( $_POST['color'] ) ? sanitize_text_field( $_POST['color'] ) : '';

	// Force le nombre flottant à être sans virgule, et 2 décimals.
	$price_ttc = str_replace( ',', '', number_format( $price_ttc, 2 ) );
	$weight    = str_replace( ',', '', number_format( $weight, 2 ) );

	$product = Product_Class::g()->create( array(
		'title'     => $title,
		'price_ttc' => $price_ttc,
		'weight'    => $weight,
		'color'     => $color,
	) );

	ob_start();
	\eoxia\View_Util::exec( 'my-plugin', 'product', 'item', array(
		'product' => $product,
	) );

	wp_send_json_success( array(
		'namespace'        => 'myPlugin',
		'module'           => 'product',
		'callback_success' => 'createdProductSuccess',
		'view'             => ob_get_clean(),
	) );
}

Ligne 13: On récupère l’objet renvoyé par Product_Class::g()->create() qui correspond aux données de notre produit

Ligne 20: On enclenche la temporisation de sortie.

Ligne 21: On appelle notre vue « item.view.php »

Ligne 26,27,28: Permet d’appeler la méthode JavaScript « createdProductSuccess » dans le module « product » qui est dans le namespace « myPlugin ».

Ligne 29: Récupère le contenu de la temporisation de sortie, ce qui nous permettra de l’utiliser en JavaScript.

Utiliser la vue en JS

Dans notre fichier product.backend.js nous allons ajouter la méthode createdProductSuccess. Cette méthode va ajoutée la vue en bas de notre tableau.

window.eoxiaJS.myPlugin.product.createdProductSuccess = function(triggeredElement, response) {
	jQuery( '.wpeo-table tbody tr:last' ).before( response.data.view );
};

Cette méthode ajoute notre vue (qui est donc un <tr>) devant le dernier <tr> du tableau qui est donc notre formulaire permettant de créer un produit.

Nous pouvons enfin créer un produit et le voir s’afficher sans actualiser la page!

Modifier un produit

Passer un produit du mode lecture à l’édition

Nous allons ajouter une action AJAX quand nous cliquons sur le bouton pour éditer un produit: 

Dans le fichier item.view.php nous allons modifier la ligne:

<div class="wpeo-button button-square-40"><i class="button-icon fas fa-edit"></i></div>

Par ceci:

<div class="action-attribute wpeo-button button-square-40"
			data-action="load_edit_mode"
			data-nonce="<?php echo esc_attr( wp_create_nonce( 'load_edit_mode' ) ); ?>"
			data-id="<?php echo esc_attr( $product->data['id'] ); ?>"><i class="button-icon fas fa-edit"></i></div>

Nous utilisons action-attribute pour envoyé en XHR les données data de notre élément.

Nous avons définis les données suivantes:

  • action: pour utiliser le hook de WordPress wp_ajax_*
  • nonce: le token de sécurité
  • id: l’ID du produit (afin de le charger dans le script PHP)

Nous devons également modifier la vue « edit.view.php » afin qu’elle utilise les données du produit qu’elle reçoit, voici les modifications du fichier en entier:


<tr class="wpeo-form">
	<td>
		<div class="form-element">
			<label class="form-field-container">
				<input type="text" class="form-field" name="title" value="<?php echo esc_attr( $product->data['title'] ); ?>" />
			</label>
		</div>
	</td>
	<td>
		<div class="form-element">
			<label class="form-field-container">
				<input type="text" class="form-field" name="price_ttc" value="<?php echo esc_attr( $product->data['price_ttc'] ); ?>" />
				<span class="form-field-label-next">$</span>
			</label>
		</div>
	</td>
	<td>
		<div class="form-element">
			<label class="form-field-container">
				<input type="text" class="form-field" name="weight" value="<?php echo esc_attr( $product->data['weight'] ); ?>" />
				<span class="form-field-label-next">KG</span>
			</label>
		</div>
	</td>
	<td>
		<div class="form-element">
			<label class="form-field-container">
				<input type="text" class="form-field" name="color" value="<?php echo esc_attr( $product->data['color'] ); ?>" />
			</label>
		</div>
	</td>
	<td>
		<input type="hidden" name="action" value="create_product" />
		<?php wp_nonce_field( 'create_product' ); ?>

		<?php
		if ( empty( $product->data['id'] ) ) :
			?>
			<div class="wpeo-button button-square-40 action-input"
				data-parent="wpeo-form"><i class="button-icon fas fa-plus"></i></div>
			<?php
		else :
			?>
			<div class="wpeo-button button-square-40 action-input"
				data-parent="wpeo-form"><i class="button-icon fas fa-save"></i></div>
			<?php
		endif;
		?>
	</td>
</tr>

Explications à venir

Ligne 5:

Ligne 12:

Ligne 20:

Ligne 28:

Ligne 37:

Ligne 42:

Si nous actualisons la page, nous pouvons voir que la page comporte de nombreux « warning ». C’est du au fait que la vue « edit.view.php » appelé en bas du tableau ne connait pas l’objet $product.

Pour régler ce problème, nous allons ajouter un « faux produit » à notre vue lorsque nous sommes en création d’un nouveau produit.

Pour créer notre faux produit, nous allons récupérer un produit depuis l’ID 0 qui est inexistant, EO Framework vas créer un objet en respectant les données d’un produit, mais avec que des entrées vides, ou les données par défaut si défini.

Dans la méthode « display » de la classe Product_Class:

public function display() {
	$products = $this->get();

	$product_schema = $this->get( array( 'id' => 0 ), true );

	\eoxia\View_Util::exec( 'my-plugin', 'product', 'list', array(
		'products'       => $products,
		'product_schema' => $product_schema,
	) );
}

Ligne 4: Nous récupérons un « faux objet » et nous l’envoyons à la vue « list » du module « product ».

Une dernière modification à faire et dans cette vue « list ». Pour rappel, cette vue fait appel à « edit.view.php » sans aucun « produit » pour le moment. Nous allons lui rajouté comme suit:

Remplacer cette ligne:

\eoxia\View_Util::exec( 'my-plugin', 'product', 'edit' );

Par:

\eoxia\View_Util::exec( 'my-plugin', 'product', 'edit', array(
	'product' => $product_schema,
) );
Actualisons notre page, vous ne devriez plus voir apparaître d’erreur. Passons à l’étape suivante!

La nouvelle méthode callback_load_edit_mode

Nous allons rajouter dans le constructeur l’action wp_ajax de WordPress, puis nous allons ajouter la méthode « callback » de cette action.

Ce qui faut rajouter dans le constructeur de product.action.php

add_action( 'wp_ajax_load_edit_mode', array( $this, 'callback_load_edit_mode' ) );

Et la méthode:

public function callback_load_edit_mode() {
	check_ajax_referer( 'load_edit_mode' );

	$id = ! empty( $_POST['id'] ) ? (int) $_POST['id'] : 0;

	if ( empty( $id ) ) {
		wp_send_json_error();
	}

	$product = Product_Class::g()->get( array( 'id' => $id ), true );

	ob_start();
	\eoxia\View_Util::exec( 'my-plugin', 'product', 'edit', array(
		'product' => $product,
	) );
	wp_send_json_success( array(
		'namespace'        => 'myPlugin',
		'module'           => 'product',
		'callback_success' => 'loadedEditModeSuccess',
		'view'             => ob_get_clean(),
	) );
}

Nous allons pas nous attarder sur cette méthode, elle vérifie le nonce, récupère le produit grâce à son $id puis renvoies la vue edit.view.php à la méthode JavaScript « loadedEditModeSuccess ».

Passer la vue en mode « edit »

Nous allons rajouté la méthode loadedEditModeSuccess dans notre fichier product.backend.js:

window.eoxiaJS.myPlugin.product.loadedEditModeSuccess = function(triggeredElement, response) {
	triggeredElement.closest( 'tr' ).replaceWith( response.data.view );
};

Il est important de comprendre qu’EOFramework a son propre système de callback pour communiquer depuis PHP vers JavaScript. Ses callback contiennent toujours deux paramètres qui sont:

  • triggeredElement: l’élément déclenchant l’action AJAX, dans notre cas le bouton « edit ».
  • response: Toutes les données envoyées à la fonction wp_send_json_success de WordPress.

Dans le code précédent, rien de bien sorcier, nous remontons depuis l’élément déclencheur jusqu’au <tr> grace à closest puis nous remplaçons la vue avec la nouvelle.

Editer le produit

Nous allons maintenant ajouter une action AJAX sur notre bouton « Editer » qui appellera la même action PHP que le bouton « Créer ». Nous allons ensuite devoir modifier un petit peu la méthode callback_create_product pour qu’elle réponde mieux à nos attentes.

Nous voulons que cette méthode gère la création et l’édition, pour cela nous allons créer un objet produit « temporaire » comme nous l’avons déjà fait plus haut, puis nous allons affecter les données reçu du formulaire et afin nous allons le modifier en base de donnée. Cette méthode marchera aussi bien pour la création que la mise à jour, à l’exception du callback JS qui devra effectuer une action différente.

Ajouter l’action attribute sur le bouton « Editer »

Modifier la méthode callback_create_product

Modifier la méthode JS

Supprimer un produit