JSON string in MySQL to json response - php

I have a JSON formatted string stored in a column (meta_data) in a mysql database, stored in the table it looks something like this for example:
{"Format":"JPEG","Geometry":"3216x2136","size":{"width":3216,"height":2136}}
Now if I use the following:
$meta_data = DB::query->get();
return $meta_data;
I get:
[
{
"meta_data": "{\"Format\":\"JPEG\",\"Geometry\":\"3216x2136\",\"size\":{\"width\":3216,\"height\":2136}
}
]
I also get the same result if I use:
$meta_data = json_decode(DB::query->get());
return $meta_data;
Similarly, using response()->json($meta_data); returns it as a string.
It seems to me that it needs to go the next step down but I haven't been able to get anything close to what I'm after, which is ideally:
[
{
"meta_data":
{
"Format":"JPEG",
"Geometry":"3216x2136",
"size":
{
"width":3216,
"height":2136
}
}
}
]

DB::query->get() will return an array of stdClass objects (assuming query is just shorthand for your query conditions). You will need to loop through the array and convert the meta_data field of each entry to a json object manually.
$records = DB::query->get();
foreach ($records as $record) {
$record->meta_data = json_decode($record->meta_data);
}
return $records;
Another option would be to create a Model for the table, and then add the meta_data field to the $casts property to automatically cast it to json.
Model:
class Attachment extends Model
{
protected $casts = [
'meta_data' => 'json',
];
}
Controller:
// assume "query" is shorthand for your query conditions
$records = Attachment::query->get();
return $records;

Related

Create array key inside foreach loop in laravel

I'm fetching data from database, inside foreach loop i have to add one array index list_array
$list = Lists::where('name',$request->name)->get();
$Data=[];
foreach($list as $key => $list_row)
{
$list_row['list_array'][]=$list_row['data_1'];
$list_row['list_array'][]=$list_row['data_2'];
$Data[]=$list_row;
}
It should be of array type but when i declare it as array it is not working,
message: "Indirect modification of overloaded property
App\Models\Lists
Any solution to create list_array as an array index inside foreach loop. Thanks
You are not dealing with an array, you are dealing with an App/Models/Lists model. What you probably instead want to do is adding a custom attribute to the model. This happens one step before.
// your model
class Lists
{
protected $appends = ['list_array'];
public function getListArrayAttribute()
{
$data = [
// your array data from where ever
];
return $data;
}
}
You then can access (without adding it when you want to access the data) the new attribute from within the model, like so:
// where you access your data
$lists = Lists::where('name', 'whatever')->get();
foreach($lists as $list) {
dd($lists->list_array)
}
You can read more about the so called Accessors here:
https://laravel.com/docs/8.x/eloquent-mutators#defining-an-accessor (Laravel 8)
https://laravel.com/docs/9.x/eloquent-mutators#defining-an-accessor (Laravel 9)
I hope I got the intention of your question right. From your question it's not very clear what data you want to add and where it is coming from. There might be other and better ways too.
Edit:
Based on the comment from #user3653474 the Accessor function could look like this.
public function getListArrayAttribute()
{
$data = [
$this->data_1,
$this->data_2,
];
return $data;
}
data_1 and data_2 are the column names in the table of the same model.

Laravel 5.8 query from column containing json data

I have a column that contains a json array, but I am having difficulty outputting them as individual items.
ListingController#show
public function show(Listing $listing)
{
$services = collect($listing->services_offered);
return view('listings.listing', compact('banner', 'listing', 'services'));
}
listing.blade.php
#foreach($services as $service)
<li>{{$service}}</li>
#endforeach
Output
In your Listing model you can cast the json array into a PHP array like this:
protected $casts = [
'services_offered' => 'array',
];
Then you can wrap that into a collection as you already do and list them on the view.
You need to convert the JSON string to actual JSON array.
$services = collect(json_decode($listing->services_offered, true));
or you can create an accessor in your model
public function getServicesOfferedAttribute($value)
{
return json_decode($value);
}

How can I make laravel casting array on the model?

