Theme-Mod nach Button-Klick im Customizer entfernen

In der kommenden Version meines Hannover-Themes soll es neben überarbeitetem Design und Code auch eine verbesserte User-Experience im Customizer geben. Dabei soll es für den Nutzer unter anderem möglich sein, Sections über einen Button-Klick zu entfernen (wie beim Löschen eines Menüs im Customizer). Daraufhin soll natürlich nicht nur die Section aus dem Customizer verschwinden, sondern auch die entsprechenden Theme-Mods aus der Datenbank gelöscht werden. Hier zeige ich, wie ich dazu vorgegangen bin.

Vorgehensweise zusammengefasst

Cool wäre natürlich, wenn wir nach einem Button-Klick direkt via JS-API die Theme-Mod entfernen können. Ich habe dazu allerdings nichts gefunden, weshalb wir für diesen Teil PHP nutzen werden. Hier die anstehenden Schritte:

  1. Wir fügen eine Customize-Control hinzu, die als Lösch-Button fungiert.
  2. Wir erstellen eine versteckte Control mit Setting, die als Kennzeichnung dient, ob der Button gedrückt worden ist.
  3. Wir fangen die Klicks auf den Lösch-Button ab und verändern bei einem Klick den Wert der versteckten Control.
  4. Via PHP prüfen wir nach dem Speichern der Customize-Settings ob ein Button geklickt wurde und entfernen die Theme-Mods.

1. Lösch-Button-Control erstellen

Beim Button zum Entfernen einer Section habe ich mich an die Vorgehensweise gehalten, die im Core für die Buttons zum Löschen eines Menüs genutzt wird. Dabei kommt ein kleines Control-Template zum Einsatz, das wir mit dem folgenden PHP-Snippet in den Customizer einbinden:

<?php
// Include customize control templates into the customizer.
add_action( 'customize_controls_print_footer_scripts', 'hannover_customize_control_templates' );

/**
 * Prints control templates into the customizer.
 */
function hannover_customize_control_templates() { ?>
	<script type="text/html" id="tmpl-hannover-delete-portfolio-category-page-button">
		<div class="hannover-customize-control-text-wrapper delete-portfolio-category-page">
			<button type="button" class="button-link button-link-delete">
				<?php _e( 'Delete category page', 'hannover' ); ?>
			</button>
		</div>
	</script>
<?php }

Dieses Template können wir jetzt in der Customize-JS-API für eine Control nutzen. Dafür gehen wir wie folgt vor:

/**
 * Custom JavaScript functions for the customizer controls.
 *
 * @version 2.0.0
 *
 * @package Hannover
 */
;(function (api) {
	api.bind('ready', function () {
		// Create remove button control.
		api.control.add(
			new api.Control('portfolio_category_page[1][delete]', {
				section: 'hannover_portfolio_category_page_section[1]',
				templateId: 'hannover-delete-portfolio-category-page-button'
			})
		);
	});
})(wp.customize);

Wichtig ist hier der Wert für templateId, der dem id-Attribut des script-Elements aus hannover_customize_control_templates() entspricht – abzüglich des tmpl--Präfixes. Die Ziffer in dem Bezeichner für Control und Section ist in dem Theme zur Unterscheidung, da der User dynamisch neue Sections mit den entsprechenden Controls und Settings erstellen kann.

Damit haben wir den Lösch-Button erstellt und einer Section zugewiesen. Damit es funktioniert, muss natürlich eine entsprechende Section vorhanden sein, das soll aber nicht Teil dieses Tutorials sein. Wie das Anlegen von Panels, Sections und Controls funktioniert, habe ich in meinem Artikel »Customize-JS-API zur Erstellung von Panels, Sections und Controls nutzen« gezeigt.

2. Versteckte Control und Setting für Kennzeichnung erstellen

Um später im PHP-Teil zu prüfen ob ein Löschen-Button geklickt wurde, erstellen wir eine versteckte Control und ein passendes Setting – der Wert der Control wird dann bei einem Klick auf den Lösch-Button aktualisiert.  Der JS-Teil sieht so aus:

// Add setting for removal flag.
api.add(new api.Setting('portfolio_category_page[1][deleted]'));

// Add control for removal flag.
api.control.add(
	new api.Control('portfolio_category_page[1][deleted]', {
		setting: 'portfolio_category_page[1][deleted]',
		type: 'number',
		section: 'hannover_portfolio_category_page_section[1]',
		active: false,
	})
);

Das Erstellen einer Setting über die JS-API habe ich im Beitrag »Settings mit der Customize-JS-API erstellen« thematisiert. Die Control verbinden wir über den setting-Schlüssel mit der erstellten Setting, als Typ geben wir number an, für section dieselbe Section wie für den Löschen-Button und für active den Wert false. Dadurch ist das Feld nicht sichtbar.

Wie in dem verlinkten Artikel erwähnt wird, müssen wir einen kleinen Teil PHP schreiben, um mit der JS-API registrierte Settings auch abzuspeichern:

// Filter the dynamically created customizer settings.
add_filter( 'customize_dynamic_setting_args', 'hannover_filter_dynamic_setting_args', 10, 2 );

