Last active
April 7, 2022 09:50
-
-
Save actual-saurabh/a479571b6ceb5e6b32e30f31575050f0 to your computer and use it in GitHub Desktop.
Serial Numbers for Transaction Receipts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php // Do not copy this line | |
// copy from below this line | |
/** | |
* Generates sequential serial numbers for a given post type | |
*/ | |
class LLMS_Serial_Number_Generator { | |
private $post_type = ''; | |
private $post_status = ''; | |
private $prefix = ''; | |
private $length = 7; | |
public function __construct() { | |
$this->hook(); | |
} | |
/** | |
* Hooks into Purchase Receipt html | |
*/ | |
public function hook() { | |
add_filter( 'llms_notification_viewpurchase_receipt_get_email_html', array( $this, 'replace_merge_code' ), 10, 2 ); | |
} | |
/** | |
* Initialises serial number generation. | |
* | |
* @param string $post_type Post Type to generate for | |
* @param string $post_status Generate when transitioning to this post status | |
* @param string $length Desired length of the serial number including leading zeroes but excluding prefix | |
* @param string $prefix Desired prefix | |
* | |
* @return bool|null | |
*/ | |
public function init ( $length = false, $prefix = false ) { | |
$this->post_type = 'llms_transaction'; | |
$this->post_status = array( 'llms-txn-succeeded', 'llms-txn-refunded' ); | |
$this->length = ! empty( $length ) ? $length : $this->length; | |
$this->prefix = ! empty( $prefix ) ? $prefix : $this->prefix; | |
} | |
/* | |
* Replace serial number merge code | |
* | |
* @param string $email_html The email html | |
* @param LLMS_Notification_View_Purchase_Receipt $notification_view The view handler of purchase receipt notification | |
* | |
* @return string | |
*/ | |
public function replace_merge_code( $email_html, $notification_view ){ | |
// get the notification. | |
$notification = new LLMS_Notification( $notification_view->id ); | |
// get the transaction post from the notification. | |
$transaction = get_post( $notification->get( 'post_id' ) ); | |
// generate serial number. | |
$serial_number = $this->maybe_generate( $transaction ); | |
// don't replace merge code on error. | |
if( is_wp_error( $serial_number ) ){ | |
return $email_html; | |
} | |
// replace merge code | |
$email_html = str_replace( '{{SERIAL_NUMBER}}', $serial_number, $email_html ); | |
return $email_html; | |
} | |
/** | |
* Generates and saves serial number for post. | |
* | |
* @param string $old_status Earlier post status | |
* @param string $new_status New post status | |
* @param WP_Post $post The current post | |
*/ | |
public function maybe_generate( $transaction ) { | |
// initiliase. | |
$this->init(); | |
// first check if the serial number is already in the meta. | |
$existing_serial_number = get_post_meta( $transaction->ID, '_llms_serial_number', true ); | |
// if it's there, save further execution and bail. | |
if( ! empty( $existing_serial_number ) ) { | |
return $existing_serial_number; | |
} | |
// no serial number in meta, will need to generate. | |
$serial_number = $this->generate( $transaction ); | |
return $serial_number; | |
} | |
/** | |
* Generates a serial number for support. | |
* | |
* @param WP_Post $post The post to generate serial number for | |
* | |
* @return string | |
*/ | |
public function generate( $transaction ) { | |
$is_test = ( 'live' !== get_post_meta( $transaction->ID, '_llms_api_mode', true ) ) ? true : false; | |
$last_serial_number = $this->get_last_serial_number( $transaction, $is_test ); | |
// either this is the first post of its kind (last serial number is 0), or we have a definite existing last serial number. | |
$current_serial_number = $this->increment_serial_number( $last_serial_number, $is_test ); | |
if ( ! $is_test ) { | |
// save the serial number in meta, only for live transactions. | |
update_post_meta( $transaction->ID, '_llms_serial_number', $current_serial_number ); | |
} | |
return $current_serial_number; | |
} | |
/** | |
* Gets the last serial number value. | |
* | |
* @param WP_Post $post The post to generate serial number for | |
* | |
* @return string | |
*/ | |
public function get_last_serial_number( $transaction, $is_test ) { | |
$last_serial_number = 0; | |
// get previous post. | |
$previous_transaction = $this->get_previous_transaction( $transaction ); | |
// there is a previous transaction, attempt getting the previous serial number. | |
if( ! empty ( $previous_transaction ) ) { | |
$last_serial_number = get_post_meta( $previous_transaction->ID, '_llms_serial_number', true ); | |
} | |
// there is a previous transaction but it doesn't have a serial number, this is probably the first run | |
if( empty ( $last_serial_number ) && ! $is_test ) { | |
$last_serial_number = $this->maybe_first_run( $previous_transaction ); | |
} | |
// there is no previous transaction, serial number is 0 | |
return $last_serial_number; | |
} | |
/** | |
* Increments the last serial number. | |
* | |
* @param string $last_serial_number The post to generate serial number for | |
* | |
* @return string | |
*/ | |
public function increment_serial_number( &$last_serial_number ) { | |
// replace the prefix, if needed | |
if( ! empty( $this->prefix ) ) { | |
$last_serial_number = str_replace( $this->prefix, '', $last_serial_number ); | |
} | |
$last_serial_number = intval( $last_serial_number ) + 1; | |
// increment and add leading zeroes. | |
$new_serial_number = sprintf( "%0{$this->length}d", $last_serial_number ) ; | |
// prefix if needed. | |
if( ! empty( $this->prefix ) ) { | |
$new_serial_number = $this->prefix . $new_serial_number; | |
} | |
return $new_serial_number; | |
} | |
/** | |
* Fetches the previous post | |
* | |
* @param WP_Post $post The current post | |
* | |
* @return WP_Post | |
*/ | |
private function get_previous_transaction( $transaction ){ | |
// bail, if no post is provided | |
if ( ! $transaction ) { | |
return null; | |
} | |
$post_status__in = "'" . implode( "','", $this->post_status ) . "'"; | |
global $wpdb; | |
$where = $wpdb->prepare( | |
"WHERE p.post_date < %s AND p.post_type = %s AND p.post_status IN ( $post_status__in ) AND m.meta_key = %s AND m.meta_value = %s", | |
$transaction->post_date, $transaction->post_type , '_llms_api_mode', 'live' | |
); | |
$sort = "ORDER BY p.post_date DESC LIMIT 1"; | |
$query = "SELECT p.ID FROM $wpdb->posts AS p LEFT JOIN $wpdb->postmeta as m ON p.ID = m.post_id $where $sort"; | |
$previous_transaction = $wpdb->get_var( $query ); | |
if ( null === $previous_transaction ) { | |
$previous_transaction = ''; | |
} | |
if ( $previous_transaction ) { | |
$previous_transaction = get_post( $previous_transaction ); | |
} | |
return $previous_transaction; | |
} | |
/** | |
* Creates all older serial numbers on first run. | |
* | |
* @param WP_Post $previous_post The previous post. | |
* | |
* @return WP_Post | |
*/ | |
public function maybe_first_run( $previous_transaction ) { | |
$older_transactions = $this->get_older(); | |
if( ! $older_transactions ) { | |
return 0; | |
} | |
return $this->first_run( $older_transactions, $previous_transaction ); | |
} | |
public function first_run( $older_transactions, $previous_transaction ) { | |
$older_serial_numbers_generated = $this->generate_all_at_once( $older_transactions ); | |
// return the error if there was one. | |
if( is_wp_error( $older_serial_numbers_generated ) ) { | |
return $older_serial_numbers_generated; | |
} | |
/* | |
* bypass cache for last serial number by directly querying the database | |
* because it was just added and won't show up in get_post_meta untill a reload. | |
*/ | |
if( empty ( $previous_transaction ) ) { | |
return 0; | |
} | |
global $wpdb; | |
$last_serial_number = $wpdb->get_var( | |
$wpdb->prepare( | |
"SELECT meta_value FROM $wpdb->postmeta WHERE meta_key=%s AND post_id=%d ", | |
'_llms_serial_number', $previous_transaction->ID | |
) | |
); | |
return $last_serial_number; | |
} | |
public function get_older() { | |
// set up query args. | |
$older_transactions_query_args = array( | |
'post_type' => $this->post_type, | |
'post_status' => $this->post_status, | |
'posts_per_page' => -1, // we want all the posts at once | |
'fields' => 'ids', // we just need the IDs | |
'order' => 'ASC', | |
'orderby' => 'date', | |
'meta_query' => array( | |
array( | |
'key' => '_llms_api_mode', | |
'value' => 'live', | |
), | |
), | |
); | |
// get query results. | |
$older_transactions = new WP_Query( $older_transactions_query_args ); | |
// no posts found. bail! | |
if( empty( $older_transactions->found_posts ) ) { | |
return false; | |
} | |
return $older_transactions; | |
} | |
/** | |
* Retroactively generates and saves serial numbers for all post | |
* | |
* @return bool|WP_Error | |
*/ | |
private function generate_all_at_once( $older_transactions ) { | |
// get the post IDs. | |
$older_transaction_ids = $older_transactions->posts; | |
// generate as many serial numbers as needed. | |
$serial_numbers = range( 1, intval( $older_transactions->found_posts ) ); | |
$meta_key = '_llms_serial_number'; | |
global $wpdb; | |
// initialise db query | |
$sql_query = "INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value ) "; | |
$counter = 0; | |
foreach( $older_transaction_ids as $post_id ) { | |
// use up one serial number from left in each iteration | |
$meta_value = $this->increment_serial_number( $counter ); | |
// set up part of the database query | |
$sql_query_sel[]= "SELECT $post_id, '$meta_key', '$meta_value'"; | |
} | |
// setup complete | |
$sql_query.= implode( " UNION ALL ", $sql_query_sel ); | |
$results = $wpdb->query( $sql_query ); | |
if( ! $results ){ | |
return new WP_Error( 'failed', __( 'Failed saving serial numbers for older transactions.', 'lifterlms' ) ); | |
} | |
// this would be the last, ie, current serial number | |
return true; | |
} | |
} | |
new LLMS_Serial_Number_Generator(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment