Laravel: Resources with json field - json

I am on Laravel 7.x and I have two models (CustomerOrder composed of many CustomerOrderLines) with parent - child relationship. Parent (CustomerOrder) model has a json type field among its fields.
CustomerOrderResource.php:
return [
'id' => $this->id,
'wfx_oc_no' => $this->wfx_oc_no,
'qty_json' => json_decode($this->qty_json)
];
CustomerOrderLineResource.php:
return [
'id' => $this->id,
'description' => $this->description,
'customer-order' => $this->customerOrder
];
CustomerOrder->GET request returns properly formatted data as:
"data": {
"id": 11,
"wfx_oc_no": 12,
"qty_json": {
"L": "20",
"M": "30",
"S": "20",
"XL": "100"
}
}
But for CustomerOrderLine->GET, the response is as:
"data": {
"id": 15,
"description": "test desc",
"customer-order": {
"id": 11,
"wfx_oc_no": 12,
"qty_json": "{\"L\": \"20\", \"M\": \"30\", \"S\": \"20\", \"XL\": \"100\"}"
}
}
json field is not properly formatted. It seems it doesn't go through Resource class. Please let me know, how can I get this fixed?
FYI
CustomerOrderLine.php:
public function parent()
{
return $this->belongsTo(CustomerOrder::class);
}

Finally managed to get it solved using json cast. The field was included in $casts array in the model.

Related

Parsing JSON from CouchDB to ElasticSearch via Logstash

I am not able to parse JSON from CouchDB to Elasticsearch index in the desired way.
My CouchDB data looks like this:
{
"_id": "56161609157031561692637",
"_rev": "4-4119e8df293a6354be4c9fd7e8b12e68",
"deleteFlag": "N",
"entryUser": "John",
"parameter": "{\"id\":\"14188\",\"rcs_p\":null,\"rcs_e\":null,\"dep_p\":null,\"dep_e\":null,\"dep_place\":null,\"rcf_p\":null,\"rcf_e\":null,\"rcf_place\":null,\"dlv_p\":\"3810\",\"dlv_e\":\"1569\",\"seg_no\":null,\"trans_type\":\"incoming\",\"trans_service\":\"delivery\"}",
"physicalId": "0",
"recordDate": "2020-12-28T17:50:16+05:45",
"tag": "CARGO",
"uId": "56161609157031561692637",
"~version": "CgMBKgA="
}
What I am trying to do is be able to search using the nested field of the parameter of the above JSON.
When I put the data in ES index it is stored like this:
{
"_index": "del3",
"_type": "_doc",
"_id": "XRCV9XYBx5PRwauO--qO",
"_version": 1,
"_score": 0,
"_source": {
"#version": "1",
"doc_as_upsert": true,
"doc": {
"physicalId": "0",
"recordDate": "2020-12-27T12:56:45+05:45",
"tag": "CARGO",
"~version": "CgMBGgA=",
"uId": "48541609052212485430933",
"_rev": "3-937bf92e6010afec13664b1d9d06844b",
"deleteFlag": "N",
"entryUser": "John",
"parameter": "{\"id\":\"4038\",\"rcs_p\":null,\"rcs_e\":null,\"dep_p\":null,\"dep_e\":null,\"dep_place\":null,\"rcf_p\":null,\"rcf_e\":null,\"rcf_place\":null,\"dlv_p\":\"5070\",\"dlv_e\":\"2015\",\"seg_no\":null,\"trans_type\":\"incoming\",\"trans_service\":\"delivery\"}"
},
"#timestamp": "2021-01-12T07:53:33.978Z"
},
"fields": {
"#timestamp": [
"2021-01-12T07:53:33.978Z"
],
"doc.recordDate": [
"2020-12-27T07:11:45.000Z"
]
}
}
I want to be able to access the fields inside the parameter (id, rcs_p, rcs_e, ..) in Elasticsearch.
Here is my logstash.conf file:
input {
couchdb_changes {
host => "<host_name>"
port => 5984
db => "mychannel_asset$management"
keep_id => false
keep_revision => true
#initial_sequence => 0
always_reconnect => true
sequence_path => "/usr/share/logstash/config/seqfile"
}
}
filter {
json {
source => "[parameter]"
remove_field => ["[parameter]"]
}
}
output {
if([doc][tag] == "CARGO") {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "del3"
user => elastic
password => changeme
}
}
}
How do I achieve my desired result? I also tried to do by creating a custom template by defining a nested type for parameter but no luck yet. Any help would be appreciated.
I think you did almost everything right. I'm not too sure about the actual structure, but one of these might work:
filter {
json {
source => "parameter"
target => "parameter"
}
}
filter {
json {
source => "[doc][parameter]"
target => "[doc][parameter]"
}
}
I don't know how CouchDB source input plugins works but it seems to be putting everything under doc object.

