I'm fairly new to Mongo and PHP. I've been fine with the relatively simple stuff but I've hit a snag performing a php find within a mongo collection conditionally limited by an array of _id's.
Here's a walkthrough...
// "characters" collection
{ "_id":{"$id":"3f177b70df1e69fe5c000001"}, "firstname":"Bugs", "lastname":"Bunny" }
{ "_id":{"$id":"3f2872eb43ca8d4704000002"}, "firstname":"Elmer", "lastname":"Fudd" }
{ "_id":{"$id":"3f287bb543ca8de106000003"}, "firstname":"Daffy", "lastname":"Duck" }
// "items" collection
{ "_id":{"$id":"4f177b70df1e69fe5c000001"}, "mdl":"carrot", "mfg":"Wild Hare Farms ltd.", "ownerid":{"$id":"3f177b70df1e69fe5c000001"} }
{ "_id":{"$id":"4f2872eb43ca8d4704000002"}, "mdl":"hat", "mfg":"Acme Inc.", "ownerid":{"$id":"3f2872eb43ca8d4704000002"} }
{ "_id":{"$id":"4f287bb543ca8de106000003"}, "mdl":"spaceship", "mfg":"Acme Inc.", "ownerid":{"$id":"3f287bb543ca8de106000003"} }
// Let's say I do a find on the item collection for a specific manufacturer...
$itemOwners = $db->items->find(array("mfg" => "Acme Inc."), array("ownerid"));
// The result looks something like this...
[
"4f2872eb43ca8d4704000002":{"_id":{"$id":"4f2872eb43ca8d4704000002"},"ownerid":{"$id":"3f2872eb43ca8d4704000002"}},
"4f287bb543ca8de106000003":{"_id":{"$id":"4f287bb543ca8de106000003"},"ownerid":{"$id":"3f287bb543ca8de106000003"}}
]
// I'd now like to perform a find on the character collection and get the corresponding owner documents.
// To do that I need to build the $in array from the previous find results...
foreach ($itemOwners as $doc)
$itemOwnersTemp[] = $doc["ownerid"];
$itemOwners = $itemOwnersTemp;
// The resulting array looks like this. I've read that the ids need to be in MongoId format. Seems like they are?
[
{"$id":"3f2872eb43ca8d4704000002"},
{"$id":"3f287bb543ca8de106000003"}
]
// and (finally) the conditional find. The result set is always empty. What am I tripping up on?
$characterDocs = $db->characters->find(array("_id" => array('$in' => $itemOwners));
I've just tried this with slightly modified code:
<?php
$m = new Mongo('localhost:13000', array( 'replicaSet' => 'a' ) );
$db = $m->demo;
$db->authenticate('derick', 'xxx');
// "characters" collection
$c = $db->characters;
$c->insert(array( '_id' => new MongoID("3f177b70df1e69fe5c000001"), 'firstname' => 'Bugs', 'lastname' => 'Bunny' ));
$c->insert(array( '_id' => new MongoID("3f2872eb43ca8d4704000002"), 'firstname' => 'Elmer', 'lastname' => 'Fudd' ));
$c->insert(array( '_id' => new MongoID("3f287bb543ca8de106000003"), 'firstname' => 'Daffy', 'lastname' => 'Duck' ));
// "items" collection
$c = $db->items;
$c->insert(array( '_id' => new MongoId("4f177b70df1e69fe5c000001"), 'mdl' => 'carrot', 'ownerid' => new MongoID('3f177b70df1e69fe5c000001')));
$c->insert(array( '_id' => new MongoId("4f2872eb43ca8d4704000002"), 'mdl' => 'hat', 'ownerid' => new MongoID('3f2872eb43ca8d4704000002')));
$c->insert(array( '_id' => new MongoId("4f287bb543ca8de106000003"), 'mdl' => 'space', 'ownerid' => new MongoID('3f287bb543ca8de106000003')));
// Let's say I do a find on the item collection for a specific manufacturer...
$itemOwners = $db->items->find(array("mdl" => "hat"), array("ownerid"));
// The result looks something like this...
/*[
"4f2872eb43ca8d4704000002":{"_id":{"$id":"4f2872eb43ca8d4704000002"},"ownerid":{"$id":"3f2872eb43ca8d4704000002"}},
"4f287bb543ca8de106000003":{"_id":{"$id":"4f287bb543ca8de106000003"},"ownerid":{"$id":"3f287bb543ca8de106000003"}}
]*/
// I'd now like to perform a find on the character collection and get the corresponding owner documents.
// To do that I need to build the $in array from the previous find results...
foreach ($itemOwners as $doc) {
$itemOwnersTemp[] = $doc["ownerid"];
}
$itemOwners = $itemOwnersTemp;
// The resulting array looks like this. I've read that the ids need to be in MongoId format. Seems like they are?
/*
[
{"$id":"3f2872eb43ca8d4704000002"},
{"$id":"3f287bb543ca8de106000003"}
]
*/
// and (finally) the conditional find. The result set is always empty. What am I tripping up on?
$characterDocs = $db->characters->find(array("_id" => array('$in' => $itemOwners)));
var_dump( iterator_to_array( $characterDocs ) );
?>
And it provides the output that I expect:
array(1) {
["3f2872eb43ca8d4704000002"]=>
array(3) {
["_id"]=>
object(MongoId)#3 (1) {
["$id"]=>
string(24) "3f2872eb43ca8d4704000002"
}
["firstname"]=>
string(5) "Elmer"
["lastname"]=>
string(4) "Fudd"
}
}
Somewhere, I think you're doing a wrong conversion but it's difficult to tell as you don't show the output of PHP.
Instead of this:
$itemOwnersTemp[] = $doc["ownerid"];
Try this:
$itemOwnersTemp[] = new MongoID($doc["ownerid"]);
Related
First Post, any help is appreciated.
I have a PHP file (Array) with data from a database, which i need to compare to a JSON file (Object).
By looking through forums i have seen stuff about jQuery and AJAX requests, so i was thinking maybe i need to use those for my solution.
The PHP Array
<?php
$codes = [
[
'code' => '1111',
'name' => 'Management',
],
[
'code' => '1305',
'name' => 'Price',
],
[
'code' => '1161',
'name' => 'Service',
]
and the array goes on.
The JSON Object
[{"name":"Management","code":"1111","accountingArea":"3194","managerUsername":null},
{"name":"Storage","code":"9033","accountingArea":"3194","managerUsername":null}]
is the way the JSON Object is formatted.
For some reason it has no name, which i don't know if it's a problem, when it comes to comparing two different files.
The product will need to be a PHP script that tells the user which entries are missing in the other file.
The only thing that needs to be compared are the codes.
I have not done anything with JSON files yet and am quite new to PHP too.
So far i have only included both the files in my script file and don't know where to start things off.
You can take JSON from file as string and use php json_decode() function that function will return a php array, then you can just make a foreach cicle and check the codes
Your code should be similar to this
<?php
$codes = [
[
'code' => '1111',
'name' => 'Management',
],
[
'code' => '1305',
'name' => 'Price',
],
[
'code' => '1161',
'name' => 'Service',
]];
$json_str = '[{"name":"Management","code":"1111","accountingArea":"3194","managerUsername":null},
{"name":"Management","code":"11141","accountingArea":"3194","managerUsername":null},
{"name":"Management","code":"1305","accountingArea":"3194","managerUsername":null},
{"name":"Management","code":"1161","accountingArea":"3194","managerUsername":null},
{"name":"Storage","code":"9033","accountingArea":"3194","managerUsername":null}]';
$json_array = json_decode($json_str, true);
$result = array();
$codesN = array_column($codes, 'code');
$i = 0;
for($i; $i < count($json_array); $i++) {
if(in_array($json_array[$i]["code"], $codesN)) {
$result[] = $json_array[$i]["code"];
}
}
var_dump($result);
This will return:
array(3) {
[0]=>
string(4) "1111"
[1]=>
string(4) "1305"
[2]=>
string(4) "1161"
}
Try this. See comments for step-by-step explanation.
Output:
array(2) {
[1]=>
string(4) "1305"
[2]=>
string(4) "1161"
}
Code:
<?php
// Your input array.
$codes = [
[
'code' => '1111',
'name' => 'Management',
],
[
'code' => '1305',
'name' => 'Price',
],
[
'code' => '1161',
'name' => 'Service',
]
];
// Your input JSON.
$json = '[{"name":"Management","code":"1111","accountingArea":"3194","managerUsername":null},{"name":"Storage","code":"9033","accountingArea":"3194","managerUsername":null}]';
// Get differences:
// Get values from $codes that are not present in $json.
// Switch the arguments to perform the inverse comparison.
$diff = array_diff(
array_column($codes, 'code'),
array_column(json_decode($json, TRUE), 'code')
);
var_dump($diff);
/*
Output:
array(2) {
[1]=>
string(4) "1305"
[2]=>
string(4) "1161"
}
*/
Consider an Array
$lettersArray = [A,C,E,G]
and my MongoDB Collection has the following structure.
{
Collection : {
letters:{
A:{...},
B:{...},
...
Z:{...}
}
}
}
Consider that the Letter Sub document is a part of a larger collection. Hence I am using Aggregation.
Right Now I have tried to project -
['$Collection.letters' => ['$elemMatch' => ['$in' => $lettersArray]]
and also tried
['Letters' => ['$in' => [$lettersArray,'$Collection.letters']]
But it didn't work.
In the End, I want result like the following:
[
Collection => [
letters => [
A => [...],
C => [...],
E => [...],
G => [...]
]
]
]
Is there any way to do this?
In PHP you can use array_combine with array_fill to create the empty arrays.
$lettersArray = ['A','C','E','G'];
$letters = array_combine($lettersArray, array_fill(0,count($lettersArray), []));
Array_fill creates an array from index 0, to the count of items in $lettersArray with the contents []
Output:
array(4) {
["A"]=>
array(0) {
}
["C"]=>
array(0) {
}
["E"]=>
array(0) {
}
["G"]=>
array(0) {
}
}
https://3v4l.org/TeoFv
I think you are mistaken in the way you are trying to access the documents' information.
If you take a look at your MongoDB document, you will see that it is in fact not an array, so you should not use $elemMatch to project these fields, but simple projections. In your case, you should project in this way:
[
'$project' => [
'Collection.letters.A' => 1,
'Collection.letters.C' => 1,
'Collection.letters.E' => 1,
'Collection.letters.G' => 1
]
]
By the way, you don't need to use the aggregation framework to compute such a projection. You could just use find(), and use the projection in the options, which are the functions' second argument:
myCollection->find(query, [
'projection' => [
'Collection.letters.A' => 1,
'Collection.letters.C' => 1,
'Collection.letters.E' => 1,
'Collection.letters.G' => 1
]
]);
Cheers,
Charles
Assume that I have inserted the following document with PHP (Note the "ó".)
$dbs->insert(array('name' => 'televisión'));
In mongodb database server is saved as follows
{ "name" : "televisi��n" }
If I invoke the findOne method as follow, (NOTE THE ó)
$doc = $dbs->findOne(array('name' => "televisión"));
It return me the correct value
[name] => televisión
Everything fine until here.
So, imagine that from php I need to determine that the document televisión is into mongodb database, but I get the value from an URL without the accent "ó", i.e. television, so.
$doc = $dbs->findOne(array('name' => "television"));
findOne method is returning null, so don't match the document.
Is there any way for this not return null value and can find the document regardless of the accent?
Thanks in advance!
In mongodb database server is saved as follows
{ "name" : "televisi��n" }
That's probably because your shell doesn't show UTF-8 properly.
As for:
Is there any way for this not return null value and can find the document regardless of the accent?
You can do that with the new text search functionality:
<?php
$m = new MongoClient;
$d = $m->test;
$c = $d->so;
// Just dropping here to create a controlled output - no need to do this yourself.
$c->drop();
$c->ensureIndex(
array( 'name' => 'text' ),
array( 'default_language' => 'spanish' )
);
$c->insert( array('name' => 'televisión' ) );
$res = $d->command( array( 'text' => 'so', 'search' => 'television' ) );
var_dump( $res['results'] );
?>
Which outputs:
array(1) {
[0] =>
array(2) {
'score' =>
double(1)
'obj' =>
array(2) {
'_id' =>
class MongoId#6 (1) {
...
}
'name' =>
string(11) "televisión"
}
}
}
For text search to work, you need MongoDB 2.4.x, and you need to specifically enable it with the --setParameter textSearchEnabled=true flag to mongod or add to your code:
$d->command( array( 'setParameter' => 1, 'textSearchEnabled' => true ) );
I am using XMLRPC to build an XML structure that passes across product information to a 3rd party system. I need to build an associative array of the product custom options and I don't know what syntax to use as the value in each case is an object.
I can't debug and play about with it as I normally would as believe it or not I've had to do this on a live site so I've been emailing myself the array to make sure it looks alright, then when it does I've applied it to the site, XMLRPC throws an error saying it can't serialize the object I've built, then I quickly switch it back out again.
If I hardcode it like this it works fine.
$item_array = array(
"product_id" => new xmlrpcval($item->getSku()),
"name" => new xmlrpcval($item->getName()),
"price" => new xmlrpcval($item->getBaseCalculationPrice(), 'double'),
"vat_inclusive" => new xmlrpcval(0,'int'),
"quantity" => new xmlrpcval($item->getQty(),'int'),
"option_text" => new xmlrpcval(
array(
"option_1" => new xmlrpcval("Colour: Military Black"),
"option_2" => new xmlrpcval("Sizes: L")
),
"struct")
);
It's the folowing section I need to generate, specifically the array in a foreach loop as I don't know how many options there will be;
"option_text" => new xmlrpcval(
array(
"option_1" => new xmlrpcval("Colour: Military Black"),
"option_2" => new xmlrpcval("Sizes: L")
),
"struct")
If I do it like below then it comes out fine, but the value is a string rather than an object, which XMLRPC can't serialize;
$optioncount = 1;
$attList = array();
foreach ( $attributes as $attribute => $value ) {
$attpair = implode(": ", $value);
$attList['option_'. $optioncount] = 'new xmlrpcval("'.$attpair.'")';
$optioncount++;
}
If I var_dump($attList) I get;
array(2) {
["option_1"]=>
string(39) "new xmlrpcval("Colour: Military Black")"
["option_2"]=>
string(25) "new xmlrpcval("Sizes: L")"
}
Any other way seems to turn $attList into a total mess - I know this should be very basic but for the life of my I can't get this working. Thanks for any pointers.
If I var_dump($attList) when I use new xmlrpcval($attpair); I get;
array(2) {
["option_1"]=>
object(xmlrpcval)#469 (3) {
["me"]=>
array(1) {
["string"]=>
string(22) "Colour: Military Black"
}
["mytype"]=>
int(1)
["_php_class"]=>
NULL
}
["option_2"]=>
object(xmlrpcval)#433 (3) {
["me"]=>
array(1) {
["string"]=>
string(8) "Sizes: L"
}
["mytype"]=>
int(1)
["_php_class"]=>
NULL
}
}
Building your array must look like:
$optioncount = 1;
$attList = array();
foreach ( $attributes as $attribute => $value ) {
$attpair = implode(": ", $value);
$attList['option_'. $optioncount] = new xmlrpcval($attpair);
$optioncount++;
}
And then:
"option_text" => new xmlrpcval(
$attList,
"struct")
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