How can I convert plain nested array to collection of entity objects? - php

I have a PHP plain array which I need converted to it's original entity. Example:
class Contact
{
protected $name;
getName(){}
setName(){}
}
This gets send back and forth via an API, and at some point I have that contact as an array element:
$example = ['name'=>'Foo Bar'];
I would like that back as an Contact class. At the moment, I can do that via a serialize/deserialize, but I'm hoping there is a more efficient method for this:
foreach($examples as $example) {
$temp = $this->serializer->serialize($example, 'json');
$contact = $this->serializer->deserialize($temp, Contact::class, 'json');
}
This works, and $contact is now instance of Contact. But I have to perform this on 100 items in one go, possibly more.
I'm thinking of creating a toObject() method, which assigns the values by keys, but that doesn't seem a lot better.
Is there way to accomplish this without writing my own logic or doing the extra serializing step?
Please note: I get the data array, I cant get the 'raw' json. Please take that 'as is'.

Denormalizing from raw JSON input
If you are getting the information from an API, you could probably do away with the JSON conversion and deal with the input directly, since most likely the API is not sending you a native array, but a JSON you are converting to an array at some point
The Serializer component can handle arrays as well, directly.
Assuming an input JSON like this:
$data = '[
{
"name": "Mary"
},
{
"name": "Jane",
},
{
"name": "Alice"
}
]';
You could call deserialize() saying you expect Contact[] in your input data:
$contacts = $serializer->deserialize($data, Contact::class . '[]', 'json');
This would get you a Contact array in one single step.
Denormalizing from array to object
If for some reason the original input is not available or not readily unserializable and you really need to convert from an array to an object one by one, and your objects have setters like the ones you show in your question; you could simply use the GetSetMethodNormalizer (one of the normalizers than the Serializer component uses internally).
E.g.:
$contacts = [
['name' => 'Mary'],
['name' => 'Jane'],
['name' => 'Alice'],
];
$normalizer = new GetSetMethodNormalizer();
foreach($contacts as $arrayContact){
$contact = $normalizer->denormalize(Contact::class, $arrayContact);
// do something with $contact;
}

Related

Appending data to an object JSON field using json_encode/decode transforms my array into an int Laravel

So I have a class "File" that has a field "file_history" in JSON, and in my CRUD operations on Laravel I'd like to append some JSON values to this field, for instance, when I'm creating my File object in my database, I first have {"created_at": "2022-07-18"} in my file_history field.
And then if I update my object I would now have:
{
"created_at": "2022-07-18"
},
{
"updated_at": "2022-07-08"
}
So I first thought about transforming my initial JSON values from my previous CRUD operations on the object into an array, append the new JSON values to this array, and then encode my array again into JSON. This is the code I wrote:
public function update(Request $request, String $uuid)
{
$file = File::find($uuid);
$json_data = array(
"updated_at" => Carbon::now()
);
$file_history = json_decode($file->file_history, true);
$file_history = array_push($file_history, $json_data);
$file->file_history = json_encode($file_history);
$file->update($request->all());
return $file;
}
But using that code, I now have "2" in my database in my file_history field... I don't have JSON or anything else, just this 2 even if my field is categorized as JSON. Any idea why and how to fix this?
Thanks
You have "2" in your database, because array_push function returns the new number of elements in the array.
So your code shoud be like this:
$file_history = json_decode($file->file_history, true);
array_push($file_history, $json_data);
$file->file_history = json_encode($file_history);

How do I work with an array object in PHP?

