Generating two Woocommerce Order CSV files based off url query












1












$begingroup$


I wrote this code about one year ago. I'm looking to refactor it to be solid SOLID and secure, as this is being generated off a custom endpoint. The script is to read parameters from a query and generate custom CSV exports based off categories, tags, and order completion date. These reports can be weekly, monthly, yearly, or for all time. An email or comma separated list of emails can be passed as recipients.



The original flow I had was like this:




  • Enable errors

  • Check if correct query string set

  • If not, exit, else continue

  • Load Wordpress/Woocommerce

  • Sanitize GET variables and set interpolate things like date ranges from string values.

  • Create a collection array and loop through all orders, excluding ones that do not meet search params.

  • Sort by category

  • If no orders, exit

  • Else, write to CSV, email the CSV, and unlink files


A few questions
- Should I convert this to OOP?
- What are some design patterns I can you to make the logic more intuitive?



P.S. This is the cron url




(wget -O- "https://site.com/wp-content/themes/theme/scripts/auto-export.php?export=GET&filter_units=all&period=month&filter_categories=product&filter_skus=all&email_list=dan@email.com" --no-check-certificate >> log.txt)







// Set INI values for testing
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// Exit if export type not set (correctly)
if ( !isset($_GET[ 'export' ]) || $_GET['export'] !== 'GET')
exit;

// load Wordpress
define('WP_USE_THEMES', false);
require($_SERVER['DOCUMENT_ROOT'] . "/wp-load.php");

// Sanititze GET data
$search_data = sanitize_get();

// Get orders sorted
$orders = get_product_purchases( $search_data );
$orders = sort_orders_by( 'category', 'DESC', $orders );


// Exit if no orders
if (count($orders) == 0 ) {
echo "There were no orders given the parameters";
exit;
}

write_to_csv_get([
'search_data' => $search_data,
'orders' => $orders,
]);
echo "Export Successful";
exit;


/**
* get_product_purchases
* Return array of orders
*/
function get_product_purchases( $search_data ) {
// Destructure search data
$cat_data = $search_data['categories'];
$sku_data = $search_data['skus'];
$period = $search_data['period'];
$unit_data = $search_data['units'];

// See set_order_args() doc-block for more details
$args = set_order_args( $search_data );
$WC_Orders = wc_get_orders( $args );
$results = ;

foreach ( $WC_Orders as $WC_Order ) {
if ( empty($WC_Order)) continue;

$order_data = $WC_Order->get_data();

$my_order[ 'order_id' ] = $WC_Order->get_id();
$my_order['order_date'] = $order_data['date_created']->date('Y-m-d');
$my_order['shipping_first_name'] = $order_data['shipping']['first_name'];
$my_order['shipping_last_name'] = $order_data['shipping']['last_name'];
$my_order['shipping_address_1'] = $order_data['shipping']['address_1'];
$my_order['shipping_address_2'] = $order_data['shipping']['address_2'];
$my_order['shipping_city'] = $order_data['shipping']['city'];
$my_order['shipping_state'] = $order_data['shipping']['state'];
$my_order['shipping_postcode'] = $order_data['shipping']['postcode'];

foreach ( $WC_Order->get_items() as $item_key => $item_values ) {
$my_order_copy = $my_order;

## Using WC_Order_Item methods ##
$my_item['id'] = $item_values->get_id(); // Item ID is directly accessible from the $item_key in the foreach loop or

## Access Order Items data properties (in an array of values) ##
$item_data = $item_values->get_data();

$product_id = $item_data['product_id'];
$product['product_name'] = $item_data['name'];
$product['quantity'] = $item_data['quantity'];

// Get data from The WC_product object using methods (examples)
$WC_Product = $item_values->get_product(); // the WC_Product object
if ( empty($WC_Product)) {
continue;
};

$item['item_name'] = preg_replace( '/[x00-x1Fx7F-xFF]/', '', $item_values->get_name() ) ; // Name of the product
$product['sku'] = $WC_Product->get_sku();
$product['price'] = ( $WC_Product->get_price() )
? sprintf( '%.2f', $WC_Product->get_price() )
: '';

// Tags = business units
$product['tag'] = strtolower( get_tag_name( $product_id ) );

// Term names are categories
$terms = get_the_terms( $product_id, 'product_cat' );
$cats = ;

foreach( $terms as $term ) {
array_push( $cats, strtolower( $term->name ));
}

$product['category'] = $cats[0];

/*
Perform condition check on whether or not to push to export

If a filter list is set, check the product property against
the filter list and see if it matches. If it does not,
exclude it from the CSV
*/
$push = check_against_list( $cat_data, $product, 'category' );
if ( $push == 0 ) continue;

$push = check_against_list( $unit_data, $product, 'tag' );
if ( $push == 0 ) continue;

$push = check_against_list( $sku_data, $product, 'sku' );
if ( $push == 0 ) continue;

if ( $push == 1 ) {
$row = array_merge( $my_order_copy, $product );
array_push( $results, $row );
}
}
}

return $results;
}

/**
* @param array $params [search_data & orders]
* @return [void]
*/
function write_to_csv_get( $params ) {
// Destructure params
$period = $params['search_data']['period'];
$orders = $params['orders'];

$date_summary = ( $period['all'] == 0 )
? "from {$period['range']['start_date']} to {$period['range']['end_date']}"
: "Since Beginning";

$breakdown_filename = "company-{$period['type']}-order-details.csv";
$path = "/tmp";
$fp = fopen("$path/$breakdown_filename", 'w');

if ( fopen( "$path/$breakdown_filename", "w+" ) ) {
$totals = ; // Sum of quantities
$placed_header = false;

foreach ( $orders as $index => $row) {
$totals = sum( $row, $totals );

// Old code for writing all details of orders
if (! $placed_header)
{
fputcsv( $fp, array_keys( $row ) );
$placed_header = true;
}

// If row set, write it to CSV
if ( count( $row ) != 0 )
fputcsv( $fp, $row );
}
} else {
fclose( $fp );
unlink("$path/$breakdown_filename");
error_log( "Auto-report: CSV Breakdown - failed to open CSV" );
}

notify( $breakdown_filename, $path, 'Download Order Details Export', 'detailed', $params );

fclose($fp);
unlink("$path/$filename");

$totals_filename = "company-{$period['type']}-orders-totals.csv";
$fp = fopen("$path/$totals_filename", 'w');

if ( fopen( "$path/$totals_filename", "w+" ) ) {
// Send totals email
fputcsv( $fp, [ "Orders - {$date_summary}"] );
fputcsv( $fp, [ 'SKU', 'Name', 'Totals' ] );

// Output two column totals ( name | total )
foreach( $totals as $name => $details ) {
$num = $details[0];
$sku = $details[1];
/*
Excel doesn't like some of the ASCII characters, use 7 bit ASCII
https://stackoverflow.com/questions/1176904/php-how-to-remove-all-non-printable-characters-in-a-string
*/
fputcsv( $fp, [ $sku, $name, $num ]);
}

} else {
fclose( $fp );
unlink("$path/$totals_filename");
error_log( "Auto-report: Totals CSV - Failed to open CSV" );
}

// Send break down CSV
notify( $totals_filename, $path, 'Download Totals Export', 'totals for', $params );

fclose($fp);
unlink("$path/$totals_filename");

return;
}

function sort_orders_by_date( $results, $order = 'DESC' ) {
foreach ($results as $key => $part) {
$sort[$key] = strtotime($part['order_date']);
}

( $order == 'DESC' )
? array_multisort( $sort, SORT_DESC, $results )
: array_multisort( $sort, SORT_ASC, $results );

return $results;
}

function sort_orders_by( $field, $order = 'DESC', $results ) {
if ( count( $results ) > 0 )
{
foreach ($results as $key => $part) {
$sort[$key] = $part[ $field ];
}

( $order == 'DESC' )
? array_multisort( $sort, SORT_DESC, $results )
: array_multisort( $sort, SORT_ASC, $results );
}

return $results;
}

