Improve reusability of my data transformation functions - reactjs

I have currently done with my code transformation on my api data by its roleId.
However, I need to display another view which will group the data according to which projectId the users are in.
I can simply copy and paste and create another one for projectIds data transformation, however, I feel that my approach may be messy and not easily reusable.
So I would like to ask if there is a better way to do this?
Because i cannot simply swap roleIds in the function to projectIds by putting rolesId or projectIds in a variable to be reused in the function.
Can anyone help me please ?
Code for the api data transformation to dislay in ant design tree data table:
let apiData = [
{
email: "alyssayo#xxx.com",
permissionIds: null,
roleIds: ["raa", "baa", "caa"],
projectIds: ["1aa", "3aa"]
},
{
email: "chiuewww#xxx.com",
permissionIds: null,
roleIds: ["baa", "caa"],
projectIds: ["1aa", "2aa", "3aa"]
},
{
email: "lalaqq#xxx.com",
permissionIds: null,
roleIds: ["caa"],
projectIds: ["1aa"]
},
{
email: "sqssq#xxx.com",
permissionIds: null,
roleIds: [],
projectIds: []
}
];
//Isolate and transform data by roleId
const transData = apiData.reduce((arr, item) => {
let formatted = item.roleIds.map((id) => {
return {
roleIds: id,
children: [{ ...item, roleIds: id }]
};
});
return [...arr, ...formatted];
}, []);
//Group transformed data by roleIds
const findMatch = (arr, roleIds) =>
arr.find((item) => item.roleIds === roleIds);
const groupArray = (originalArr) => {
return Array.isArray(originalArr)
? originalArr.reduce((previousObj, obj) => {
if (findMatch(previousObj, obj.roleIds)) {
findMatch(previousObj, obj.roleIds).children.push(...obj.children);
} else {
previousObj.push(obj);
}
return previousObj;
}, [])
: "Need an array";
};
//Call the group roleId function on transformed data by roleId
const userRoledata = groupArray(transData);
//Add key to parent and children
let key = 1;
userRoledata.forEach((item) => {
item.key = key++;
item.children.forEach((child) => {
child.key = key++;
});
});
setData(userRoledata); //this will be dataSource for table rendering in ant design
What will the data transformed display when used as dataSource in ant design:
If grouped by roleIds:
[
{
"roleIds": "raa",
"children": [
{
"email": "alyssayo#xxx.com",
"permissionIds": null,
"roleIds": "raa",
"projectIds": [
"1aa",
"3aa"
],
"key": 2
}
],
"key": 1
},
{
"roleIds": "baa",
"children": [
{
"email": "alyssayo#xxx.com",
"permissionIds": null,
"roleIds": "baa",
"projectIds": [
"1aa",
"3aa"
],
"key": 4
},
{
"email": "chiuewww#xxx.com",
"permissionIds": null,
"roleIds": "baa",
"projectIds": [
"1aa",
"2aa",
"3aa"
],
"key": 5
}
],
"key": 3
},
{
"roleIds": "caa",
"children": [
{
"email": "alyssayo#xxx.com",
"permissionIds": null,
"roleIds": "caa",
"projectIds": [
"1aa",
"3aa"
],
"key": 7
},
{
"email": "chiuewww#xxx.com",
"permissionIds": null,
"roleIds": "caa",
"projectIds": [
"1aa",
"2aa",
"3aa"
],
"key": 8
},
{
"email": "lalaqq#xxx.com",
"permissionIds": null,
"roleIds": "caa",
"projectIds": [
"1aa"
],
"key": 9
}
],
"key": 6
}
]
If grouped by projectIds:
[
{
"projectIds": "1aa",
"children": [
{
"email": "alyssayo#xxx.com",
"permissionIds": null,
"roleIds": [
"raa",
"baa",
"caa"
],
"projectIds": "1aa",
"key": 2
},
{
"email": "chiuewww#xxx.com",
"permissionIds": null,
"roleIds": [
"baa",
"caa"
],
"projectIds": "1aa",
"key": 3
},
{
"email": "lalaqq#xxx.com",
"permissionIds": null,
"roleIds": [
"caa"
],
"projectIds": "1aa",
"key": 4
}
],
"key": 1
},
{
"projectIds": "3aa",
"children": [
{
"email": "alyssayo#xxx.com",
"permissionIds": null,
"roleIds": [
"raa",
"baa",
"caa"
],
"projectIds": "3aa",
"key": 6
},
{
"email": "chiuewww#xxx.com",
"permissionIds": null,
"roleIds": [
"baa",
"caa"
],
"projectIds": "3aa",
"key": 7
}
],
"key": 5
},
{
"projectIds": "2aa",
"children": [
{
"email": "chiuewww#xxx.com",
"permissionIds": null,
"roleIds": [
"baa",
"caa"
],
"projectIds": "2aa",
"key": 9
}
],
"key": 8
}
]

