Salta al contenuto

Come vedere dove è usato un pattern WordPress e scollegarlo da tutti i contenuti in un colpo solo

Se usi un tema a blocchi da un po’, prima o poi ti ritrovi con una lista di pattern che nessuno ricorda più a cosa servano. Alcuni li hai creati tu per provare, altri sono rimasti da una vecchia impaginazione. Il problema è sempre lo stesso: se ne cancelli uno e quello era inserito in venti articoli, in quegli articoli sparisce il blocco. E WordPress, di suo, non ti dice dove un pattern è usato.

Mi è capitato sul mio sito e ho risolto con un piccolo snippet. Lo condivido qui, con il ragionamento che c’è dietro, nel caso servisse anche a te.

Interfaccia WordPress con elenco di pattern a sinistra, pulsanti blu "Importa da JSON" e "Aggiungi pattern" in alto e numeri

Prima di tutto: pattern sincronizzato o no?

È il punto che chiarisce quasi tutto, quindi vale la pena fermarsi un attimo.

Pubblicità

  • Pattern sincronizzato (quello che una volta si chiamava blocco riutilizzabile): quando lo inserisci in una pagina, WordPress non copia il contenuto ma salva un riferimento, qualcosa come <!-- wp:block {"ref":123} -->. Se modifichi il pattern, cambiano tutte le pagine dove è inserito.
  • Pattern non sincronizzato: WordPress copia i blocchi nella pagina e basta. Nessun riferimento, nessun legame. Puoi cancellare il pattern senza conseguenze, ma per lo stesso motivo nessuno snippet può dirti dove l’hai usato.

Quello che segue funziona quindi per i pattern sincronizzati, che sono anche gli unici che danno problemi quando li elimini.

Il trucco: cercare i riferimenti nel database

Siccome ogni inserimento lascia nel contenuto un riferimento con l’ID del pattern, basta scorrere gli articoli, le pagine e gli altri contenuti, cercare quei riferimenti e contarli. È quello che fa la prima parte dello snippet. Aggiunge alla lista dei pattern:

  1. una colonna Utilizzi con il numero di contenuti in cui il pattern compare (cliccando sul numero vedi l’elenco, con tipo, stato e link per modificare o aprire);
  2. un filtro Non usati, per trovare subito i pattern che non servono a niente;
  3. l’indicazione dei contenuti nel cestino, che altrimenti ti ingannano.

Il risultato viene tenuto in cache per 10 minuti, così la pagina non diventa lenta sui siti grandi.

Leggi anche: Come eliminare i transient scaduti nel Database WordPress senza plugin utilizzando WP-CLI

Dove si trova la lista (e perché non la vedi)

Qui ho perso un po’ di tempo anch’io. Con un tema a blocchi, Aspetto → Pattern ti porta nell’Editor del sito, una schermata completamente diversa che non può essere modificata con i filtri classici di WordPress. La colonna “Utilizzi” compare invece nella vecchia lista, che si raggiunge scrivendo a mano questo indirizzo:

tuosito.it/wp-admin/edit.php?post_type=wp_block

Per non dover ricordare l’URL, nello snippet c’è anche una voce di menu in più, Aspetto → Pattern (lista).

Come installare lo snippet

Ti consiglio di non toccare il functions.php del tema: se lo cambi, perdi tutto al primo aggiornamento. Usa un plugin per gli snippet, per esempio WPCode:

  1. Vai in Snippet → Aggiungi snippet → Aggiungi il tuo codice personalizzato e scegli PHP.
  2. Incolla il codice qui sotto.
  3. Come posizione scegli Esegui ovunque (con “Solo amministrazione” la cache non si azzera quando salvi dall’editor a blocchi, perché il salvataggio passa dalla REST API).
  4. Attiva e salva.
Interfaccia del plugin WPCode con finestra Modifica Snippet, campo titolo "pattern", anteprima codice PHP con commenti e

Parte 1: colonna Utilizzi, filtro Non usati, elenco dei contenuti