function sanitize_get() {
/*---------------------------------------------
Filter / Sanitize Dates
--------------------------------------------- */
if ( isset( $_GET['period']))
{
$period['all'] = 0;

switch( $_GET['period'])
{
case( 'week' ):
$period['type'] = 'weekly';
$previous_week = strtotime("-1 week +1 day");

$start_week = strtotime( "last sunday", $previous_week );
$end_week = strtotime( "next saturday", $start_week );

$period['range']['start_date'] = filter_var( date( "Y-m-d", $start_week ), FILTER_SANITIZE_STRING );
$period['range']['end_date'] = filter_var( date( "Y-m-d", $end_week), FILTER_SANITIZE_STRING );
break;
case( 'month' ):
$period['type'] = 'monthly';

$period['range']['start_date'] = date('Y-m-d',strtotime('first day of last month'));
$period['range']['end_date'] = date('Y-m-d',strtotime('last day of last month'));
break;
case( 'year'):
$year = date( 'Y' ) - 1;

$period['type'] = 'yearly';
$period['range']['start_date'] = "{$year}-01-01";
$period['range']['end_date'] = "{$year}-12-31";
break;
case( 'forever' ):
$period['type'] = 'full';
$period[ 'all' ] = 1;
break;
}
}
else
{
error_log( 'Autoexport: no period given to auto export');
exit;
}

/*---------------------------------------------
Filter / Sanitize Categories
---------------------------------------------*/
if ( isset( $_GET['filter_categories']))
{
$categories = ;
$categories['all'] = 0;

if ( $_GET['filter_categories'] == 'all' )
{
$categories['all'] = 1;
}
else
{
$categories['list'] = explode( ',', $_GET['filter_categories']);

foreach( $categories['list'] as $i => $cat )
{
$categories['list'][$i] = str_replace( '-', ' ', strtolower( filter_var( $cat, FILTER_SANITIZE_STRING ) ) );
}


if ( $categories['all'] === 0 && count( $categories['list'] ) < 1 )
error_log( 'Empty value for categories was passed' );
}
}
else
{
error_log( 'Autoexport: Empty value for categories was passed' );
exit;
}

/*---------------------------------------------
Filter / Sanitize Business Units
---------------------------------------------*/
if ( isset( $_GET['filter_units']) )
{
if ( $_GET['filter_units'] == 'all' )
{
$units['all'] = 1;
}
else
{
$units = ;
$units['list'] = explode( ',', $_GET['filter_units']);

foreach( $units['list'] as $i => $unit )
{
$units['list'][$i] = str_replace( '-', ' ', strtolower( filter_var( $unit, FILTER_SANITIZE_STRING ) ) );
}

if ( $units['all'] == 0 && count( $units['list'] ) < 1 )
error_log( 'Empty value for business units was passed' );
}
}
else
{
error_log( 'Autoexport: Empty value for business units was passed' );
exit;
}

/*---------------------------------------------
Filter / Sanitize Skus
---------------------------------------------*/
if ( isset( $_GET['filter_skus']) )
{
$skus = ;

if ( $_GET['filter_skus'] == 'all' )
{
$skus['all'] = 1;
}
else
{
$skus['list'] = explode( ',', $_GET['filter_skus'] );

// Filter unit string and push it to the search list
foreach( $skus['list'] as $i => $sku )
{
$skus['list'][$i] = str_replace( '-', ' ', filter_var( $sku, FILTER_SANITIZE_STRING ) );
}

if ( $skus['all'] === 0 && count( $skus['list'] ) < 1 )
error_log( 'Autoexport: Empty value for skus was passed' );
}
}
else
{
error_log( 'Autoexport: Empty value for skus was passed' );
exit;
}

/*---------------------------------------------
Filter / Sanitize Emails
--------------------------------------------- */
// Optionally pass in string of double pipe separated emails to send to
if ( isset( $_GET['email_list']))
{
$email_list = explode( ',', $_GET['email_list'] );

foreach ( $email_list as $i => $email )
{
$email_list[ $i ] = filter_var( $email, FILTER_SANITIZE_EMAIL );
}

$email_list = implode( ',', $email_list );
}
else
{
error_log( 'Autoexport: email given to auto export');
exit;
}

$search_data = [
'validated' => 1,
'period' => $period,
'categories' => $categories,
'units' => $units,
'skus' => $skus,
'email_list' => $email_list,
];

if ( $search_data['validated'] != 1 )
die( 'Sorry, the data you entered is invalid' );

return $search_data;
}

/*
More info on wc_order arguments
https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query
*/
function set_order_args( $search_data ) {
$period = $search_data['period'];

$args = [
// 'status' => [ 'completed' ],
'status' => 'completed',
'order_by' => 'date',
'order' => 'DESC',
];

if ( isset( $period['range'] ) ) {
if ( isset( $period['range']['start_date']) && isset( $period['range']['end_date'])) {
$args['date_before'] = $period['range']['end_date'];
$args['date_after'] = $period['range']['start_date'];
// Old way WC worked
// WC date range format (2018-08-01...2018-08-31)
// $range = implode( '...', $period['range'] );
// $args[ 'date_created'] = $range;
}
}

return $args;
}

function get_term_names() {
$tags = get_terms( array('product_tag'), "hide_empty=1" );
$tag_list = ;

if ( ! empty( $tags ) && ! is_wp_error( $tags ) ){
foreach ( $tags as $tag ) {
$tag_list = $tag->name;
}
}

return $tag_list;
}

/*
Read more here:
https://stackoverflow.com/questions/21858431/woocommerce-get-a-list-for-all-sku-product
*/
function get_sku_list() {
$skus = ;
$args = array( 'post_type' => 'product', 'posts_per_page' => -1 );

query_posts( $args );

if( have_posts() ):
while ( have_posts() ) : the_post();
global $post;
$product = wc_get_product($post->ID);
$sku = $product->get_sku();

if ( $sku != '')
{
array_push( $skus, $sku);
}
endwhile;
endif;

return $skus;
}

function get_tag_name( $product_id ) {
$tags = wp_get_post_terms( $product_id, 'product_tag' );

if ( count($tags) > 0 ) {
foreach ( $tags as $tag ) {
return $tag->name;
}
} else {
return 'blank';
}
}

function check_against_list( $data, $product, $property ) {
$push = (int) true;

if ( $data['all'] == 0 ) {
if ( is_array( $data['list'])) {
if ( ! in_array( htmlspecialchars_decode( $product['tag']), $data['list'] ) ) {
$push = 0;
}
} else {
$push = 0;
}
}

return $push;
}

function sum($row, $totals) {
(empty( $totals[$row['product_name' ]] ))
? $totals[$row['product_name' ]] = [(int) 1, $row['sku']]
: $totals[$row['product_name' ]][0]++;

return $totals;
}

function check_in_range( $start_date, $end_date, $order_date ) {
// Convert to timestamp
$start_ts = strtotime( $start_date );
$end_ts = strtotime( $end_date );
$user_ts = strtotime( $order_date );

// Check that user date is between start & end
return (( $user_ts >= $start_ts) && ( $user_ts <= $end_ts ));
}

// EMAIL
function mail_attachment($filename, $path, $mailto,
$from_mail, $from_name, $replyto,
$subject, $message)
{
$file = $path."/".$filename;
$file_size = filesize($file);
$handle = fopen($file, "r");
$content = fread($handle, $file_size);
fclose($handle);
$content = chunk_split(base64_encode($content));
$uid = md5(uniqid(time()));
$header = "From: ".$from_name." <".$from_mail.">rn";
$header .= "Reply-To: ".$replyto."rn";
$header .= "MIME-Version: 1.0rn";
$header .= "Content-Type: multipart/mixed; boundary="".$uid.""rn";

$my_message = $message;

$message = "";
$message .= "This is a multi-part message in MIME format.rn";
$message .= "--".$uid."rn";
$message .= "Content-type:text/plain; charset=iso-8859-1rn";
$message .= "Content-Transfer-Encoding: 7bitrnrn";
$message .= $my_message."rnrn";
$message .= "--".$uid."rn";
$message .= "Content-Type: application/octet-stream; name="".$filename.""rn"; // use different content types here
$message .= "Content-Transfer-Encoding: base64rn";
$message .= "Content-Disposition: attachment; filename="".$filename.""rnrn";
$message .= $content."rnrn";
$message .= "--".$uid."--";

mail($mailto, $subject, $message, $header);
}

function notify( $filename, $path,
$subject = 'Export',
$report_type, $params)
{
$subject = $params['period']['type'] . ' Export';
$recipients = $params['search_data']['email_list'];

$sender_address = "web@thecompanydomain.com";
$sender_name = "Company Web site";
$reply_to = "web@thecompanydomain.com";
$subject = "Brand $subject";
$message = "Attached is the list of $report_type orders.";


mail_attachment( $filename, $path, $recipients,
$sender_address, $sender_name,
$reply_to, $subject, $message);

return 0;
}

if( !function_exists('pre')){
function pre($var = '') {
echo "<pre>";
print_r( $var );
echo "</pre><br>n";
}
}









share|improve this question









