How do I return JSON Arrays instead of objects using FuelPHP's ORM and Controller_Rest - fuelphp

Controller_Something extends Controller_Rest {
public function get_something() {
$query = Model_Something::query()->related('hasMany')->get();
return $this->response($query);
}
}
Returns:
{
stuff: here,
looks: good,
hasMany: {
151251: {
id: 151251,
other: stuff
}
}
}
I want the relations as arrays:
{
stuff: here,
looks: good,
hasMany: [
{
id: 151251,
other: stuff
}
]
}
This happens because the ORM returns related result arrays with keys corresponding to the record's PKEY, and JSON interprets this as an Object. I want these arrays to go through array_values() or something, so the JSON result will use Array.
Currently I am doing this to "solve" the problem:
$res = Format::forge($result_set)->to_array();
$res['hasMany'] = array_values($res['hasMany']);
return $this->response($res);
But this is only useful to one or two levels, where I know the data will be.
If there are relations that are not guaranteed, I don't what to have to error-check every potential subset of a complex Model.
I just want all the one-to-many arrays to be keyed sequentially instead of by the records PKEY.

$query = Model_Something::find()->related('hasMany');
returns a query object in < 1.6, an exception in 1.6, and null in 1.6.1+. So I assume you do something else that produces that result.
If you want arrays as a result instead of objects, you need to convert the result. You can do that by calling to_array() on a model object, or by using the Format class to convert an array of model objects to an array:
$result = \Format::forge($result)->to_array();

function object_to_array($data){
$new_data2= array();
$keys = array_keys((array)$data);
foreach ($keys as $key)
{
$value = $data[$key];
if(is_numeric($key))
{
$new_data2[] = object_to_array($value);
}elseif(is_string($value) || is_null($value))
{
$new_data2[$key] = $data[$key];
}else
{
$new_data2[$key] = object_to_array($value);
}
}
return $new_data2;
}
$formattedArray = \Format::forge(Model_Product::query()->get())->to_array();
$cleanData=object_to_array($formattedArray);
echo \Format::forge($cleanData)->to_json();
This way checks the array key; if key is number and value is object or array clean key

In short: you can't unless you create a hook in Query:hydrate https://github.com/fuel/orm/blob/1.7/develop/classes/query.php#L1083, or shadowing the Query class with some implementation that returns the very same results except for hydrate.

Programmatically it is possible to be done. Following the model below, but for very deep relationships is not interesting by the complexity of the algorithm.
Model:
class Model_Something extends \Orm\Model
{
...
public function relatedAsArray()
{
$this->relationsAsArray($this->_data_relations);
}
private function relationsAsArray(&$relations)
{
foreach ($relations as $key => $relation) {
foreach ($relation as $fields) {
foreach ($fields as $field) {
if (isset($field->_data_relations)) {
$this->relationsAsArray($field->_data_relations);
}
}
}
if (is_array($relation)) {
$relations[$key] = array_values($relation);
}
}
}
}
Call of method:
$something = Model_Something::find($somethingId, array('related' => array('hasMany', 'hasMany.hasOthers')));
$something->relatedAsArray();
The result was exactly like you wanted.
Result:
{
stuff: here,
looks: good,
hasMany: [
{
id: 151251,
other: stuff,
hasOthers: [
{
id: 12312,
field: other
}, ...
]
}, ...
]
}

Related

MongoDB: How to make a query that goes through an array

I have a database filled with classes and I want to be able to find all of the classes that match the subject code and the course number provided.
course_number = ['2920', '3100', '3200', '3300', '3500', '4100', '4200', '4300', '4310', '4400', '4500']
for doc in db.all_classes.find(
{"class_schedule.subject_code": "CSCI" },
{ "class_schedule.course_number": { '$elemMatch': { course_number } } } ):
print doc

Query key with value anywhere in object hierarchy in Mongo

In Mongo how can I find all documents that have a given key and value, regardless of where that key appears in the document's key/value hierarchy?
For example the input key roID and value 5 would match both:
{
roID: '5'
}
and
{
other: {
roID: '5'
}
}
There is no built in way to do this. You might have to scan each matched document recursively to try and locate that attribute. Not recommended. You might want to think about restructuring your data or perhaps manipulating it into a more unified format so that it will be easier (and faster) to query.
If your desired key appears in a fixed number of different locations, you could use the $or operator to scan all the possibilities.
Taking your sample documents as an example, your query would look something like this:
db.data.find( { "$or": [
{ "roID": 5 },
{ "other.roID": 5 },
{ "foo.bar.roID": 5 },
{ any other possbile locations of roID },
...
] } )
If the number of documents in collection is not so large, then it can be done by this:
db.system.js.save({_id:"keyValueExisted", value: function (key, value) {
function findme(obj) {
for (var x in obj) {
var v = obj[x];
if (x == key && v == value) {
return true;
} else if (v instanceof Object) {
if (findme(v)) return true;
}
}
return false;
}
return findme(this);
}});
var param = ['roID', '5'];
db.c.find({$where: "keyValueExisted.apply(this, " + tojsononeline(param) + ");"});