function bdpu_scan() {
	$cached = get_transient( 'bdpu_scan' );
	if ( is_array( $cached ) ) {
		return $cached;
	}
	global $wpdb;
	$map  = array();
	$info = array();
	$last = 0;
	do {
		$rows = $wpdb->get_results( $wpdb->prepare(
			"SELECT ID, post_type, post_status, post_title, post_content FROM {$wpdb->posts}
			 WHERE ID > %d AND post_type NOT IN ('revision','auto-draft','nav_menu_item','customize_changeset')
			 AND post_content LIKE %s ORDER BY ID ASC LIMIT 300",
			$last,
			'%' . $wpdb->esc_like( '"ref":' ) . '%'
		) );
		foreach ( $rows as $r ) {
			$last = (int) $r->ID;
			if ( ! preg_match_all( '/"ref":(\d+)/', $r->post_content, $m ) ) {
				continue;
			}
			$info[ $last ] = array(
				'type'   => $r->post_type,
				'status' => $r->post_status,
				'title'  => $r->post_title,
			);
			$key = ( 'trash' === $r->post_status ) ? 'trash' : 'live';
			foreach ( array_unique( $m[1] ) as $bid ) {
				$bid = (int) $bid;
				if ( $bid !== $last ) {
					$map[ $bid ][ $key ][] = $last;
				}
			}
		}
	} while ( 300 === count( $rows ) );
	$data = array( 'map' => $map, 'info' => $info );
	set_transient( 'bdpu_scan', $data, 10 * MINUTE_IN_SECONDS );
	return $data;
}

function bdpu_reset() {
	delete_transient( 'bdpu_scan' );
}
add_action( 'save_post', 'bdpu_reset' );
add_action( 'trashed_post', 'bdpu_reset' );
add_action( 'untrashed_post', 'bdpu_reset' );
add_action( 'deleted_post', 'bdpu_reset' );

function bdpu_unused_ids() {
	$d   = bdpu_scan();
	$ids = get_posts( array(
		'post_type'   => 'wp_block',
		'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future' ),
		'numberposts' => -1,
		'fields'      => 'ids',
	) );
	return array_values( array_filter( $ids, function ( $i ) use ( $d ) {
		return empty( $d['map'][ $i ]['live'] );
	} ) );
}

// Colonna.
add_filter( 'manage_wp_block_posts_columns', function ( $cols ) {
	$out = array();
	foreach ( $cols as $k => $v ) {
		$out[ $k ] = $v;
		if ( 'title' === $k ) {
			$out['bdpu_uses'] = 'Utilizzi';
		}
	}
	if ( ! isset( $out['bdpu_uses'] ) ) {
		$out['bdpu_uses'] = 'Utilizzi';
	}
	return $out;
} );

add_action( 'manage_wp_block_posts_custom_column', function ( $col, $id ) {
	if ( 'bdpu_uses' !== $col ) {
		return;
	}
	$d     = bdpu_scan();
	$live  = isset( $d['map'][ $id ]['live'] ) ? count( $d['map'][ $id ]['live'] ) : 0;
	$trash = isset( $d['map'][ $id ]['trash'] ) ? count( $d['map'][ $id ]['trash'] ) : 0;
	$url   = admin_url( 'edit.php?post_type=wp_block&bdpu_uses=' . (int) $id );
	if ( $live ) {
		printf( '<a href="%s"><strong>%d</strong></a>', esc_url( $url ), $live );
	} else {
		echo '<span style="color:#999">0 · non usato</span>';
	}
	if ( $trash ) {
		printf( ' <a href="%s" style="color:#999">(+%d nel cestino)</a>', esc_url( $url ), $trash );
	}
}, 10, 2 );

add_action( 'admin_head-edit.php', function () {
	$s = get_current_screen();
	if ( $s && 'edit-wp_block' === $s->id ) {
		echo '<style>.column-bdpu_uses{width:150px}</style>';
	}
} );

// Filtro "Non usati".
add_filter( 'views_edit-wp_block', function ( $views ) {
	$cur = ! empty( $_GET['bdpu_unused'] ) ? ' class="current" aria-current="page"' : '';
	$views['bdpu_unused'] = sprintf(
		'<a href="%s"%s>Non usati <span class="count">(%d)</span></a>',
		esc_url( admin_url( 'edit.php?post_type=wp_block&bdpu_unused=1' ) ),
		$cur,
		count( bdpu_unused_ids() )
	);
	return $views;
} );