New contributor




Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$

















    1












    $begingroup$


    I wrote this code about one year ago. I'm looking to refactor it to be solid SOLID and secure, as this is being generated off a custom endpoint. The script is to read parameters from a query and generate custom CSV exports based off categories, tags, and order completion date. These reports can be weekly, monthly, yearly, or for all time. An email or comma separated list of emails can be passed as recipients.



    The original flow I had was like this:




    • Enable errors

    • Check if correct query string set

    • If not, exit, else continue

    • Load Wordpress/Woocommerce

    • Sanitize GET variables and set interpolate things like date ranges from string values.

    • Create a collection array and loop through all orders, excluding ones that do not meet search params.

    • Sort by category

    • If no orders, exit

    • Else, write to CSV, email the CSV, and unlink files


    A few questions
    - Should I convert this to OOP?
    - What are some design patterns I can you to make the logic more intuitive?



    P.S. This is the cron url




    (wget -O- "https://site.com/wp-content/themes/theme/scripts/auto-export.php?export=GET&filter_units=all&period=month&filter_categories=product&filter_skus=all&email_list=dan@email.com" --no-check-certificate >> log.txt)







    // Set INI values for testing
    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);

    // Exit if export type not set (correctly)
    if ( !isset($_GET[ 'export' ]) || $_GET['export'] !== 'GET')
    exit;

    // load Wordpress
    define('WP_USE_THEMES', false);
    require($_SERVER['DOCUMENT_ROOT'] . "/wp-load.php");

    // Sanititze GET data
    $search_data = sanitize_get();

    // Get orders sorted
    $orders = get_product_purchases( $search_data );
    $orders = sort_orders_by( 'category', 'DESC', $orders );


    // Exit if no orders
    if (count($orders) == 0 ) {
    echo "There were no orders given the parameters";
    exit;
    }

    write_to_csv_get([
    'search_data' => $search_data,
    'orders' => $orders,
    ]);
    echo "Export Successful";
    exit;


    /**
    * get_product_purchases
    * Return array of orders
    */
    function get_product_purchases( $search_data ) {
    // Destructure search data
    $cat_data = $search_data['categories'];
    $sku_data = $search_data['skus'];
    $period = $search_data['period'];
    $unit_data = $search_data['units'];

    // See set_order_args() doc-block for more details
    $args = set_order_args( $search_data );
    $WC_Orders = wc_get_orders( $args );
    $results = ;

    foreach ( $WC_Orders as $WC_Order ) {
    if ( empty($WC_Order)) continue;

    $order_data = $WC_Order->get_data();

    $my_order[ 'order_id' ] = $WC_Order->get_id();
    $my_order['order_date'] = $order_data['date_created']->date('Y-m-d');
    $my_order['shipping_first_name'] = $order_data['shipping']['first_name'];
    $my_order['shipping_last_name'] = $order_data['shipping']['last_name'];
    $my_order['shipping_address_1'] = $order_data['shipping']['address_1'];
    $my_order['shipping_address_2'] = $order_data['shipping']['address_2'];
    $my_order['shipping_city'] = $order_data['shipping']['city'];
    $my_order['shipping_state'] = $order_data['shipping']['state'];
    $my_order['shipping_postcode'] = $order_data['shipping']['postcode'];

    foreach ( $WC_Order->get_items() as $item_key => $item_values ) {
    $my_order_copy = $my_order;

    ## Using WC_Order_Item methods ##
    $my_item['id'] = $item_values->get_id(); // Item ID is directly accessible from the $item_key in the foreach loop or

    ## Access Order Items data properties (in an array of values) ##
    $item_data = $item_values->get_data();

    $product_id = $item_data['product_id'];
    $product['product_name'] = $item_data['name'];
    $product['quantity'] = $item_data['quantity'];

    // Get data from The WC_product object using methods (examples)
    $WC_Product = $item_values->get_product(); // the WC_Product object
    if ( empty($WC_Product)) {
    continue;
    };

    $item['item_name'] = preg_replace( '/[x00-x1Fx7F-xFF]/', '', $item_values->get_name() ) ; // Name of the product
    $product['sku'] = $WC_Product->get_sku();
    $product['price'] = ( $WC_Product->get_price() )
    ? sprintf( '%.2f', $WC_Product->get_price() )
    : '';

    // Tags = business units
    $product['tag'] = strtolower( get_tag_name( $product_id ) );

    // Term names are categories
    $terms = get_the_terms( $product_id, 'product_cat' );
    $cats = ;

    foreach( $terms as $term ) {
    array_push( $cats, strtolower( $term->name ));
    }

    $product['category'] = $cats[0];

    /*
    Perform condition check on whether or not to push to export

    If a filter list is set, check the product property against
    the filter list and see if it matches. If it does not,
    exclude it from the CSV
    */
    $push = check_against_list( $cat_data, $product, 'category' );
    if ( $push == 0 ) continue;

    $push = check_against_list( $unit_data, $product, 'tag' );
    if ( $push == 0 ) continue;

    $push = check_against_list( $sku_data, $product, 'sku' );
    if ( $push == 0 ) continue;

    if ( $push == 1 ) {
    $row = array_merge( $my_order_copy, $product );
    array_push( $results, $row );
    }
    }
    }

    return $results;
    }

    /**
    * @param array $params [search_data & orders]
    * @return [void]
    */
    function write_to_csv_get( $params ) {
    // Destructure params
    $period = $params['search_data']['period'];
    $orders = $params['orders'];

    $date_summary = ( $period['all'] == 0 )
    ? "from {$period['range']['start_date']} to {$period['range']['end_date']}"
    : "Since Beginning";

    $breakdown_filename = "company-{$period['type']}-order-details.csv";
    $path = "/tmp";
    $fp = fopen("$path/$breakdown_filename", 'w');

    if ( fopen( "$path/$breakdown_filename", "w+" ) ) {
    $totals = ; // Sum of quantities
    $placed_header = false;

    foreach ( $orders as $index => $row) {
    $totals = sum( $row, $totals );

    // Old code for writing all details of orders
    if (! $placed_header)
    {
    fputcsv( $fp, array_keys( $row ) );
    $placed_header = true;
    }

    // If row set, write it to CSV
    if ( count( $row ) != 0 )
    fputcsv( $fp, $row );
    }
    } else {
    fclose( $fp );
    unlink("$path/$breakdown_filename");
    error_log( "Auto-report: CSV Breakdown - failed to open CSV" );
    }

    notify( $breakdown_filename, $path, 'Download Order Details Export', 'detailed', $params );

    fclose($fp);
    unlink("$path/$filename");

    $totals_filename = "company-{$period['type']}-orders-totals.csv";
    $fp = fopen("$path/$totals_filename", 'w');

    if ( fopen( "$path/$totals_filename", "w+" ) ) {
    // Send totals email
    fputcsv( $fp, [ "Orders - {$date_summary}"] );
    fputcsv( $fp, [ 'SKU', 'Name', 'Totals' ] );

    // Output two column totals ( name | total )
    foreach( $totals as $name => $details ) {
    $num = $details[0];
    $sku = $details[1];
    /*
    Excel doesn't like some of the ASCII characters, use 7 bit ASCII
    https://stackoverflow.com/questions/1176904/php-how-to-remove-all-non-printable-characters-in-a-string
    */
    fputcsv( $fp, [ $sku, $name, $num ]);
    }

    } else {
    fclose( $fp );
    unlink("$path/$totals_filename");
    error_log( "Auto-report: Totals CSV - Failed to open CSV" );
    }

    // Send break down CSV
    notify( $totals_filename, $path, 'Download Totals Export', 'totals for', $params );

    fclose($fp);
    unlink("$path/$totals_filename");

    return;
    }

    function sort_orders_by_date( $results, $order = 'DESC' ) {
    foreach ($results as $key => $part) {
    $sort[$key] = strtotime($part['order_date']);
    }

    ( $order == 'DESC' )
    ? array_multisort( $sort, SORT_DESC, $results )
    : array_multisort( $sort, SORT_ASC, $results );

    return $results;
    }

    function sort_orders_by( $field, $order = 'DESC', $results ) {
    if ( count( $results ) > 0 )
    {
    foreach ($results as $key => $part) {
    $sort[$key] = $part[ $field ];
    }

    ( $order == 'DESC' )
    ? array_multisort( $sort, SORT_DESC, $results )
    : array_multisort( $sort, SORT_ASC, $results );
    }

    return $results;
    }

    function sanitize_get() {
    /*---------------------------------------------
    Filter / Sanitize Dates
    --------------------------------------------- */
    if ( isset( $_GET['period']))
    {
    $period['all'] = 0;

    switch( $_GET['period'])
    {
    case( 'week' ):
    $period['type'] = 'weekly';
    $previous_week = strtotime("-1 week +1 day");

    $start_week = strtotime( "last sunday", $previous_week );
    $end_week = strtotime( "next saturday", $start_week );

    $period['range']['start_date'] = filter_var( date( "Y-m-d", $start_week ), FILTER_SANITIZE_STRING );
    $period['range']['end_date'] = filter_var( date( "Y-m-d", $end_week), FILTER_SANITIZE_STRING );
    break;
    case( 'month' ):
    $period['type'] = 'monthly';

    $period['range']['start_date'] = date('Y-m-d',strtotime('first day of last month'));
    $period['range']['end_date'] = date('Y-m-d',strtotime('last day of last month'));
    break;
    case( 'year'):
    $year = date( 'Y' ) - 1;

    $period['type'] = 'yearly';
    $period['range']['start_date'] = "{$year}-01-01";
    $period['range']['end_date'] = "{$year}-12-31";
    break;
    case( 'forever' ):
    $period['type'] = 'full';
    $period[ 'all' ] = 1;
    break;
    }
    }
    else
    {
    error_log( 'Autoexport: no period given to auto export');
    exit;
    }

    /*---------------------------------------------
    Filter / Sanitize Categories
    ---------------------------------------------*/
    if ( isset( $_GET['filter_categories']))
    {
    $categories = ;
    $categories['all'] = 0;

    if ( $_GET['filter_categories'] == 'all' )
    {
    $categories['all'] = 1;
    }
    else
    {
    $categories['list'] = explode( ',', $_GET['filter_categories']);

    foreach( $categories['list'] as $i => $cat )
    {
    $categories['list'][$i] = str_replace( '-', ' ', strtolower( filter_var( $cat, FILTER_SANITIZE_STRING ) ) );
    }


    if ( $categories['all'] === 0 && count( $categories['list'] ) < 1 )
    error_log( 'Empty value for categories was passed' );
    }
    }
    else
    {
    error_log( 'Autoexport: Empty value for categories was passed' );
    exit;
    }

    /*---------------------------------------------
    Filter / Sanitize Business Units
    ---------------------------------------------*/
    if ( isset( $_GET['filter_units']) )
    {
    if ( $_GET['filter_units'] == 'all' )
    {
    $units['all'] = 1;
    }
    else
    {
    $units = ;
    $units['list'] = explode( ',', $_GET['filter_units']);

    foreach( $units['list'] as $i => $unit )
    {
    $units['list'][$i] = str_replace( '-', ' ', strtolower( filter_var( $unit, FILTER_SANITIZE_STRING ) ) );
    }

    if ( $units['all'] == 0 && count( $units['list'] ) < 1 )
    error_log( 'Empty value for business units was passed' );
    }
    }
    else
    {
    error_log( 'Autoexport: Empty value for business units was passed' );
    exit;
    }

    /*---------------------------------------------
    Filter / Sanitize Skus
    ---------------------------------------------*/
    if ( isset( $_GET['filter_skus']) )
    {
    $skus = ;

    if ( $_GET['filter_skus'] == 'all' )
    {
    $skus['all'] = 1;
    }
    else
    {
    $skus['list'] = explode( ',', $_GET['filter_skus'] );

    // Filter unit string and push it to the search list
    foreach( $skus['list'] as $i => $sku )
    {
    $skus['list'][$i] = str_replace( '-', ' ', filter_var( $sku, FILTER_SANITIZE_STRING ) );
    }

    if ( $skus['all'] === 0 && count( $skus['list'] ) < 1 )
    error_log( 'Autoexport: Empty value for skus was passed' );
    }
    }
    else
    {
    error_log( 'Autoexport: Empty value for skus was passed' );
    exit;
    }

    /*---------------------------------------------
    Filter / Sanitize Emails
    --------------------------------------------- */
    // Optionally pass in string of double pipe separated emails to send to
    if ( isset( $_GET['email_list']))
    {
    $email_list = explode( ',', $_GET['email_list'] );

    foreach ( $email_list as $i => $email )
    {
    $email_list[ $i ] = filter_var( $email, FILTER_SANITIZE_EMAIL );
    }

    $email_list = implode( ',', $email_list );
    }
    else
    {
    error_log( 'Autoexport: email given to auto export');
    exit;
    }

    $search_data = [
    'validated' => 1,
    'period' => $period,
    'categories' => $categories,
    'units' => $units,
    'skus' => $skus,
    'email_list' => $email_list,
    ];

    if ( $search_data['validated'] != 1 )
    die( 'Sorry, the data you entered is invalid' );

    return $search_data;
    }

    /*
    More info on wc_order arguments
    https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query
    */
    function set_order_args( $search_data ) {
    $period = $search_data['period'];

    $args = [
    // 'status' => [ 'completed' ],
    'status' => 'completed',
    'order_by' => 'date',
    'order' => 'DESC',
    ];

    if ( isset( $period['range'] ) ) {
    if ( isset( $period['range']['start_date']) && isset( $period['range']['end_date'])) {
    $args['date_before'] = $period['range']['end_date'];
    $args['date_after'] = $period['range']['start_date'];
    // Old way WC worked
    // WC date range format (2018-08-01...2018-08-31)
    // $range = implode( '...', $period['range'] );
    // $args[ 'date_created'] = $range;
    }
    }

    return $args;
    }

    function get_term_names() {
    $tags = get_terms( array('product_tag'), "hide_empty=1" );
    $tag_list = ;

    if ( ! empty( $tags ) && ! is_wp_error( $tags ) ){
    foreach ( $tags as $tag ) {
    $tag_list = $tag->name;
    }
    }

    return $tag_list;
    }

    /*
    Read more here:
    https://stackoverflow.com/questions/21858431/woocommerce-get-a-list-for-all-sku-product
    */
    function get_sku_list() {
    $skus = ;
    $args = array( 'post_type' => 'product', 'posts_per_page' => -1 );

    query_posts( $args );

    if( have_posts() ):
    while ( have_posts() ) : the_post();
    global $post;
    $product = wc_get_product($post->ID);
    $sku = $product->get_sku();

    if ( $sku != '')
    {
    array_push( $skus, $sku);
    }
    endwhile;
    endif;

    return $skus;
    }

    function get_tag_name( $product_id ) {
    $tags = wp_get_post_terms( $product_id, 'product_tag' );

    if ( count($tags) > 0 ) {
    foreach ( $tags as $tag ) {
    return $tag->name;
    }
    } else {
    return 'blank';
    }
    }

    function check_against_list( $data, $product, $property ) {
    $push = (int) true;

    if ( $data['all'] == 0 ) {
    if ( is_array( $data['list'])) {
    if ( ! in_array( htmlspecialchars_decode( $product['tag']), $data['list'] ) ) {
    $push = 0;
    }
    } else {
    $push = 0;
    }
    }

    return $push;
    }

    function sum($row, $totals) {
    (empty( $totals[$row['product_name' ]] ))
    ? $totals[$row['product_name' ]] = [(int) 1, $row['sku']]
    : $totals[$row['product_name' ]][0]++;

    return $totals;
    }

    function check_in_range( $start_date, $end_date, $order_date ) {
    // Convert to timestamp
    $start_ts = strtotime( $start_date );
    $end_ts = strtotime( $end_date );
    $user_ts = strtotime( $order_date );

    // Check that user date is between start & end
    return (( $user_ts >= $start_ts) && ( $user_ts <= $end_ts ));
    }

    // EMAIL
    function mail_attachment($filename, $path, $mailto,
    $from_mail, $from_name, $replyto,
    $subject, $message)
    {
    $file = $path."/".$filename;
    $file_size = filesize($file);
    $handle = fopen($file, "r");
    $content = fread($handle, $file_size);
    fclose($handle);
    $content = chunk_split(base64_encode($content));
    $uid = md5(uniqid(time()));
    $header = "From: ".$from_name." <".$from_mail.">rn";
    $header .= "Reply-To: ".$replyto."rn";
    $header .= "MIME-Version: 1.0rn";
    $header .= "Content-Type: multipart/mixed; boundary="".$uid.""rn";

    $my_message = $message;

    $message = "";
    $message .= "This is a multi-part message in MIME format.rn";
    $message .= "--".$uid."rn";
    $message .= "Content-type:text/plain; charset=iso-8859-1rn";
    $message .= "Content-Transfer-Encoding: 7bitrnrn";
    $message .= $my_message."rnrn";
    $message .= "--".$uid."rn";
    $message .= "Content-Type: application/octet-stream; name="".$filename.""rn"; // use different content types here
    $message .= "Content-Transfer-Encoding: base64rn";
    $message .= "Content-Disposition: attachment; filename="".$filename.""rnrn";
    $message .= $content."rnrn";
    $message .= "--".$uid."--";

    mail($mailto, $subject, $message, $header);
    }

    function notify( $filename, $path,
    $subject = 'Export',
    $report_type, $params)
    {
    $subject = $params['period']['type'] . ' Export';
    $recipients = $params['search_data']['email_list'];

    $sender_address = "web@thecompanydomain.com";
    $sender_name = "Company Web site";
    $reply_to = "web@thecompanydomain.com";
    $subject = "Brand $subject";
    $message = "Attached is the list of $report_type orders.";


    mail_attachment( $filename, $path, $recipients,
    $sender_address, $sender_name,
    $reply_to, $subject, $message);

    return 0;
    }

    if( !function_exists('pre')){
    function pre($var = '') {
    echo "<pre>";
    print_r( $var );
    echo "</pre><br>n";
    }
    }









    share|improve this question









    New contributor




    Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.







    $endgroup$















      1












      1








      1





      $begingroup$


      I wrote this code about one year ago. I'm looking to refactor it to be solid SOLID and secure, as this is being generated off a custom endpoint. The script is to read parameters from a query and generate custom CSV exports based off categories, tags, and order completion date. These reports can be weekly, monthly, yearly, or for all time. An email or comma separated list of emails can be passed as recipients.



      The original flow I had was like this:




      • Enable errors

      • Check if correct query string set

      • If not, exit, else continue

      • Load Wordpress/Woocommerce

      • Sanitize GET variables and set interpolate things like date ranges from string values.

      • Create a collection array and loop through all orders, excluding ones that do not meet search params.

      • Sort by category

      • If no orders, exit

      • Else, write to CSV, email the CSV, and unlink files


      A few questions
      - Should I convert this to OOP?
      - What are some design patterns I can you to make the logic more intuitive?



      P.S. This is the cron url




      (wget -O- "https://site.com/wp-content/themes/theme/scripts/auto-export.php?export=GET&filter_units=all&period=month&filter_categories=product&filter_skus=all&email_list=dan@email.com" --no-check-certificate >> log.txt)







      // Set INI values for testing
      ini_set('display_errors', 1);
      ini_set('display_startup_errors', 1);
      error_reporting(E_ALL);

      // Exit if export type not set (correctly)
      if ( !isset($_GET[ 'export' ]) || $_GET['export'] !== 'GET')
      exit;

      // load Wordpress
      define('WP_USE_THEMES', false);
      require($_SERVER['DOCUMENT_ROOT'] . "/wp-load.php");

      // Sanititze GET data
      $search_data = sanitize_get();

      // Get orders sorted
      $orders = get_product_purchases( $search_data );
      $orders = sort_orders_by( 'category', 'DESC', $orders );


      // Exit if no orders
      if (count($orders) == 0 ) {
      echo "There were no orders given the parameters";
      exit;
      }

      write_to_csv_get([
      'search_data' => $search_data,
      'orders' => $orders,
      ]);
      echo "Export Successful";
      exit;


      /**
      * get_product_purchases
      * Return array of orders
      */
      function get_product_purchases( $search_data ) {
      // Destructure search data
      $cat_data = $search_data['categories'];
      $sku_data = $search_data['skus'];
      $period = $search_data['period'];
      $unit_data = $search_data['units'];

      // See set_order_args() doc-block for more details
      $args = set_order_args( $search_data );
      $WC_Orders = wc_get_orders( $args );
      $results = ;

      foreach ( $WC_Orders as $WC_Order ) {
      if ( empty($WC_Order)) continue;

      $order_data = $WC_Order->get_data();

      $my_order[ 'order_id' ] = $WC_Order->get_id();
      $my_order['order_date'] = $order_data['date_created']->date('Y-m-d');
      $my_order['shipping_first_name'] = $order_data['shipping']['first_name'];
      $my_order['shipping_last_name'] = $order_data['shipping']['last_name'];
      $my_order['shipping_address_1'] = $order_data['shipping']['address_1'];
      $my_order['shipping_address_2'] = $order_data['shipping']['address_2'];
      $my_order['shipping_city'] = $order_data['shipping']['city'];
      $my_order['shipping_state'] = $order_data['shipping']['state'];
      $my_order['shipping_postcode'] = $order_data['shipping']['postcode'];

      foreach ( $WC_Order->get_items() as $item_key => $item_values ) {
      $my_order_copy = $my_order;

      ## Using WC_Order_Item methods ##
      $my_item['id'] = $item_values->get_id(); // Item ID is directly accessible from the $item_key in the foreach loop or

      ## Access Order Items data properties (in an array of values) ##
      $item_data = $item_values->get_data();

      $product_id = $item_data['product_id'];
      $product['product_name'] = $item_data['name'];
      $product['quantity'] = $item_data['quantity'];

      // Get data from The WC_product object using methods (examples)
      $WC_Product = $item_values->get_product(); // the WC_Product object
      if ( empty($WC_Product)) {
      continue;
      };

      $item['item_name'] = preg_replace( '/[x00-x1Fx7F-xFF]/', '', $item_values->get_name() ) ; // Name of the product
      $product['sku'] = $WC_Product->get_sku();
      $product['price'] = ( $WC_Product->get_price() )
      ? sprintf( '%.2f', $WC_Product->get_price() )
      : '';

      // Tags = business units
      $product['tag'] = strtolower( get_tag_name( $product_id ) );

      // Term names are categories
      $terms = get_the_terms( $product_id, 'product_cat' );
      $cats = ;

      foreach( $terms as $term ) {
      array_push( $cats, strtolower( $term->name ));
      }

      $product['category'] = $cats[0];

      /*
      Perform condition check on whether or not to push to export

      If a filter list is set, check the product property against
      the filter list and see if it matches. If it does not,
      exclude it from the CSV
      */
      $push = check_against_list( $cat_data, $product, 'category' );
      if ( $push == 0 ) continue;

      $push = check_against_list( $unit_data, $product, 'tag' );
      if ( $push == 0 ) continue;

      $push = check_against_list( $sku_data, $product, 'sku' );
      if ( $push == 0 ) continue;

      if ( $push == 1 ) {
      $row = array_merge( $my_order_copy, $product );
      array_push( $results, $row );
      }
      }
      }

      return $results;
      }

      /**
      * @param array $params [search_data & orders]
      * @return [void]
      */
      function write_to_csv_get( $params ) {
      // Destructure params
      $period = $params['search_data']['period'];
      $orders = $params['orders'];

      $date_summary = ( $period['all'] == 0 )
      ? "from {$period['range']['start_date']} to {$period['range']['end_date']}"
      : "Since Beginning";

      $breakdown_filename = "company-{$period['type']}-order-details.csv";
      $path = "/tmp";
      $fp = fopen("$path/$breakdown_filename", 'w');

      if ( fopen( "$path/$breakdown_filename", "w+" ) ) {
      $totals = ; // Sum of quantities
      $placed_header = false;

      foreach ( $orders as $index => $row) {
      $totals = sum( $row, $totals );

      // Old code for writing all details of orders
      if (! $placed_header)
      {
      fputcsv( $fp, array_keys( $row ) );
      $placed_header = true;
      }

      // If row set, write it to CSV
      if ( count( $row ) != 0 )
      fputcsv( $fp, $row );
      }
      } else {
      fclose( $fp );
      unlink("$path/$breakdown_filename");
      error_log( "Auto-report: CSV Breakdown - failed to open CSV" );
      }

      notify( $breakdown_filename, $path, 'Download Order Details Export', 'detailed', $params );

      fclose($fp);
      unlink("$path/$filename");

      $totals_filename = "company-{$period['type']}-orders-totals.csv";
      $fp = fopen("$path/$totals_filename", 'w');

      if ( fopen( "$path/$totals_filename", "w+" ) ) {
      // Send totals email
      fputcsv( $fp, [ "Orders - {$date_summary}"] );
      fputcsv( $fp, [ 'SKU', 'Name', 'Totals' ] );

      // Output two column totals ( name | total )
      foreach( $totals as $name => $details ) {
      $num = $details[0];
      $sku = $details[1];
      /*
      Excel doesn't like some of the ASCII characters, use 7 bit ASCII
      https://stackoverflow.com/questions/1176904/php-how-to-remove-all-non-printable-characters-in-a-string
      */
      fputcsv( $fp, [ $sku, $name, $num ]);
      }

      } else {
      fclose( $fp );
      unlink("$path/$totals_filename");
      error_log( "Auto-report: Totals CSV - Failed to open CSV" );
      }

      // Send break down CSV
      notify( $totals_filename, $path, 'Download Totals Export', 'totals for', $params );

      fclose($fp);
      unlink("$path/$totals_filename");

      return;
      }

      function sort_orders_by_date( $results, $order = 'DESC' ) {
      foreach ($results as $key => $part) {
      $sort[$key] = strtotime($part['order_date']);
      }

      ( $order == 'DESC' )
      ? array_multisort( $sort, SORT_DESC, $results )
      : array_multisort( $sort, SORT_ASC, $results );

      return $results;
      }

      function sort_orders_by( $field, $order = 'DESC', $results ) {
      if ( count( $results ) > 0 )
      {
      foreach ($results as $key => $part) {
      $sort[$key] = $part[ $field ];
      }

      ( $order == 'DESC' )
      ? array_multisort( $sort, SORT_DESC, $results )
      : array_multisort( $sort, SORT_ASC, $results );
      }

      return $results;
      }

      function sanitize_get() {
      /*---------------------------------------------
      Filter / Sanitize Dates
      --------------------------------------------- */
      if ( isset( $_GET['period']))
      {
      $period['all'] = 0;

      switch( $_GET['period'])
      {
      case( 'week' ):
      $period['type'] = 'weekly';
      $previous_week = strtotime("-1 week +1 day");

      $start_week = strtotime( "last sunday", $previous_week );
      $end_week = strtotime( "next saturday", $start_week );

      $period['range']['start_date'] = filter_var( date( "Y-m-d", $start_week ), FILTER_SANITIZE_STRING );
      $period['range']['end_date'] = filter_var( date( "Y-m-d", $end_week), FILTER_SANITIZE_STRING );
      break;
      case( 'month' ):
      $period['type'] = 'monthly';

      $period['range']['start_date'] = date('Y-m-d',strtotime('first day of last month'));
      $period['range']['end_date'] = date('Y-m-d',strtotime('last day of last month'));
      break;
      case( 'year'):
      $year = date( 'Y' ) - 1;

      $period['type'] = 'yearly';
      $period['range']['start_date'] = "{$year}-01-01";
      $period['range']['end_date'] = "{$year}-12-31";
      break;
      case( 'forever' ):
      $period['type'] = 'full';
      $period[ 'all' ] = 1;
      break;
      }
      }
      else
      {
      error_log( 'Autoexport: no period given to auto export');
      exit;
      }

      /*---------------------------------------------
      Filter / Sanitize Categories
      ---------------------------------------------*/
      if ( isset( $_GET['filter_categories']))
      {
      $categories = ;
      $categories['all'] = 0;

      if ( $_GET['filter_categories'] == 'all' )
      {
      $categories['all'] = 1;
      }
      else
      {
      $categories['list'] = explode( ',', $_GET['filter_categories']);

      foreach( $categories['list'] as $i => $cat )
      {
      $categories['list'][$i] = str_replace( '-', ' ', strtolower( filter_var( $cat, FILTER_SANITIZE_STRING ) ) );
      }


      if ( $categories['all'] === 0 && count( $categories['list'] ) < 1 )
      error_log( 'Empty value for categories was passed' );
      }
      }
      else
      {
      error_log( 'Autoexport: Empty value for categories was passed' );
      exit;
      }

      /*---------------------------------------------
      Filter / Sanitize Business Units
      ---------------------------------------------*/
      if ( isset( $_GET['filter_units']) )
      {
      if ( $_GET['filter_units'] == 'all' )
      {
      $units['all'] = 1;
      }
      else
      {
      $units = ;
      $units['list'] = explode( ',', $_GET['filter_units']);

      foreach( $units['list'] as $i => $unit )
      {
      $units['list'][$i] = str_replace( '-', ' ', strtolower( filter_var( $unit, FILTER_SANITIZE_STRING ) ) );
      }

      if ( $units['all'] == 0 && count( $units['list'] ) < 1 )
      error_log( 'Empty value for business units was passed' );
      }
      }
      else
      {
      error_log( 'Autoexport: Empty value for business units was passed' );
      exit;
      }

      /*---------------------------------------------
      Filter / Sanitize Skus
      ---------------------------------------------*/
      if ( isset( $_GET['filter_skus']) )
      {
      $skus = ;

      if ( $_GET['filter_skus'] == 'all' )
      {
      $skus['all'] = 1;
      }
      else
      {
      $skus['list'] = explode( ',', $_GET['filter_skus'] );

      // Filter unit string and push it to the search list
      foreach( $skus['list'] as $i => $sku )
      {
      $skus['list'][$i] = str_replace( '-', ' ', filter_var( $sku, FILTER_SANITIZE_STRING ) );
      }

      if ( $skus['all'] === 0 && count( $skus['list'] ) < 1 )
      error_log( 'Autoexport: Empty value for skus was passed' );
      }
      }
      else
      {
      error_log( 'Autoexport: Empty value for skus was passed' );
      exit;
      }

      /*---------------------------------------------
      Filter / Sanitize Emails
      --------------------------------------------- */
      // Optionally pass in string of double pipe separated emails to send to
      if ( isset( $_GET['email_list']))
      {
      $email_list = explode( ',', $_GET['email_list'] );

      foreach ( $email_list as $i => $email )
      {
      $email_list[ $i ] = filter_var( $email, FILTER_SANITIZE_EMAIL );
      }

      $email_list = implode( ',', $email_list );
      }
      else
      {
      error_log( 'Autoexport: email given to auto export');
      exit;
      }

      $search_data = [
      'validated' => 1,
      'period' => $period,
      'categories' => $categories,
      'units' => $units,
      'skus' => $skus,
      'email_list' => $email_list,
      ];

      if ( $search_data['validated'] != 1 )
      die( 'Sorry, the data you entered is invalid' );

      return $search_data;
      }

      /*
      More info on wc_order arguments
      https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query
      */
      function set_order_args( $search_data ) {
      $period = $search_data['period'];

      $args = [
      // 'status' => [ 'completed' ],
      'status' => 'completed',
      'order_by' => 'date',
      'order' => 'DESC',
      ];

      if ( isset( $period['range'] ) ) {
      if ( isset( $period['range']['start_date']) && isset( $period['range']['end_date'])) {
      $args['date_before'] = $period['range']['end_date'];
      $args['date_after'] = $period['range']['start_date'];
      // Old way WC worked
      // WC date range format (2018-08-01...2018-08-31)
      // $range = implode( '...', $period['range'] );
      // $args[ 'date_created'] = $range;
      }
      }

      return $args;
      }

      function get_term_names() {
      $tags = get_terms( array('product_tag'), "hide_empty=1" );
      $tag_list = ;

      if ( ! empty( $tags ) && ! is_wp_error( $tags ) ){
      foreach ( $tags as $tag ) {
      $tag_list = $tag->name;
      }
      }

      return $tag_list;
      }

      /*
      Read more here:
      https://stackoverflow.com/questions/21858431/woocommerce-get-a-list-for-all-sku-product
      */
      function get_sku_list() {
      $skus = ;
      $args = array( 'post_type' => 'product', 'posts_per_page' => -1 );

      query_posts( $args );

      if( have_posts() ):
      while ( have_posts() ) : the_post();
      global $post;
      $product = wc_get_product($post->ID);
      $sku = $product->get_sku();

      if ( $sku != '')
      {
      array_push( $skus, $sku);
      }
      endwhile;
      endif;

      return $skus;
      }

      function get_tag_name( $product_id ) {
      $tags = wp_get_post_terms( $product_id, 'product_tag' );

      if ( count($tags) > 0 ) {
      foreach ( $tags as $tag ) {
      return $tag->name;
      }
      } else {
      return 'blank';
      }
      }

      function check_against_list( $data, $product, $property ) {
      $push = (int) true;

      if ( $data['all'] == 0 ) {
      if ( is_array( $data['list'])) {
      if ( ! in_array( htmlspecialchars_decode( $product['tag']), $data['list'] ) ) {
      $push = 0;
      }
      } else {
      $push = 0;
      }
      }

      return $push;
      }

      function sum($row, $totals) {
      (empty( $totals[$row['product_name' ]] ))
      ? $totals[$row['product_name' ]] = [(int) 1, $row['sku']]
      : $totals[$row['product_name' ]][0]++;

      return $totals;
      }

      function check_in_range( $start_date, $end_date, $order_date ) {
      // Convert to timestamp
      $start_ts = strtotime( $start_date );
      $end_ts = strtotime( $end_date );
      $user_ts = strtotime( $order_date );

      // Check that user date is between start & end
      return (( $user_ts >= $start_ts) && ( $user_ts <= $end_ts ));
      }

      // EMAIL
      function mail_attachment($filename, $path, $mailto,
      $from_mail, $from_name, $replyto,
      $subject, $message)
      {
      $file = $path."/".$filename;
      $file_size = filesize($file);
      $handle = fopen($file, "r");
      $content = fread($handle, $file_size);
      fclose($handle);
      $content = chunk_split(base64_encode($content));
      $uid = md5(uniqid(time()));
      $header = "From: ".$from_name." <".$from_mail.">rn";
      $header .= "Reply-To: ".$replyto."rn";
      $header .= "MIME-Version: 1.0rn";
      $header .= "Content-Type: multipart/mixed; boundary="".$uid.""rn";

      $my_message = $message;

      $message = "";
      $message .= "This is a multi-part message in MIME format.rn";
      $message .= "--".$uid."rn";
      $message .= "Content-type:text/plain; charset=iso-8859-1rn";
      $message .= "Content-Transfer-Encoding: 7bitrnrn";
      $message .= $my_message."rnrn";
      $message .= "--".$uid."rn";
      $message .= "Content-Type: application/octet-stream; name="".$filename.""rn"; // use different content types here
      $message .= "Content-Transfer-Encoding: base64rn";
      $message .= "Content-Disposition: attachment; filename="".$filename.""rnrn";
      $message .= $content."rnrn";
      $message .= "--".$uid."--";

      mail($mailto, $subject, $message, $header);
      }

      function notify( $filename, $path,
      $subject = 'Export',
      $report_type, $params)
      {
      $subject = $params['period']['type'] . ' Export';
      $recipients = $params['search_data']['email_list'];

      $sender_address = "web@thecompanydomain.com";
      $sender_name = "Company Web site";
      $reply_to = "web@thecompanydomain.com";
      $subject = "Brand $subject";
      $message = "Attached is the list of $report_type orders.";


      mail_attachment( $filename, $path, $recipients,
      $sender_address, $sender_name,
      $reply_to, $subject, $message);

      return 0;
      }

      if( !function_exists('pre')){
      function pre($var = '') {
      echo "<pre>";
      print_r( $var );
      echo "</pre><br>n";
      }
      }









      share|improve this question









      New contributor




      Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.







      $endgroup$




      I wrote this code about one year ago. I'm looking to refactor it to be solid SOLID and secure, as this is being generated off a custom endpoint. The script is to read parameters from a query and generate custom CSV exports based off categories, tags, and order completion date. These reports can be weekly, monthly, yearly, or for all time. An email or comma separated list of emails can be passed as recipients.



      The original flow I had was like this:




      • Enable errors

      • Check if correct query string set

      • If not, exit, else continue

      • Load Wordpress/Woocommerce

      • Sanitize GET variables and set interpolate things like date ranges from string values.

      • Create a collection array and loop through all orders, excluding ones that do not meet search params.

      • Sort by category

      • If no orders, exit

      • Else, write to CSV, email the CSV, and unlink files


      A few questions
      - Should I convert this to OOP?
      - What are some design patterns I can you to make the logic more intuitive?



      P.S. This is the cron url




      (wget -O- "https://site.com/wp-content/themes/theme/scripts/auto-export.php?export=GET&filter_units=all&period=month&filter_categories=product&filter_skus=all&email_list=dan@email.com" --no-check-certificate >> log.txt)







      // Set INI values for testing
      ini_set('display_errors', 1);
      ini_set('display_startup_errors', 1);
      error_reporting(E_ALL);

      // Exit if export type not set (correctly)
      if ( !isset($_GET[ 'export' ]) || $_GET['export'] !== 'GET')
      exit;

      // load Wordpress
      define('WP_USE_THEMES', false);
      require($_SERVER['DOCUMENT_ROOT'] . "/wp-load.php");

      // Sanititze GET data
      $search_data = sanitize_get();

      // Get orders sorted
      $orders = get_product_purchases( $search_data );
      $orders = sort_orders_by( 'category', 'DESC', $orders );


      // Exit if no orders
      if (count($orders) == 0 ) {
      echo "There were no orders given the parameters";
      exit;
      }

      write_to_csv_get([
      'search_data' => $search_data,
      'orders' => $orders,
      ]);
      echo "Export Successful";
      exit;


      /**
      * get_product_purchases
      * Return array of orders
      */
      function get_product_purchases( $search_data ) {
      // Destructure search data
      $cat_data = $search_data['categories'];
      $sku_data = $search_data['skus'];
      $period = $search_data['period'];
      $unit_data = $search_data['units'];

      // See set_order_args() doc-block for more details
      $args = set_order_args( $search_data );
      $WC_Orders = wc_get_orders( $args );
      $results = ;

      foreach ( $WC_Orders as $WC_Order ) {
      if ( empty($WC_Order)) continue;

      $order_data = $WC_Order->get_data();

      $my_order[ 'order_id' ] = $WC_Order->get_id();
      $my_order['order_date'] = $order_data['date_created']->date('Y-m-d');
      $my_order['shipping_first_name'] = $order_data['shipping']['first_name'];
      $my_order['shipping_last_name'] = $order_data['shipping']['last_name'];
      $my_order['shipping_address_1'] = $order_data['shipping']['address_1'];
      $my_order['shipping_address_2'] = $order_data['shipping']['address_2'];
      $my_order['shipping_city'] = $order_data['shipping']['city'];
      $my_order['shipping_state'] = $order_data['shipping']['state'];
      $my_order['shipping_postcode'] = $order_data['shipping']['postcode'];

      foreach ( $WC_Order->get_items() as $item_key => $item_values ) {
      $my_order_copy = $my_order;

      ## Using WC_Order_Item methods ##
      $my_item['id'] = $item_values->get_id(); // Item ID is directly accessible from the $item_key in the foreach loop or

      ## Access Order Items data properties (in an array of values) ##
      $item_data = $item_values->get_data();

      $product_id = $item_data['product_id'];
      $product['product_name'] = $item_data['name'];
      $product['quantity'] = $item_data['quantity'];

      // Get data from The WC_product object using methods (examples)
      $WC_Product = $item_values->get_product(); // the WC_Product object
      if ( empty($WC_Product)) {
      continue;
      };

      $item['item_name'] = preg_replace( '/[x00-x1Fx7F-xFF]/', '', $item_values->get_name() ) ; // Name of the product
      $product['sku'] = $WC_Product->get_sku();
      $product['price'] = ( $WC_Product->get_price() )
      ? sprintf( '%.2f', $WC_Product->get_price() )
      : '';

      // Tags = business units
      $product['tag'] = strtolower( get_tag_name( $product_id ) );

      // Term names are categories
      $terms = get_the_terms( $product_id, 'product_cat' );
      $cats = ;

      foreach( $terms as $term ) {
      array_push( $cats, strtolower( $term->name ));
      }

      $product['category'] = $cats[0];

      /*
      Perform condition check on whether or not to push to export

      If a filter list is set, check the product property against
      the filter list and see if it matches. If it does not,
      exclude it from the CSV
      */
      $push = check_against_list( $cat_data, $product, 'category' );
      if ( $push == 0 ) continue;

      $push = check_against_list( $unit_data, $product, 'tag' );
      if ( $push == 0 ) continue;

      $push = check_against_list( $sku_data, $product, 'sku' );
      if ( $push == 0 ) continue;

      if ( $push == 1 ) {
      $row = array_merge( $my_order_copy, $product );
      array_push( $results, $row );
      }
      }
      }

      return $results;
      }

      /**
      * @param array $params [search_data & orders]
      * @return [void]
      */
      function write_to_csv_get( $params ) {
      // Destructure params
      $period = $params['search_data']['period'];
      $orders = $params['orders'];

      $date_summary = ( $period['all'] == 0 )
      ? "from {$period['range']['start_date']} to {$period['range']['end_date']}"
      : "Since Beginning";

      $breakdown_filename = "company-{$period['type']}-order-details.csv";
      $path = "/tmp";
      $fp = fopen("$path/$breakdown_filename", 'w');

      if ( fopen( "$path/$breakdown_filename", "w+" ) ) {
      $totals = ; // Sum of quantities
      $placed_header = false;

      foreach ( $orders as $index => $row) {
      $totals = sum( $row, $totals );

      // Old code for writing all details of orders
      if (! $placed_header)
      {
      fputcsv( $fp, array_keys( $row ) );
      $placed_header = true;
      }

      // If row set, write it to CSV
      if ( count( $row ) != 0 )
      fputcsv( $fp, $row );
      }
      } else {
      fclose( $fp );
      unlink("$path/$breakdown_filename");
      error_log( "Auto-report: CSV Breakdown - failed to open CSV" );
      }

      notify( $breakdown_filename, $path, 'Download Order Details Export', 'detailed', $params );

      fclose($fp);
      unlink("$path/$filename");

      $totals_filename = "company-{$period['type']}-orders-totals.csv";
      $fp = fopen("$path/$totals_filename", 'w');

      if ( fopen( "$path/$totals_filename", "w+" ) ) {
      // Send totals email
      fputcsv( $fp, [ "Orders - {$date_summary}"] );
      fputcsv( $fp, [ 'SKU', 'Name', 'Totals' ] );

      // Output two column totals ( name | total )
      foreach( $totals as $name => $details ) {
      $num = $details[0];
      $sku = $details[1];
      /*
      Excel doesn't like some of the ASCII characters, use 7 bit ASCII
      https://stackoverflow.com/questions/1176904/php-how-to-remove-all-non-printable-characters-in-a-string
      */
      fputcsv( $fp, [ $sku, $name, $num ]);
      }

      } else {
      fclose( $fp );
      unlink("$path/$totals_filename");
      error_log( "Auto-report: Totals CSV - Failed to open CSV" );
      }

      // Send break down CSV
      notify( $totals_filename, $path, 'Download Totals Export', 'totals for', $params );

      fclose($fp);
      unlink("$path/$totals_filename");

      return;
      }

      function sort_orders_by_date( $results, $order = 'DESC' ) {
      foreach ($results as $key => $part) {
      $sort[$key] = strtotime($part['order_date']);
      }

      ( $order == 'DESC' )
      ? array_multisort( $sort, SORT_DESC, $results )
      : array_multisort( $sort, SORT_ASC, $results );

      return $results;
      }

      function sort_orders_by( $field, $order = 'DESC', $results ) {
      if ( count( $results ) > 0 )
      {
      foreach ($results as $key => $part) {
      $sort[$key] = $part[ $field ];
      }

      ( $order == 'DESC' )
      ? array_multisort( $sort, SORT_DESC, $results )
      : array_multisort( $sort, SORT_ASC, $results );
      }

      return $results;
      }

      function sanitize_get() {
      /*---------------------------------------------
      Filter / Sanitize Dates
      --------------------------------------------- */
      if ( isset( $_GET['period']))
      {
      $period['all'] = 0;

      switch( $_GET['period'])
      {
      case( 'week' ):
      $period['type'] = 'weekly';
      $previous_week = strtotime("-1 week +1 day");

      $start_week = strtotime( "last sunday", $previous_week );
      $end_week = strtotime( "next saturday", $start_week );

      $period['range']['start_date'] = filter_var( date( "Y-m-d", $start_week ), FILTER_SANITIZE_STRING );
      $period['range']['end_date'] = filter_var( date( "Y-m-d", $end_week), FILTER_SANITIZE_STRING );
      break;
      case( 'month' ):
      $period['type'] = 'monthly';

      $period['range']['start_date'] = date('Y-m-d',strtotime('first day of last month'));
      $period['range']['end_date'] = date('Y-m-d',strtotime('last day of last month'));
      break;
      case( 'year'):
      $year = date( 'Y' ) - 1;

      $period['type'] = 'yearly';
      $period['range']['start_date'] = "{$year}-01-01";
      $period['range']['end_date'] = "{$year}-12-31";
      break;
      case( 'forever' ):
      $period['type'] = 'full';
      $period[ 'all' ] = 1;
      break;
      }
      }
      else
      {
      error_log( 'Autoexport: no period given to auto export');
      exit;
      }

      /*---------------------------------------------
      Filter / Sanitize Categories
      ---------------------------------------------*/
      if ( isset( $_GET['filter_categories']))
      {
      $categories = ;
      $categories['all'] = 0;

      if ( $_GET['filter_categories'] == 'all' )
      {
      $categories['all'] = 1;
      }
      else
      {
      $categories['list'] = explode( ',', $_GET['filter_categories']);

      foreach( $categories['list'] as $i => $cat )
      {
      $categories['list'][$i] = str_replace( '-', ' ', strtolower( filter_var( $cat, FILTER_SANITIZE_STRING ) ) );
      }


      if ( $categories['all'] === 0 && count( $categories['list'] ) < 1 )
      error_log( 'Empty value for categories was passed' );
      }
      }
      else
      {
      error_log( 'Autoexport: Empty value for categories was passed' );
      exit;
      }

      /*---------------------------------------------
      Filter / Sanitize Business Units
      ---------------------------------------------*/
      if ( isset( $_GET['filter_units']) )
      {
      if ( $_GET['filter_units'] == 'all' )
      {
      $units['all'] = 1;
      }
      else
      {
      $units = ;
      $units['list'] = explode( ',', $_GET['filter_units']);

      foreach( $units['list'] as $i => $unit )
      {
      $units['list'][$i] = str_replace( '-', ' ', strtolower( filter_var( $unit, FILTER_SANITIZE_STRING ) ) );
      }

      if ( $units['all'] == 0 && count( $units['list'] ) < 1 )
      error_log( 'Empty value for business units was passed' );
      }
      }
      else
      {
      error_log( 'Autoexport: Empty value for business units was passed' );
      exit;
      }

      /*---------------------------------------------
      Filter / Sanitize Skus
      ---------------------------------------------*/
      if ( isset( $_GET['filter_skus']) )
      {
      $skus = ;

      if ( $_GET['filter_skus'] == 'all' )
      {
      $skus['all'] = 1;
      }
      else
      {
      $skus['list'] = explode( ',', $_GET['filter_skus'] );

      // Filter unit string and push it to the search list
      foreach( $skus['list'] as $i => $sku )
      {
      $skus['list'][$i] = str_replace( '-', ' ', filter_var( $sku, FILTER_SANITIZE_STRING ) );
      }

      if ( $skus['all'] === 0 && count( $skus['list'] ) < 1 )
      error_log( 'Autoexport: Empty value for skus was passed' );
      }
      }
      else
      {
      error_log( 'Autoexport: Empty value for skus was passed' );
      exit;
      }

      /*---------------------------------------------
      Filter / Sanitize Emails
      --------------------------------------------- */
      // Optionally pass in string of double pipe separated emails to send to
      if ( isset( $_GET['email_list']))
      {
      $email_list = explode( ',', $_GET['email_list'] );

      foreach ( $email_list as $i => $email )
      {
      $email_list[ $i ] = filter_var( $email, FILTER_SANITIZE_EMAIL );
      }

      $email_list = implode( ',', $email_list );
      }
      else
      {
      error_log( 'Autoexport: email given to auto export');
      exit;
      }

      $search_data = [
      'validated' => 1,
      'period' => $period,
      'categories' => $categories,
      'units' => $units,
      'skus' => $skus,
      'email_list' => $email_list,
      ];

      if ( $search_data['validated'] != 1 )
      die( 'Sorry, the data you entered is invalid' );

      return $search_data;
      }

      /*
      More info on wc_order arguments
      https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query
      */
      function set_order_args( $search_data ) {
      $period = $search_data['period'];

      $args = [
      // 'status' => [ 'completed' ],
      'status' => 'completed',
      'order_by' => 'date',
      'order' => 'DESC',
      ];

      if ( isset( $period['range'] ) ) {
      if ( isset( $period['range']['start_date']) && isset( $period['range']['end_date'])) {
      $args['date_before'] = $period['range']['end_date'];
      $args['date_after'] = $period['range']['start_date'];
      // Old way WC worked
      // WC date range format (2018-08-01...2018-08-31)
      // $range = implode( '...', $period['range'] );
      // $args[ 'date_created'] = $range;
      }
      }

      return $args;
      }

      function get_term_names() {
      $tags = get_terms( array('product_tag'), "hide_empty=1" );
      $tag_list = ;

      if ( ! empty( $tags ) && ! is_wp_error( $tags ) ){
      foreach ( $tags as $tag ) {
      $tag_list = $tag->name;
      }
      }

      return $tag_list;
      }

      /*
      Read more here:
      https://stackoverflow.com/questions/21858431/woocommerce-get-a-list-for-all-sku-product
      */
      function get_sku_list() {
      $skus = ;
      $args = array( 'post_type' => 'product', 'posts_per_page' => -1 );

      query_posts( $args );

      if( have_posts() ):
      while ( have_posts() ) : the_post();
      global $post;
      $product = wc_get_product($post->ID);
      $sku = $product->get_sku();

      if ( $sku != '')
      {
      array_push( $skus, $sku);
      }
      endwhile;
      endif;

      return $skus;
      }

      function get_tag_name( $product_id ) {
      $tags = wp_get_post_terms( $product_id, 'product_tag' );

      if ( count($tags) > 0 ) {
      foreach ( $tags as $tag ) {
      return $tag->name;
      }
      } else {
      return 'blank';
      }
      }

      function check_against_list( $data, $product, $property ) {
      $push = (int) true;

      if ( $data['all'] == 0 ) {
      if ( is_array( $data['list'])) {
      if ( ! in_array( htmlspecialchars_decode( $product['tag']), $data['list'] ) ) {
      $push = 0;
      }
      } else {
      $push = 0;
      }
      }

      return $push;
      }

      function sum($row, $totals) {
      (empty( $totals[$row['product_name' ]] ))
      ? $totals[$row['product_name' ]] = [(int) 1, $row['sku']]
      : $totals[$row['product_name' ]][0]++;

      return $totals;
      }

      function check_in_range( $start_date, $end_date, $order_date ) {
      // Convert to timestamp
      $start_ts = strtotime( $start_date );
      $end_ts = strtotime( $end_date );
      $user_ts = strtotime( $order_date );

      // Check that user date is between start & end
      return (( $user_ts >= $start_ts) && ( $user_ts <= $end_ts ));
      }

      // EMAIL
      function mail_attachment($filename, $path, $mailto,
      $from_mail, $from_name, $replyto,
      $subject, $message)
      {
      $file = $path."/".$filename;
      $file_size = filesize($file);
      $handle = fopen($file, "r");
      $content = fread($handle, $file_size);
      fclose($handle);
      $content = chunk_split(base64_encode($content));
      $uid = md5(uniqid(time()));
      $header = "From: ".$from_name." <".$from_mail.">rn";
      $header .= "Reply-To: ".$replyto."rn";
      $header .= "MIME-Version: 1.0rn";
      $header .= "Content-Type: multipart/mixed; boundary="".$uid.""rn";

      $my_message = $message;

      $message = "";
      $message .= "This is a multi-part message in MIME format.rn";
      $message .= "--".$uid."rn";
      $message .= "Content-type:text/plain; charset=iso-8859-1rn";
      $message .= "Content-Transfer-Encoding: 7bitrnrn";
      $message .= $my_message."rnrn";
      $message .= "--".$uid."rn";
      $message .= "Content-Type: application/octet-stream; name="".$filename.""rn"; // use different content types here
      $message .= "Content-Transfer-Encoding: base64rn";
      $message .= "Content-Disposition: attachment; filename="".$filename.""rnrn";
      $message .= $content."rnrn";
      $message .= "--".$uid."--";

      mail($mailto, $subject, $message, $header);
      }

      function notify( $filename, $path,
      $subject = 'Export',
      $report_type, $params)
      {
      $subject = $params['period']['type'] . ' Export';
      $recipients = $params['search_data']['email_list'];

      $sender_address = "web@thecompanydomain.com";
      $sender_name = "Company Web site";
      $reply_to = "web@thecompanydomain.com";
      $subject = "Brand $subject";
      $message = "Attached is the list of $report_type orders.";


      mail_attachment( $filename, $path, $recipients,
      $sender_address, $sender_name,
      $reply_to, $subject, $message);

      return 0;
      }

      if( !function_exists('pre')){
      function pre($var = '') {
      echo "<pre>";
      print_r( $var );
      echo "</pre><br>n";
      }
      }






      php csv wordpress






      share|improve this question









      New contributor




      Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question









      New contributor




      Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question








      edited 1 hour ago









      200_success

      129k15153417




      129k15153417






      New contributor




      Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked 4 hours ago









      Daniel FoustDaniel Foust

      1063




      1063




      New contributor




      Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      Daniel Foust is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });






          Daniel Foust is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214289%2fgenerating-two-woocommerce-order-csv-files-based-off-url-query%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          Daniel Foust is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          Daniel Foust is a new contributor. Be nice, and check out our Code of Conduct.













          Daniel Foust is a new contributor. Be nice, and check out our Code of Conduct.












          Daniel Foust is a new contributor. Be nice, and check out our Code of Conduct.
















          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f214289%2fgenerating-two-woocommerce-order-csv-files-based-off-url-query%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          How to make a Squid Proxy server?

          Is this a new Fibonacci Identity?

          19世紀