json encode is not working with array of objects - php

I want to convert array of objects to json encoding, I make like this
$allVisits = $mapper->getAllVisits($year, $month);
echo json_encode($allVisits);
and here's is getAllVisists method
function getAllVisits($year, $month) {
$where = array(
'year = ?' => $year,
'month = ?' => $month
);
$resultSet = $this->getDbTable()->fetchAll( $where);
$visitsEntries = array();
foreach ($resultSet as $row) {
$entry = new Visits_Model_Visit();
$entry->setId($row->visit_id)
->setDay($row->day)
->setDate($row->date)
->setTarget($row->target)
->setStatus($row->visit_status)
->setTime($row->visit_time);
$visitsEntries[] = $entry;
}
return $visitsEntries;
}
when I echo the size of $allVisits it return correct number of records, but in js the values are received empty like this [{},{},{},{}]
Edit
When I print_r($allVisists) brfore encoding it it returns
Array
(
[0] => Visits_Model_Visit Object
(
[day:private] => sunday
[date:private] => 2012-03-06
[target:private] => شسي
[id:private] => 1
[status:private] => 0
[time:private] => 12:00:00
)
[1] => Visits_Model_Visit Object
(
[day:private] => sunday
[date:private] => 2012-03-06
[target:private] => clinnics
[id:private] => 4
[status:private] => 0
[time:private] => 00:00:00
)
[2] => Visits_Model_Visit Object
(
[day:private] => Tuesday
[date:private] => 2012-03-06
[target:private] => clinnics
[id:private] => 5
[status:private] => 0
[time:private] => 00:00:00
)
[3] => Visits_Model_Visit Object
(
[day:private] => Wednesday
[date:private] => 2012-03-28
[target:private] => ??????? ???????
[id:private] => 7
[status:private] => 0
[time:private] => 12:00:00
)
)

You are using json_encode with objects that don't have any public members. json_encode only works on the members it can "see", that's why those are empty.
Since PHP 5.4 you can make use of the JsonSerializable interface to control which data will be offered for json_encode, e.g.:
class Visits_Model_Visit implements JsonSerializable {
...
public function jsonSerialize() {
return (object) get_object_vars($this);
}
...
}
If you are below 5.4 you can also implement that function w/o extending from the interface and then assigning the correct value manually:
$visitsEntries[] = $entry->jsonSerialize();
Hope this helps.

As Ray says if your class properties are protected or private, these will not be jsoned.
Since PHP 5.4 instead of using the commented toJson method, you have the ability to specify which data will be serialized implementing the JsonSerializable interface, so json_encode knows how to work on this.
/* PHP >= 5.4 only */
class Visits_Model_Visit implement JsonSerializable {
public function jsonSerialize()
{
return array(
'day' => $this->day,
'date' => $this->date,
'target' => $this->target,
'id' => $this->id,
'status' => $this->status,
);
}
}

By default, json_encode() only serializes public properties of an object. Making all properties you want serialized public is NOT the solution! PHP 5.4 and later has the JsonSerializable interface, but I propose a straightforward solution for earlier versions of PHP.
Since JsonSerializable is only part of PHP 5.4 and later, create it yourself.
if (!interface_exists('JsonSerializable')) {
interface JsonSerializable {
public function jsonSerialize();
}
}
That wasn't so hard, was it? Now we can implement JsonSerializable without worrying about what version of PHP we are using!
class Visits_Model_Visit implements JsonSerializable {
...
// Only put properties here that you want serialized.
public function jsonSerialize() {
return Array(
'day' => $this->day,
'date' => $this->date,
'target' => $this->target,
'id' => $this->id,
'status' => $this->status,
'obj' => $this->obj->jsonSerialize(), // example for other objects
'time' => $this->time
);
}
...
}
Now you can just call jsonSerialize() to get an associative array that you can encode with json_encode().
...
$entry = new Visits_Model_Visit();
$entry->setId($row->visit_id)
->setDay($row->day)
->setDate($row->date)
->setTarget($row->target)
->setStatus($row->visit_status)
->setTime($row->visit_time);
$visitsEntries[] = $entry->jsonSerialize();
...
You may then call json_encode($visitsEntries) to get your desired result.
[
{
"day":"sunday",
"date":"2012-03-06",
"target":"\u0634\u0633\u064a",
"id":1,
"status":0,
"time":"12:00:00"
},
{
"day":"sunday",
"date":"2012-03-06",
"target":"clinnics",
"id":4,
"status":0,
"time":"00:00:00"
},
...
]