I try like this
Data type of votes_detail in database is json
My model like this :
<?php
class Store extends Model{
protected $fillable = [ ...,'votes_detail',...];
protected $casts = [
'votes_detail' => 'array',
];
}
My controller like this :
$store = Store::find($id)
$votes_detail = $store->votes_detail;
dd($votes_detail);
The result of dd($votes_detail) is :
{"1": "1", "5": "2"}
Why the result is still json?
The result should be an array
Whereas I've set the array in cast model
How can I solve this problem?
You could use Laravel accessors. In you model define a method called exactly getVotesDetailAttribute($details):
public function getVotesDetailAttribute($details)
{
return json_decode($details, true);
}
then when you will call $store->votes_detail you will get the expected result.
After that you can use mutators to convert an array back to JSON when it is saved back in the DB. Define the method setVotesDetailAttribute($value) as follows:
public function setVotesDetailsAttribute($value)
{
$this->attributes['votes_detail'] = json_encode($value);
}
You can easily do it by converting your data to the array by using toArray() function. So it should be like
$store = Store::find($id)->toArray();
//$store contain a array.
$votes_detail = $store['votes_detail'];
make it dd($votes_detail) and get your desire result.
I think you are storing array data after json_encode() to a column which is not defined as json() in the migration like:
$table->text('votes_detail');
or
$table->integer('votes_detail');
And seems you have defined votes_detail as string, number or something else.
This is the reason behind your problem. So, define column type as json in migration like:
$table->json('votes_detail');
And then refresh your migration, your casting in the model will work and you will get your desired array.

Can I use transformers to transform data coming from API rather than from database?

I have been using laravel to build my APIs. I use transformers to tranform data from model object.
Now instead of database, I have a response coming from an API as the data source and I want to transform that data back to a user, but I am unable to do so.
My Controller
public function rocByName(Request $request)
{
try {
$this->roc_by_name_validator->with( $request->all() )->passesOrFail();
$company_name = $request->input('company_name');
$result = $this->my_service->getDetailsByName($company_name); //$result has the response object from the API which I want to transform and give it as a response.
return $this->response->collection($result,new OnboardingTransformer()); //Tried using tranformer like this
}
catch (ValidatorException $e) {
dd($e);
}
}
My Transformer
<?php
namespace Modules\Onboarding\Transformers;
use League\Fractal\TransformerAbstract;
use App\Entities\OnboardingEntity; //I dont have an entity since the response is coming from an API!! What will give here?
/**
* Class OnboardingTransformerTransformer
* #package namespace App\Transformers;
*/
class OnboardingTransformer extends TransformerAbstract
{
/**
* Transform the \OnboardingTransformer entity
* #param \OnboardingTransformer $model
*
* #return array
*/
public function transform(OnboardingEntity $data_source)
{
return [
'company_name' => $data_source->company_name,
];
}
}
Here the OnboardingEntity refers to data coming from database ideally. Here I am not fetching data from database, instead my data is from an API source. How do I go about it. I am little consfused here. Can someone give a solution?
$result has the following response
[
[
{
"companyID": "U72400MHTC293037",
"companyName": "pay pvt LIMITED"
},
{
"companyID": "U74900HR2016PT853",
"companyName": "dddd PRIVATE LIMITED"
}
]
]
$this->response->collection is meant to get a collection of objects, not the array. Then all of this objects is going to transformer that is transform OnboardingEntity objects as you want. So first you should transform your input array into collection of objects. The example how i did it above (you should change it to your own input array)
$data = json_decode('[
[
{
"companyID": "U72400MHTC293037",
"companyName": "pay pvt LIMITED"
},
{
"companyID": "U74900HR2016PT853",
"companyName": "dddd PRIVATE LIMITED"
}
]
]');
$data = collect( array_map( function($ob){
return (new OnboardingEntity($ob));
}, $data[0]));
And then pass this collection of OnboardingEntity objects to $this->response->collection method, like here
$this->response->collection($data,new TestTransformer());
You may want to send common data structure to Fractal as sources of data are different. Array is best possible type for you.
Consider this when you are fetching data from Eloquent(DB):
$result = $yourModel->get(); // This will return you with a collection object.
Before passing this object to fractal convert it to array.
$this->response->collection($result->toArray(),new OnboardingTransformer());
In case of first or single model object. check for null before calling toArray().
$result = $yourModel->first();
if($result){
$result = $result->toArray();
}
// Fractal itself can handle null
Now for second scenario where data is coming from external source like API or file.
$result = $this->my_service->getDetailsByName($company_name);
// Try converting your response object to array from within
You can do this with json_decode(<Body of response>, true). And then pass this array to Fractal.
Why Array?
Because source of data could be anything from Database to File, from Cache to APIs. Format could be JSON or XML. Converting all these to array comes built in PHP.