// La lista mostra solo i non usati, oppure solo il blocco di cui si guardano gli utilizzi.
add_action( 'pre_get_posts', function ( $q ) {
	global $pagenow;
	if ( ! is_admin() || 'edit.php' !== $pagenow || ! $q->is_main_query() || 'wp_block' !== $q->get( 'post_type' ) ) {
		return;
	}
	if ( ! empty( $_GET['bdpu_unused'] ) ) {
		$ids = bdpu_unused_ids();
		$q->set( 'post__in', $ids ? $ids : array( 0 ) );
	} elseif ( ! empty( $_GET['bdpu_uses'] ) ) {
		$q->set( 'post__in', array( (int) $_GET['bdpu_uses'] ) );
	}
} );

// Riquadro "Dove è usato".
add_action( 'admin_notices', function () {
	$s = get_current_screen();
	if ( ! $s || 'edit-wp_block' !== $s->id || empty( $_GET['bdpu_uses'] ) ) {
		return;
	}
	$id   = (int) $_GET['bdpu_uses'];
	$d    = bdpu_scan();
	$live = isset( $d['map'][ $id ]['live'] ) ? $d['map'][ $id ]['live'] : array();
	$tr   = isset( $d['map'][ $id ]['trash'] ) ? $d['map'][ $id ]['trash'] : array();
	echo '<div class="notice notice-info" style="padding:12px 16px">';
	printf(
		'<p><strong>Dove è usato: %s</strong> (%d)<a href="%s" style="margin-left:12px">Mostra tutti i pattern</a></p>',
		esc_html( get_the_title( $id ) ),
		count( $live ),
		esc_url( admin_url( 'edit.php?post_type=wp_block' ) )
	);
	if ( $live || $tr ) {
		echo '<div style="max-height:320px;overflow:auto"><table class="widefat striped"><thead><tr><th>Titolo</th><th>Tipo</th><th>Stato</th><th></th></tr></thead><tbody>';
		foreach ( array_merge( $live, $tr ) as $pid ) {
			$i    = $d['info'][ $pid ];
			$pt   = get_post_type_object( $i['type'] );
			$edit = get_edit_post_link( $pid );
			$view = ( 'publish' === $i['status'] ) ? get_permalink( $pid ) : '';
			printf(
				'<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>',
				$edit ? '<a href="' . esc_url( $edit ) . '">' . esc_html( $i['title'] ?: '(senza titolo)' ) . '</a>' : esc_html( $i['title'] ),
				esc_html( $pt ? $pt->labels->singular_name : $i['type'] ),
				esc_html( 'trash' === $i['status'] ? 'cestino' : $i['status'] ),
				$view ? '<a href="' . esc_url( $view ) . '" target="_blank" rel="noopener">Apri</a>' : ''
			);
		}
		echo '</tbody></table></div>';
	}
	echo '</div>';
} );

// Voce di menu: Aspetto → Pattern (lista).
add_action( 'admin_menu', function () {
	add_submenu_page( 'themes.php', 'Pattern (lista)', 'Pattern (lista)', 'edit_posts', 'edit.php?post_type=wp_block' );
} );

// Azzeramento cache manuale: .../edit.php?post_type=wp_block&bdpu_flush=1
add_action( 'admin_init', function () {
	if ( isset( $_GET['bdpu_flush'] ) && current_user_can( 'manage_options' ) ) {
		bdpu_reset();
	}
} );

Dopo averlo salvato, apri Aspetto → Pattern (lista): accanto a ogni pattern vedrai quante volte compare. Se un numero ti sembra vecchio, aggiungi &bdpu_flush=1 all’indirizzo per rifare la scansione.

Scollegare i pattern dai contenuti in blocco

Ora la parte che, a mio parere, è la più utile. Hai trovato dei pattern che vuoi eliminare, ma sono usati in decine di articoli. L’idea è sostituire, dentro ogni contenuto, il riferimento con i blocchi veri che il pattern contiene. Gli articoli restano visivamente identici, e a quel punto il pattern non serve più a nessuno e puoi cestinarlo.

