CakePHP Model Query Return Data Formating - php

I'm looking for a way to make it so cake returns all database data in the same format/structure... Currently it returns two different types of format depending on the relationship.
If a model 'B' is associated with the current model 'A' being queried it will then place model associations for 'B' underneath it as you can see in [User] below. I want it so that all queries use that structure.
example:
$this->find('all', ....
returns:
Array
(
[0] => Array
(
[UserGroup] => Array
(
[id] => 53
[user_id] => 100003332014851
[media_id] =>
[name] => john
[description] => qwasdfad
)
[User] => Array
(
[id] => 100003332014851
[session_id] => ssm2qbrotmm13ho1ipm8ii2492
[username] =>
[password] => -1
[Planner] => Array
(
)
[Purchase] => Array
(
)
[Listing] => Array
(
)
)
)
I want this to look like:
Array
(
[0] => Array
(
[UserGroup] => Array
(
[id] => 53
[user_id] => 100003332014851
[media_id] =>
[name] => john
[description] => qwasdfad
[User] => Array
(
[id] => 100003332014851
[session_id] => ssm2qbrotmm13ho1ipm8ii2492
[username] =>
[password] => -1
[Planner] => Array
(
)
[Purchase] => Array
(
)
[Listing] => Array
(
)
)
)
)
)

In CakePHP, the find() method return data like your first format. But If you want to format like second one then you have to process it by hand (try to avoid this if possible)
$data = $this->find('all');
$assocs = Set::extract('/User', $data); // extracting all `User` array
foreach($assocs as $key => $assoc) {
unset($data[$key]['User']); // removing the associate `User` from `$data`
$data[$key]['UserGroup']['User'] = $assoc['User']; // adding associate under `UserGroup`
}

ended up doing this... it changes the output to what we need. The top level item does not have a header which is fine I just adjusted our scripts for that... maybe this will help somebody else if they need a custom idea
also no guarantee this covers all possible results but so far it works with all the queries we have.
class AppModel extends Model {
function afterFind($results, $primary) {
//if this is a primary, structure like a secondary so entire site is same format
if ($primary) {
$class = get_class($this);
//simple fix for primary
foreach ($results as $key => $result) {
$result = $this->formatData($result, $class);
$results[$key] = $result;
}
}
return $results;
}
function formatData($result, $class) {
$array = array();
if (isset($result[$class])) {
$array = $result[$class];
unset($result[$class]);
}
$array += $result;
return $array;
}

You can also use contain in this case along with find as UserGroup.User for your desired result

Related

Extracting a property from an array of an array of objects

I've got an object, containing an array of objects, containing an array of values:
stdClass Object (
[devices] => Array (
[0] => stdClass Object (
[location] => 1
[delegate] =>
[type] => 1
[id] => 1234
[IP] => 1.2.3.4
[name] => host1
[owner] => user6
[security] => 15
)
[1] => stdClass Object (
[location] => 2
[delegate] =>
[type] => 1
[id] => 4321
[IP] => 4.3.2.1
[name] => host2
[owner] => user9
[security] => 15
)
)
)
I want to extract just the id and name into an array in the form of:
$devices['id'] = $name;
I considered using the array_map() function, but couldn't work out how to use it... Any ideas?
This will generate you a new array like I think you want
I know you says that delegate is an object but the print does not show it that way
$new = array();
foreach($obj->devices as $device) {
$new[][$device->id] = $device->name;
}
Would something like this not work? Untested but it cycles through the object structure to extract what I think you need.
$devices = myfunction($my_object);
function myfunction($ob){
$devices = array();
if(isset($ob->devices)){
foreach($ob->devices as $d){
if(isset($d->delegate->name && isset($d->delegate->id))){
$devices[$d->delegate->id] = $d->delegate->name;
}
}
}
return($devices);
}
Im usually using this function to generate all child and parent array stdclass / object, but still make key same :
function GenNewArr($arr=array()){
$newarr = array();
foreach($arr as $a=>$b){
$newarr[$a] = is_object($b) || is_array($b) ? GenNewArr($b) : $b ;
}
return $newarr;
}

How to transform the results of a stored procedure in to a list of object then convert to json in CI?

I am using CodeIgniter 2.2.6 + DataMapper, I have a question regarding how to transform the results of a stored procedure into DataMapper Models, then convert them to json.
I have a model called Event:
class Event extends DataMapper {
var $table = 'EVENT'; // map to EVENT table
}
with the following code, I can easily get the top 10 Event:
$event = new Event();
$event->get( 10, 0 );
$event->all_to_json(); //this convert the result to json following the table structure
Now I have a stored procedure getSpecialEvent(), the result of this SP has exactly same structure as Table EVENT,
With the followng code, I do get the content but they are in Array format:
$sql = "call getSpecialEvent()";
$event = new Event();
$event = $this->db->query ($sql);
print_r ($event->result_array());
this will returned some thing like this:
Array
(
[0] => Array
(
[event_id] => 11
[title] => Test1
...
)
[1] => Array
(
[event_id] => 2
[title] => Test1
...
)
)
if I use this
foreach ( $event as $obj ) {
print_r($obj);
}
I get empty array:
Array
(
)
Array
(
)
then I tried
print_r ($event->result());
it returns
Array
(
[0] => stdClass Object
(
[event_id] => 11
[title] => Test1
...
)
[1] => stdClass Object
(
[event_id] => 2
[title] => Test2
...
)
}
I used some code found on internet to cast stdClass Object to Event, it looks like ok, but when I call to_json() method, it doesn't work.
function objectToObject($instance, $className) {
return unserialize(sprintf(
'O:%d:"%s"%s',
strlen($className),
$className,
strstr(strstr(serialize($instance), '"'), ':')
));
}
foreach ( $event->result() as $obj ) {
$newObj = $this->objectToObject($obj, "Event");
print_r ($newObj);
print_r ($newObj->to_json());
}
I printed he casted object, here it is:
Event Object
(
[table] => EVENT
[error] =>
[stored] =>
[model] =>
[primary_key] => id
[valid] =>
[cascade_delete] => 1
[fields] => Array
(
)
[all] => Array
(
)
[parent] => Array
(
)
[validation] => Array
(
)
[has_many] => Array
(
)
[has_one] => Array
(
)
[production_cache] =>
[free_result_threshold] => 100
[default_order_by] =>
[_validated:protected] =>
[_force_validation:protected] =>
[_instantiations:protected] =>
[_field_tracking:protected] =>
[_query_related:protected] => Array
(
)
[_include_join_fields:protected] =>
[_force_save_as_new:protected] =>
[_where_group_started:protected] =>
[_group_count:protected] => 0
[event_id] => 11
[title] => test11
...
)
but $newObj->to_json() returns empty
Array
(
)
Array
(
)
if I do a small test
$event = new Event ();
$event->event_id = 13;
$event->title = "xxxxx";
echo json_encode($event->to_json());
I do get:
{"event_id":13,"title":"xxxxx"....}
I don't know why the casted object doesn't work with to_json?
It seems to be a limitation, the casted DataMapper object (Event here) is not taken as a real DataMapper object, then I create a method in Event to export the needed info to aother pure object model and use json_encode(), this works.

Retrieving correct record from multidimentional array

I'm having a mental freeze moment. If I have an array in the following format:
$myData = Array
(
[0] => stdClass Object
(
[id] => 1
[busID] => 5
[type] => SMS
[number] => 5128888888
)
[1] => stdClass Object
(
[id] => 2
[busID] => 5
[type] => APP
[number] => 5125555555
)
[2] => stdClass Object
(
[id] => 4
[busID] => 5
[type] => APP
[number] => 5129999988
[verified] => 1
[default] => 0
)
)
And I only have an var for ID of the record, how do I retrieve the rest of the detail for that set.
$myID = 2;
// get number 5125555555 and it's type
echo $myData[][$myID]['number']; // ???
The way you have your data arranged your going to have to loop through your array to identify the object corresponding to $myID.
foreach($myData as $object) if($object->id == $myID) echo $object->number;
The alternative is to arrange your $myData as an associative array with the id field as the key. Then you could access it simply with $myData[$myID]->number.
Actually it's an array that contains StdClass objects , try looping over $myData and access each attribute :
foreach ( $myData as $data )
{
print_r($data->id);
// ...
}
You can avoid loop by using following logic:
<?php
$myID = 2;
$myData = json_decode(json_encode($myData),1); // Convert Object to Array
$id_arr = array_column($myData, 'id'); // Create an array with All Ids
$idx = array_search($myID, $id_arr);
if($idx !== false)
{
echo $myData[$idx]['type'] . ' -- ' . $myData[$idx]['number'];
}
?>
Working Demo
Note: array_column is supported from PHP 5.5.
For lower versions you can use this beautiful library https://github.com/ramsey/array_column/blob/master/src/array_column.php
You can create a custom function to achieve this, you need to pass the array and id whose details you want and the function will return the array with matching id, like below
function detailsById($myData,$id){
foreach($myData as $data){
if($data->id == $id){
return $data;
}
}
}
Just call this function with your array and id..
$data=detailsById($myData,2);
echo "<pre>";print_r($data);
This will give you :
stdClass Object
(
[id] => 2
[busID] => 5
[type] => APP
[number] => 5125555555
)
And further to print 'number' and 'type' use $data array
$data['type'];
$data['number'];

PHP Help to simplify an IF statement when retrieving results from a function

Let's say i have this function that seeks for a value inside a bidimensional array:
function findValueBi($array, $field, $value, $returnfield)
{
foreach($array as $key => $product)
{
if ( $product[$field] === $value )
return $product[$returnfield];
}
return false;
}
And the bidimensional array looks like this:
Array
(
[0] => Array
(
[number] => 2
[type] => unimodal
)
[1] => Array
(
[number] => 6
[type] => unimodal
)
[2] => Array
(
[number] => 8
[type] => multimodal
)
[3] => Array
(
[number] => 27
[type] => multimodal
)
[4] => Array
(
[number] => 29
[type] => multimodal
)
)
What the function does, is to look for a given value inside the 'number' key. If it's found, i retrieve its corresponding 'type' key value. For example, if i am looking for the 'number' 29, then i will get the 'type' value "multimodal" (the last item of the array sample). Otherwise, if the value is not found, the function returns false.
So, the way i retrieve this value is as follows:
if(findValueBi($numbers_patterns,'number',$number,'type')!==false){
$resultado=findValueBi($numbers_patterns,'number',$number,'type');
return $resultado;
}
else{ ... }
Is there a better and/or faster way to do this? Is it possible to retrieve the info right inside the if statement? As you can see, I am calling the function twice, so how can i call it once with the if statement???
You could just elect to use it the first time, then use it inside the if:
$resultado = findValueBi($numbers_patterns,'number',$number,'type'); // call it once
if($resultado !== false){
// use $resultado here
}

Laravel 3 — how to return specific fields from has_many()

I'm trying to figure out the best way to do something. I have a table "groups" with a has_many relationship to another table "vehicles" (groups of vehicles). I want to query the groups table and get back an array of groups, with each group containing an array of related vehicle IDs (if any). The end result should be an JSON array of objects:
[{"id":4534,"group_name":"Annual",vehicles:[2311,3357]},{"id":4752,"group_name":"Summer",vehicles:[5,3348,4316]},{"id":4533,"group_name":"Winter",vehicles:[3116]}];
So far, with these methods:
public function vehicle()
{
return $this->has_many('Vehicle');
}
public static function many()
{
return self::with (array('vehicle'))
->where('account_id', '=', Session::get('account_id_selected'))
->order_by('group_name')
->select(array('id', 'group_name'))
->get();
}
I get this result:
[0] => Array
(
[id] => 4534
[group_name] => Annual
[vehicle] => Array
(
[0] => Array
(
[id] => 2311
[created] => 2007-06-01
)
[1] => Array
(
[id] => 3357
[created] => 2008-08-25
)
)
)
[1] => Array
(
[id] => 4752
[group_name] => Summer
[vehicle] => Array
(
[0] => Array
(
[id] => 5
[created] => 0000-00-00
[1] => Array
(
[id] => 3348
[created] => 2008-08-18
[2] => Array
(
[id] => 4316
[created] => 2011-02-24
)
)
[2] => Array
(
[id] => 4533
[group_name] => Winter
[vehicle] => Array
(
[0] => Array
(
[id] => 3116
[created] => 2008-05-15
)
)
At the moment, after the query, I use the following to melt it all down to JSON:
foreach (Group::many() as $group) {
$groups[] = $group->to_array();
}
var Groups = {{ json_encode($groups); }};
There are two problems with the above approach (for me): (1) It returns all the fields of the vehicles table (I only want the ID). (2) I want the vehicles property to simply contain an array of IDs — not bunch of nested objects.
Now, I know can parse the vehicle property while iterating through the Eloquent object and format the query result:
$groups = array();
foreach (Group::many() as $group) {
$v = array();
foreach ($group->vehicle as $vehicle) {
$v[] = $vehicle->id;
}
$groups[] = array('id' => $group->id, 'group_name' => $group->group_name, 'vehicles' => $v);
}
var Groups = {{ json_encode($groups); }};
But I really think this should be accomplished in the Model instead. I guess what I'm asking is, what is the best way in your opinion to go from this sort of model relationship to the resulting JSON? It is possible to eliminate the foreach loop and extra parsing code to produce the simpler JSON object described above?
Just use a join instead - more flexibility.
public static function many()
{
return self::join('vehicles', 'vehicles.account_id', '=, 'accounts.id')
->where('account_id', '=', Session::get('account_id_selected'))
->order_by('group_name')
->select(array('id', 'group_name', 'vehicles.id as vehicle_id'))
->get();
}
Note: Im not sure of the table structure of your database so have had to assume the table names are plural and have assumed key relationships, but you should be able to work it out.
You'll then get a row, or array element for every vechicle_id that matches (i.e. id will repeat). Just run it through some sort of foreach loop to get it the way you want.
You can use a closure to limit what gets selected.
public static function getManyByAccountId( $account_id )
{
return self::with (
array(
'vehicle' => function($query) use ($account_id ) {
$query->select('id as vehicle_id');
}
)
)->select(array('id', 'group_name'))
->order_by('group_name')
->where('account_id', '=', $account_id);
}
And the do something around:
$vehicles = json_encode( Vehicles::getManyByAccountId($account_id)->to_array() );

Categories