Is it possible to remove fields with *RECURSION* when using toArray() in Propel?

I am using Propel 2. I am hydrating objects through the relations, like so:
$return = OrderQuery::create()
->joinWith('Customer')
->joinWith('Status')
->find()
->toArray(TableMap::TYPE_PHPNAME, true, [], true);
The resulting Array would look something like this:
{
"Id": 1,
"CustomerId": 1,
"StatusId": 1,
"Initiated": "2016-01-01T01:01:01+00:00",
"Customer": {
"Id": 1,
"Forname": "Test",
"Surname": "Smith",
"Orders": [
"*RECURSION*"
]
}
"Status": {
"Id": 1,
"Title": "title 1",
"Priority": 1,
"Orders": [
"*RECURSION*"
]
},
}
I want to remove the fields where the value is *RECURSION*. I tried using the $alreadyDumpedObjects (3rd) parameter to toArray() but that didn't seem to help. I could also do some form of array walking with unset() calls, but I'm hoping there's a better way, maybe with a formatter or something?
For bonus points, I'd quite like to remove the columns which define the foreign key relationship. For instance, CustomerId would go, but Customer would remain.
Note for brevity: This answer has some really helpful information, but there is a solution, despite this answer saying there isn't.
The RECURSION string is pretty much hardcoded in propel (in src/Propel/Generator/Builder/Orm/ObjectBuilder.php):
if (isset(\$alreadyDumpedObjects['$objectClassName'][\$this->hashCode()])) {
return '*RECURSION*';
}
I suppose you could override the object builder, but I doubt that's what you are looking for. Thus, the only other viable way is what you did not want to do, looping over the array and using unset. Something like this:
$array = StudyQuery::create()
->leftJoinWithInstitute()
->find()
->toArray();
var_dump($array);
echo PHP_EOL . PHP_EOL . PHP_EOL;
var_dump(cleanupData($array));
/**
* #param array $array
*
* #return array
*/
function cleanupData(array $array)
{
$relationsFound = [];
foreach ($array as $key => $item) {
if ($item === ["*RECURSION*"] || $item == "*RECURSION*") {
unset($array[$key]);
} elseif (is_array($item)) {
$array[$key] = cleanupData($item);
$relationsFound[] = $key;
}
}
foreach ($relationsFound as $relation) {
$key = $relation . 'Id';
if (isset($array[$key])) {
unset($array[$key]);
}
}
return $array;
}
This should also filter out the ***Id-fields, if that relation is present (provided the PHPName for the relation matches the column name).
When you call toArray method on a Propel ObjectCollection it will call the toArray on every item of the collection (the result of the query).
So there are two way to achieve what you're trying to do:
1) Overwrite the toArray method in the Customer and Status model classes or in the CustomerCollection and StatusCollection ones (a quick&dirty solution since it gets applied every time you use the toArray method...).
2) Extend the ArrayFormatter class to define a "skip Customer and Status strategy" in the format method and then add it as the formatter to use (with the setFormatter method on the collection) only when you need this particular behavior.
Some refs:
http://propelorm.org/documentation/reference/model-criteria.html#using-an-alternative-collection-class
http://propelorm.org/documentation/reference/model-criteria.html#using-an-alternative-formatter
The answer, it seems, appears to be very simple.
If I use a standard ArrayFormatter like this:
$return = OrderQuery::create()
->setFormatter('Propel\Runtime\Formatter\ArrayFormatter');
->joinWith('Customer')
->joinWith('Status')
->find()
->toArray();
What is returned is an ArrayCollection that, when toArray() is called on, is exactly the same as my original output apart from the recursion fields are missing.
Note, when getting one result, for instance with ->findPk(1), it will return an associative array, so you shouldn't use ->toArray() explicitly.
Did you try calling:
unset($return['Customer']['Orders']);
unset($return['Status']['Orders']);

Categories