Woocommerce prodotti variabili divenuti improvvisamente semplici

Sto scrivendo questo post per il bene dell’umanità, perché nemmeno il più empio dei bipedi umanoidi del globo terraqueo merita di passare per quello che sono passato io.

Vi dico subito che le soluzioni semplici sono le più efficaci e che se vi trovate nella situazione in cui, di punto in bianco, tutti i prodotti variabili del vostro sito sono divenuti semplici sia da backend che da frontend, allora questo articolo può aiutarvi.
Più precisamente se il vostro prodotto fintamente semplice, quando siete in modifica e cambiate il tipo in variabile, carica delle pre-esistenti variazioni, e salvando bonificate la situazione, allora direi che siete proprio nel posto giusto.

Non sto qui a spiegarvi tutti gli approcci che ho adottato e che sono miseramente falliti, andiamo direttamente al punto spiegando in primis qual era la situazione.

Cosa era successo?

Lo ignoro, ho ereditato questa grana da terzi e senza la possibilità di tornare in uno stato precedente, senza backup e senza informazione.
Ho letto la qualunque sull’internet, per taluni si trattava di un conflitto ( interiore? 😀 ) ma nel mio caso:

  • disattivando tutti i plugin
  • passando al tema ad un tema di default (Twenty twenty three)
  • togliendo la cache dall’equazione
  • assicurandomi che la CDN non rompesse le scatole
  • riavviando il pc (ovviamente questo non l’ho fatto davvero!)

e facendo tutte le altre prove che suggerisce l’intuizione o l’intelligenza artificiale non ho cavato un ragno dal buco. Zero carbonella, come dicono in capitale, nisba, nada … crio.

Con un elenco di più di 500 prodotti affetti dal problema capite bene che andare lì manualmente a editare prodotto per prodotto sarebbe stato un guaio passato.

Pertanto sono andato alla ricerca di fix programmatici, ho scritto plugin, soluzioni che eseguivano operazioni in batch e altre cose anche molto sofisticate, ma sono solo riuscito a sfondare l’archivio prodotti. Tristezza per favore vai via.

Alla fine mi sono messo con calma zen e ho chiesto a chatgpt di farmi uno script che mi consentisse di ottenere un report in json descrittivo del prodotto.

Tale fix sarebbe stato lanciato prima e dopo la modifica manuale del prodotto fintamente semplice in variabile (cosa che come vi ho già detto, aggiustava le cose).

Ho preso il codice php che mi ha suggerito la lattina (povero ChatGPT) e ho creato un file report.php che ho messo nella root di wordpress per poterlo richiamare da browser, eccolo:

<?php
// Assicurati che questo file venga eseguito in un contesto WordPress
require_once('./wp-load.php');

if (!isset($_GET['product_id']) || !is_numeric($_GET['product_id'])) {
header('Content-Type: application/json');
echo json_encode(['error' => 'Missing or invalid product_id parameter.']);
exit;
}

$product_id = intval($_GET['product_id']);
$data = [];

// Recupera dati da wp_posts
$product_post = get_post($product_id);
if ($product_post) {
$data['post'] = [
'ID' => $product_post->ID,
'post_title' => $product_post->post_title,
'post_type' => $product_post->post_type,
'post_status' => $product_post->post_status,
];
} else {
$data['post'] = 'Product not found in wp_posts.';
}

// Recupera dati da wp_postmeta
$postmeta = get_post_meta($product_id);
$data['postmeta'] = $postmeta ?: 'No metadata found for this product.';

// Recupera termini collegati nella tassonomia product_type
$terms = wp_get_object_terms($product_id, 'product_type');
if (!is_wp_error($terms)) {
$data['taxonomy'] = array_map(function ($term) {
return [
'term_id' => $term->term_id,
'name' => $term->name,
'taxonomy' => $term->taxonomy,
];
}, $terms);
} else {
$data['taxonomy'] = 'Error fetching terms: ' . $terms->get_error_message();
}

// Imposta l'intestazione per il JSON
header('Content-Type: application/json');

// Ritorna i dati in formato JSON
echo json_encode($data, JSON_PRETTY_PRINT);

Richiamando la url miosito.it/report.php?product_id=123, ho ottenuto il report relativo al prodotto, quindi ho salvato il prodotto da interfaccia cambiando manualmente il type da simple a variable e ho rigenerato il report.

Dal confronto dei due file è emerso che si era sballata la tassonomia che gestisce il tipo prodotto. Se siete confusi è giusto che sia così, perché sappiate che c’è una tassonomia che gestisce il tipo prodotto …

Tale tassonomia ha vari termini (uno per tipo di prodotto) e ciascuno ha il proprio identificativo (term_id). Il mio termine “variabile” aveva term_id = 842.

Pertanto mi sono detto, perché non fare una bamonda query che prenda tutti i prodotti fintamente semplici e ci metta su, questa cavolo di associazione con il termine giusto?

I prodotti fintamente semplici (e intrinsecamente variabili) erano reperibili verificando che avessero effettivamente almeno un post_parent che gli puntasse (ovvero le variazioni).

Ha funzionato bene, ecco la query:

