Veröffentlicht am Schreib einen Kommentar

has_block() für Block-Widgets

Mit WordPress 5.8 wurden die Widgets ordentlich aufgebohrt und können nun auch Blöcke beinhalten. Doch wie kann man diese neuen Block-Widgets durchsuchen? In meinem Fall ging es konkret darum, dass ich erkennen wollte, welche Blöcke innerhalb von Widgets verwendet werden, ähnlich zu has_block(), das es bereits für Inhalte im Block-Editor auf Basis von Inhaltstypen gibt.

Eine Suche in der Datenbank ergab, dass die Inhalte der Block-Widgets in der Option widget_block gespeichert werden. Allerdings ist dort nur eine Zuordnung von Block-ID zu Inhalt zu finden. Aufgrund meiner bisherigen Erfahrung mit der Suche in Widgets kam mir dann schnell in den Sinn, mal in wp_get_sidebars_widgets() zu schauen, wie dort die Zuordnung von Sidebars zu Block-Widgets aussieht.

Das Ergebnis: Man muss zusätzlich die Sidebars alle durchgehen und dort dann die Zuordnung von Widget zu Block-ID herstellen. Denn in den Sidebars ist ein Block-Widget nach dem Schema block-\d+ gespeichert. Entfernt man nun das block-, erhält man die ID, die man wiederum mit den Block-Widgets aus der Option verbinden kann.

Zuletzt fehlt in meinem Fall dann noch die Prüfung auf den eigentlichen Block-Typ. Dafür kann man dann die reguläre has_block()-Funktion verwenden. Das sieht dann insgesamt als Code folgendermaßen aus:

/**
 * Check if a block is inside widget areas.
 * 
 * @param	string	$block_name Full block type to look for
 * @return	bool Whether a widget area contains the block
 */
function has_block_in_widgets( string $block_name ): bool {
	$widget_block = get_option( 'widget_block' );
	
	foreach ( wp_get_sidebars_widgets() as $sidebar => $widgets ) {
		// ignore inactive widgets
		if ( $sidebar === 'wp_inactive_widgets' ) {
			continue;
		}
			
		if ( empty( $widgets ) || ! is_array( $widgets ) ) {
			continue;
		}
		
		foreach ( $widgets as $widget ) {
			// ignore any widgets that are no block widgets
			if ( strpos( $widget, 'block-' ) !== 0 ) {
				continue;
			}
			
			// block ID is part of the widget ID
			$block_id = (string) str_replace( 'block-', '', $widget );
			$block = $widget_block[ $block_id ];
			
			if ( empty( $block['content'] ) ) {
				continue;
			}
			
			// don't use self::has_block() since this only allows to pass
			// an ID and not the content directly
			if ( has_block( $block_name, $block['content'] ) ) {
				return true;
			}
			
			// search in reusable blocks
			$parsed_blocks = parse_blocks( $block['content'] );
			
			if ( ! is_array( $parsed_blocks ) || empty( $parsed_blocks ) ) {
				continue;
			}
			
			foreach ( $parsed_blocks as $parsed_block ) {
				if ( has_block_recursive( $block_name, $parsed_block ) ) {
					return true;
				}
			}
		}
	}
	
	return false;
}

/**
 * Check recursive if a block contains a specific block by name.
 * 
 * @param	string	$block_name Full block type to look for
 * @param	array	$block Current block's data
 * @return	bool True if the post contains this block, false otherwise
 */
function has_block_recursive( string $block_name, array $block ): bool {
	if ( ! empty( $block['attrs']['ref'] ) && has_block( $block_name, $block['attrs']['ref'] ) ) {
		return true;
	}
	
	if ( ! empty( $block['innerBlocks'] ) ) {
		foreach ( $block['innerBlocks'] as $inner_block ) {
			if ( has_block_recursive( $block_name, $inner_block ) ) {
				return true;
			}
		}
	}
	
	// check reusable blocks inside reusable blocks
	if ( ! empty( $block['attrs']['ref'] ) ) {
		$content = get_post_field( 'post_content', $block['attrs']['ref'] );
		$blocks = parse_blocks( $content );
		
		foreach ( $blocks as $block ) {
			if ( has_block_recursive( $block_name, $block ) ) {
				return true;
			}
		}
	}
	
	return false;
}

Zusätzlich habe ich auch direkt die Funktion implementiert, wiederverwendbare Blöcke in Widgets zu erkennen. Zwar gibt es zum gegenwärtigen Zeitpunkt die Funktion für Widgets noch nicht, wiederverwendbare Blöcke dort zu verwenden, allerdings ist das bereits geplant. Mein Code fußt auf der Annahme, dass der HTML-Code innerhalb des Widgets hierbei identisch aufgebaut ist wie bei Inhalten aus dem Block-Editor.

Wer das erst einmal nicht möchte, kann die Zeilen 40–51 sowie alles ab Zeile 57 entfernen.

Schreibe einen Kommentar

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