sub iterator object in mongodb C driver

Here's the structure part of my collection :
{
_id: {
id:"6a6ca923517f304900badd98",
target:"00badd6a6ca923517f304998e4df"
},
...
}
The use of :
if(bson_find(iterator, mongo_cursor_bson(cursor), "_id")){
bson_iterator_subiterator(iterator, sub);
id = (char*)bson_iterator_string(sub);
}
is "working" but in reality simply returns me the result of the first field of the array found...
How to recover precisely the value of the "id" or "target" field please ?
You can also use bson_iterator_more and bson_iterator_next upon the sub-iterator(It was still an iterator).
try this:
if(bson_find(iterator, mongo_cursor_bson(cursor), "_id"))
{
bson_iterator_subiterator(iterator, sub);
while(bson_iterator_more(sub))
{
if (bson_iterator_next(sub) != BSON_EOO)
{
printf("%s: %s\n", bson_iterator_key(sub), bson_iterator_string(sub));
}
}
}

Picking an array of hashes from a hash

I have a hash coming back from an XML datasource that looks like this:
{...,
'records' :{
'record' :[
{'availability' :{'$t' :'available'}, ...},
{'availability' :{'$t' :'available'}, ...}
]
}
};
I'd like to get all the record hashes into an array so I can filter() it and do some other operations. However, when I have this statement in my pre block,
raw_records = raw.pick("$..record");
the array that gets returned is an array of two empty strings:
var raw_records = ['', ''];
The odd thing is that I can pick out just availability with expected results:
availability = raw.pick("$..availability.$t");
producing
var availability = ['available', 'available'];
What's wrong with my first pick()?
EDIT: Here is a more complete version that should help with reproducing the problem. It's slightly different, since I'm using the JSON version of the web service now:
global {
datasource hbll <- "https://svc.lib.byu.edu/services/catalog/v1/search/?field=isbn&format=json&terms=";
}
rule new_rule {
select when pageview "amazon.com/.*/?dp/(.*)/" setting (isbn)
pre {
//This is the array with two empty strings...
raw = datasource:hbll(isbn);
myfilter = function(x) { x.pick("availability") eq "available"; };
records = raw.filter(myfilter);
len = records.length();
availability = records.pick("$..availability");
middleman = len > 1 => availability[0] | availability;
available = middleman eq "available" => true | false;
url_list = records.pick("$..url");
url = len > 1 => url_list[0] | url_list;
msg = <<
<p>This book is available for checkout at the BYU Library.</p>
More information
>>;
}
notify("BYU Harold B. Lee Library", msg) with sticky=true;
}
I'm going to need a more complete example. The test app and results I got are below:
ruleset a8x167 {
meta {
name "Pick - Array of Hashes"
description <<
Testing
>>
author "Sam Curren"
logging on
}
dispatch {}
global {
raw = {
'records' :{
'record' :[
{'availability' :{'$t' :'available'}},
{'availability' :{'$t' :'available'}}
]
}
};
}
rule test {
select when pageview ".*" setting ()
pre {
raw_records = raw.pick("$..record");
availability = raw.pick("$..availability.$t");
}
notify("Hello World", "This is a sample rule.");
}
}
And Results:
var raw_records = [{'availability' :{'$t' :'available'}}, {'availability' :{'$t' :'available'}}];
var availability = ['available', 'available'];

Detect empty pick() from hash in KRL

I have a bunch of data in a hash and I am picking from it. Sometimes there will be data there to pick and sometimes there won't. What is the best way to know when there was something found by the pick operator and when there wasn't so I can react to that in my code?
The pick operator will take a second optional parameter that will make it so that it always returns the results in an array. This means that if something is picked, the length of the array will be greater than 0, otherwise it will be 0. You can then use that to do what you are wanting to do.
Example code/app taken from http://kynetxappaday.wordpress.com/2011/01/04/day-30-detecting-empty-pick/
ruleset a60x526 {
meta {
name "hash-pick-detect"
description <<
hash-pick-detect
>>
author "Mike Grace"
logging on
}
global {
dataHash = {
"one": {
"name": "Mike"
}, // number
"two": {
"random": 8
}, // number
"three": {
"name": "Alex"
} // number
}; // dataHash
} // global
rule detect_the_pick {
select when pageview ".*"
foreach dataHash setting (key, value)
pre {
userName = value.pick("$.name", true);
length = userName.length();
}
if (length > 0) then {
notify("Key: #{key}","Name: #{userName}<br/>Length: #{length}") with sticky = true;
}
notfired {
raise explicit event empty_pick
with pickedKey = key;
}
}
rule empty_pick_found {
select when explicit empty_pick
pre {
pickedKey = event:param("pickedKey");
results =<<
Key: #{pickedKey}<br/>
doesn't have a name associated with it to pick from
>>; //' fixing syntax highlighting
}
{
notify("An empty pick was detected",results) with sticky = true;
}
}
}