Define a transform function with 2 parameter. First the apiData, which is the data you want to transform and secondly the transformation_key which is a string of either roleIds or projectIds.
Within this function you first to generate an object with the different roleIds/projectIds as keys and for each key an array of all the items included.
To do so you make use of a reducer and loop over the items
apiData.reduce((obj, item) => {
if (!item[transformation_key]) return obj; // in case item[transformation_key] is null, you can skip the item an just return the item as is.
... // if not, we've to reduce over the array of roleIds/projectIds within the item as well.
}, {}) // {} is the new object (`obj` refers to this within the reducer)
Within each item we also have to loop over all the items in the roleIds/projectIds of that item, so we add a second/inner reducer.
// item[transformation_key] is the array of roleIds/projectIds within your item.
item[transformation_key].reduce((cur, id) => {
// `cur` is actually the same object as the `obj` from the outer reducer.
if (!cur[id]) cur[id] = [] // if the key/id doesn't excist yet on the object, we set it equal to an empty array.
cur[id].push({
...item,
[transformation_key]: id
}) // we push the item to the array (using the spread operator and updating the value for the `transformation_key` within the item.
return cur // you must return the object `cur`.
}, obj) // we pass to `obj` from the outer reducer into the inner reducer.
This will generate an object like
const transformedObject = {
[roleIds/projectIds] : [
... all the children
]
}
next we map the ids to the required output
return Object.keys(transformedObject).map(key => {
return {
[transformation_key]: key,
children: transformedObject[key]
}
})
To summarize
function transform(apiData, transformation_key) {
if (!(transformation_key == 'roleIds' || transformation_key == 'projectIds')) throw new Error("Choose either 'roleIds' or 'projectIds' as a transformation_key")
const transformedObject = apiData
.reduce((obj, item) => {
if (!item[transformation_key]) return obj;
return item[transformation_key].reduce((cur, id) => {
if (!cur[id]) cur[id] = []
cur[id].push({
...item,
[transformation_key]: id
})
return cur
}, obj)
}, {});
return Object.keys(transformedObject).map(key => {
return {
[transformation_key]: key,
children: transformedObject[key]
}
})
}
const transDataByRoleIds = transform(res.data, 'roleIds')
const transDataByProjectIds = transform(res.data, 'projectIds')

Related

How to parse a nested json file in typescript (.tsx)?