WordPress lo fa già a mano, dal menu del blocco con “Scollega”, ma un articolo alla volta. Con questa seconda parte aggiungi invece un’azione di gruppo, Scollega dai contenuti, che lavora su tutti i pattern che spunti.

Parte 2: azione di gruppo “Scollega dai contenuti”

Incollala in fondo allo stesso snippet.

// Sostituisce ricorsivamente i blocchi core/block (pattern sincronizzati) con il loro contenuto.
function bdpu_walk( $b, $contents, &$count, $depth = 0 ) {
	if ( 'core/block' === $b['blockName'] && $depth < 5 ) {
		$ref = isset( $b['attrs']['ref'] ) ? (int) $b['attrs']['ref'] : 0;
		if ( $ref && isset( $contents[ $ref ] ) ) {
			$count++;
			$res = array();
			foreach ( parse_blocks( $contents[ $ref ] ) as $ib ) {
				if ( null === $ib['blockName'] && '' === trim( $ib['innerHTML'] ) ) {
					continue;
				}
				foreach ( bdpu_walk( $ib, $contents, $count, $depth + 1 ) as $x ) {
					$res[] = $x;
				}
			}
			return $res;
		}
	}
	if ( ! empty( $b['innerBlocks'] ) ) {
		$new_inner   = array();
		$new_content = array();
		$idx         = 0;
		foreach ( $b['innerContent'] as $piece ) {
			if ( null === $piece ) {
				foreach ( bdpu_walk( $b['innerBlocks'][ $idx++ ], $contents, $count, $depth ) as $x ) {
					$new_inner[]   = $x;
					$new_content[] = null;
				}
			} else {
				$new_content[] = $piece;
			}
		}
		$b['innerBlocks']  = $new_inner;
		$b['innerContent'] = $new_content;
	}
	return array( $b );
}

add_filter( 'bulk_actions-edit-wp_block', function ( $actions ) {
	$actions['bdpu_detach'] = 'Scollega dai contenuti';
	return $actions;
} );

add_filter( 'handle_bulk_actions-edit-wp_block', function ( $redirect, $action, $ids ) {
	if ( 'bdpu_detach' !== $action || ! current_user_can( 'manage_options' ) ) {
		return $redirect;
	}
	@set_time_limit( 300 );
	bdpu_reset();
	$d        = bdpu_scan();
	$contents = array();
	$targets  = array();
	foreach ( $ids as $id ) {
		$id = (int) $id;
		if ( 'wp_block' !== get_post_type( $id ) ) {
			continue;
		}
		$contents[ $id ] = get_post_field( 'post_content', $id );
		foreach ( array( 'live', 'trash' ) as $k ) {
			if ( ! empty( $d['map'][ $id ][ $k ] ) ) {
				foreach ( $d['map'][ $id ][ $k ] as $pid ) {
					$targets[ (int) $pid ] = true;
				}
			}
		}
	}
	$posts  = 0;
	$blocks = 0;
	foreach ( array_keys( $targets ) as $pid ) {
		$n   = 0;
		$new = array();
		foreach ( parse_blocks( get_post_field( 'post_content', $pid ) ) as $b ) {
			foreach ( bdpu_walk( $b, $contents, $n ) as $x ) {
				$new[] = $x;
			}
		}
		if ( ! $n ) {
			continue;
		}
		$res = wp_update_post( wp_slash( array(
			'ID'           => $pid,
			'post_content' => serialize_blocks( $new ),
		) ), true );
		if ( ! is_wp_error( $res ) ) {
			$posts++;
			$blocks += $n;
		}
	}
	bdpu_reset();
	return add_query_arg( array(
		'bdpu_done'   => 1,
		'bdpu_posts'  => $posts,
		'bdpu_blocks' => $blocks,
	), $redirect );
}, 10, 3 );

add_action( 'admin_notices', function () {
	$s = get_current_screen();
	if ( ! $s || 'edit-wp_block' !== $s->id || empty( $_GET['bdpu_done'] ) ) {
		return;
	}
	printf(
		'<div class="notice notice-success is-dismissible"><p>Scollegati <strong>%d</strong> blocchi in <strong>%d</strong> contenuti. Ora puoi eliminare i pattern.</p></div>',
		(int) $_GET['bdpu_blocks'],
		(int) $_GET['bdpu_posts']
	);
} );