Are the properties private or protected for the object? If so, json encode can't see them inside the object. I get around this by creating a 'toJson' method in my objects that I need to serialize into json. In this method, I walk the objects properties and manually construct a generic object, that I pass to json_encode. Then I return the Json string from this method.
Do not just make all your object properties public!!!!

For those who are looking for simple answer, unlike other complicated answers my is piece of art:
json_encode(array(
Protocol::PARAM_CODE => Protocol::CODE_SUCCESS,
Protocol::PARAM_USER => (object)$user->jsonSerialize()
));
Even when $user->jsonSerialize() outputs stdObject, json_encode is so dumb, it has no idea it is object so you have to state that explicitly with casting it to (object) - don't you love PHP for it's simplicity?

Related

Laravel 5.6 - get changed values after updateOrCreate

I have used laravel 5.6 and used the updateOrCreate model to add or update some data.
But I need to get all the values which changed
$q=Userssub::updateOrCreate(
['userid' => $uid ],
['model' => $model]
);
and the result shows like in this image
How can I get the changes array?
I tried to get it with
$u->changes
and
$u->changes->toarray()
but both return null.
What can I do to get the changed values?
Eloquent models have two protected arrays, $original and $changes, which contain the attributes as they were when fetched from storage and the attrbirutes which have been modified, respectively.
So you can use getOriginal() and getChanges() and compare the differences.
$model = Model::createOrUpdate([...]);
// wasRecentlyCreated is a boolean indicating if the model was inserted during the current request lifecycle.
if (!$model->wasRecentlyCreated) {
$changes = $model->getChanges();
}
This creates an array which will contain the original attribute value and what it was changed to:
if (!$model->wasRecentlyCreated) {
$original = $model->getOriginal();
$changes = [];
foreach ($model->getChanges() as $key => $value) {
$changes[$key] = [
'original' => $original[$key],
'changes' => $value,
];
}
}
e.g.
(
[first_name] => [
[original] => Kevinn
[changes] => Kevin
]
[website] => [
[original] => google.com
[changes] => google.ca
]
)

Accessing the properties of an object of an object dynamically in PHP