I'm new to typescript and looking for a way to parse this nested json data given below and display the following info after parsing:
Date: 2022-10-27T16:28:01Z
Typename": Asset,
PolicyId: 12345678abcdef,
Asset Name: 12345678abc,
Quantity:5000,
Output txHash: 1212345678abcdef,
Output txHash: 1212345678abcdef1234
{
"transactions": [
{
"__typename": "Transaction",
"includedAt": "2022-10-27T16:28:01Z",
"mint": [
{
"__typename": "Token",
"asset": {
"__typename": "Asset",
"policyId": "12345678abcdef",
"assetName": "12345678abc",
"name": null
},
"quantity": "5000"
}
],
"outputs": [
{
"__typename": "TransactionOutput",
"txHash": "1212345678abcdef",
"value": "1500000",
"tokens": [
{
"__typename": "Token",
"asset": {
"__typename": "Asset",
"policyId": "12345678abcdef",
"assetName": "12345678abc"
},
"quantity": "5000"
}
]
},
{
"__typename": "TransactionOutput",
"txHash": "12bcd12345678abcdef1234",
"value": "9998312015",
"tokens": []
}
]
}
]
}
`
I tried the following code:
const myJSON = JSON.stringify(data, null, 2);
const myArray = JSON.parse(myJSON);
and was able to display the Date:
const DisplayData1 = myArray.transactions.map(
(info:any) => {
return(
<tr>
<td>{info.includedAt}</td>
</tr>
)
}
)
I'm not able to proceed due to the nested nature of the json file and my lack of familiarity with tsx. All help appreciated. Thanks.
Need to add the nested loop to show data.
Also in order to access keys starting with -, use a square bracket instead of the . operator. like obj['__typename']
const DisplayData1 = myArray.transactions.map(
(info:any) => {
return(
<tr>
<td>{info.includedAt}</td>
{
info.mint.map((i) => {
return (
<td>{i['__typename']}</td>
<td>{i.quantity}</td>
<td>{i.asset.policyId}</td> // access `asset` object
)
})
}
</tr>
)
}
)

How to filter JSON data based on another JSON data in typescript

I have 2 JSON Data 1. Payers 2. Rules. I need to filter Payers JSON data based on PayerId from Rules JSON data.
{
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "23456",
"name": "Test Payer2",
},
{
"payerId": "34567",
"name": "Test Payer3"
}}
Rules JSON file
{
"Rules": [
{
"actions": {
"canCopyRule": true
},
"RuleId": 123,
"description": "Test Rule",
"isDisabled": false,
"Criteria": [
{
"autoSecondaryCriteriaId": 8888,
"criteriaType": { "code": "primaryPayer", "value": "Primary Payer" },
"payerId": ["12345", "34567"]
}
]
}
}]}
I need to filter Payers JSON data based on Rules JSON data if PayerID matches
I need output like below
{
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "34567",
"name": "Test Payer3"
}
}
How to filter?
You can use Array.filter like that (based on your data structure):
const filteredPayers = payersObj.Payers.filter((p) => rulesObj.Rules[0].Criteria[0].payerId.includes(p.payerId));
I can't figure out why your Rules json looks like this, I guess you have multiple rules. If so, you will need to iterate over each rule and invoke includes. Same for Criteria.
Code will check each rule and each critirias
and will return payers if payerId found in any of the given rules of any criteria
const payers = {
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "23456",
"name": "Test Payer2",
},
{
"payerId": "34567",
"name": "Test Payer3"
}]}
const rules = {
"Rules": [
{
"actions": {
"canCopyRule": true
},
"RuleId": 123,
"description": "Test Rule",
"isDisabled": false,
"Criteria": [
{
"autoSecondaryCriteriaId": 8888,
"criteriaType": { "code": "primaryPayer", "value": "Primary Payer" },
"payerId": ["12345", "34567"]
}
]
}
]
}
const data = payers.Payers.filter(payer => rules.Rules.findIndex(rule => rule.Criteria.findIndex(criteria => criteria.payerId.includes(payer.payerId)) != -1) !== -1)
console.log(data)

Update object value in array within array React

I am trying to update my product attributes and i came to a solution only for one attribute.
this is my carrtItems state
this.state = {
cartItems: [],
selectedAttributes: []
Json Object
{
"__typename": "Product",
"name": "iPhone 12 Pro",
"inStock": true,
"attributes": [
{
"__typename": "AttributeSet",
"id": "Capacity",
"name": "Capacity",
"type": "text",
"items": [
{
"__typename": "Attribute",
"id": "512G",
"value": "512G"
},
{
"__typename": "Attribute",
"id": "1T",
"value": "1T"
}
]
},
{
"__typename": "AttributeSet",
"id": "Color",
"items": [
{
"__typename": "Attribute",
"id": "Black",
"value": "#000000"
},
{
"__typename": "Attribute",
"id": "White",
"value": "#FFFFFF"
}
]
}
],
"id": "product1",
"quantity": 1,
"selectedAttributes": [
{
"value": "512G",
"type": "Capacity",
"id": "product1"
},
{
"value": "#44FF03",
"type": "Color",
"id": "product1"
}
]
}
This is my update function:
updateCartItem = (cart, product, selectedAttribute, newAttributes) => {
const existingCartItem = cart.find(
(cartItem) => cartItem.id === product.id
);
const thisCart = cart.filter((cartItem) => cartItem.id !== product.id)
if (existingCartItem) {
cart.map((cartItem) =>
cartItem.selectedAttributes.map((attr)=> attr.type===selectedAttribute.map((newAttr)=>newAttr.type
? newAttributes= [{
...attr,
value: newAttr.value,
}]
: [newAttributes = attr]
)));
return [
...thisCart,
{ ...product, selectedAttributes: newAttributes },
];
}
};
This is update state function
updateItemToCart = (product, selectedAttributes) => {
this.setState({
cartItems: this.updateCartItem(this.state.cartItems, product, selectedAttributes),
});
};
This my is my selected attributes functions
selectAttribute = (attribute, newAttribute, type, id) => {
const existingAttribute = attribute.find(
(attr) => attr.type === type && attr.id === id
);
if (existingAttribute) {
return attribute.map((attr) =>
attr.type === type && attr.id === id
? { ...attr, value: newAttribute }
: attr
);
}
return [...attribute, { value: newAttribute, type, id }];
};
selectedAttributesHandler = (newAttribute, type, id) => {
this.setState({
selectedAttributes: this.selectAttribute(
this.state.selectedAttributes,
newAttribute,
type,
id
),
});
};
What i want here is to update selectedAttributes if color or size changes but to remain the other attribute.
Example:
Color: white, Size: M
Here i change the size to L on click
Result:
Color: white, Size:L
How to update updateCartItem function to achieve this result.
You can use the spread operator
It allows you to easily populate an object or array with a shallow copy of the contect of another object or array.
The syntax is three dots followed by the name of the object or array that we want to copy.
Example:
this.setState({
...this.state,
selectedAttributes: this.selectAttribute(
this.state.selectedAttributes,
newAttribute,
type,
id
),
});
There are other ways of achieving this, but this is the likely the simplest way and is a common pattern when working with state in React.

React setState - Add array to nested object with multiple arrays

I'm currently working on a new application in React. This is the first time I'm creating something in React. The application will display our own promotions.
My initial state is as follows:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510558814960,
"endDateTimeStamp": 1510558814960,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
This is created from my defaultPromotion constant. This constant is stored in a separate file, which I call api.js
export const defaultPromotion = {
name: '',
campaign: '',
url: 'https://',
position: 0,
periods: [
{
startDateTimeStamp: Date.now(),
endDateTimeStamp: Date.now(),
variants: [
{
title: '',
text: '',
image: '',
},
]
},
]
}
In my createPromotion component it's created as followed
let promotionState = api.promotions.defaultPromotion;
this.state = {
promotion: promotionState
};
I can add a new period with the following:
addPromotion() {
let promotion = this.state.promotion;
promotion.periods.push( api.promotions.defaultPromotion.periods[0] );
this.forceUpdate();
}
After that, a new period is added as expected. Suggestions to do this with setState() are very welcome! So, my new state is now:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
Now, I want to add a new variant for this promotion period, this is where I'm stuck for 2 days now.
I'm adding a new period as follows:
addVariant( periodKey ) {
const promotion = this.state.promotion;
promotion.periods[periodKey].variants.push(api.promotions.defaultPromotion.periods[0].variants[0]);
this.setState({ promotion: promotion });
}
periodKey is here "1", so, I'm expecting that there will be added a new variant for periods[1], but, it's added to both periods. State is now as follows:
{
"promotion": {
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
}
Can someone explain me why this is happening and how I can add a new variant the right way?
Many, many thanks in advance!
UPDATE 1
Based on the answers from bennygenel and Patrick Hübl-Neschkudla, my implementation is now as follows:
Setting the initial state:
constructor(props) {
super(props);
let promotionState = api.promotions.defaultPromotion;
this.state = { ...promotionState };
}
Method:
addVariant( periodKey ) {
this.setState((prevState) => {
const { periods } = prevState;
periods[periodKey].variants.push(
Object.assign({}, { ...periods[periodKey].variants, api.promotions.defaultPromotion.periods[0].variants[0]})
);
return { periods };
});
}
But this still is setting the new variant in all the periods. I've also tried the exact code from Benny, but with the same results. The method is called as
this.props.addVariant( this.props.periodKey );
Even when I call it as:
this.props.addVariant(2);
The same behaviour is happening.
UPDATE 2
I now have rewritten everything to redux, this is so I have access to my promotion in every component the easy way, instead off passing them through certain components. Based on the answer of #mersocarlin, I now have the following reducer cases:
Add period
case PROMOTION_ADD_PERIOD:
const { periods } = { ...state };
periods.push(api.promotions.defaultPromotion.periods[0]);
state = {
...state,
periods: periods
};
break;
Add a period variant
case PROMOTION_ADD_PERIOD_VARIANT :
state = {
...state,
periods: [
...state.periods[action.payload.period],
{
variants: [
...state.periods[action.payload.period].variants,
api.promotions.defaultPromotion.periods[0].variants[0]
]
}
]
};
break;
The following case:
Add a new variant, works, state:
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"startDateTimeStamp": 1510599968588,
"endDateTimeStamp": 1510599968588,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
},
{
"startDateTimeStamp": 1510599968594,
"endDateTimeStamp": 1510599968594,
"variants": [
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
After that, adding a new variant, kinda works, well, the variant is added, but I'm losing my 2nd period. State:
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [
{
"variants": [
{
"title": "",
"text": "",
"image": ""
},
{
"title": "",
"text": "",
"image": ""
}
]
}
]
}
I think this is a small thing I'm not see'ing. Does someone have the solution for the "PROMOTION_ADD_PERIOD_VARIANT" case?
Update 3
Changed the "PROMOTION_ADD_PERIOD" case as follows:
case PROMOTION_ADD_PERIOD:
state = {
...state,
periods: [
...state.periods,
initialState.periods[0]
]
};
break;
Update 4
Finaly found the solution. See the final code for PROMOTION_ADD_PERIOD_VARIANT below:
state = {
...state,
periods: [
...state.periods.map((item, index) => {
if ( index !== action.payload.period ) {
return item;
}
return {
...item,
variants: [
...item.variants,
initialState.periods[0].variants[0]
]
}
})
]
};
Thank you all so much for your help!!
Rather destruct your state object and avoid mutating it directly. This also happens to be a bad pattern.
Whenever you need to add a new item to the array:
const state = {
arrayProp: [{ prop1: 'prop1', prop2: 'prop2' }]
}
const newItem = {
prop1: 'value1',
prop2: 'value2',
}
const newState = {
...state,
arrayProp: [
...state.arrayProp,
newItem,
]
}
console.log('newState', newState)
Same applies for nested properties within your state:
Redux also uses this very same approach
const state = {
objectProp: {
arrayPropWithinArray: [
{ id: '0', otherProp: 123, yetAnotherProp: 'test' },
{ id: '1', otherProp: 0, yetAnotherProp: '' }
]
}
}
const { objectProp } = state
const index = objectProp.arrayPropWithinArray.findIndex(obj => obj.id === '1')
const newSubItem = {
otherProp: 1,
yetAnotherProp: '2',
}
const newState = {
...state,
objectProp: {
...objectProp,
arrayPropWithinArray: [
...objectProp.arrayPropWithinArray.slice(0, index),
{
...objectProp.arrayPropWithinArray[index],
...newSubItem,
},
...objectProp.arrayPropWithinArray.slice(index + 1),
]
}
}
console.log('newState', newState)
Your specific case (as described in your comment)
const periodKey = '2' // your periodKey var. Get it from the right place, it can be your action for example
const index = state.periods.findIndex(period => period.id === periodKey) // find which index has to be updated
state = {
...state, // propagates current state
periods: [
...state.periods.slice(0, index), // propagates everything before index
{
...state.periods[index],
variants: [
...state.periods[index].variants,
api.promotions.defaultPromotion.periods[0].variants[0],
],
},
...state.periods.slice(0, index + 1) // propagates everything after index
]
}
So, what's happening here is that you have an array with two references to the same object.
Imagine it like this:
myArray[0] = reference to defaultPromotion
myArray[1] = reference to defaultPromotion
That's actually a wonderful example of why immutability concepts got so much attention in the past few years :)
What you'd want to do here is instead of adding the defaultPromotion object to the promotions array, you create a new object with the same props as this object and add it. It would look something like this (depending on your ES version etc.)
promotion.periods.push(
Object.assign({}, api.promotions.defaultPromotion.periods[0])
);
This way, you're creating a new object and pass this to the array instead of a reference to the already existing one.
First suggestion, if you are going to have only one promotion object in your state and not an array, lose the promotion level. this will reduce the complexity of your state. You can use spread syntax to easily set your initial state.
Example
let promotionState = api.promotions.defaultPromotion;
this.state = { ...promotionState };
Above code would end up creating a state like below;
{
"name": "",
"campaign": "",
"url": "https://",
"position": 0,
"periods": [{
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [{
"title": "",
"text": "",
"image": ""
}]
}, {
"startDateTimeStamp": 1510559984421,
"endDateTimeStamp": 1510559984421,
"variants": [{
"title": "",
"text": "",
"image": ""
}]
}]
}
Another suggestion I can make is to use functional setState to reduce possibility to mutate.
Example
addPromotion() {
this.setState((prevState) => {
const { periods } = prevState;
periods.push(api.promotions.defaultPromotion.periods[0]);
return { periods };
});
}
addVariant( periodKey ) {
this.setState((prevState) => {
const { periods } = prevState;
periods[periodKey].variants.push(api.promotions.defaultPromotion.periods[0].variants[0]);
return { periods };
});
}

Yii2 return JSON format with array inside an array

I am trying to get these data below,
Table relations:
people(one) <---> (many) people_info (one) <---> (many) people_contact
in the following format,
people: {
p_id: 10,
p_price: 3.99,
people_info : [
{
pl_id: 3,
pl_state: 2,
pl_district: 6,
pl_latitude: 6.323434,
pl_longitude: 108.23499,
people_contact: [
{
plc_id: 2
},
{
plc_id: 1
}
]
},
{
pl_id: 2,
pl_state: 7,
pl_district: 12,
pl_latitude: 6.000434,
pl_longitude: 108.9910003,
people_contact: [
{
plc_id: 5
},
{
plc_id: 9
}
]
}
]
}
Currently with these controller codes,
class PeopleController extends Controller
{
public function actionPeople($params){
Yii::$app->response->format = Response::FORMAT_JSON;
....//some other codes//.....
$people= People::find()->select(['p_id', 'p_price'] )->where(['p_id' => $itemId])->one();
$info= PeopleContact::find()->with(['plPeople'])->asArray([])->all();
return array(
'people' => $people,
'info' => $info,
);
}
}
I got these,
"people": {
"p_id": "3",
"p_price": "32.42"
}, "locations": [{
"pl_id": "1",
"pl_people": "3",
"pl_title": "",
"pl_latitude": "6.16438700000000000000",
"pl_longitude": "102.28314649999993000000",
"pl_place": null,
"pl_premise": null,
"pl_street": "1",
"pl_area": "1",
"pl_postcode": "1",
"pl_district": "1",
"pl_state": "3",
"pl_country": 1,
"place": null,
"premise": null,
"street": null,
"area": null,
"postcode": null,
"district": null,
"state": null,
"country": "United Kingdom",
"contacts": [{
"plc_name": "joe",
"plc_phone": "123456",
"plc_email": null
}]
}]
}
How do I achieve it in the format mentioned at the top?
$output;
$people=People::find()->select(['p_id', 'p_price'] )->asArray()->all();
foreach($people as $person) {
$infos = PersonInfo::find()->where(['person_id' => $person->id])->asArray()->all();
foreach($infos as $info) {
$contacts = PersonContact::find()->where(['person_info_id' => $info->id])->asArray()->all();
foreach($contacts as $contact) {
$info['contacts'][] = $contact;
}
$person['info'][] = $info
}
$output['people'][] = $person
}
return $output;
You should loop through and fetch data like this: people > info > contact each next level relying on info fetched from the previous one. Then store it in the format you want such as demonstrated above.
This will output something like:
"people": [{
...
"info": [{
...
"contacts": [{
...
},{
...
}]
}]
},{
...
}]

Resources