Creating nested json with php - php

How can I convert the data I send with post to php code?
I have 3 select fields. adult, child, baby.
I want to create the following json structure up to the number of rooms when I post. The number of children and babies is not correct by area. How can I do it?
foreach ($this->input->post('adult') as $key => $value) {
$rooms[] = array(
'adult' => $_POST['adult'][$key],
'child' => array(
'child_total' => $_POST['child'][$key],
'child_date' => $_POST['child_date']
),
'baby' => array(
'baby_total' => $_POST['baby'][$key],
'baby_date' => null
)
);
}
I want to this json...
rooms: [
{
adult: 2,
child: [
child_total: 1
child_date: [
"2017-08-10"
],
],
baby: [
baby_total: 1
baby_date: [
"2017-07-01"
],
],
},
{
adult: 1,
child: [
child_total: 2
child_date: [
"2017-08-08",
"2017-08-09"
],
],
baby: [
baby_total: 2
baby_date: [
"2017-06-08",
"2017-05-09"
],
],
}
],

To figure out the data structure needed to make you json encoded string, let's start by defining the objects you're working with. Assuming this is something like a hotel booking system, let's map it out:
A hotel has rooms. They are identified by room number. This can be illustrated in code by $room[$room_number]. Using the room number as the key allows you to uniquely identify a particular room.
Each room has occupants: adults, children, and babies. This can be illustrated in code by
$room[$room_number]['adults']
$room[$room_number]['children']
$room[$room_number]['babies']
The set of babies can be illustrated as $baby[]. We really don't need to identify the baby with a unique identifier other that the index number; we're just interested in the list.
So, let's replace ['babies'] with ['baby'][]
$room[$room_number]['adults']
$room[$room_number]['children']
$room[$room_number]['baby'][]
Each baby has attributes. We're only going to use one, but for the sake of example, let's say we want to record two: birthdate and name. Another way of saying that would be each $baby = array('birthday'=>$birthdate, 'name'=>$name);
This is a little harder to illustrate, since you have to gather all the babies information before you assign it to $room[$room_number]['baby'][]. So I will show it using the index number:
$room[$room_number]['adults']
$room[$room_number]['children']
$room[$room_number]['baby'][0]['birthdate']
$room[$room_number]['baby'][0]['name']
The same functionality is desired for children:
$room[$room_number]['adults']
$room[$room_number]['children'][0]['birthdate']
$room[$room_number]['children'][0]['name']
$room[$room_number]['baby'][0]['birthdate']
$room[$room_number]['baby'][0]['name']
See a pattern? [identifier][attribute][identifier][attribute]
With this data structure, we can build your html inputs, assuming 2 children and 2 babies:
<?php
// assuming room number is 123
$room_number = '123';
?>
<!-- child 1 name -->
<label><input name="room[<?= $room_number ?>]['child'][0]['name']">Child 1 name</label>
<!-- child 1 birthdate -->
<label><input name="room[<?= $room_number ?>]['child'][0]['birthdate']">Child 1 birthdate</label>
<!-- child 2 name -->
<label><input name="room[<?= $room_number ?>]['child'][1]['name']">Child 2 name</label>
<!-- child 2 birthdate -->
<label><input name="room[<?= $room_number ?>]['child'][1]['birthdate']">Child 2 birthdate</label>
When you receive these inputs in your php script, it will already be properly formed as an array (I'm excluding adults and filled in sample values):
php > $room_number='123';
php > $room[$room_number]['child'][0]['birthdate'] = '20010-01-01';
php > $room[$room_number]['child'][0]['name'] ='Junior';
php > $room[$room_number]['child'][1]['birthdate'] = '2019-01-01';
php > $room[$room_number]['child'][1]['name'] = 'Bubba';
php > print_r($room);
Array
(
[123] => Array
(
[child] => Array
(
[0] => Array
(
[birthdate] => 20010-01-01
[name] => Junior
)
[1] => Array
(
[birthdate] => 2019-01-01
[name] => Bubba
)
)
)
)
This can easily be fed into json_encode: print json_encode($room);
However, you might ask what about the counts (totals)?
Those can easily be figured from the array structure, so they don't really need to be included:
php > print count($room[$room_number]['child']);
2
php >

You can use the json_encode function like this:
json_encode(['rooms' => $rooms])

Related

How to aggregate by dynamic or unknown fields in Elasticsearch 6.x

I'm fairly new to ElasticSearch, currently using v6.2 and I seem to have run into a problem while trying to add some aggregations to a query. Trying to wrap my head around the various types of aggregation, as well as the best ways to store the data.
When the query runs, I have some variable attributes that I would like to aggregate and then return as filters to the user. For example, one character may have attributes for "size", "shape" and "colour", while another only has "shape" and "colour".
The full list of attributes is unknown so I don't think I would be able to construct the query that way.
My data is currently structured like this:
{
id : 1,
title : 'New Character 1',
group : 1,
region : 1,
attrs : [
moves : 2,
# These would be dynamic, would only apply to some rows, not others.
var_colours : ['Blue', Green', 'Red'],
var_shapes : ['Round', 'Square', 'Etc'],
effects : [
{ id : 1, value: 20},
{ id : 2, value: 60},
{ id : 3, value: 10},
]
]
}
I currently have an aggregation of groups and regions that looks like this. It seems to be working wonderfully and I would like to add something similar for the attributes.
[
'aggs' => [
'group_ids' => [
'terms' => [
'field' => 'group',
'order' => [ '_count' => 'desc' ]
]
],
'region_ids' => [
'terms' => [
'field' => 'region',
'order' => [ '_count' => 'desc' ]
]
]
]
]
I'm hoping to get a result that looks like the below. I am also not sure if the data structure is setup in the best way either, I can make changes there if necessary.
[aggregations] => [
[groups] => [
[doc_count_error_upper_bound] => 0
[sum_other_doc_count] => 0
[buckets] => [
[0] => [
[key] => 5
[doc_count] => 27
],
[1] => [
[key] => 2
[doc_count] => 7
]
]
],
[var_colours] => [
[doc_count_error_upper_bound] => 0
[sum_other_doc_count] => 0
[buckets] => [
[0] => [
[key] => 'Red'
[doc_count] => 27
],
[1] => [
[key] => 'Blue'
[doc_count] => 7
]
]
],
[var_shapes] => [
[doc_count_error_upper_bound] => 0
[sum_other_doc_count] => 0
[buckets] => [
[0] => [
[key] => 'Round'
[doc_count] => 27
],
[1] => [
[key] => 'Polygon'
[doc_count] => 7
]
]
]
// ...
]
Any insight that anyone could provide would be extremely appreciated.
You should do this within your PHP script.
I can think of the following:
Use the Dynamic field mapping for your index.
By default, when a previously unseen field is found in a document, Elasticsearch will add the new field to the type mapping. This behaviour can be disabled, both at the document and at the object level, by setting the dynamic parameter to false (to ignore new fields) or to strict (to throw an exception if an unknown field is encountered).
Get all the existing fields in your index. Use the Get mapping API for this.
Loop over the results of Step 2 so you can get all the existing fields in your index. You can store them in a list (or array), for example.
You can create a PHP Elasticsearch terms aggregation for each of the fields in your list (or array). This is: create an empty or base query with no terms aggregation and add one terms for each element you got from step 3.
Add to each terms, the missing field with an empty empty string ("").
That's it. Following this, you have creating a query in such way that, no matter what index you're searching, you'll get a terms agg with all the existing fields for it.
Advantages:
Your terms aggregations will be generated dynamically with all the existing fields.
For each of the doc that does not contain any of the fields, an empty string will be shown.
Disadvantages:
Looping through the GET mapping API's result could be a little frustrating (but I trust you).
Performance (time & resources) will be affected for every new field you find in your mappings.
I hope this is helpful! :D

PHP Array organization

I'm trying to get a well-better formated list of information.
Basically what I have right now is a multi dimensional array that has multiple entries in it with different data.
If I would do foreach $bigarray, I would get something like this:
array(
"id" => 1,
"company" => "bar",
"advertisment" => "Selling X",
"user_id" => "200",
"uri" => "bbbbbbxa"
);
array(
"id" => 2,
"company" => "bar",
"advertisment" => "Selling ABC",
"user_id" => "200",
"uri" => "xxxaaaa"
);
array(
"id" => 3,
"company" => "CROMUS",
"advertisment" => "Selling BBB",
"user_id" => "222",
"uri" => "bsaxxaa"
);
** notice the same user_id and company name **
** I want a unique list of companies with ads corresponding to it **
From the above I would get 3 'squares' of data like this:
Company Name
Advertisment Title
================
Same Company Name
Same advertisment title
================
Different Company Name
Different advertisment title
=========================
but I would want to get:
=========================
Company Name
first advertisment title
second advertisment title
** so on if any other entries with same user_id are found **
=========================
Different Company Name
Different Advertisment
=========================
So basically I would want array 1 to add somehow to array 0 so when I echo in the foreach($bigarray as $post), I go something like
echo $post['advertisment'].' '.$post['uri']
echo $post['advertisment_2'].' '.$post['uri2']
Note: I might have more than 2 values , I might have 6 ads on the same company and I basically want to organise them by the id (the top one would be the newest)
Until now I did something like this just to realise i took the wrong approach:
foreach($latest->posts as $key => $post) {
if(in_array($post->user_id,$iduri)){
array_push($iduri, $post->user_id);
}
if(in_array($post->user_id,$iduri)) {
array_push($cuie,$post->title);
array_push($urix,$post->uri);
} else {
for ($i = 0; $i <= count($cuie); $i++) {
echo $cuie[i];
echo $urix[i];
}
} // end if in array
} //end foreach

Multidimensional MySQL result – subqueries as arrays

MySQL query:
SELECT
seats,
( SELECT tires, bumpers FROM exterior ) AS stuff
FROM
interior
The result I was expecting:
0 => [
'seats' => 'comfort',
'stuff' => [
0 => [
'tires' => 'winter',
'bumpers' => 'front'
],
1 => [
'tires' => 'summer',
'bumpers' => 'back'
],
...
]
],
1 => ...
However, this did not work. Error message: Operand should contain 1 column(s).
Is possible to get the subquery results returned as arrays, the way/format the base query is? Or grouping it this way is only possible with e.g. PHP? Thanks.
A solution for this would be the following:
SELECT seats,
( SELECT CONCAT_WS(',',tires, bumpers) FROM exterior ) AS stuff
FROM interior
// Where $array would be the result of the query
function stringtoarray($string) {
return explode(',',$string);
}
for($i=0;!empty($array);$i++) {
$array[''stuff'] = stringtoarray($array[''stuff']);
}
I realize they don't have the key value yet, but you should be able to fix this within this method as well.
As your "exterior" table has multiple rows, so you can't alias multiple row like this: ( SELECT tires, bumpers FROM exterior ) AS stuff
Either use limit in your sub-query or let me know where there is any relation between "interior" & "exterior" table.If any relation exists, then use join

JSON - Structuring the data

Essentially, I am trying to format the follow:
I have a series of users, that are part of different communities and each community has a series of tasks to complete BUT some of the tasks within the community are only related to some users. Like so:
Community 1
Members: Joe, James
Tasks: Task 1, Task 2, Task 3
Assigned: Task 1 -> Joe, Task 3 -> James
Community 2:
Members: James
Tasks: Talk 1, Task 14, Task 15
Assigned: Task 1 -> Joe, Task 14 -> James
So essentially. Joe has to complete Task 1, and James has to complete Task 3.
I need a array (that can be encoded to json) that stores the community ID as well as all of the tasks that they have completed but, it should be easily accessible to get the community and the tasks that they have completed.
I have a list of communities that currently exist, and I would like to show all of tasks that have been completed (by the specific user) depending on the community id, as well as this, I also want to add and delete things from the "tasks" category, so need a way to easily get access to these members
I have come up with the following so far:
$progress = array (
"communities" => array(
"id" => 1,
"tasks" => array(
1 => "completed",
2 => "completed"
),
"id" => 2,
"tasks" => array(
150 => "completed",
140 => "completed"
)
),
);
But I don't know if this is the right style of array for this, since, I don't know how complex it will be when I need to add/remove or show the total amount of tasks left for a communities
UPDATE:
This array I'm working with now:
$x = array(
1 => array(
1,
2,
3,
4,
),
2 => array(
3,
5,
6,
10
)
);
Then produces this kind of JSON:
{"1":[1,2,3,4],"2":[3,5,6,10]}
Is this somewhere right? Will I be able to add and delete nodes, as well as add top layer sections to this?
Code sample as a reply to your comment
$progress = array (
"communities" => array(
1 => array(
"tasks" => array(
1 => array("status" => "completed", "assigned_users" => array("James", "Joe")),
2 => array("status" => "pending", "assigned_users" => array("James"))
),
),
2 => array(
//Content of task2
)
)
);
With an extended model like this, you'll be able to:
assign a task to one user or more
remove a user from a specific task
know which users are busy or available
count tasks and getting resources (users) assigned
Getting the task 1 of the first community
echo $progress['communities'][1]['tasks'][1];
Walk the communities collection
foreach ($progress['communities'] as $c) {
//Browse the tasks
foreach ($c['tasks'] as $t) {
var_dump($t);
}
}

CakePHP edit multiple records at once

I have a HABTM relationship between two tables: items and locations, using the table items_locations to join them.
items_locations also stores a bit more information. Here's the schema
items_locations(id, location_id, item_id, quantity)
I'm trying to build a page which shows all the items in one location and lets the user, through a datagrid style interface, edit multiple fields at once:
Location: Factory XYZ
___________________________
|___Item____|___Quantity___|
| Widget | 3 |
| Sprocket | 1 |
| Doohickey | 15 |
----------------------------
To help with this, I have a controller called InventoryController which has:
var $uses = array('Item', 'Location'); // should I add 'ItemsLocation' ?
How do I build a multidimensional form to edit this data?
Edit:
I'm trying to get my data to look like how Deceze described it below but I'm having problems again...
// inventory_controller.php
function edit($locationId) {
$this->data = $this->Item->ItemsLocation->find(
'all',
array(
"conditions" => array("location_id" => $locationId)
)
);
when I do that, $this->data comes out like this:
Array (
[0] => Array (
[ItemsLocation] => Array (
[id] => 16
[location_id] => 1
[item_id] => 1
[quantity] => 5
)
)
[1] => Array (
[ItemsLocation] => Array (/* .. etc .. */)
)
)
If you're not going to edit data in the Item model, it probably makes most sense to work only on the join model. As such, your form to edit the quantity of each item would look like this:
echo $form->create('ItemsLocation');
// foreach Item at Location:
echo $form->input('ItemsLocation.0.id'); // automatically hidden
echo $form->input('ItemsLocation.0.quantity');
Increase the counter (.0., .1., ...) for each record. What you should be receiving in your controllers $this->data should look like this:
array(
'ItemsLocation' => array(
0 => array(
'id' => 1,
'quantity' => 42
),
1 => array(
...
You can then simply save this like any other model record: $this->Item->ItemsLocation->saveAll($this->data). Adding an Item to a Location is not much different, you just leave off the id and let the user select the item_id.
array(
'location_id' => 42, // prepopulated by hidden field
'item_id' => 1 // user selected
'quantity' => 242
)
If you want to edit the data of the Item model and save it with a corresponding ItemsLocation record at the same time, dive into the Saving Related Model Data (HABTM) chapter. Be careful of this:
By default when saving a HasAndBelongsToMany relationship, Cake will delete all rows on the join table before saving new ones. For example if you have a Club that has 10 Children associated. You then update the Club with 2 children. The Club will only have 2 Children, not 12.
And:
3.7.6.5 hasAndBelongsToMany (HABTM)
unique: If true (default value) cake will first delete existing relationship records in the foreign keys table before inserting new ones, when updating a record. So existing associations need to be passed again when updating.
Re: Comments/Edit
I don't know off the top of my head if the FormHelper is intelligent enough to autofill Model.0.field fields from a [0][Model][field] structured array. If not, you could easily manipulate the results yourself:
foreach ($this->data as &$data) {
$data = $data['ItemsLocation'];
}
$this->data = array('ItemsLocation' => $this->data);
That would give you the right structure, but it's not very nice admittedly. If anybody has a more Cakey way to do it, I'm all ears. :)

Categories