-- Associare i prodotti fintamente variabili al termine "variable" (term_id 842)
INSERT INTO wpon_term_relationships (object_id, term_taxonomy_id)
SELECT
p.ID AS product_id,
842 AS term_taxonomy_id
FROM
wpon_posts AS p
LEFT JOIN
wpon_posts AS v ON v.post_parent = p.ID AND v.post_type = 'product_variation'
LEFT JOIN
wpon_term_relationships AS tr ON p.ID = tr.object_id
LEFT JOIN
wpon_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
LEFT JOIN
wpon_terms AS t ON tt.term_id = t.term_id
WHERE
p.post_type = 'product' -- È un prodotto
AND v.ID IS NOT NULL -- Ha variazioni associate
AND (t.term_id IS NULL OR t.term_id != 842) -- Non è già associato a "variable"
GROUP BY
p.ID
ON DUPLICATE KEY UPDATE term_taxonomy_id = VALUES(term_taxonomy_id);

Ovviamente 842 è il mio id, trova il tuo nel database e modifica lo script prima di lanciarlo.

ATTENZIONE: non essere dissennato EFFETTUA UN BACKUP prima di distruggere tutto.

In fine

Questo problema non voglio doverlo risolvere mai più! 🙂

È stato uno stress non indifferente venire a capo dei perché e dei percome, spero torni utile a voi altri.

Uno script php per assegnare i giusti permessi a file e cartelle di WordPress

Qualche tempo fa, abbiamo pubblicato un post con uno script sh, che poteva essere lanciato da console Linux che consentiva di settare i permessi a file e cartelle utili al corretto funzionamento di WordPress.

Un nostro lettore, ci ha segnalato che in alcuni hosting non è possibile lanciare script sh, quindi abbiamo deciso di pubblicare uno script php, che può essere lanciato anche visitandone il path.

Nel test che abbiamo fatto il file si chiamava set_wordpress_permissions.php e pertanto abbiamo avuto la possibilità, accedendo alla pagina nomesito.com/set_wordpress_permissions.php, di settare i permessi giusti a tutta l’alberatura di file e cartelle di wordpress e in un pochi attimi.

Lo script php

<?php
// Configurazione della directory root di WordPress
$wp_root = "/percorso/al/tuo/sito"; // Cambia con il percorso della tua installazione di WordPress

// Configurazione dell'utente e gruppo (facoltativo, solo su server Linux/Unix con funzione `chown`)
$user = "www-data"; // Utente del web server (es: www-data, apache, nginx)
$group = "www-data"; // Gruppo del web server

/**
* Funzione per impostare i permessi sui file e directory.
*
* @param string $path Percorso della directory o file
* @param int $filePerm Permessi da impostare sui file (es: 0644)
* @param int $dirPerm Permessi da impostare sulle directory (es: 0755)
*/
function set_permissions($path, $filePerm = 0644, $dirPerm = 0755) {
// Controlla se il percorso è valido
if (!file_exists($path)) {
echo "Percorso non trovato: $path\n";
return;
}

// Scansiona ricorsivamente file e directory
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);

foreach ($iterator as $item) {
if ($item->isDir()) {
chmod($item->getPathname(), $dirPerm); // Imposta permessi sulle directory
} else {
chmod($item->getPathname(), $filePerm); // Imposta permessi sui file
}
}

// Imposta i permessi sulla directory principale
chmod($path, $dirPerm);
echo "Permessi impostati su $path\n";
}

/**
* Funzione per impostare proprietario e gruppo (facoltativo).
*
* @param string $path Percorso della directory o file
* @param string $user Nome utente
* @param string $group Nome gruppo
*/
function set_owner($path, $user, $group) {
if (!function_exists('chown') || !function_exists('chgrp')) {
echo "Funzioni chown/chgrp non disponibili su questo sistema.\n";
return;
}

// Scansiona ricorsivamente file e directory
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);

foreach ($iterator as $item) {
chown($item->getPathname(), $user);
chgrp($item->getPathname(), $group);
}

// Imposta proprietario e gruppo sulla directory principale
chown($path, $user);
chgrp($path, $group);
echo "Proprietario impostato su $path a $user:$group\n";
}

// Imposta i permessi di base per il sito WordPress
set_permissions($wp_root, 0644, 0755);

// Imposta permessi sicuri per wp-config.php
$wp_config = $wp_root . "/wp-config.php";
if (file_exists($wp_config)) {
chmod($wp_config, 0600);
echo "Permessi sicuri impostati per wp-config.php\n";
}

// Imposta permessi speciali per wp-content/uploads
$uploads_dir = $wp_root . "/wp-content/uploads";
if (is_dir($uploads_dir)) {
set_permissions($uploads_dir, 0664, 0775); // Scrivibile da utente e gruppo
}

// Imposta proprietario e gruppo (facoltativo)
set_owner($wp_root, $user, $group);

echo "Permessi configurati con successo per WordPress.\n";

Snippet PHP per scaricare un intero database da un server remoto

