php - mensaje - ¿Cómo transmitir la creación de un archivo JSON?
json php mysql (6)
Estoy tratando de crear un archivo JSON a partir de un volcado grande de una consulta de base de datos, y funciona cuando configuro un LÍMITE para que se devuelvan 100000 filas, pero cuando quiero que se devuelvan todas las filas, solo va a un error 502 fue cancelado porque tomó demasiado tiempo para completar). ¿Se pregunta si hay una forma en que pueda simplificar la creación de un archivo JSON en bits usando php, o si hay una biblioteca que me permita compilar el archivo json en partes?
Básicamente, estoy ejecutando un archivo .php aquí para intentar obtener todos los pedidos en formato json de woocommerce, ya que el complemento que compré "CSV Import Suite" no funciona cuando se importan pedidos, simplemente permanece en la cola.
Entonces, decidí intentar exportar todas las órdenes por mi cuenta, pero al seguir golpeando una página de error 502 y nunca se crea el archivo .json, creo que necesito una forma de transmitir esto de alguna manera. Cualquier ayuda en esto sería apreciada...
ini_set(''memory_limit'', ''-1'');
ini_set(''max_execution_time'', ''-1'');
set_time_limit(0);
error_reporting(E_ALL);
ob_implicit_flush(TRUE);
ob_end_flush();
global $wpdb, $root_dir;
if (!defined(''ABSPATH''))
$root_dir = dirname(__FILE__) . ''/'';
else
$root_dir = ABSPATH;
$download = isset($_GET[''download'']);
// Allows us to use WP functions in a .php file without 404 headers!
require_once($root_dir . ''wp-config.php'');
$wp->init();
$wp->parse_request();
$wp->query_posts();
$wp->register_globals();
if (empty($download))
$wp->send_headers();
// exclude
$exclude_post_statuses = array(''trash'', ''wc-refunded'', ''wc_cancelled'');
$start_date = !empty($_GET[''start_date'']) ? DateTime::createFromFormat(''Y-m-d'', $_GET[''start_date'']) : '''';
$end_date = !empty($_GET[''end_date'']) ? DateTime::createFromFormat(''Y-m-d'', $_GET[''end_date'']) : '''';
$order_db = array(
''columns'' => array(
''p'' => array(''ID'', ''post_author'', ''post_date'', ''post_date_gmt'', ''post_content'', ''post_title'', ''post_excerpt'', ''post_status'', ''comment_status'', ''ping_status'', ''post_password'', ''post_name'', ''to_ping'', ''pinged'', ''post_modified'', ''post_modified_gmt'', ''post_content_filtered'', ''post_parent'', ''guid'', ''menu_order'', ''post_type'', ''post_mime_type'', ''comment_count''),
''pm'' => array(''meta_id'', ''post_id'', ''meta_key'', ''meta_value''),
''oi'' => array(''order_item_id'', ''order_item_name'', ''order_item_type'', ''order_id''),
''oim'' => array(''meta_id'', ''order_item_id'', ''meta_key'', ''meta_value'')
)
);
$select_data = '''';
$total_columns = count($order_db[''columns'']);
$i = 1;
foreach($order_db[''columns''] as $column_key => $columns)
{
$select_data .= implode('', '', array_map(
function ($v, $k) { return $k . ''.'' . $v . '' AS '' . $k . ''_'' . $v; },
$columns,
array_fill(0, count($columns), $column_key)
));
if ($i < $total_columns)
$select_data .= '', '';
$i++;
}
// HUGE DATABASE DUMP HERE, needs to be converted to JSON, after getting all columns of all tables...
$orders_query = $wpdb->get_results(''
SELECT '' . $select_data . ''
FROM '' . $wpdb->posts . '' AS p
INNER JOIN '' . $wpdb->postmeta . '' AS pm ON (pm.post_id = p.ID)
LEFT JOIN '' . $wpdb->prefix . ''woocommerce_order_items AS oi ON (oi.order_id = p.ID)
LEFT JOIN '' . $wpdb->prefix . ''woocommerce_order_itemmeta AS oim ON (oim.order_item_id = oi.order_item_id)
WHERE p.post_type = "shop_order"'' . (!empty($exclude_post_statuses) ? '' AND p.post_status NOT IN ("'' . implode(''","'', $exclude_post_statuses) . ''")'' : '''') . (!empty($start_date) ? '' AND post_date >= "'' . $start_date->format(''Y-m-d H:i:s'') . ''"'' : '''') . (!empty($end_date) ? '' AND post_date <= "'' . $end_date->format(''Y-m-d H:i:s'') . ''"'' : '''') . ''
ORDER BY p.ID ASC'', ARRAY_A);
$json = array();
if (!empty($orders_query))
{
foreach($orders_query as $order_query)
{
if (!isset($json[$order_query[''p_post_type'']], $json[$order_query[''p_post_type'']][$order_query[''p_post_name'']]))
$json[$order_query[''p_post_type'']][$order_query[''p_post_name'']] = array(
''posts'' => array(),
''postmeta'' => array(),
''woocommerce_order_items'' => array(),
''woocommerce_order_itemmeta'' => array()
);
if (!empty($order_query[''p_ID'']))
$json[$order_query[''p_post_type'']][$order_query[''p_post_name'']][''posts''][$order_query[''p_ID'']] = array_filter($order_query, function($k) {
$is_p = strpos($k, ''p_'');
return $is_p !== FALSE && empty($is_p);
}, ARRAY_FILTER_USE_KEY);
if (!empty($order_query[''pm_meta_id'']))
$json[$order_query[''p_post_type'']][$order_query[''p_post_name'']][''postmeta''][$order_query[''pm_meta_id'']] = array_filter($order_query, function($k) {
$is_pm = strpos($k, ''pm_'');
return $is_pm !== FALSE && empty($is_pm);
}, ARRAY_FILTER_USE_KEY);
if (!empty($order_query[''oi_order_item_id'']))
$json[$order_query[''p_post_type'']][$order_query[''p_post_name'']][''woocommerce_order_items''][$order_query[''oi_order_item_id'']] = array_filter($order_query, function($k) {
$is_io = strpos($k, ''oi_'');
return $is_io !== FALSE && empty($is_io);
}, ARRAY_FILTER_USE_KEY);
if (!empty($order_query[''oim_meta_id'']))
$json[$order_query[''p_post_type'']][$order_query[''p_post_name'']][''woocommerce_order_itemmeta''][$order_query[''oim_meta_id'']] = array_filter($order_query, function($k) {
$is_oim = strpos($k, ''oim_'');
return $is_oim !== FALSE && empty($is_oim);
}, ARRAY_FILTER_USE_KEY);
}
}
// Downloading or viewing?
if (!empty($download))
{
// Outputs json in a textarea for you to copy and paste into a .json file for import...
if (!empty($json))
{
$filename = uniqid(''orders_'') . ''.json'';
$fp = fopen($filename, ''w'');
fwrite($fp, json_encode($json));
fclose($fp);
$size = filesize($root_dir . ''/'' . $filename);
header(''Content-Description: File Transfer'');
header(''Content-Type: application/octet-stream'');
header("Content-Disposition: attachment; filename=/"" . $filename . "/"");
header(''Content-Transfer-Encoding: binary'');
header(''Connection: Keep-Alive'');
header(''Expires: 0'');
header(''Cache-Control: must-revalidate, post-check=0, pre-check=0'');
header(''Pragma: public'');
header(''Content-Length: '' . $size);
readfile($root_dir . ''/'' . $filename);
}
}
else
{
// Outputs json in a textarea for you to copy and paste into a .json file for import...
if (!empty($json))
echo ''<textarea cols="200" rows="50">'', json_encode($json), ''</textarea>'';
}
El archivo JSON creado podría tener más de 500 MB, y posiblemente incluso hasta 1 Gig de datos. Por lo tanto, creo que PHP se está quedando sin memoria aquí, y debe procesarse poco a poco de alguna manera, ya sea en segundo plano o completamente, sin llegar al límite de memoria de PHP. Creo que el límite de memoria está establecido en 1024 MB, que es bastante alto, pero no lo suficientemente alto y tbh, por lo que estoy haciendo, no creo que podamos tener suficiente memoria para realizar la operación tal como está. Algo debe cambiar la forma en que proceso el json y / o lo descargo. Y no quiero crear varios archivos json, por favor solo 1 archivo JSON.
Creando el archivo
El problema con su código es que está tratando de colocar un conjunto de datos completo en la memoria, que eventualmente fallará tan pronto como su base de datos sea lo suficientemente grande. Para superar esto tienes que buscar los datos en lotes.
Vamos a generar la consulta varias veces, así que extraje su consulta en una función. Sin embargo, omití pasar los parámetros requeridos (o hacerlos globales si lo desea) por brevedad, por lo que debe hacer que esto funcione por sí mismo.
function generate_query($select, $limit = null, $offset = null) {
$query = ''SELECT '' . $select . ''
FROM '' . $wpdb->posts . '' AS p
INNER JOIN '' . $wpdb->postmeta . '' AS pm ON (pm.post_id = p.ID)
LEFT JOIN '' . $wpdb->prefix . ''woocommerce_order_items AS oi ON (oi.order_id = p.ID)
LEFT JOIN '' . $wpdb->prefix . ''woocommerce_order_itemmeta AS oim ON (oim.order_item_id = oi.order_item_id)
WHERE p.post_type = "shop_order"'' . (!empty($exclude_post_statuses) ? '' AND p.post_status NOT IN ("'' . implode(''","'', $exclude_post_statuses) . ''")'' : '''') . (!empty($start_date) ? '' AND post_date >= "'' . $start_date->format(''Y-m-d H:i:s'') . ''"'' : '''') . (!empty($end_date) ? '' AND post_date <= "'' . $end_date->format(''Y-m-d H:i:s'') . ''"'' : '''') . ''
ORDER BY p.ID ASC'';
if ($limit && $offset) {
$query .= '' LIMIT '' . $limit . '' OFFSET '' . $offset;
}
return $query;
}
Ahora obtendremos resultados de la base de datos en lotes, definimos el conteo de lotes que es el número de registros por iteración que cargaremos en la memoria. Más tarde, puede jugar con este valor para encontrar uno que sea lo suficientemente rápido y que no haga que PHP se bloquee. Tenga en cuenta que queremos reducir la cantidad de consultas de base de datos tanto como sea posible:
define(''BATCH_COUNT'', 500);
Antes de crear el bucle, necesitamos saber cuántas iteraciones (llamadas a la base de datos) realizaremos, por lo que necesitamos el recuento total de pedidos. Teniendo esto y el conteo de lotes, podemos calcular este valor fácilmente:
$orders_count = $wpdb->get_col(generate_query(''COUNT(*)''));
$iteration_count = ceil($orders_count / BATCH_COUNT);
Como resultado, nos gustaría tener una gran cadena JSON dentro del archivo de resultados. Ya que con cada iteración tendremos un JSON separado que contiene una matriz de objetos, simplemente quitaremos [
y ]
de cada lado de la cadena JSON y pondremos esa cadena en el archivo.
Código final:
define(''FILE'', ''dump.json'');
file_put_contents(FILE, ''['');
for ($i = 0; $i < $iteration_count; $i++) {
$offset = $i * BATCH_COUNT;
$result = $wpdb->get_results(
generate_query($select_data, BATCH_COUNT, $offset),
ARRAY_A
);
// do additional work here, add missing arrays etc.
// ...
// I assume here the $result is a valid array ready for
// creating JSON from it
// we append the result file with partial JSON
file_put_contents(FILE, trim(json_encode($result), ''[]''), FILE_APPEND);
}
file_put_contents(FILE, '']'', FILE_APPEND);
Enhorabuena, acaba de crear su primer volcado JSON enorme;) Debe ejecutar este script en la línea de comandos para que pueda durar todo lo que sea necesario, no es necesario modificar el límite de memoria a partir de ahora, porque nunca vamos a hacerlo. para llegar al límite con suerte.
Enviando el archivo
La transmisión de archivos de gran tamaño con PHP es fácil y ya se ha respondido TAN muchas veces. Sin embargo, personalmente no te recomiendo que hagas nada en PHP, ya que es un proceso de larga ejecución, ya sea en la línea de comandos o como un servidor de archivos.
Supongo que está utilizando Apache. Debería considerar usar SendFile y dejar que Apache haga el trabajo duro por usted. Este método es mucho más eficiente cuando se trata de grandes archivos. Este método es muy fácil, todo lo que necesita hacer es pasar la ruta al archivo en el encabezado:
header(''X-Sendfile: '' . $path_to_the_file);
Si usas Nginx, también hay soporte para XSendFile .
Este método no usa mucha memoria, no bloquea el proceso de PHP. El archivo no necesita ser accesible en el webroot también. Uso XSendFile todo el tiempo para mostrar videos 4K a usuarios autenticados.
Así que hay muchas cosas que necesitas para trabajar a este derecho. Voy a tener todo el punto que tengo en mente.
Terminación por el servidor web
Si usa Apache o Nginx / PHP-FPM, ambos, de manera predeterminada, tienen un tiempo de espera para la URL que se encuentra. Así que aunque hayas usado
ini_set(''memory_limit'', ''-1'');
ini_set(''max_execution_time'', ''-1'');
set_time_limit(0);
Para permitir que el script se ejecute por mucho tiempo, pero Apache, Nginx, PHP-FPM tienen un tiempo de espera que no permite que su script funcione. Así que necesitas arreglar esto para que funcione. Nunca mencionaste qué servidor usaste. Pero un NGINX + PHP-FPM resultará en 502 seguro con la configuración predeterminada.
Uso de memoria
Aunque hayas usado
ini_set(''memory_limit'', ''-1'');
Si sus necesidades de memoria aumentan, PHP puede comenzar a usar la paginación y su código podría ser lento.
PHP CLI o PHP Web?
No estoy seguro de cuál es la frecuencia de ejecución aquí, pero si es baja, puede considerar que el script de descarga de datos se ejecuta a través de una CLI de PHP en lugar de HTTP. Esto significaría que ejecutaría un script PHP directamente a través de la terminal para volcar el JSON en un archivo y luego utilizaría una URL para descargar el archivo directamente.
Usando X-Sendfile o X-Accel-Redirect
Si está utilizando apache puede enviar un encabezado.
header(''X-Sendfile: /data/generated.json'');
En caso de Nginx puedes enviar un
header(''X-Accel-Redirect: /data/generated.json'');
Esto lo haría solo en caso de que haya decidido ejecutar el script como web y no como un CLI. Cuando la generación de JSON ha finalizado, no desea que su secuencia de comandos lea el archivo y el servidor. Usted sólo quiere que el servidor web se encargue de ello.
Consulta no almacenada en lugar de consulta WPDB
https://core.trac.wordpress.org/browser/tags/4.9/src/wp-includes/wp-db.php#L2480
Por defecto, la consulta WPDB obtiene todos los datos en la memoria. Pero puede consultar la base de datos usted mismo mediante una consulta sin búfer, esto no inundará la memoria.
Example #1 Unbuffered query example: mysqli
<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
$uresult = $mysqli->query("SELECT Name FROM City", MYSQLI_USE_RESULT);
if ($uresult) {
while ($row = $uresult->fetch_assoc()) {
echo $row[''Name''] . PHP_EOL;
}
}
$uresult->close();
?>
Example #2 Unbuffered query example: pdo_mysql
<?php
$pdo = new PDO("mysql:host=localhost;dbname=world", ''my_user'', ''my_pass'');
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$uresult = $pdo->query("SELECT Name FROM City");
if ($uresult) {
while ($row = $uresult->fetch(PDO::FETCH_ASSOC)) {
echo $row[''Name''] . PHP_EOL;
}
}
?>
Example #3 Unbuffered query example: mysql
<?php
$conn = mysql_connect("localhost", "my_user", "my_pass");
$db = mysql_select_db("world");
$uresult = mysql_unbuffered_query("SELECT Name FROM City");
if ($uresult) {
while ($row = mysql_fetch_assoc($uresult)) {
echo $row[''Name''] . PHP_EOL;
}
}
?>
https://secure.php.net/manual/en/mysqlinfo.concepts.buffering.php
PD: Puede que haya algunos puntos más que me estoy perdiendo en este momento, actualizaría esto pronto
Creo que puede estar buscando Generators
http://php.net/manual/en/language.generators.overview.php https://scotch.io/tutorials/understanding-php-generators
En lugar de crear esa enorme matriz $json
, $order_query
cada $order_query
y realiza operaciones en cada iteración, lo que elimina la necesidad de almacenarla en la memoria.
Creo que puede haber un par de problemas. En primer lugar le sugiero que haga un poco de perfil.
// HUGE DATABASE DUMP HERE, needs to be converted to JSON, after getting all columns of all tables...
echo ''Start Time: ''. date("Y-m-d H:i:s");
echo '' Memory Usage: '' . (memory_get_usage()/1048576) . '' MB /n'';
$orders_query = $wpdb->get_results(''
SELECT '' . $select_data . ''
FROM '' . $wpdb->posts . '' AS p
INNER JOIN '' . $wpdb->postmeta . '' AS pm ON (pm.post_id = p.ID)
LEFT JOIN '' . $wpdb->prefix . ''woocommerce_order_items AS oi ON (oi.order_id = p.ID)
LEFT JOIN '' . $wpdb->prefix . ''woocommerce_order_itemmeta AS oim ON (oim.order_item_id = oi.order_item_id)
WHERE p.post_type = "shop_order"'' . (!empty($exclude_post_statuses) ? '' AND p.post_status NOT IN ("'' . implode(''","'', $exclude_post_statuses) . ''")'' : '''') . (!empty($start_date) ? '' AND post_date >= "'' . $start_date->format(''Y-m-d H:i:s'') . ''"'' : '''') . (!empty($end_date) ? '' AND post_date <= "'' . $end_date->format(''Y-m-d H:i:s'') . ''"'' : '''') . ''
ORDER BY p.ID ASC'', ARRAY_A);
echo ''End Time: ''. date("Y-m-d H:i:s");
echo '' Memory Usage: '' . (memory_get_usage()/1048576) . '' MB /n'';
die(''Finished'');
$json = array();
Lo anterior le ayudará a saber cuánta memoria está en uso, hasta este punto. Si falla antes de que se imprima ''Finalizado'', sabemos que no es un problema de json. Si el script funciona bien, primero podemos crear un archivo csv en lugar de json. Dado que está ejecutando una consulta de selección, (en este punto) no tiene que estar anidado el archivo json que necesita. Se puede lograr una estructura plana simplemente creando un archivo CSV.
$csvFile = uniqid(''orders'') . ''.csv'';
$fp = fopen($csvFile, ''w'');
if (!empty($orders_query))
{
$firstRow = true;
foreach($orders_query as $order_query)
{
if(true === $firstRow) {
$keys = array_keys($order_query);
fputcsv($fp, $order_query);
$firstRow = false;
}
fputcsv($fp, $order_query);
}
}
fclose($fp);
Si lo anterior funciona bien, al menos tienes un archivo csv con el que trabajar.
En este punto, no estoy seguro de cuán compleja es la estructura de datos anidada. Por ejemplo, cuántos valores distintos existen para ''p_post_type'' y ''p_post_name'' que está teniendo. Es posible que necesite analizar el archivo csv y crear varios archivos json para cada [''p_post_type''] [''p_post_name''] [''posts''], [''p_post_type''] [''p_post_name''] [''ppspost_type ''] ['' p_post_name ''] ['' woocommerce_order_items ''] y ['' p_post_type ''] ['' p_post_name ''] ['' woocommerce_order_itemmeta ''] .
Si el número de archivos es reducido, puede escribir un script para fusionarlos automáticamente o hacerlos manualmente. Si tiene demasiados elementos anidados, la cantidad de archivos json que pueden crearse puede ser mucho y puede ser difícil fusionarlos y puede que no sea una opción viable.
Si el número de archivos json es mucho, me gustaría saber cuál es el propósito de tener un archivo json tan enorme. Si la exportación es un problema, la importación también podría ser un problema, especialmente al ingerir en la memoria un archivo json tan enorme. Creo que si el propósito de crear el archivo json es importarlo de alguna forma, en algún momento en el futuro, creo que deberías ver la opción de tener un archivo csv, que se usa para filtrar lo que sea. requerido en ese punto del tiempo.
Espero que esto ayude.
MÁS ACTUALIZACIÓN
Me parece que $ wpdb-> get_results está usando mysqli_query / mysql_query (dependiendo de su configuración) para obtener los resultados. Ver documentos de consulta de wordpress . No es una forma eficiente de recuperar los datos de esta manera. Creo que podría estar fallando en este punto ( $ wpdb-> get_results ). Le sugiero que ejecute la consulta sin usar $ wpdb . Existe un concepto de consulta sin búfer cuando se requiere una gran recuperación de datos, lo que tiene un impacto muy bajo en la memoria. Más información se puede encontrar aquí mysql unbuffering .
Incluso si supera este punto, seguirá teniendo problemas de memoria, debido a la forma en que almacena todo en la variable $ json , que está consumiendo mucha memoria. $ json es una matriz y sería interesante saber cómo funciona la matriz PHP. Las matrices de PHP son dinámicas y no asignan memoria adicional cada vez que se agrega un nuevo elemento, ya que sería extremadamente lento. En cambio, aumenta el tamaño de la matriz a la potencia de dos, lo que significa que cada vez que se agota el límite, aumenta el límite de la matriz al doble de su límite actual y, en el proceso, trata de aumentar la memoria al doble del límite. Esto ha sido un problema menor con PHP 7, ya que han hecho algunos cambios importantes en el núcleo de php. Por lo tanto, si tiene datos de 2 GB que podrían ser necesarios para almacenar en $ json , la secuencia de comandos podría asignarse fácilmente entre 3 y 4 GB de memoria, dependiendo de cuándo llegue al límite. Puede encontrar más detalles aquí php array y Cómo funciona realmente la memoria PHP
Si considera la sobrecarga de $ orders_query, que es una matriz combinada con la sobrecarga de $ json , es bastante considerable debido a la forma en que funciona la matriz de PHP.
También puede intentar crear otra base de datos B. Entonces, mientras lee la base de datos A, comienza a escribir datos en la base de datos B. Al final, tiene la base de datos B con todos los datos con la potencia de MySQL. También puede insertar los mismos datos en un MongoDB que sería muy rápido y podría ayudarlo con el anidamiento json que está buscando. MongoDB está diseñado para funcionar de manera realmente eficiente con grandes conjuntos de datos.
JSON STREAMING SOLUTION
En primer lugar, me gustaría decir que la transmisión es un proceso secuencial / lineal. Como tal, no tiene memoria de lo que se agregó antes de este punto de tiempo o lo que se agregará después de este punto de tiempo. Funciona en trozos pequeños y esa es la razón por la que es tan eficiente en memoria. Entonces, cuando realmente escribes o lees, la responsabilidad recae en el script, que mantiene un orden específico, es decir, que estás escribiendo / leyendo tu propio json, ya que la transmisión solo comprende el texto y no tiene idea de lo que es json. No se molestará en escribir / leer una correcta.
He encontrado una biblioteca en github https://github.com/skolodyazhnyy/json-stream que te ayudaría a conseguir lo que deseas. He experimentado con el código y veo que funcionará para ti con algunos ajustes en tu código.
Voy a escribir un pseudocódigo para ti.
//order is important in this query as streaming would require to maintain a proper order.
$query1 = select distinct p_post_type from ...YOUR QUERY... order by p_post_type;
$result1 = based on $query1;
$filename = ''data.json'';
$fh = fopen($filename, "w");
$writer = new Writer($fh);
$writer->enter(Writer::TYPE_OBJECT);
foreach($result1 as $fields1) {
$posttype = $fields1[''p_post_type''];
$writer->enter($posttype, Writer::TYPE_ARRAY);
$query2 = select distinct p_post_name from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype order by p_post_type,p_post_name;
$result2 = based on $query2;
foreach($result2 as $fields2) {
$postname = $fields1[''p_post_name''];
$writer->enter($postname, Writer::TYPE_ARRAY);
$query3 = select ..YOUR COLUMNS.. from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype and p_post_name=$postname where p_ID is not null order by p_ID;
$result3 = based on $query3;
foreach($result2 as $field3) {
$writer->enter(''posts'', Writer::TYPE_ARRAY);
// write an array item
$writer->write(null, $field3);
}
$writer->leave();
$query4 = select ..YOUR COLUMNS.. from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype and p_post_name=$postname where pm_meta_id is not null order by pm_meta_id;
$result4 = based on $query4;
foreach($result4 as $field4) {
$writer->enter(''postmeta'', Writer::TYPE_ARRAY);
// write an array item
$writer->write(null, $field4);
}
$writer->leave();
$query5 = select ..YOUR COLUMNS.. from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype and p_post_name=$postname where oi_order_item_id is not null order by oi_order_item_id;
$result5 = based on $query5;
foreach($result5 as $field5) {
$writer->enter(''woocommerce_order_items'', Writer::TYPE_ARRAY);
// write an array item
$writer->write(null, $field5);
}
$writer->leave();
$query6 = select ..YOUR COLUMNS.. from ...YOUR QUERY... YOUR WHERE ... and p_post_type= $posttype and p_post_name=$postname where oim_meta_id is not null order by oim_meta_id;
$result6 = based on $query6;
foreach($result6 as $field6) {
$writer->enter(''woocommerce_order_itemmeta'', Writer::TYPE_ARRAY);
// write an array item
$writer->write(null, $field5);
}
$writer->leave();
}
$writer->leave();
fclose($fh);
Es posible que tenga que comenzar a limitar sus consultas a 10 puntos hasta que lo haga bien. Dado que el código anterior podría no funcionar como está. Debería poder leer el código de manera similar, ya que la misma biblioteca tiene una clase de Reader para ayudar. He probado tanto el lector como el escritor y parecen funcionar bien.
Primero, debe hacerse una pregunta: ¿Necesito escribir el volcado de base de datos yo mismo?
Si no es así, simplemente puede utilizar algún servicio que hará el trabajo por usted. Mysqldump-php debería ser capaz de hacer el trabajo.
Entonces puedes simplemente:
include_once(dirname(__FILE__) . ''/mysqldump-php-2.0.0/src/Ifsnop/Mysqldump/Mysqldump.php'');
$dump = new Ifsnop/Mysqldump/Mysqldump(''mysql:host=localhost;dbname=testdb'', ''username'', ''password'');
$dump->start(''storage/work/dump.sql'');
Esto debería crear un archivo .sql
. Sin embargo, querías el archivo json
. Aunque eso no debería ser un problema. Esta herramienta hará el resto del trabajo: http://www.csvjson.com/sql2json
También puede encontrar el código fuente de sql2json
en github: https://github.com/martindrapeau/csvjson-app
Su problema es que obtiene un gran conjunto de resultados de su consulta, que es pesado ya que tiene 3 uniones.
Podría definir un límite y usar la compensación para obtener los datos en trozos y luego generar su json en partes. El principal problema es obtener de alguna manera los datos json en la memoria y luego acceder a la salida en partes.
Para el último caché o una base de datos nosql podría ser utilizado. Mi solución utilizará caché y en particular memcache:
class Cache {
private $cache;
public function __construct($cache)
{
$this->cache = $cache;
}
public function addPostName($postName)
{
$this->addKeyToJsonObject(''postNames'', $postName);
}
public function addKeyToJsonObject($rootName, $key)
{
$childNames = $this->cache->get($rootName);
if($childNames === false) {
$this->cache->set($rootName, [$key]);
}
else {
$childNamesList = $childNames;
// not found
if(array_search($key, $childNamesList) === false) {
$childNamesList[] = $key;
$this->cache->set($rootName, $childNamesList);
}
}
}
public function getPostNames()
{
return $this->cache->get(''postNames'');
}
public function set($key, $value) {
$this->cache->add($key, $value);
}
public function addPostIdsByNameAndType($postName, $type, $pid)
{
$this->addKeyToJsonObject($postName . ''-'' . $type, $pid);
}
public function getPostIdsByNameAndType($postName, $type)
{
return $this->cache->get($postName . ''-'' . $type);
}
public function addPostValueByNameTypeAndId($postName, $type, $pid, $value)
{
$this->cache->set($postName . ''-'' . $type . ''-'' . $pid, $value);
}
public function getPostValueByNameTypeAndId($postName, $type, $pid)
{
return $this->cache->get($postName . ''-'' . $type . ''-'' . $pid);
}
}
y entonces:
$memcache = new Memcache();
$memcache->connect(''127.0.0.1'', 11211) or die ("Could not connect");
$memcache->flush();
$cache = new Cache($memcache);
header(''Content-disposition: attachment; filename=file.json'');
header(''Content-type: application/json'');
echo ''{"shop_order":{'';
function getResultSet($wpdb, $offset = 1) {
return $wpdb->get_results(''
SELECT '' . $select_data . ''
FROM '' . $wpdb->posts . '' AS p
INNER JOIN '' . $wpdb->postmeta . '' AS pm ON (pm.post_id = p.ID)
LEFT JOIN '' . $wpdb->prefix . ''woocommerce_order_items AS oi ON (oi.order_id = p.ID)
LEFT JOIN '' . $wpdb->prefix . ''woocommerce_order_itemmeta AS oim ON (oim.order_item_id = oi.order_item_id)
WHERE p.post_type = "shop_order"'' . (!empty($exclude_post_statuses) ? '' AND p.post_status NOT IN ("'' . implode(''","'', $exclude_post_statuses) . ''")'' : '''') . (!empty($start_date) ? '' AND post_date >= "'' . $start_date->format(''Y-m-d H:i:s'') . ''"'' : '''') . (!empty($end_date) ? '' AND post_date <= "'' . $end_date->format(''Y-m-d H:i:s'') . ''"'' : '''') . ''
ORDER BY p.ID ASC LIMIT 1000 OFFSET '' . $offset, ARRAY_A);
}
$offset = 1;
$orders_query = getResultSet($wpdb, 1);
while(!empty($orders_query)) {
cacheRowData($cache, $orders_query);
$offset = $offset + 1000;
$orders_query = getResultSet($wpdb, $offset);
}
outputRowData($cache);
function cacheRowData($cache, $orders_query)
{
foreach($orders_query as $order_query) {
if(empty($order_query)) { continue; }
$cache->addPostName($order_query[''p_post_name'']);
// posts
if (!empty($order_query[''p_ID''])) {
$cache->addPostIdsByNameAndType($order_query[''p_post_name''],''posts'', $order_query[''p_ID'']);
$value = array_filter($order_query, function($k) {
$is_p = strpos($k, ''p_'');
return $is_p !== FALSE && empty($is_p);
}, ARRAY_FILTER_USE_KEY);
$cache->addPostValueByNameTypeAndId($order_query[''p_post_name''],''posts'', $order_query[''p_ID''], $value);
}
if (!empty($order_query[''pm_meta_id''])) {
$cache->addPostIdsByNameAndType($order_query[''p_post_name''],''postmeta'', $order_query[''pm_meta_id'']);
$value = array_filter($order_query, function($k) {
$is_pm = strpos($k, ''pm_'');
return $is_pm !== FALSE && empty($is_pm);
}, ARRAY_FILTER_USE_KEY);
$cache->addPostValueByNameTypeAndId($order_query[''p_post_name''],''postmeta'', $order_query[''pm_meta_id''], $value);
}
// here do the same for "woocommerce_order_items" and "woocommerce_order_itemmeta"
}
}
function outputRowData($cache)
{
$cachedPostNames = $cache->getPostNames();
$firstRow = true;
foreach($cachedPostNames as $postName) {
if(empty($postName)) { continue; }
if($firstRow === false) {
echo '','';
}
$firstRow = false;
echo ''"'' . $postName . ''":{'';
$postIds = $cache->getPostIdsByNameAndType($postName, ''posts'');
if(!$postIds) {
$postIds = [];
}
// generate posts
$postValues = [];
foreach ($postIds as $postId) {
$postValues[$postId] = $cache->getPostValueByNameTypeAndId($postName, ''posts'', $postId);
}
$postMetaIds = $cache->getPostIdsByNameAndType($postName, ''postmeta'');
if(!$postMetaIds) {
$postMetaIds = [];
}
$postMetaValues = [];
foreach ($postMetaIds as $postMetaId) {
$postMetaValues[$postMetaId] = $cache->getPostValueByNameTypeAndId($postName, ''postmeta'', $postMetaId);
}
// here do the same for "woocommerce_order_items" and "woocommerce_order_itemmeta"
echo ''"posts":'' . json_encode($postValues) . '','';
echo ''"postmeta":'' . json_encode($postMetaValues);
echo ''}'';
ob_flush();
flush(); // flush the output to start the download
}
}
echo ''}}'';