add_action( 'admin_footer-edit.php', function () {
	$s = get_current_screen();
	if ( ! $s || 'edit-wp_block' !== $s->id ) {
		return;
	}
	?>
	<script>
	document.getElementById('posts-filter').addEventListener('submit', function (e) {
		var a = document.getElementById('bulk-action-selector-top').value;
		var b = document.getElementById('bulk-action-selector-bottom').value;
		if ((a === 'bdpu_detach' || b === 'bdpu_detach') &&
			!confirm('Scollegare i pattern selezionati da tutti i contenuti? I pattern non verranno eliminati.')) {
			e.preventDefault();
		}
	});
	</script>
	<?php
} );

Come si usa

Schermata dell'editor WordPress con elenco pattern, campi titolo blu, checkbox selezionati, pulsanti "Scegliere dai
  1. Fai un backup (io uso UpdraftPlus). Questo codice modifica il contenuto degli articoli, quindi non saltare il passaggio.
  2. Vai su Aspetto → Pattern (lista) e spunta i pattern che vuoi scollegare.
  3. Da “Azioni di gruppo” scegli Scollega dai contenuti e premi Applica. Ti chiede conferma.
  4. Compare un messaggio con il numero di blocchi e di contenuti modificati. La colonna Utilizzi torna a 0.
  5. Controlla a campione un paio di articoli. Se è tutto a posto, cestina i pattern.

Il consiglio è di provare prima con un solo pattern, aprire uno degli articoli che lo usavano e verificare che sia identico a prima. Solo dopo fai il resto.

Leggi anche: Controllare un articolo WordPress prima della pubblicazione con WPCode

Cose da sapere prima di eliminare

  • Revisioni: WordPress salva una revisione per ogni contenuto modificato, quindi per articoli e pagine puoi tornare indietro.
  • Contenuti nel cestino: vengono modificati anche quelli, così se li ripristini non trovano blocchi orfani.
  • Pattern dentro altri pattern: vengono scollegati anche lì.
  • Cosa non viene scansionato: widget a blocchi, template del tema salvati come file (templates/*.html, parts/*.html) e template mai modificati dall’Editor del sito. Se elimini un pattern usato solo in quei punti, il blocco sparisce da lì. Controlla prima.
  • Classi CSS aggiuntive: se avevi aggiunto una classe al singolo inserimento del pattern, non viene riportata. Restano i blocchi interni con le loro classi.
  • Contatore: conta in quanti contenuti compare il pattern, non quante volte. Se lo hai inserito tre volte nella stessa pagina, vedi 1.

In breve

WordPress ti lascia creare pattern sincronizzati ma non ti aiuta a capire dove sono finiti, e questo rende rischioso fare pulizia. Con questo snippet vedi dove è usato ogni pattern, trovi quelli inutilizzati e puoi scollegare quelli che non vuoi più senza rompere gli articoli. Lo uso sul mio sito da poco e mi ha tolto di mezzo un bel po’ di roba vecchia.

Leggi anche: WordPress: come correggere l’anteprima mobile e tablet del Customizer

Altro per te

PHP
PHP Compatibility Checker
Controlla il tuo plugin WordPress prima di aggiornare PHP Verifica la compatibilità con PHP 8.0–8.5 Scopri eventuali problemi nel codice PHP Analizza il tuo plugin WordPress online Preparati a PHP 8.5
Verifica ora →
PDF Strumenti PDF gratis Nel browser, senza upload Apri gli strumenti

Scritto da

Webmaster

Mi impegno costantemente nello studio e nell'apprendimento delle ultime novità e tendenze del settore tecnologico, sia dal punto di vista teorico che pratico. Questa passione mi spinge a mettermi alla prova e a superare ogni sfida che mi viene posta, permettendomi di raggiungere i miei…

Profilo e articoli →
🔒

Contenuto riservato

Per scaricare questo file devi prima registrarti oppure accedere con il tuo account.

XWhatsAppTelegram