Given that I'm getting different object results depending of the API I query, here are two examples out of 35 I have to implement:
stdClass Object
(
[mid] => 7127.75
[bid] => 7126.6
[ask] => 7128.9
[last_price] => 7128.8
[low] => 7000.0
[high] => 7492.1
[volume] => 53255.4195502
[timestamp] => 1510265647.9803913
)
stdClass Object
(
[Success] => 1
[Message] =>
[Data] => stdClass Object
(
[AskPrice] => 7095
[BidPrice] => 7070
[Low] => 7001
[High] => 7540
[Volume] => 17.38943459
[LastPrice] => 7090
[Change] => -1.02
[Open] => 7163
[Close] => 7090
)
[Error] =>
)
I want to build mapping variable array to access the object easily.
$xmap["a"]["bid"] = "bid";
$xmap["b"]["bid"] = "Data->BidPrice";
Let's assume $content has the first example object, this will work:
print $content->{$xmap["a"]["bid"]}; // the result is 7128.9
As for the second example object, it does not:
print $content->{$xmap["b"]["bid"]}; // the result is PHP Notice: Undefined property: stdClass::$Data->BidPrice in ...
Can this be done or am I stuck with if statements!
First, convert all the objects into assoc. arrays by using json_encode/decode. You'll find this code multiple times here in stackoverflow
$arr = json_decode(json_encode($obj), true);
Second, I recommend a dot-notation for the key-path, plus a tiny function to find the value in a multidimensional array.
Example:
function fromArray($key, $arr) {
$keys = explode('.', $key);
foreach($keys as $k) {
if (!isset($arr[$k]))
return array(); // return empty array
$arr = $arr[$k];
}
return $arr; // can be a scalar value or array
}
$key = 'my.super.array.content';
$array = ['my' => [
'super' => [
'array' =>[
'content'=>4711
]
]
]
];
var_dump($array, fromArray($key, $array));
/*
array(1) {
["my"]=>
array(1) {
["super"]=>
array(1) {
["array"]=>
array(1) {
["content"]=>
int(4711)
}
}
}
}
int(4711)
*/
I found the dot-notation very useful when dealing with complex structures.
You can convert your objects to arrays and build a large mapping array that needs to be maintained and then explode it and loop through it to access the other arrays; or you can try using patterns. I'm thinking Adapter, but maybe another is a better fit. This is using your second object as an example, but just add as many as needed:
class ContentAdapter {
public function __get($name) {
return $this->obj->{$xmap[$name]};
}
}
class ContentAdapter_API_B extends ContentAdapter {
public $xmap = ['bid' => 'BidPrice', 'ask' => 'AskPrice'];
public function __construct($obj) {
$this->obj = $obj->data;
}
}
Now it is consistant regardless of the object since each has an adapter:
$content = new ContentAdapter_API_B($content);
echo $content->bid;
Using your first object you can either create a child as well (ContentAdapter_API_A in case the structure ever changes) or instantiate directly:
$content = new ContentAdapter($content);
echo $content->bid;
Or obviously just use it as is:
echo $content->bid;
An alternate way without inheritance is to use getters:
class ContentAdapter_API_B {
public function __construct($obj) {
$this->obj = $obj->data;
}
public function getBid() { return $this->obj->BidPrice; }
}
So long as the methods are consistent then it will always work:
$content = new ContentAdapter_API_B($content);
echo $content->getBid;

How to make an object public?