Access nested JSON in React table

I want to display nested JSON data in a react-table.
I tried it like this:
render() {
const columns = [{
//Not Displaying
Header: 'Owner ID',
id: 'ownerid',
accessor: '_links.customer.href.ownerid', // <- i think this is wrong
Cell: this.renderEditable
},
{
//Displaying
Header: 'Price',
accessor: 'price',
Cell: this.renderEditable
}, {
The data i am getting back and have bound to the table is structured as follows:
[
{
"id": 1,
"date": "20.07.2019",
"price": 3.2,
"customer": {
"ownerid": 1,
"firstname": "John",
"lastname": "Johnson"
}
}
]
Here i am using the columns array:
import ReactTable from "react-table";
<ReactTable data={this.state.offers} columns={columns}
filterable={true} pageSize={10}/>
Binding the data:
fetchOffers = () => {
const token = sessionStorage.getItem("jwt");
fetch(SERVER_URL + 'api/offers',
{
headers : {'Authorization':token}
})
.then((response) => response.json())
.then((responsteData) => {
this.setState({
offers: responsteData._embedded.offers,
});
console.log(this.state);
})
.catch(err=> console.error(err));
}
The data i am using after binding:
Check the Accessors documentation. It has several examples for complex data structure.
I don't see _links or href in your sample data. So I think that you need just:
accessor: 'customer.ownerid'
The data structure from the console screenshot doesn't match your sample data. And it doesn't seem to contain ownerid. Try accessor: '_links.customer.href' to check whether it outputs anything to the table.
I figured it out.
I called the endpoint "localhost:8080/api/offers" and saved the following response:
"offers": [
{
"date": "20.07.2019",
"price": 3.2,
"_links": {
"self": {
"href": "http://localhost:8080/api/offers/1"
},
"offer": {
"href": "http://localhost:8080/api/offers/1"
},
"customer": {
"href": "http://localhost:8080/api/offers/1/customer"
}
}
}
]
there is no customer object
But when i call "localhost:8080/offers" i get:
[
{
"id": 1,
"date": "20.07.2019",
"price": 3.2,
"customer": {
"ownerid": 1,
"firstname": "John",
"lastname": "Johnson"
}
}
]
i changed the URI in my project and now the number is displaying.
I still don't know why i get data from "../api/offers" but i will research.
I had to access a nested object and display it with some styling, and this ended up working for me:
(Note: I was using typescript, so some of the typing might not be necessary)
{
Header: 'Thing To Display',
accessor: 'nested.thing.to.display',
Cell: ({ row }: { row: Row }) => (
<p>{row.values['nested.thing.to.display']}</p>
),
}

How to remove brakets on Json respone

im working on Laravel Rest Api with passeport ,
in return response()->json() i want to trim the brackets
I've tried trim($json,'[]') function but it's not what i want
public function getOffers()
{
$offers = Offer::where('type', 'primary')->where('active', 1)->get();
$paks = Offer::where('type', 'pack')->where('active', 1)->get();
return response()->json([
'offersList' => $offers,
'packsList' => $paks,
], 200);
}
i expect the output will be
{
"offersList": {
{
"id": 3,
"name": "Gold",
"description": null
}
},
"packsList":[]
}
but the actual result is
{
"offersList": [
{
"id": 3,
"name": "Gold",
"description": null
}
],
"packsList":[]
}
$offers is a collection, and thus an array in JSON.
If $offers should be a single item, use first() instead of get() and it will be rendered as a single object in your JSON instead of an array of objects.
$offers = Offer::where('type', 'primary')->where('active', 1)->first();
If $offers should, at times, contain multiple offers, leave it as-is; it's correct!
Braces {} nested in another object is not valid JSON.
Objects can be used in property values and as array elements.
Not valid JSON
{
"offersList": {
{
"id": 3,
"name": "Gold",
"description": null
}
}
}
Valid option 1
{
"offersList": [
{
"id": 3,
"name": "Gold",
"description": null
}
]
}
Valid option 2
{
"offersList": {
"id": 3,
"name": "Gold",
"description": null
}
}
You can use online linters to quickly validate your JSON structure.
https://jsonformatter.curiousconcept.com/

How to add custom properties to Laravel paginate json response

I have the following simple index method:
public function index()
{
// dd(Poll::paginate(2));
return response()->json(Poll::paginate(2),200);
}
The output of that method is looks like the following json object:
{
"current_page": 1,
"data": [
{
"id": 1,
"title": "I suppose?' said Alice. 'Why, you don't know.",
"created_at": "2018-09-14 16:42:11",
"updated_at": "2018-09-14 16:42:11"
},
{
"id": 2,
"title": "Alice; but she knew that it seemed quite.",
"created_at": "2018-09-14 16:42:11",
"updated_at": "2018-09-14 16:42:11"
}
],
"first_page_url": "http://127.0.0.1:8000/polls?page=1",
"from": 1,
"last_page": 6,
"last_page_url": "http://127.0.0.1:8000/polls?page=6",
"next_page_url": "http://127.0.0.1:8000/polls?page=2",
"path": "http://127.0.0.1:8000/polls",
"per_page": 2,
"prev_page_url": null,
"to": 2,
"total": 11
}
I want to add another array property after "total: 11" attribute such as:
,
"anotherData" : [
"userId": 1,
"userName": "john10",
"message": "This is a message"
]
I have tried to understand how response()->json() works, so it can extract some data from LengthAwarePaginator object which it is the output of Poll::paginate(2) from this resource, but I could not able to understand how does it able to get an array from LengthAwarePaginator that holds the resultant keys in the json object?!
From the regarded resource above, the json() method takes an array and, I guess that, if the parameter is not an array, it tries to convert it to array, specially if it is an object like LengthAwarePaginator, so it may use toArray() method.
I have tried replacing return response()->json(Poll::paginate(2),200) with return response()->json(Poll::paginate(2)->toArray,200), then I have got the same output. Hence, I have decided to replace the code of my index method to be like the following:
public function index()
{
//dd(Poll::paginate(2)->toArray());
$output = Poll::paginate(2)->toArray();
$output['userData'] = ['userId' => \Auth::user()->id, 'userEmail' => \Auth::user()->email];
return response()->json($output,200);
}
The resultant output is:
...
"path": "http://127.0.0.1:8000/polls",
"per_page": 2,
"prev_page_url": null,
"to": 2,
"total": 11,
"userData": {
"userId": 1,
"userEmail": "lion#example.com"
}
}
If you're directly responding with the paginated data, a better option would be to wrap it in a Resource Collection and adding meta data to it.
This is how I implemented it.
CategoryController.php
public function index()
{
return new CategoryCollection(Category::paginate(10));
}
CategoryCollection.php
public function toArray($request)
{
return [
'status' => 1,
'message' => 'Success',
'data' => $this->collection,
];
}
Since Collection extends JsonResource, your response would automatically be converted to JSON when returned from the Controller.

Laravel Array & JSON Casting to Algolia

I am trying to send some data along to Algolia through the toSearchableArray. Any strings I have stored in my DB are sending along fine, but I hit a roadblock when trying to push nested JSON data along—the information is being sent as a string with characters escaped.
This is a sample of the nested object that I am storing in my table (MySQL with a JSON data type):
[
{
"id": 19,
"name": "Mathematics",
"short": "Math"
},
{
"id": 23,
"name": "Science",
"short": "Science"
},
{
"id": 14,
"name": "Health and Life Skills",
"short": "Health"
}
]
My model looks like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Resource extends Model
{
use Searchable;
protected $primaryKey = 'objectID';
public function toSearchableArray()
{
$data = $this->toArray();
$data['grades'] = explode(';', $data['grades']);
$data['units'] = explode(';', $data['units']);
return $data;
}
}
I get an output that looks like this:
array:22 [
"objectID" => 1
"name" => "Resource #1"
"slug" => "resource-1"
"write_up" => """
This is an example write up.
"""
"author" => "johnny"
"type_name" => "Lesson Plan"
"language" => "English"
"grades" => array:3 [
0 => "Kindergarten"
1 => "Grade 1"
2 => "Grade 4"
]
"subjects" => "[{"id": 19, "name": "Mathematics", "short": "Math"}, {"id": 23, "name": "Science", "short": "Science"}, {"id": 14, "name": "Health and Life Skills", "short": "Health"}]"
"units" => array:2 [
0 => "Unit A"
1 => "Unit B"
]
"main_image" => "https://dummyimage.com/250x325/000000/fff.png&text=Just+a+Test"
"loves" => 88
"downloads" => 280
"created_at" => "2018-01-01 13:26:47"
"updated_at" => "2018-01-02 10:10:32"
]
As you can see, the 'subjects' attribute is being stored as a string. I know there is attribute casting in 5.5 (I am running 5.5), but I am not too clear on how I would implement the example they have for Array & JSON Casting in my work above. https://laravel.com/docs/5.5/eloquent-mutators#attribute-casting
Would anyone be willing to show me an example?
I'd rely on Attribute Casting for this, add a $casts property in your model and it will be done automatically.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Resource extends Model
{
use Searchable;
protected $primaryKey = 'objectID';
protected $casts = [
'subjects' => 'array',
];
public function toSearchableArray()
{
// Same function as you posted
}
}
You can also do it manually in your toSearchableArray method with $data['subjects'] = json_decode($this->subjects, true);
I answered with more details on this other posts: https://discourse.algolia.com/t/laravel-array-json-casting-to-algolia/4125/2