First off thanks for writing this class. It has made life much easier for me in building applications.
I have CIM set up and I have no problem adding users, processing payments, etc. However I am stuck on adding line items. The examples on github use static population of the array used to create the XML request EX:
'lineItems' => array(
'itemId' => 'ITEM00001',
'name' => 'name of item sold',
'description' => 'Description of item sold',
'quantity' => '1',
'unitPrice' => '6.95',
'taxable' => 'true'
),
'lineItems' => array(
'itemId' => 'ITEM00002',
'name' => 'other name of item sold',
'description' => 'Description of other item sold',
'quantity' => '1',
'unitPrice' => '1.00',
'taxable' => 'true'
),
This works great if you are manually creating things but I am dynamically creating these line items based on user input. Unfortunately, I am unable do add multiple line items to the array due to the fact that the key ('lineItems') gets overwritten and I end up with one line item.
I have tried creating an array of lineItems and then merging it with no luck. Hopefully I am just missing a simple fix for this.
Thanks for responding John! Once again, great work on this class it has made my life much easier.
Here is what I ended up doing for simplicity. I am sure this can be expounded upon if necessary, but for me this worked perfect. Instead of passing multiple line items on the same level of the array I created line items as their own array and then modified setParamaters() to iterate through that array.
private function setParameters($xml, $array)
{
if (is_array($array))
{
foreach ($array as $key => $value)
{
if (is_array($value))
{
if($key == 'lineItems'){
foreach($value as $lineitems){
$line_item = $xml->addChild('lineItems');
foreach($lineitems as $itemkey => $itemvalue) {
$line_item->addChild($itemkey, $itemvalue);
}
}
}
else
{
$xml->addChild($key);
$this->setParameters($xml->$key, $value);
}
}
else
{
$xml->$key = $value;
}
}
}
}
This suited my needs perfectly and made it so I did not have to change anything on the front end except nesting the lineItems array. So the array I am sending looks more like this:
["lineItems"]=>
array(2) {
[0]=>
array(6) {
["itemId"]=>
string(9) "ITEM00010"
["name"]=>
string(21) "Blah Blah"
["description"]=>
string(21) "Blah Blah Description"
["quantity"]=>
string(1) "1"
["unitPrice"]=>
string(4) "100"
["taxable"]=>
string(5) "false"
}
[1]=>
array(6) {
["itemId"]=>
string(9) "ITEM00011"
["name"]=>
string(25) "Thing Thing"
["description"]=>
string(25) "Thing Thing Description"
["quantity"]=>
string(1) "2"
["unitPrice"]=>
string(3) "50"
["taxable"]=>
string(5) "false"
}
}
Also, for anyone out there looking to build the arrays for the line items I did this:
foreach ($services as $key => $service){
$line_items["lineItems"][] = array(
'itemId' => 'ITEM000'.$key,
'name' => $service->name,
'description' => $service->name,
'quantity' => $service_count[$key],
'unitPrice' => $service->price,
'taxable' => 'false'
);
}
And then just added it to the transaction_array that I passed to the AuthnetXML instance.
Thanks again!
Joel
I am the author of that class. The AuthnetXML class currently has a bug in it that results in the results you saw. You would need to make a change to core class to work around this.
I received an email from a user who offered a solution which I have not had a chance to review yet. I'll give you the same information they gave me and hopefully it helps you:
The problem is that you can't have duplicate keys at the same level in an array. If you do the last one entered wins and the rest are overwritten.
So you need a way to represent repeating items from XML in and array. I decide to use the JSON methods to keep it simple. A quick wat to convert Simple XML to and array is to pass it through JSON.
$array = json_decode( json_encode( $simpleXML), true);
That will convert XML like this:
<transactionSettings>
<setting>
<settingName>allowPartialAuth</settingName>
<settingValue>false</settingValue>
</setting>
<setting>
<settingName>duplicateWindow</settingName>
<settingValue>0</settingValue>
</setting>
<setting>
<settingName>emailCustomer</settingName>
<settingValue>false</settingValue>
</setting>
<setting>
<settingName>recurringBilling</settingName>
<settingValue>false</settingValue>
</setting>
<setting>
<settingName>testRequest</settingName>
<settingValue>false</settingValue>
</setting>
</transactionSettings>
To an array like this:
array(
'transactionSettings' => array(
'setting' => array(
0 => array('settingName' =>'allowPartialAuth' , 'settingValue' => 'false',),
1 => array('settingName' => 'duplicateWindow', 'settingValue' => '0', ),
2 => array('settingName' => 'emailCustomer', 'settingValue' => 'false', ),
3 => array('settingName' => 'recurringBilling', 'settingValue' => 'false',),
4 => array( 'settingName' => 'testRequest', false, ),
)
);
So you need to modify AuthNetXML.class to recognize this format. Just replace your setParameters() method with:
private function setParameters($xml, $array)
{
if (is_array($array))
{
$first = true;
foreach ($array as $key => $value)
{
if (is_array($value)) {
if( is_numeric($key) ) {
if($first){
$xmlx = $xml;
$first = false;
} else {
$parent = $xml->xpath('parent::*');
$xmlx = $parent[0]->addChild($xml->getName());
}
} else {
$xmlx = $xml->addChild($key);
}
$this->setParameters($xmlx, $value);
}
else
{
$xml->$key = $value;
}
}
}
}
UPDATE 2012-08-21
This bug has been fixed. Sample code has been updated.
EDIT: I've solved this issue. The lineItems key must be passed in like so:
'lineItems' => array(
'itemId' => '13',
'name' => 'hello',
'description' => 'hello description',
'quantity' => '1',
'unitPrice' => '55.00'
),
Note this differs from what's provided the samples over at the Authorize.net-XML repo. I'm going to head over there now and submit a fix.
ORIGINAL QUESTION:
I'm running into a similar problem involving the Authorize.net-XML class; when executing the createCustomerProfileTransactionRequest() method I receive the following error:
The element 'lineItems' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd' has
invalid child element 'lineItem' in namespace
'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.
List of possible elements expected: 'itemId' in namespace
'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.' (length=272)
I've even gone so far as to paste in the sample input provided here, but receive the identical error? The class is otherwise working just fine; I use the createTransactionRequest() and createCustomerProfileRequest() methods to process AIM transactions and create CIM profiles without problem.
To reiterate, I'm even attempting to use identical sample input as that found on GitHub.
Any ideas?
Thanks so much!
Jason
Related
I have multidimensional array like this:
$obj = array(
"a" => array(
"aa" => array(
"aaa" => 1
),
"bb" => 2,
),
"b" => array(
"ba" => 3,
"bb" => 4,
),
"c" => array(
"ca" => 5,
"cb" => 6,
),
);
I can not figured out a neatest way, e.g. custom-depth function, to extract item at specific location with arguments to function (or array of key names). For example:
echo $obj[someFunc("a", "aa", "aaa")];
... should return 1.
print_r($obj[someFunc("a")]);
... should return:
Array
(
[aa] => Array
(
[aaa] => 1
)
[bb] => 2
)
What is the best way to accomplished this with php7 features?
Since PHP 5.6, ["Variadic functions"][1] have existed. These provide us with a nice simple to read way to collect arguments used in calling a function into a single array. For example:
function getValue(...$parts) {
var_dump($parts);
}
getValue('test', 'part');
Will output:
array(2) {
[0]=>
string(4) "test"
[1]=>
string(4) "part"
}
This was all possible before using built-in functions to get the parameters, but this is more readable.
You could also be a little more explicit with the argument types, but I'll leave that for you to figure out if necessary.
You next major challenge is to loop through the arguments. Something like this will produce the value that you desire.
function getValue(...$parts) {
$returnValue = $obj;
foreach ($parts as $part) {
$returnValue = $obj[$part];
}
return $returnValue;
}
However, this is rather crude code and will error when you try calling it to access non-existent parts. Have a play and fix those bits.
[1]: https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list
I'm trying to get an array to output in a key and value format. When I use the second style shown below it works fine, but when I use the first style I don't get the same results. I think something is different in how the keys are used but I'm not entirely sure.
So, is there any difference between building an array like this:
$featured = get_post_meta($profileID, 'profile_featured', true);
if ($featured == '1'){$my_fake_pages["featured"] = "Featured";};
$celebs = get_post_meta($profileID, 'profile_celebs', true);
if ($celebs== '1'){$my_fake_pages["coversandcelebrities"] = "Covers & Celebrities";};
$fashion = get_post_meta($profileID, 'profile_fashion', true);
if ($fashion == '1'){$my_fake_pages["fashion"] = "Fashion";};
$beauty = get_post_meta($profileID, 'profile_beauty', true);
if ($beauty == '1'){$my_fake_pages["beauty"] = "Beauty";};
$advertising = get_post_meta($profileID, 'profile_advertising', true);
if ($advertising == '1'){$my_fake_pages["advertising"] = "Advertising";};
$bio = get_post_meta($profileID, 'profile_bio', true);
if ($bio == '1'){$my_fake_pages["bio"] = "Bio";};
and writing one like this:
$my_fake_pages = array(
'featured' => 'Featured',
'coversandcelebrities' => 'Covers & Celebrities',
'fashion' => 'Fashion',
'beauty' => 'Beauty',
'advertising' => 'Advertising',
'bio' => 'Bio'
);
Thanks in advance.
** To be clear, I know one is conditional and the other isn't. What I'm wanting to know is if the output style of the first example is equivalent to that of the second, where the key is the index of the array rather than a number being the index, and the value is still the value.
They are the same. To prove it, I've simplified your code and compared the two generated arrays
<?php
$a["featured"] = "Featured";
$a["coversandcelebrities"] = "Covers & Celebrities";
$a["fashion"] = "Fashion";
$a["beauty"] = "Beauty";
$a["advertising"] = "Advertising";
$a["bio"] = "Bio";
$b = array(
'featured' => 'Featured',
'coversandcelebrities' => 'Covers & Celebrities',
'fashion' => 'Fashion',
'beauty' => 'Beauty',
'advertising' => 'Advertising',
'bio' => 'Bio'
);
$same = !array_diff($a, $b) && !array_diff($b, $a);
var_dump($a);
var_dump($b);
echo "<br>Same = $same";
This outputs:
array(6) { ["featured"]=> string(8) "Featured" ["coversandcelebrities"]=> string(24) "Covers & Celebrities" ["fashion"]=> string(7) "Fashion" ["beauty"]=> string(6) "Beauty" ["advertising"]=> string(11) "Advertising" ["bio"]=> string(3) "Bio" }
array(6) { ["featured"]=> string(8) "Featured" ["coversandcelebrities"]=> string(24) "Covers & Celebrities" ["fashion"]=> string(7) "Fashion" ["beauty"]=> string(6) "Beauty" ["advertising"]=> string(11) "Advertising" ["bio"]=> string(3) "Bio" }
Same = 1
The if() version only adds to the array if the conditions are met. the second one adds everything, unconditionally. that has nothing to do with the keys.
It's the difference between going to the grocery store with a shopping list and only getting what's on the list, and going to the store and buying 1 of everything.
If i understand your question right, you want to know if building an array like this:
$my_fake_pages["featured"] = "Featured";
$my_fake_pages["coversandcelebrities"] = "Covers & Celebrities";
$my_fake_pages["fashion"] = "Fashion";
$my_fake_pages["beauty"] = "Beauty";
$my_fake_pages["advertising"] = "Advertising";
$my_fake_pages["bio"] = "Bio";
is any different than building it like this:
$my_fake_pages = array(
'featured' => 'Featured',
'coversandcelebrities' => 'Covers & Celebrities',
'fashion' => 'Fashion',
'beauty' => 'Beauty',
'advertising' => 'Advertising',
'bio' => 'Bio'
);
The answer is no. Both generate an associative array (instead of a numbered array) like this:
array(6) {
["featured"]=>
string(8) "Featured"
["coversandcelebrities"]=>
string(24) "Covers & Celebrities"
["fashion"]=>
string(7) "Fashion"
["beauty"]=>
string(6) "Beauty"
["advertising"]=>
string(11) "Advertising"
["bio"]=>
string(3) "Bio"
}
And also since PHP 5.4.x you can have a "short syntax" array generation like this (notice the []):
$my_fake_pages = [
'featured' => 'Featured',
'coversandcelebrities' => 'Covers & Celebrities',
'fashion' => 'Fashion',
'beauty' => 'Beauty',
'advertising' => 'Advertising',
'bio' => 'Bio'
];
** To be clear, I know one is conditional and the other isn't. What I'm wanting to know is if the output style of the first example is equivalent to that of the second, where the key is the index of the array rather than a number being the index, and the value is still the value.
Yes, if all conditions evaluate as true, then the array format is exactly the same. Both will be associative arrays with the same key
Here is the example PHP array representation
array(
"test1" => array(
"test1subtest1" => array(..)
),
"test2" => array(
"test2subtest1" => array(..)
)
)
So, here is the question: is there any tool in PHP which can be used to assign values to multidimensional array with random depth and index names? It suppose to look like this:
$valuetoassing = "TESTVALUE";
$testarray = array();
array_assign_multidimensional($testarray, $valuetoassing, array("test1", "test1subtest1", "test1subtest1subtest1"));
Problem is that I do not know what depth the array will have, so I can not code it. Index names are also generated at the run time.
EDIT: I figured that my particular case can be solved using some kind of linked list (stored as array with items that contain actual data and pointer to the index of the next element), but I'm sure I'll meet this problem again in the future so I will not close the question right now.
This is pretty easy to do using references.
function array_assign_multidimensional(&$input_array, $value, $list){
$assignee = &$input_array;
foreach($list as $key){
$assignee = &$assignee[$key];
}
$assignee = $value;
}
$input = array(
'firstLayer' => array(
'secondLayer' => array(
'test' => 'old'
)
),
'randomOutlyingValue' => ''
);
array_assign_multidimensional($input, 'new', array('firstLayer', 'secondLayer', 'test'));
var_dump($input);
/*
array(2) {
["firstLayer"]=>
array(1) {
["secondLayer"]=>
array(1) {
["test"]=>
string(3) "new"
}
}
["randomOutlyingValue"]=>
string(0) ""
}
*/
Here's a generic query sample that represents my problem.
$query = "SELECT Account.Name, Name from Contract WHERE AccountId = '001U0000003q6scIAA'";
$response = $mySforceConnection->query($query);
foreach ($response->records as $record) {
$sObject = new SObject($response->records[0]);
var_dump($sObject);
echo $sObject->sobjects[0]->Name."<br/>\n";
echo $record->fields->Name."<br/>\n";
echo "*******************************<br/>\n";
echo "*******************************<br/>\n";
}
The problem is that the Account.Name field will not display. Just in case my entire method is wrong, The idea is to grab all of the contracts assigned to a specific account and display both the account name and the pertinent info from the contract. (so far all contract fields display perfectly. Just can't display the account name)
Thanks for the assistance in advance!
Here's the var_export().. (I removed all of my custom fields)
SObject::__set_state(array( 'type' => 'Contract', 'fields' => stdClass::__set_state(array(
'AccountId' => '001U0000003q6scIAA', 'Name' => 'test contract', '1' =>
SObject::__set_state(array( 'type' => 'Account', 'fields' => stdClass::__set_state(array(
'Name' => 'test Account name', )), )), )), ))
Here's the var_dump() ...
object(SObject)#5 (2) { ["type"]=> string(8) "Contract" ["fields"]=> object(stdClass)#1871 (14) {
["AccountId"]=> string(18) "001U0000003q6scIAA" ["Name"]=> string(18) "Test Contract Name"
["1"]=> object(SObject)#1869 (2) { ["type"]=> string(7) "Account" ["fields"]=> object(stdClass)#1870 (1) { ["Name"]=> string(15) "Account Name" } } } }
So I finally found something that put me on the right path. here's how I solved it...
foreach ($response->records as $record) {
echo $record->fields->{1}->fields->Name."<br/>\n";
echo $record->fields->Name."<br/>\n";
echo "*******************************<br/>\n";
echo "*******************************<br/>\n";
}
I had tried this approach originally but without the {} which gave an error. So all you have to do is look at the print_r() output, change the number to the number it gives you here - ["1"]=> object(SObject)#1869 and this query will show!
Thanks Daan for trying to help!
I have an PHP array that looks something like this:
Index Key Value
[0] 1 Awaiting for Confirmation
[1] 2 Assigned
[2] 3 In Progress
[3] 4 Completed
[4] 5 Mark As Spam
When I var_dump the array values i get this:
array(5) { [0]=> array(2) { ["key"]=> string(1) "1" ["value"]=> string(25) "Awaiting for Confirmation" } [1]=> array(2) { ["key"]=> string(1) "2" ["value"]=> string(9) "Assigned" } [2]=> array(2) { ["key"]=> string(1) "3" ["value"]=> string(11) "In Progress" } [3]=> array(2) { ["key"]=> string(1) "4" ["value"]=> string(9) "Completed" } [4]=> array(2) { ["key"]=> string(1) "5" ["value"]=> string(12) "Mark As Spam" } }
I wanted to remove Completed and Mark As Spam. I know I can unset[$array[3],$array[4]), but the problem is that sometimes the index number can be different.
Is there a way to remove them by matching the value name instead of the key value?
Your array is quite strange : why not just use the key as index, and the value as... the value ?
Wouldn't it be a lot easier if your array was declared like this :
$array = array(
1 => 'Awaiting for Confirmation',
2 => 'Asssigned',
3 => 'In Progress',
4 => 'Completed',
5 => 'Mark As Spam',
);
That would allow you to use your values of key as indexes to access the array...
And you'd be able to use functions to search on the values, such as array_search() :
$indexCompleted = array_search('Completed', $array);
unset($array[$indexCompleted]);
$indexSpam = array_search('Mark As Spam', $array);
unset($array[$indexSpam]);
var_dump($array);
Easier than with your array, no ?
Instead, with your array that looks like this :
$array = array(
array('key' => 1, 'value' => 'Awaiting for Confirmation'),
array('key' => 2, 'value' => 'Asssigned'),
array('key' => 3, 'value' => 'In Progress'),
array('key' => 4, 'value' => 'Completed'),
array('key' => 5, 'value' => 'Mark As Spam'),
);
You'll have to loop over all items, to analyse the value, and unset the right items :
foreach ($array as $index => $data) {
if ($data['value'] == 'Completed' || $data['value'] == 'Mark As Spam') {
unset($array[$index]);
}
}
var_dump($array);
Even if do-able, it's not that simple... and I insist : can you not change the format of your array, to work with a simpler key/value system ?
...
$array = array(
1 => 'Awaiting for Confirmation',
2 => 'Asssigned',
3 => 'In Progress',
4 => 'Completed',
5 => 'Mark As Spam',
);
return array_values($array);
...
$key = array_search("Mark As Spam", $array);
unset($array[$key]);
For 2D arrays...
$remove = array("Mark As Spam", "Completed");
foreach($arrays as $array){
foreach($array as $key => $value){
if(in_array($value, $remove)) unset($array[$key]);
}
}
You can use this
unset($dataArray['key']);
Why do not use array_diff?
$array = array(
1 => 'Awaiting for Confirmation',
2 => 'Asssigned',
3 => 'In Progress',
4 => 'Completed',
5 => 'Mark As Spam',
);
$to_delete = array('Completed', 'Mark As Spam');
$array = array_diff($array, $to_delete);
Just note that your array would be reindexed.
Try this:
$keys = array_keys($array, "Completed");
/edit
As mentioned by JohnP, this method only works for non-nested arrays.
I kinda disagree with the accepted answer. Sometimes an application architecture doesn't want you to mess with the array id, or makes it inconvenient. For instance, I use CakePHP quite a lot, and a database query returns the primary key as a value in each record, very similar to the above.
Assuming the array is not stupidly large, I would use array_filter. This will create a copy of the array, minus the records you want to remove, which you can assign back to the original array variable.
Although this may seem inefficient it's actually very much in vogue these days to have variables be immutable, and the fact that most php array functions return a new array rather than futzing with the original implies that PHP kinda wants you to do this too. And the more you work with arrays, and realize how difficult and annoying the unset() function is, this approach makes a lot of sense.
Anyway:
$my_array = array_filter($my_array,
function($el) {
return $el["value"]!="Completed" && $el!["value"]!="Marked as Spam";
});
You can use whatever inclusion logic (eg. your id field) in the embedded function that you want.
The way to do this to take your nested target array and copy it in single step to a non-nested array.
Delete the key(s) and then assign the final trimmed array to the nested node of the earlier array.
Here is a code to make it simple:
$temp_array = $list['resultset'][0];
unset($temp_array['badkey1']);
unset($temp_array['badkey2']);
$list['resultset'][0] = $temp_array;
for single array Item use reset($item)