Funcționalitate de export CSV în plugin-ul meu
Am nevoie să implementez o funcționalitate de "export ca .csv" în plugin-ul meu. Problema este: primesc avertizarea despre header-uri deja trimise, pentru că în momentul în care funcția mea de "export" este apelată, WordPress a început deja procesul de redare.
Am configurat asta prin intermediul unui mic formular în ecranul de Setări, al cărui atribut action indică către un fișier (download.csv.php) din folderul plugin-ului meu, care nu ar trebui să fie afișat, ci doar să deschidă un dialog de descărcare pentru dump-ul CSV.
Iată codul meu.
În ecranul de Setări al plugin-ului meu:
<form method="post" id="download_form" action="<?php echo plugins_url( 'download.csv.php' , __FILE__ ); ?>">
<input type="hidden" name="download" value="<?php echo get_home_path(); ?>" />
<input type="submit" name="download_csv" class="button-primary" value="<?php _e('Descarcă jurnalul (.csv)', $this->localizationDomain); ?>" />
</form>
Și fișierul download.csv.php:
$core = $_POST['download'].'wp-load.php';
if(isset($_POST['download']) && is_file($core)){
require_once( $core );
global $wpdb;
$rows = $wpdb->get_results('SELECT * FROM `'.$this->dbName.'`;',ARRAY_A);
if($rows){
require_once('classes/csvmaker.class.php');
$CSV = new CSVMaker();
$CSVHeader = array();
$CSVHeader['id'] = "ID";
$CSVHeader['when'] = "CÂND";
$CSVHeader['who'] = "CINE";
$CSVHeader['description'] = "DESCRIERE";
$CSVHeader['type'] = "TIP";
foreach($rows as $r){
$CSV->addEntry($r);
}
$file_name = plugin_dir_path(__FILE__).'data.csv';
file_put_contents($file_name,$CSV->buildDoc);
ob_start();
header("Expires: Mon, 1 Apr 1974 05:00:00 GMT");
header("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-type: application/CSV");
header("Content-Disposition: attachment; filename=$file_name.csv");
echo $CSV->buildDoc;
return ob_get_clean();
exit;
}
Ce fac greșit?

Începutul a ceea ce faci greșit poate fi explicat prin acest citat:
al cărui atribut action indică către un fișier (download.csv.php) în directorul meu de plugin, care nu ar trebui afișat, ci doar să deschidă un dialog de descărcare pentru dump-ul CSV.
și acest cod:
$core = $_POST['download'].'wp-load.php';
if(isset($_POST['download']) && is_file($core)){
require_once( $core );
Ceea ce faci în esență este a) ieși din mediul WordPress și accesezi direct o parte din pluginul tău și b) apoi încerci să încarci mediul WordPress din interiorul acelui fișier (și o faci într-un mod extrem de nesigur, trebuie să menționez).
În loc să faci asta, este mai bine să rămâi în mediul WordPress de la bun început și să suprascrii output-ul să fie așa cum vrei tu.
O metodă mai bună ar fi să rămâi în admin, să te conectezi la admin_init și să detectezi când ai nevoie să obții output-ul CSV și să returnezi acel lucru în schimb.
Deci pentru formularul tău, fă ceva de genul:
<form method="post" id="download_form" action="">
<input type="submit" name="download_csv" class="button-primary" value="<?php _e('Descarcă jurnalul (.csv)', $this->localizationDomain); ?>" />
</form>
Observă că nu este folosit niciun action aici. Asta înseamnă că se trimite înapoi la aceeași pagină de admin, fără modificări. Acum poți detecta asta în orice funcție ai conectat la action hook-ul admin_init, astfel:
global $plugin_page;
if ( isset($_POST['download_csv']) && $plugin_page == 'whatever' ) {
echo "BUNĂ"; die;
}
Variabila globală $plugin_page va fi setată la page=whatever pe care îl ai în ecranul normal de setări al pluginului tău. În loc să afișezi "BUNĂ" cum am făcut eu aici, apelează o funcție din pluginul tău care să genereze și să afișeze corect acel CSV, cu header-e și toate cele, apoi die. Sau ceva de genul ăsta.
Reține că acest lucru este simplist și poate fi nesigur. Poți dori de asemenea să faci o verificare nonce și de capabilități aici, pentru a te asigura că utilizatorul are permisiunea să descărce acest CSV și a intenționat să facă acest lucru.

Codul complet actualizat.
În actualizarea plugin-ului tău
<form method="post" id="download_form" action="">
<input type="submit" name="download_csv" class="button-primary" value="<?php _e('Descarcă jurnalul (.csv)', $this->localizationDomain); ?>" />
</form>
Acum în fișierul tău function.php
add_action("admin_init", "download_csv");
function download_csv() {
if (isset($_POST['download_csv'])) {
global $wpdb;
$sql = "SELECT * FROM {$wpdb->prefix}table_name";
$rows = $wpdb->get_results($sql, 'ARRAY_A');
if ($rows) {
$csv_fields = array();
$csv_fields[] = "prima_coloană";
$csv_fields[] = 'a_doua_coloană';
$output_filename = 'nume_fisier' .'.csv';
$output_handle = @fopen('php://output', 'w');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Description: Transfer Fișier');
header('Content-type: text/csv');
header('Content-Disposition: attachment; filename=' .
$output_filename);
header('Expires: 0');
header('Pragma: public');
$first = true;
// Parsează rezultatele în format csv
foreach ($rows as $row) {
// Adaugă antetele tabelului
if ($first) {
$titles = array();
foreach ($row as $key => $val) {
$titles[] = $key;
}
fputcsv($output_handle, $titles);
$first = false;
}
$leadArray = (array) $row; // Transformă Obiectul în array
// Adaugă rândul în fișier
fputcsv($output_handle, $leadArray);
}
//echo '<a href="'.$output_handle.'">test</a>';
// Închide fluxul de ieșire al fișierului
fclose($output_handle);
die();
}
}
}

Am întâmpinat câteva probleme cu metodele menționate mai sus - add_action. Nu am dorit ca plugin-ul să execute această funcționalitate automat. În schimb, aveam nevoie de o soluție în administrare.
1. Am descoperit că trebuie să cureți cache-ul, altfel conținutul HTML va fi inclus în fișierul CSV.
2. Este necesar să oprești procesul cu "exit".
Iată soluția mea. Modificați tabelele și câmpurile conform nevoilor dumneavoastră:
function Export()
{
global $wpdb;
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="export.csv"');
// curăță buffer-ul de ieșire
ob_end_clean();
$fp = fopen('php://output', 'w');
$header_row = array(
0 => 'eticheta câmp 1',
1 => 'eticheta câmp 2',
2 => 'eticheta câmp 3',
);
fputcsv($fp, $header_row);
$Table_Name = $wpdb->prefix.'TabelulTau';
$sql_query = $wpdb->prepare("SELECT * FROM $Table_Name", 1) ;
$rows = $wpdb->get_results($sql_query, ARRAY_A);
if(!empty($rows))
{
foreach($rows as $Record)
{
$OutputRecord = array($Record['camp1'],$Record['camp2'],$Record['camp3']);
fputcsv($fp, $OutputRecord);
}
}
fclose( $fp );
exit;
}
ACTUALIZARE: De când am scris acest cod, am observat că datele din header-ul HTML sunt incluse în fișier dacă utilizați codul prin shortcode sau apel direct în front-end. Dacă îl folosiți în zona de administrare / backend, funcționează perfect.
