Generating two Woocommerce Order CSV files based off url query
$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";
}
}
php csv wordpress
New contributor
$endgroup$
add a comment |
$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";
}
}
php csv wordpress
New contributor
$endgroup$
add a comment |
$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";
}
}
php csv wordpress
New contributor
$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
php csv wordpress
New contributor
New contributor
edited 1 hour ago
200_success
129k15153417
129k15153417
New contributor
asked 4 hours ago
Daniel FoustDaniel Foust
1063
1063
New contributor
New contributor
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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.
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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