/**
 * Filters a dynamic setting's constructor args.
 *
 * For a dynamic setting to be registered, this filter must be employed
 * to override the default false value with an array of args to pass to
 * the WP_Customize_Setting constructor.
 *
 * @link https://wordpress.stackexchange.com/a/286503/112824
 *
 * @param false|array $setting_args The arguments to the WP_Customize_Setting constructor.
 * @param string      $setting_id   ID for dynamic setting, usually coming from `$_POST['customized']`.
 *
 * @return array|false
 */
function hannover_filter_dynamic_setting_args( $setting_args, $setting_id ) {
	// Create array of ID patterns.
	$id_patterns = [
		'portfolio_category_page_deleted' => '/^portfolio_category_page\[(\d+)\]\[deleted\]/',
	];

	// Match for the deleted portfolio category page setting.
	if ( preg_match( $id_patterns['portfolio_category_page_deleted'], $setting_id, $matches ) ) {
		$setting_args = [
			'type' => 'theme_mod',
		];
	}

	return $setting_args;
}

3. Klicks auf Lösch-Button abfangen und versteckte Control aktualisieren

Der komplette JS-Code zum Abfangen der Klicks und die entsprechenden Aktionen daraufhin sieht so aus:

// Add click event listener to the delete button.
var deleteButton = document.querySelector('.delete-portfolio-category-page button');
deleteButton.addEventListener('click', function () {
	// Close the section.
	api.section('hannover_portfolio_category_page_section[1]').collapse();

	// Set flag that the setting needs to be removed.
	api.control('portfolio_category_page[1][deleted]', function (control) {
		control.setting.set(-1);
	});

	// Remove the section.
	api.section('hannover_portfolio_category_page_section[1]').active(false);
});

Nach einem Klick auf den Button wird zunächst die Section geschlossen. Danach setzen wir den Wert der versteckten Control auf -1 und verstecken die Section, sodass der User sie in der aktuellen Customizer-Sitzung nicht mehr sehen und öffnen kann.

4. Theme-Mods entfernen

Der PHP-Code zum Entfernen der Theme-Mods:

// Remove theme mods after portfolio category page was removed.
add_action( 'customize_save_after', 'hannover_customize_save_after' );

/**
 * Fires after customize settings are saved.
 *
 * @param WP_Customize_Manager $manager Instance of WP_Customize_Manager.
 */
function hannover_customize_save_after( $manager ) {
	// Get the theme mods.
	$theme_mods = get_theme_mods();

	// Loop them.
	foreach ( $theme_mods['portfolio_category_page'] as $id => $portfolio_category_page_theme_mod ) {
		// Check if the delete flag is set.
		if ( isset( $portfolio_category_page_theme_mod['deleted'] ) && - 1 === $portfolio_category_page_theme_mod['deleted'] ) {
			// Code inspired by the remove_theme_mod() function.
			unset( $theme_mods['portfolio_category_page'][ $id ] );

			// Check if we have no more portfolio category pages and if so, unset the array index.
			if ( empty( $theme_mods['portfolio_category_page'] ) ) {
				unset( $theme_mods['portfolio_category_page'] );
			} // End if().

			// Check if that was the last theme mod and if so, remove the entry from the database.
			if ( empty ( $theme_mods ) ) {
				remove_theme_mods();
			} else {
				// Get theme slug and update the theme mod option.
				$theme = get_option( 'stylesheet' );
				update_option( "theme_mods_$theme", $theme_mods );
			} // End if().
		} // End if().
	} // End foreach().
}

Wir nutzen hier die customize_save_after-Action, die nach dem Speichern der Customize-Settings abgefeuert wird. In der Funktion holen wir uns über
get_theme_mods() alle Theme-Mods und durchlaufen die Einträge von $theme_mods['portfolio_category_page'] (durch meine Namensgebung der Settings mit den eckigen Klammern werden sie in der Datenbank in einem Array verschachtelt, was ich zur Übersichtlichkeit ganz nett finde).

Innerhalb der Schleife prüfen wir, ob der deleted-Key existiert und den Wert -1 hat. Ist das gegeben, entfernen wir diese Theme-Mods aus dem $theme_mods-Array. Anschließend wird geschaut, ob der Eintrag $theme_mods['portfolio_category_page'] jetzt leer ist und in diesem Fall ebenfalls entfernt. Wenn das darüber hinaus auch noch die letzte Theme-Mod war (also $theme_mods jetzt leer ist), dann entfernen wir mit remove_theme_mods() den Theme-Mod-Eintrag aus der Datenbank.

Andernfalls holen wir uns den Slug vom Theme und aktualisieren den Theme-Mods-Eintrag mit einem update_option()-Aufruf, dem wir als ersten Parameter den Namen der Option und als zweiten unser verändertes $theme_mods-Array übergeben.

Und damit wären wir am Ziel angekommen ? Nicht gerade einfach und vermutlich nicht die eleganteste Lösung, aber es tut was es soll 🙂 Wenn jemand von euch Verbesserungsvorschläge zu dem doch relativ aufwendigen Vorgehen hat, gerne her damit!

Das könnte auch interessant sein

Schreib einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.