I have a Laravel site I am modifying, but there are some parts of the PHP code I don't quite understand, which are "array objects" or "object arrays". You see, I don't even know what to call them and so can't find a tutorial or basic data on it. Below is the code that I am dealing with:
private function parseMetric($result, $view)
{
$data = collect([]);
$result->each(function($item) use ($data, $view) {
if (isset($item->metric->{$view})) {
$data->push((object)[
'label' => $item->metric->{$view},
'value' => $item->metric->count
]);
}
});
...
From what I can tell, this creates an object out of $result. If I json_encode this and echo it out I get this:
[{"label":"1k-25k","value":14229},
{"label":"1mm+","value":1281},
{"label":"25k-50k","value":398},
{"label":"50k-75k","value":493},
{"label":"75k-100k","value":3848},
{"label":"100k-150k","value":9921},
{"label":"150k-200k","value":4949},
{"label":"200k-250k","value":3883},
{"label":"250k-300k","value":2685},
{"label":"300k-350k","value":2744},
{"label":"350k-500k","value":4526},
{"label":"500k-1mm","value":8690}]
Now this is obviously an array of arrays... or is it? Is it an array of objects? Or is it an object containing arrays? But the most important question is, how do I access and move or change the individual objects/arrays in this object? For example, I want to take the second object/array, which is:
{"label":"1mm+","value":1281}
and move it to the end. How do I do that? How do I find it? I used the following piece of code to find it which is pretty clunky:
$pos = strpos(json_encode($result), '1mm+');
if($pos){
Log::debug('Enrich 73, I found it!!!!!!!!!!!!!!!!!!!!!!!!!!!');
}
And once I find it, how do I move that array/object to the end of the whole object?
And finally, where can I find some kind of tutorial, or documentation, that describes this construct and how to work with it?
There is no need to json_encode the data. Since the data is an instance of Laravel Collection, you can manipulate it like so
$item = $data->firstWhere('label', '1mm+'); // get the item
$data = $data->filter(fn($value, $key) => $value->label !== '1mm+') // remove $item from $data
->push($item); // move $item to the end of data
Acording to Laravel documnentation for Collections, you can try something like this :
To find index of element with name = "1mm+" :
$index = $datas->search(function ($item, $key) {
return $item['name'] == "1mm+";
});
to get an element at a given index :
$element = $datas->get($index);
to Move element at index 3 to the end :
$index = 3
$elementToMove = $data->splice($index, 1);
$datas->push($elementToMove);
Here is a link to the document used : https://laravel.com/docs/8.x/collections

How to make update endpoint more general purpose? (PHP)

I have a function in my API to update the name of a person in an SQLite database. You give it the ID of the name you wish to change and the new name.
How can I build a function in a way that allows me to update a wide range of fields in the database? even things from different tables?
I started off trying to use parameters to switch which SQL query is executed, but this feels a bit clunky and not scalable. Is there a better way?
Current code:
private function json_update_authors() {
$input = json_decode(file_get_contents("php://input"));
$query = "UPDATE authors SET name = :name WHERE authorId = :authorId";
$params = ["name" => $input->name, "authorId" => $input->authorId];
$res = $this->recordset->getJSONRecordSet($query, $params);
return json_encode(array("status" => 200, "message" => "ok"));
}
Prependix
You can achieve what you want, but before reading the details, I recommend contemplating about what you would like to restrict this to, because if there is a file your function blindly trusts, then, should malicious input be inside that file, your database can easily be hacked. So, you should have a whitelist of tables/fields that you allow to be updated and apply that.
Decoding JSON
json_decode decodes your JSON into an object that you do not foresee its members. However, according to the documentation you can iterate this object like:
foreach ($obj as $key => $value) {
echo "$key => $value\n";
}
However, json_decode can decode your JSON into an array as well, like:
$input = json_decode(file_get_contents("php://input"), true);
I personally prefer to decode JSON into arrays, but you can operate with the first approach as well. In both cases, you can iterate the array in a similar manner as described above.
Recommended format
Your update has an anatomy as follows:
table
fields
filter
So, I would recommend that you could use a JSON representation of your input, that has a tableName field, which is a string, a fields field, which is an array of key-value pairs, the keys representing the fields to be updated and the values representing the values to update to and finally a filter field, which, if we intend to be very elegant, could also be an array of objects of key-value pairs, the keys representing the fields you are to filter by and the values representing the values you would filter with. A sample Javascript object that would comply to this format would look like the following:
[
{ //Your query
tableName: 'authors',
fields:
[
{
name: 'somename'
}
],
filters:
[
{
authorId: 123
}
]
},
{ //Some other example
tableName: 'books',
fields:
[
{
isbn: 'someisbn',
title: 'sometitle'
}
],
filters:
[
{
pageNumber: 123,
weight: '5kg'
}
]
},
]
I have given an example above, of two objects, so you can see that:
several updates can be notified in the JSON
you can update several fields in a single command
you can filter by several fields
I should mention that this is a rabbit hole, because you might want to vary the operator as well, but since this is a mere answer, I do not write a full elegant project for its purpose. Instead of that, let me just tell you that there is a lot of room for improvement, operator dynamicity springs to mind instantly as an improvement that you may need.
How to generate an update query:
//assuming that $JSON is a variable holding such values as describe in the previous chapter
foreach ($JSON as $obj) {
$tableName = $obj['tableName'];
$fields = [];
$filters = [];
$params = [];
$toExecute = isset($whiteList['tables'][$tableName]);
foreach ($obj['fields'] as $key => $value) {
$fields[]=($key.'=:field_value'.$key);
$params['field_value'.$key] = $value;
$toExecute = $toExecute && isset($whiteList['fields'][$key]);
}
foreach ($obj['filters'] as $key => $value) {
$filters[]=($key.'=:filter_value'.$key);
$params['filter_value'.$key] = $value;
$toExecute = $toExecute && isset($whiteList['filters'][$key]);
}
}
I have used a whitelist above to make sure that the queries will not update tables/fields using filters where the name of the table/field/filter is either badly formatted, malicious or unwanted. This code is untested, it might well contain typos, but the idea should be a good starting point.

How can I parse a JSON object in PHP? How can I retrieve the values of some specific fields of this JSON object?

I am absolutely new in PHP and moreover in Laravel framework (I don't know if Laravel provides some utility class for this kind of tasks). I came from Java.
So I have the following problem:
Into a class I perform a call to a REST web service, something like this:
$response = $client->get('http://localhost:8080/Extranet/login',
[
'auth' => [
'dummy#gmail.com',
'pswd'
]
]);
$dettagliLogin = json_decode($response->getBody());
\Log::info('response: '.(json_encode($dettagliLogin)));
$response->getBody() contains the returned JSON object, this is the output of the previous \Log::info():
{
"id":5,
"userName":"Dummy User",
"email":"dummy#gmail.com",
"enabled":true
}
So I have the following problems:
1) What exactly returns the json_decode() function? I really can't understand because PHP is not strongly typed and I have not a declared return type.
This is the method signature:
function json_decode($json, $assoc = false, $depth = 512, $options = 0)
and in the related doc it says #return mixed. What exactly means "mixed"?
2) Anyway the main problem is: I have to use the content of the previous returned JSON object and put these value into the related field of an array like this:
$attributes = array(
'id' => HERE THE id FIELD VALUE OF MY JSON OBJECT,
'username' => HERE THE email FIELD VALUE OF MY JSON OBJECT',
'name' => HERE THE userName FIELD VALUE OF MY JSON OBJECT,
);
So I think that I have to parse the value of the $response->getBody() or of the json_decode($response->getBody()) to obtain these values. But how exactly can I do it? What is the neater way to do it? Does the Laravel framework provide some utility to do it?
For better understanding, let's first describe - what's JSON?
It's a way of representing objects (arrays, objects, etc) in a string.
1) What exactly returns the json_decode() function? I really can't
understand because PHP is not strongly typed and I have not a declared
return type. This is the method signature:
function json_decode($json, $assoc = false, $depth = 512, $options =
0) and in the related doc it says #return mixed. What exatly means
mixed?
json_deocde converts the JSON string into the original "structure" it represent.
#return mixed means that the returned value of json_decode can be any type of variable. If the JSON represent an array - it would be an array type, if it represent an object - it would be an object type.
2) Anyway the main problem is: I have to use the content of the
previous returned JSON object and put these value into the related
field of an array like this:
$attributes = array(
'id' => HERE THE id FIELD VALUE OF MY JSON OBJECT,
'username' => HERE THE email FIELD VALUE OF MY JSON OBJECT',
'name' => HERE THE userName FIELD VALUE OF MY JSON OBJECT,
);
In order to make sure which type of variable your JSON represent, you can use var_dump(json_decode($json));. Anyway, it's a class object.
Therefore:
$object = json_decode($json);
$attributes = array(
'id' => $object->id,
'username' => $object->email,
'name' => $object->userName,
);
If you json string is an object (not an array) it will return an object (of type stdClass). Mixed means it can be multiple things, so if it was a json array, you'd get an array.
Best thing to do is use json_decode, and then var_dump (or var_export) to see what you actually get.

how can i loop through a json array requested from an api?

im requesting information from the instagram api in php like this:
<?php $relation = $instagram->get('users/'.$item->id.'/relationship');
..
which return this json data array for me:
object(stdClass)#58(2){
[
"meta"
]=>object(stdClass)#59(1){
[
"code"
]=>int(200)
}[
"data"
]=>object(stdClass)#60(3){
[
"outgoing_status"
]=>string(7)"follows"[
"target_user_is_private"
]=>bool(true)[
"incoming_status"
]=>string(4)"none"
}
}
note: i used var_dump($relation) to bring this out
what I'm trying to do is loop through this array and display the outgoing status and the incoming status i.e
loop(json-array){
echo outgoing_status;
echo incoming_status;
}
thank you very much..
You have an object (instance of stdClass, the generic object), not an array.
$outgoing_status = $response->data->outgoing_status;
$incoming_status = $response->data->incoming_status;
As a side note, use json_decode($json, TRUE) to return the data as an associative array instead of an object.

Categories