Quando si tratta di proteggere i dati del tuo database, l’esecuzione regolare di un backup è fondamentale. Inoltre è a volte necessario creare una copia del database, per ragioni di sviluppo oltre che di sicurezza, di un’applicazione web che è accessibile solo via FTP.

Ebbene, quando l’accesso alla console MySQL o a phpMyAdmin è interdetto, ma è disponibile un’accesso in scrittura via FTP, è possibile provare i metodi che passiamo ad illustrare.

L’dea è avere una URL come example.org/dump-database.php, che invocata da browser faccia partire il download di un file sql, ottenendo così una copia dei dati del database.

Ovviamente dovrai avere le credenziali di accesso al database per poter effettuare il dump.

Script PHP che sfrutti la console mysql per il dump del database

Questo script è più performante del precedente, ma non disponibile su tutti gli hosting, in quanto utilizza il comando MySQL tramite exec (che è la funzione che esegue i comandi direttamente sulla console del sistema operativo).

<?php
// Configurazione dei parametri
$host = 'localhost'; // Host del database
$username = 'username'; // Nome utente del database
$password = 'password'; // Password del database
$dbname = 'database_name'; // Nome del database
$tables = '*'; // Tabelle da includere nel dump (puoi specificarne una lista separata da virgole o usare '*' per tutte le tabelle)

// Crea il nome del file di dump
$filename = 'database_dump_' . date('Y-m-d_H-i-s') . '.sql';

// Esegui il dump del database
exec("mysqldump -h{$host} -u{$username} -p{$password} {$dbname} {$tables} > {$filename}");

// Forza il download del file di dump
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($filename) . "\"");
readfile($filename);

// Rimuovi il file di dump dal server
unlink($filename);
?>

NOTA BENE:

  • Se tutti possono accedere alla URL su cui gira il tuo snippet tutti potranno scaricare il tuo database (vedi “Implicazioni di sicurezza”);
  • Se il database è molto grande potresti, nel navigare la URL, non ottenere l’agognato file o ritrovarti con una copia incompleta del database a causa del timeout dell’esecuzione di processi in PHP (il famoso max_execution_time).

Script in PHP puro per il dump del database

Questo script necessita solo che sul server che lo esegue, ci sia l’interprete PHP, cosa che è effettivamente possibile su praticamente tutti gli hosting sparsi per l’infosfera.

<?php
// Configurazione dei parametri
$host = 'localhost'; // Host del database
$username = 'username'; // Nome utente del database
$password = 'password'; // Password del database
$dbname = 'dbname'; // Nome del database
$tables = '*'; // Tabelle da includere nel dump (puoi specificarne una lista separata da virgole o usare '*' per tutte le tabelle)


// Connessione al database
$mysqli = new mysqli($host, $username, $password, $dbname);

// Verifica della connessione
if ($mysqli->connect_errno) {
    die('Failed to connect to MySQL: ' . $mysqli->connect_error);
}

// Recupero dei nomi delle tabelle nel database
$tables = array();
$result = $mysqli->query("SHOW TABLES");
while ($row = $result->fetch_row()) {
    $tables[] = $row[0];
}

// Creazione del dump del database
$dump = '';
foreach ($tables as $table) {
    $result = $mysqli->query("SELECT * FROM `$table`");
    $numColumns = $result->field_count;

    $dump .= "DROP TABLE IF EXISTS `$table`;\n";
    $row2 = $mysqli->query("SHOW CREATE TABLE `$table`")->fetch_row();
    $dump .= $row2[1] . ";\n\n";

    while ($row = $result->fetch_row()) {
        $dump .= "INSERT INTO `$table` VALUES (";
        for ($i = 0; $i < $numColumns; $i++) {
            $row[$i] = $mysqli->real_escape_string($row[$i]);
            $row[$i] = "'" . $row[$i] . "'";
        }
        $dump .= implode(',', $row);
        $dump .= ");\n";
    }

    $dump .= "\n";
}

// Impostazione dell'header per il download del file
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"database_dump_" . date('Y-m-d_H-i-s') . ".sql\"");

// Output del dump del database
echo $dump;

// Chiusura della connessione al database
$mysqli->close();

Implicazioni di sicurezza

Quando hai a che fare con i database stai avendo a che fare con la tua base dati, dove la parola dati dovrebbe spaventarti un poco! 😀

Pertanto assicurati di proteggere adeguatamente il file PHP che contiene lo script, ad esempio, posizionandolo in una directory non accessibile al pubblico o dandogli un nome al file, praticamente impossibile da raggiungere per caso. Se dovevi solo scaricare il dump per lavorarci altrove, allora ricordati di cancellare il file che contiene il tuo snippet!

Come mettere lo script in un file PHP

Per utilizzare lo script PHP per il dump del database, segui questi semplici passaggi. Prima di tutto, crea un nuovo file PHP sul tuo server web. Quindi, copia il codice dello script fornito in questo articolo e incollalo nel file PHP appena creato. Assicurati di configurare correttamente i parametri, come l’host del database, il nome utente, la password e il nome del database. Salva il file PHP e caricalo sul tuo server. Prendi nota del nome del file e usalo nella URL per lanciare lo script e scaricare il tuo database tramite il browser.

Sper di esserti stato utile