how would I go about getting around the "protected" so I can output the data.
tabs\api\property\Property Object (
[id:protected] => 90_4_HH
[propertyRef:protected] => 90_4
[brandCode:protected] => HH
[url:protected] => http://hh.api.carltonsoftware.co.uk/property/90_4_HH
[accountingBrand:protected] => HH
[slug:protected] => 90-4-hh
[name:protected] => Carreg Lwyd Farmhouse
[address:protected] => tabs\api\core\Address Object (
[addr1:protected] => Port Eynon
[addr2:protected] =>
[town:protected] => Gower
[county:protected] => Swansea
[postcode:protected] => SA3 1NN
[country:protected] => GB
)
[changeOverDay:protected] => Saturday
[calendar:protected] => http://hh.api.carltonsoftware.co.uk/property/90_4_HH/calendar
[booking:protected] => http://hh.api.carltonsoftware.co.uk/booking
[pets:protected] =>
[promote:protected] =>
[smoking:protected] =>
[shortlist:protected] =>
[accommodates:protected] => 12
[rating:protected] => 5
[bedrooms:protected] => 6
[images:protected] => Array (
[90_4p1190276.jpg?APIKEY=homefromhome&hash=31b3f7b1b377184e8cb8fb64d434a11a4c3446c1091535ef6db4e119689a6372] => tabs\api\property\Image Object (
[filename:protected] => 90_4p1190276.jpg?APIKEY=homefromhome&hash=31b3f7b1b377184e8cb8fb64d434a11a4c3446c1091535ef6db4e119689a6372
[title:protected] => Carreg Lwyd Farmhouse, Port Eynon
[alt:protected] => The Lounge
[url:protected] => http://hh.api.carltonsoftware.co.uk/image/normal/1000x750/90_4p1190276.jpg
[height:protected] => 750
[width:protected] => 1000
[apiPath:protected] => http://hh.api.carltonsoftware.co.uk
)
[90_4img_4819.jpg?APIKEY=homefromhome&hash=31b3f7b1b377184e8cb8fb64d434a11a4c3446c1091535ef6db4e119689a6372] => tabs\api\property\Image Object (
The dump:
foreach ($properties as $property) {
echo sprintf('<p class="listit">%s</p>', $property);
print_r($property);
}
I did not fully understand your question, but if you want to access protected properties from outside the class, you have to use Reflection:
$reflObj = new ReflectionObject($property);
$props = $reflObj->getProperties(ReflectionProperty::IS_PROTECTED);
foreach ($props as $prop) {
$prop->setAccessible(true);
echo $prop->getName() . ":" . $prop->getValue($property), "\n";
}
Sample for outputting the address:
$reflObj = new ReflectionObject($property);
$addrProp = $reflObj->getProperty('address');
$addrProp->setAccessible(true);
echo $addrProp->getValue($property);
Your title implies that you want to make -the class (not object)- public, as opposed to "internal"/etc.. All classes are public in PHP.
Your question says that you want to get around method/property scope (disagrees with title). You didn't say what you've tried. You also didn't indicate whether this is a class you developed or not. If you have control over it, add a freaking method or two to allow you to get the data. If you don't, then inspect the methods, and/or attempt reflection. These answers apply to every OOP language in existence.
Before asking other people, read the documentation.
http://www.php.net/manual/en/language.oop5.basic.php
Answer to the question posed by the title is that all classes are public.
What you are asking is how to access protected member variables.
Taken from here (http://ajmm.org/2011/06/using-php-reflection-to-read-a-protected-property/), this is an example of how to do this:
public static function getReflectedPropertyValue($class, $propertyName)
{
$reflectedClass = new ReflectionClass($class);
$property = $reflectedClass->getProperty($propertyName);
$property->setAccessible(true);
return $property->getValue($class);
}
...
getReflectedPropertyValue($yourObject, 'protectedProperty');
That said, the question is why you want to do this. Members are marked protected specifically to prevent you from doing this. If you have access to the source code that defines this other class, then it might make more sense to either change these members to "public" or (better) to provide a getXYZ() method for whichever properties you want to access.

Unable to recreate object from var_exported data (in PHP)

A script running on on our live server sends out data from the following line:
$log_output .= '<br>'.__LINE__.'<br>recordings_data='.var_export($recordings_data,TRUE);
Which looks like this:
recordings_data=stdClass::__set_state(array( 'RecordingLongResponse'
=> array ( 0 => stdClass::__set_state(...), 1 => stdClass::__set_state(), 2 => stdClass::__set_state(), 3 =>
stdClass::__set_state(array( 'roomStartDate' => '1321977120000',
'roomEndDate' => '1321977120000', 'recordingURL' => 'serverURL1',
'secureSignOn' => false, 'recordingId' => '1287268130290',
'creationDate' => '1321977120000', 'recordingSize' => '6765975',
'roomName' => 'Stakeholder Analysis', 'sessionId' => '1287268130229',
)), ...), ))
I'm not sure how to 'recreate' the object. I tried unserializing it:
$recording_data_ser= file_get_contents('elm-ser-data.txt'); // where I've saved everything after the '='
$recording_data = unserialize($recording_data_ser);
serialize() and unserialize() are the generally accepted methods for dumping/loading PHP objects. You can also do it with json_encode() and json_decode(). Is there a reason you want to use var_export()?
Edit: you can only unserialize() the result of serialize() - var_dump() is in a completely different format and isn't designed for importing unless you use eval().
For example:
$arr = array('foo' => 'bar');
var_export($arr);
#=> array (
#=> 'foo' => 'bar',
#=> )
echo serialize($arr);
#=> a:1:{s:3:"foo";s:3:"bar";}
echo json_encode($arr);
#=> {"foo":"bar"}
Once you have the serialized data (via serialize() or json_encode()), you can use the opposite method (unserialize() or json_decode(), respectively) to recreate the object again.
var_export() is intended to dump a data structure to a file. You then have to eval() or include() or require() that file. the serialize() format is completely different from var_export.
var_export produces valid PHP code to define a data structure, as if you'd written out the array/object definition yourself. serialize/unserialize write out in a completely different format, and they're not interchangeable.
If you call var_export() on an instance of stdClass, it attempts to export it using ::__set_state(), which, for some reason, is not implemented in stdClass.
However, casting an associative array to an object usually produces the same effect (at least, it does in my case). So I wrote an improved_var_export() function to convert instances of stdClass to (object) array () calls. If you choose to export objects of any other class, I'd advise you to implement ::__set_state() in those classes.
<?php
/**
* An implementation of var_export() that is compatible with instances
* of stdClass.
* #param mixed $variable The variable you want to export
* #param bool $return If used and set to true, improved_var_export()
* will return the variable representation instead of outputting it.
* #return mixed|null Returns the variable representation when the
* return parameter is used and evaluates to TRUE. Otherwise, this
* function will return NULL.
*/
function improved_var_export ($variable, $return = false) {
if ($variable instanceof stdClass) {
$result = '(object) '.improved_var_export(get_object_vars($variable), true);
} else if (is_array($variable)) {
$array = array ();
foreach ($variable as $key => $value) {
$array[] = var_export($key, true).' => '.improved_var_export($value, true);
}
$result = 'array ('.implode(', ', $array).')';
} else {
$result = var_export($variable, true);
}
if (!$return) {
print $result;
return null;
} else {
return $result;
}
}
// Example usage:
$obj = new stdClass;
$obj->test = 'abc';
$obj->other = 6.2;
$obj->arr = array (1, 2, 3);
improved_var_export((object) array (
'prop1' => true,
'prop2' => $obj,
'assocArray' => array (
'apple' => 'good',
'orange' => 'great'
)
));
/* Output:
(object) array ('prop1' => true, 'prop2' => (object) array ('test' => 'abc', 'other' => 6.2, 'arr' => array (0 => 1, 1 => 2, 2 => 3)), 'assocArray' => array ('apple' => 'good', 'orange' => 'great'))
*/
// Example implementation in context of OP
$export = improved_var_export($data, true);
file_put_contents(dirname(__FILE__).'/data.php', '<'.'?php return '.$export.'; ?'.'>');
// "Unserialization" (evaluation)
$import = include dirname(__FILE__).'/data.php';
?>

Modify PHP Object Property Name

In PHP is it possible to change an Objects property key/name? For example:
stdClass Object
(
[cpus] => 2
[created_at] => 2011-05-23T01:28:29-07:00
[memory] => 256
)
I wish to change the key created_at to created in the Object leaving an object that looks like:
stdClass Object
(
[cpus] => 2
[created] => 2011-05-23T01:28:29-07:00
[memory] => 256
)
$object->created = $object->created_at;
unset($object->created_at);
Something like an adapter class may be a more robust choice though, depending on where and how often this operation is necessary.
class PC {
public $cpus;
public $created;
public $memory;
public function __construct($obj) {
$this->cpus = $obj->cpu;
$this->created = $obj->created_at;
$this->memory = $obj->memory;
}
}
$object = new PC($object);
No, since the key is a reference to the value, and not a value itself.
You're best off copying the original, then removing it.
$obj->created = $obj->created_at;
unset(obj->created_at);
Its similar to #deceze adapter, but without the need to create an extra class
$object = (object) array(
'cpus' => $obj->cpus,
'created' => $obj->created_at,
'memory' => $obj->memory
);

Categories