OM2 Posted January 12, 2012 Share Posted January 12, 2012 I've some code for an ecommerce plugin It outputs (in an email) all fields relating to the order I don't want all fields - only selected ones I wanted some advice on how to replace the current loop and also how best to compare 2 strings The code I have is: foreach ($p->ipn_data as $key => $value) { $body .= "\n$key: $value"; } This output something like this: shopaction: redirect shipping_1: 0.00 first_name: Firstname last_name: Lastname email: [email protected] phone: 020 address1: 10 Road address2: city: London state: zip: EC1V 0HB country: GB collection-delivery: collection shop_payment: cash item_name_1: Test item 1 eshopident_1: 3205c068dbc16cd829329b6e9917b37ab quantity_1: 1 weight_1: 0 amount_1: 3.50 item_number_1: test-item-1 postid_1: 668 numberofproducts: 1 amount: 3.50 item_name_2: Test item 2 eshopident_2: 3205c068dbc16cd829329b6e9917b37ab quantity_2: 1 weight_2: 0 amount_2: 3.50 item_number_2: test-item-2 postid_2: 669 numberofproducts: 1 amount: 4.50 submit: Proceed to Confirmation » custom: 220120112004817 reference: comments: RefNr: 32728455914f0e2dd143c06 ppsubmit: Proceed to Checkout » payer_email: Firstname Lastname [email protected] I'm scratching my head and am not sure where to start One possibility would be to check if my chosen values that I want to output matched $key or not For this, I'd need to figure out to compare strings and get a positive if a subset of the string was contained I need to do this because of things like 'quantity_1' and 'quantity_2' I've looked at string comparison functions and can't figure out which to use OK... I think I can do it using my proposed method! But... the thing is... I would have though that was a lot of waste of CPU uneccesarily If I only wanted 5 fields and there were 10 fields, then that a potentially of 50 comparisons just to check for the $key value? Is there a better way? I'm not sure where to investigate the data - I'm not sure where the numericals get put in for something like 'quantity_1' I assume it starts life as just 'quantity_'? ANY pointers where to start would be great I know I could code a half baked solution - but I don't think it would be very optimal! Thanks OM Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/ Share on other sites More sharing options...
PFMaBiSmAd Posted January 12, 2012 Share Posted January 12, 2012 I would use a preg_replace to remove any _n sequence numbers at the end of the $key value, leaving you with the base field name. You could then use an in_array statement to match only those field names that you want. Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306754 Share on other sites More sharing options...
OM2 Posted January 12, 2012 Author Share Posted January 12, 2012 I would use a preg_replace to remove any _n sequence numbers at the end of the $key value, leaving you with the base field name. You could then use an in_array statement to match only those field names that you want. ok... that makes sense but i've been thinking... i need the _n's - these could be any number - and need to be printed out? i was thinking of doing something like this instead: foreach ($p->ipn_data as $key => $value) { if (strpos($key, 'firstname') { $body .= "\n$key: $value"; } else if (strpos($key, 'itemname_') { $body .= "\n$key: $value"; } else if (strpos($key, 'total') { $body .= "\n$key: $value"; } } (the above is simplified just to explain the point) so the above would be ok if there were itemname_1, itemname_2, itemname_3 ...... itemname_10 there would be any number of these and we'd be ok i can't think of a better solution let me know what u think EDIT: is my use of strpos OK?? i looked up in manual and this seemed to be the only choice THANKS! Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306760 Share on other sites More sharing options...
PFMaBiSmAd Posted January 12, 2012 Share Posted January 12, 2012 <?php $keep = array('quantity','amount'); // fields to keep $body = ''; foreach($p->ipn_data as $key=>$value){ $base_key = preg_replace('/(_\d+)$/','',$key); // get the base key w/o the _nn if(in_array($base_key,$keep)){ // test if the base key is in the keep array $body .= "\n$key: $value"; // add the original key/value to the body } } echo '<pre>',$body,'</pre>'; // show the results for demo purposes Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306761 Share on other sites More sharing options...
OM2 Posted January 12, 2012 Author Share Posted January 12, 2012 <?php $keep = array('quantity','amount'); // fields to keep $body = ''; foreach($p->ipn_data as $key=>$value){ $base_key = preg_replace('/(_\d+)$/','',$key); // get the base key w/o the _nn if(in_array($base_key,$keep)){ // test if the base key is in the keep array $body .= "\n$key: $value"; // add the original key/value to the body } } echo '<pre>',$body,'</pre>'; // show the results for demo purposes OMG - that looks sooo much better than my code in effect, is it doing what i proposed exactly, but in less lines of code? or is it a more optimal way? ALSO: is using preg_replace better than using the strpos is used? thanks a million ASIDE: can u recommend somewhere to learn regular expressions. i'll take ur word it that what uve done inside the pregrepalce works! i've seen some big fat books on regular expressions - which i don't fancy reading. a 4 page dummies guide would do me fine to get me started. thanks Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306771 Share on other sites More sharing options...
OM2 Posted January 12, 2012 Author Share Posted January 12, 2012 ok, the code works a real treat. THANK YOU! the output i now get is: shipping_1: 0.00 first_name: Joe last_name: Bloggs email: [email protected] phone: 020 address1: 10 Road address2: city: London zip: EC1V 0HB collection-delivery: collection eshop_payment: cash item_name_1: item one quantity_1: 1 amount_1: 12.95 item_name_2: item number two quantity_2: 1 amount_2: 5.95 item_name_3: item num three quantity_3: 1 amount_3: 5.50 numberofproducts: 3 amount: 24.40 what i actually would prefer is something like this: shipping_1: 0.00 name: Joe Bloggs email: [email protected] phone: 020 address1: 10 Road, London, EC1V 0HB collection-delivery: collection eshop_payment: cash order: 1 x item one 12.95 1 x item number two 5.95 1 x item num three 5.50 amount: 24.40 much more readable that way what would be the best method of doing this? may parse twice? first time getting the other data and second time getting the item specific data (for which there could be any number) i'm thinking if i would be better to do in one loop? i think that would involve keeping track of 3 strings - the beginning, the middle part (where u have all the item_1, item_2, item_3 etc) and the end part the bit i'm not 100% sure about is how best to tie up item_1, quantity_1 and price_1 for example any ideas? thanks Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306785 Share on other sites More sharing options...
PFMaBiSmAd Posted January 12, 2012 Share Posted January 12, 2012 I would use a template - <?php // Note: the index values in this array have significance. Index values 0-11 correspond to main template tags. Index values 12-14 correspond to repeating order template tags. $keep = array( 'shipping_','first_name','last_name','email','phone','address1','address2','city','zip','collection-delivery','eshop_payment','amount', 'item_name_','quantity_','amount_'); $main_template = "shipping_1: {shipping_} name: {first_name} {last_name} email: {email} phone: {phone} address1: {address1}, {city}, {zip} collection-delivery: {collection-delivery} eshop_payment: {eshop_payment} order: {order} amount: {amount} "; $each_order_template = "{quantity_} x {item_name_} {amount_} "; foreach($p->ipn_data as $key=>$value){ $base_key = preg_replace('/(_\d+)$/','_',$key); if(in_array($base_key,$keep)){ // data (assoc) key found $pos = array_search($base_key, $keep); // get corresponding index (index 0-11 are normal items, 12-14 are repeating items) if($pos < 12){ // normal template item $tpl[$base_key] = $value; } else { // order/repeating template item $otpl[$base_key][] = $value; } } } // exchange (pivot) the order data rows and columns foreach($otpl as $key => $arr){ foreach($arr as $num=>$value){ $pivot[$num][$key] = $value; } } $tpl['order'] = ''; // build/populate the repeating order template foreach($pivot as $row){ $tpl['order'] .= preg_replace("/\{([^\{]{1,100}?)\}/e","\$row['$1']",$each_order_template); // populate template } // build/populate the main template $body = preg_replace("/\{([^\{]{1,100}?)\}/e","\$tpl['$1']",$main_template); // populate template echo "<pre>"; // display literal output for debugging echo $body; I changed the preg_replace slightly so that the trailing under_score _ is kept so that the amount value can be distinguished from the amount_n values. Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306801 Share on other sites More sharing options...
PFMaBiSmAd Posted January 12, 2012 Share Posted January 12, 2012 If I were to write that code today, I would split the $keep array into two arrays and then use two different if(in_array()){} statements, in place of the existing in_array, the array_search, and the if($pos<12){}else{} logic. Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306860 Share on other sites More sharing options...
OM2 Posted January 12, 2012 Author Share Posted January 12, 2012 OMG erm... i don't know what to say how long did that take u to write??? probably took you 5 minutes would take ma whole day but then again... i've never used templates as u describe i'm going to have to spend time going through the code now - i'll post back here after just glancing at the code... i'm not sure about the bit where u mention about 1-11 and 12-14? i'll ask again if the code really confuses me Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306895 Share on other sites More sharing options...
OM2 Posted January 12, 2012 Author Share Posted January 12, 2012 If I were to write that code today, I would split the $keep array into two arrays and then use two different if(in_array()){} statements, in place of the existing in_array, the array_search, and the if($pos<12){}else{} logic. i wasnt sure what u were saying here? is that describing a better way to code it? let me know thanks Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306896 Share on other sites More sharing options...
PFMaBiSmAd Posted January 12, 2012 Share Posted January 12, 2012 New keep arrays - $keep_main = array( 'shipping_','first_name','last_name','email','phone','address1','address2','city','zip','collection-delivery','eshop_payment','amount'); $keep_order = array('item_name_','quantity_','amount_'); New matching logic - foreach($data as $key=>$value){ $base_key = preg_replace('/(_\d+)$/','_',$key); if(in_array($base_key,$keep_main)){ // normal template item $tpl[$base_key] = $value; } if(in_array($base_key,$keep_order)){ // order/repeating template item $otpl[$base_key][] = $value; } } Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306906 Share on other sites More sharing options...
OM2 Posted January 12, 2012 Author Share Posted January 12, 2012 wow...! thanks i need some time to go through the code i've copied and pasted what u've given + added the new additions but am getting 2 errors for: foreach($otpl as $key => $arr){ and foreach($pivot as $row){ for both it says "invlaid argument supplied foreach()" any ideas? thanks Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306910 Share on other sites More sharing options...
PFMaBiSmAd Posted January 12, 2012 Share Posted January 12, 2012 Those errors mean that the coded did not match any of the order information. Either the code is doing something wrong or for the specific response being processed, the data was not expected or the status code wasn't for a successful order. Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1306919 Share on other sites More sharing options...
laffin Posted January 12, 2012 Share Posted January 12, 2012 I came up with this solution <?php $text=<<<EOF shipping_1: 0.00 first_name: Joe last_name: Bloggs email: [email protected] phone: 020 address1: 10 Road address2: city: London zip: EC1V 0HB collection-delivery: collection eshop_payment: cash item_name_1: item one quantity_1: 1 amount_1: 12.95 item_name_2: item number two quantity_2: 1 amount_2: 5.95 item_name_3: item num three quantity_3: 1 amount_3: 5.50 numberofproducts: 3 amount: 24.40 EOF; $template=<<<EOF shipping: {shipping:shipping_\d+} name: {first_name} {last_name} email: {email} phone: {phone} address: {address:address[12]}, {city} {zip} collection-delivery: {collection-delivery} eshop_payment: {eshop_payment} order: {items:(item_name|quantity|amount)_(\d+)} amount: {amount} EOF; header('Content-Type: text/plain'); function adddlm($pattern) { return "@^($pattern): (.*?)[\\r\\n]*\$@m"; } function process_address($match) { global $var; $var[]=$match[2]; } function end_address() { global $var; return implode(', ',$var); } function process_shipping($match) { global $var; $var[]=$match[2]; } function end_shipping() { global $var; return number_format(array_sum($var),2); } function process_items($match) { global $var; $var[$match[3]][$match[2]]=$match[4]; return $match[0]; } function do_item($item) { return "$item[quantity] X $item[item_name] $item[amount]"; } function end_items() { global $var; $var=array_map('do_item',$var); return implode(PHP_EOL,$var); } preg_match_all('@^([^:]+): (.*?)[\r\n]*$@m',$text,$matches,PREG_SET_ORDER); foreach($matches as $match) $vars[$match[1]]=$match[2]; preg_match_all('@{([^}]+)}@',$template,$matches); $output=$template; foreach($matches[1] as $tplfun) { if(strpos($tplfun,':')) { list($func,$patterns)=explode(':',$tplfun); $patterns=array_map('adddlm',explode(',',$patterns)); if(function_exists("process_$func")) { $var=NULL; preg_replace_callback($patterns,"process_$func",$text); $var=call_user_func("end_$func"); } else { $var='N/A'; } } else { $var=isset($vars[$tplfun])?$vars[$tplfun]:'N/A'; } $output=str_replace('{'.$tplfun.'}',$var,$output); } echo $output; ?> The only part I didnt implement was the multiple item sets line within the template, It's possible, just have to implement more code in the template structure. Takes two formats, simple replacement {var} and complex (multiple lines) Quote Link to comment https://forums.phpfreaks.com/topic/254850-need-advice-on-how-best-to-go-through-an-array-and-match-strings/#findComment-1307005 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.