I'm struggling to find a way to convert my object to the correct format.
I want to replace a function that we currently use on generating detailed array, as you can see below everything is static.
private function departmentArray($content=[])
{
return [ static::$A_DEPT_ID => $content
, static::$O_DEPT_ID => $content
];
}
A sample result when that runs is this
{"3":{"complete":0,"incomplete":0},"5":{"complete":0,"incomplete":0}}
I converted the method
private function departmentArray($content=[])
{
$depts = d::getAllMainDepartment();
$dept_array = [];
foreach ($depts as $dept) {
$dept_array[] = array($dept->id => $content);
}
return $dept_array;
}
The resulting format looks like this
[{"3":{"complete":0,"incomplete":0}},{"5":{"complete":0,"incomplete":0}}]
How can I maintain the same format on the first version of code?
You don't push into an associative array, you use the new key as an index.
$dept_array[$dept->id] = $content;
Related
During an export of my data, I can choose between CSV and JSON format.
Viewing the CSV, data, including column names are arranged in the exact same way as I would like to import into MySQL database.
CSV screenshot
I'm getting JSON from a webpage and it's stored in $data variable.
<html>
<body>
<?php
// Include Parsehub REST api wrapper
require_once __DIR__ . '/vendor/autoload.php';
use Parsehub\Parsehub;
$api_key = "XXX";
$project_token = "XXX";
$parsehub = new Parsehub($api_key);
$data = $parsehub->getLastReadyRunData($project_token);
echo $data;
?>
</body>
</html>
Echo output of it would return
JSON:
https://pastebin.com/raw/AZt4gvsC
CREATE TABLE:
CREATE TABLE `utakmice_1` (
`utakmica_name` varchar(64) NOT NULL,
`utakmica_url` varchar(256) NOT NULL,
`utakmica_liga` varchar(64) NOT NULL,
`utakmica_liga_url` varchar(256) NOT NULL,
`utakmica_vreme` varchar(64) NOT NULL,
`utakmica_datum` varchar(64) NOT NULL,
`utakmica_kvote_kvota` decimal(10,2) NOT NULL,
`utakmica_kvote_kladionica` varchar(63) NOT NULL,
`utakmica_kvote_kladionica_url` varchar(256) NOT NULL,
`utakmica_kvote_igra` varchar(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
In which way could all the json data could be inserted?
Using PHP, you need to:
Convert the JSON data to an associative array using json_decode
$data = json_decode($jsondata, true);
Process that array to get the data you need to insert in the table
$name = $data['utakmica']['name'];
$url = $data['utakmica']['age'];
etc
Insert to MySQL using the Insert query (we're using mysqli here but you could use PDO)
$sql = "INSERT INTO utakmice_1(utakmica_name, utakmica_url)
VALUES('$name', '$url')";
if(!mysqli_query($con, $sql))
{
die('Error : ' . mysql_error());
}
Given the CSV display and the structure in the JSON, when doing a standard recursive iteration over the JSON structure with the modifcation to terminate leafs being objects that no longer contain a property that is an array, it would be possible going over all leaf nodes having their property names prefixed with all parent property names (dropping array indexes) and using all lower depth current objects (from the subiterators) in depth ascending order to create a merged object to obtain such objects with such keys.
The boilerplate of this is RecursiveArrayIterator and RecursiveIteratorIterator.
The important part is that if an object has a property that is an array, it is the only property that is an array and the array is an array of objects of which are all homogeneous. This is the case for your JSON Text, and this is important as both the RecursiveArrayIterator and the RecursiveIteratorIterator need to be modified for such traversal.
The unmodified boilerplate behaviour is also shown in the related Q&A How can I parse a JSON file with PHP?:
$it = new RecursiveIteratorIterator(
new RecursiveArrayIterator($json)
);
foreach ($it as $property => $value) {
...
}
As in your use-case both objects need to be modified and it only works with leaves only iteration (as only then merging the objects makes sense), the whole construct could be extended from RecursiveIteratorIterator, overwriting the constructor taking the $json directly, using the modification of RecursiveArrayIterator.
Speaking of it, it needs to keep the key-prefix (here implemented as a private property), turn objects into non-traversable children (standard behaviour is every non-object and non-array property construes a leaf) and removal of array properties from each node as those should only be available to traverse up to leaf nodes and then when merging across the whole path in depth ascending order, they would be superfluous:
$it = new class ($json) extends \RecursiveIteratorIterator {
public function __construct(object $json)
{
$jsonObjectRecursion = new class($json) extends \RecursiveArrayIterator
{
private ?string $keyPrefix = null;
public function key()
{
return (null !== $this->keyPrefix ? $this->keyPrefix . '_' : '') . parent::key();
}
public function current(): object|array
{
$current = parent::current();
if (is_array($current)) {
return $current;
}
$current = array_filter(get_object_vars($current), static fn ($any) => !is_array($any));
if (null !== $this->keyPrefix) {
$current = array_combine(preg_filter('/^/', "{$this->keyPrefix}_", array_keys($current)), $current);
}
return (object)$current;
}
public function hasChildren(): bool
{
$current = parent::current();
if (is_array($current)) {
return parent::hasChildren();
}
return (is_object($current) && array_filter(get_object_vars($current), 'is_array'));
}
public function getChildren(): self
{
$current = parent::current();
if (is_array($current)) {
$children = parent::getChildren();
$children->keyPrefix = $this->key();
return $children;
}
$probe = array_filter(get_object_vars($current), 'is_array');
$children = new self((object) [key($probe) => current($probe)]);
$children->keyPrefix = $this->keyPrefix;
return $children;
}
};
parent::__construct($jsonObjectRecursion);
}
...
As this excerpt shows, array traversal is preserved and the key-prefix is passed along accordingly. The array handling within current() can perhaps be dropped as arrays only come into action with has/getChildren() invocations.
The $json is just this one big JSON Object decoded into PHPs' stdClass. Despite it has Array in its name, the RecursiveArrayIterator can handle these JSON Objects well without converting them into an array.
Next to the constructor which merely only defines the inner iterator, this RecursiveIteratorITerator only implements one second method, current(), which does the merging:
...
public function current(): object
{
$object = [];
for ($depth = 0; $depth <= $this->getDepth(); $depth++) {
$current = $this->getSubIterator($depth)->current();
if (is_array($current)) {
continue;
}
$object += get_object_vars($current);
}
return (object)$object;
}
};
As $it is now defined, all that needs is to iterate over it and proceed with the values:
foreach ($it as $row) {
echo json_encode($row, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), "\n";
break;
}
Output (first flat row):
{
"utakmica_name": "Deportivo Kuenka-Un. Katolika Kito",
"utakmica_url": "https://www.fudbal91.com/soccer_clubs/compare/Deportivo_Cuenca_vs_Un._Catolica_Quito/13641",
"utakmica_liga": "Ekvador 1",
"utakmica_liga_url": "https://www.fudbal91.com/competition/Ecuador,_LigaPro_betcris_-_Primera_Etapa/2022",
"utakmica_vreme": "00:00",
"utakmica_datum": "< 22.03.2022. (Utorak) >",
"utakmica_kvote_kvota": "2.75",
"utakmica_kvote_kladionica": "BetOle",
"utakmica_kvote_kladionica_url": "https://example.com/2YjM4Ft",
"utakmica_kvote_igra": "1"
}
To turn $it into an array of objects, use iterator_to_array($it, false). E.g. when you're looking forward to sort the whole list by one attribute without always needing to traverse the whole tree again and again for every operation.
Just as-if you would have used the CSV export that is already flat.
Such and even many more tips can be found in the related Q&A How to extract and access data from JSON with PHP? and many more others on the site.
I've a function which returns an array. To return the value of that array I do something like this:
$obj->methodname()[keyvalue];
This works in php 5.4 only. I want to make this code work in lower php versions.
My code:
class ObjectTest {
public $ar;
function __construct() {
$this->ar = array(
1 => 'beeldscherm',
2 => 'geluidsbox',
3 => 'toetsenbord',);
}
public function arr(){
return $this->ar;
}
}
$obj = new ObjectTest();
//by calling the method and putting square brackets and the key of the element
var_dump($obj->arr()[2]);
I've rewritten the code for lower versions like this:
public function arr($arg = null){
if(is_null($arg)){
return $this->ar;
}
return $this->ar[$arg];
}
I'm doubting if this solution is an elegant one. What would you say? Any better solutions?
You can do like, store array in a variable and than access particular array index.
$arrList = var_dump($obj->arr());
echo $arrList[2];
Well I've thought of having ArrayList in PHP because it was extremely useful in PHP, and saving a lot of time.
I thought it'd be cool to have in PHP, so I did make an arrayList class in PHP:
Class ArrayList
{
public $arrayList;
function __construct()
{
$this->arrayList = array();
}
public function add($element)
{
$this->arrayList[] = $element;
}
public function remove($index)
{
if (is_numeric($index)) {
unset($this->arrayList[$index]);
}
}
public function get($index)
{
return $this->arrayList[$index];
}
}
Now, I noticed I need an list type more of a hashmap, so I can get items by keys.
Let's say I need to get mysql db name, so Ill just do $data->hashmap->get("db_name").
This will return the database name value.
Is there a way to do this?
PHP has built-in data types that do what you want:
A "hashmap" is an associative array
An "ArrayList" is simply an array
Example:
$my_hash_map = array('x' => 5, 'y' => 10);
$my_hash_map['x'] + $my_hash_map['y'] // => 15
$my_array_list = array();
$my_array_list[] = 5;
$my_array_list[0] // => 5
See Arrays in the PHP documentation.
In PHP arrays can have string keys. Also you could use stdClass.
How can I return data from an external source as a DocumentSet?
I set up a custom data source to interface with Amazon's Product Advertising API. To do this, I subclassed lithium\data\source\Http and redefined the read method to suit my needs as described in the documentation (http://li3.me/docs/manual/working-with-data/creating-data-sources.wiki).
However, my lithium version (0.11, last release) does not seem to have a cast method like in the example and if I create one it won't get called when I do return $this->item($model, $data, $options).
So, I made a custom item function to create the Documents by calling parent::item just like the documentation example does for cast.
Then, after the recursive calls, I end up with an array of Document objects and the final call to parent::item then gives me an empty DocumentSet object.
How should I pass the data on to create a proper DocumentSet?
Here's a minimal example of my code:
// Within class Amazon extends \lithium\data\source\Http
protected function _init() {
// Define entity classes.
$this->_classes += array(
'entity' => 'lithium\data\entity\Document',
'set' => 'lithium\data\collection\DocumentSet'
);
parent::_init();
}
public function read($query, array $options = array()) {
// Extract from query object.
$parameters = $query->export($this, array('keys' => array('conditions')));
$conditions = $parameters['conditions'];
// Code stripped to validate conditions and prepare Amazon request (that part works).
// results in a $queryString variable.
// Get response from Server.
$xml = simplexml_load_string($this->connection->get($this->_config['basePath'], $queryString));
// Stripped response validation and reformatting -> $items contains an array of SimpleXMLElement objects.
return $this->item($query->model(), $items, array('class' => 'set'));
}
public function item($model, array $data = array(), array $options = array()) {
// Recursively create Documents for arrays.
foreach($data as $key => $value) {
if(is_array($value)) {
$data[$key] = $this->item($model, $value, array('class' => 'entity'));
}
else if(is_object($value) && get_class($value) == "SimpleXMLElement") {
// Stripped code to extract data from XML object and put it in array $docData.
$data[$key] = $this->item($model, $docData, array('class' => 'entity'));
}
}
// Works perfectly for every (recursive) call with $options['class'] == 'entity' but fails for the final call with $options['class'] == 'set' (for this final call $data contains an array of Document objects).
return parent::item($model, $data, $options);
}
I would track the master branch instead of the release versions.
In your case, since you're boxing your objects manually, I would do something like:
return $this->_instance('set', compact('data'));
I'm running a query to mysql that returns encrypted data. I'd like, if possible, to decode the results before sending it to the view. It seems like better form to handle the decoding in the controller (or even the model) rather than inside the view.
I can't seem to wrap my head around how to do it, though.
I was thinking I could iterate through the object, decodode it, and push it to another array that would be sent to the view. Problem with this is I won't know (and need to keep) the indexes of the query.
So the query might return something like:
[id] => 742
[client_id] => 000105
[last] => dNXcw6mQPaGQ4rXfgIGJMq1pZ1dYAim0
[first] => dDF7VoO37qdtYoYKfp1ena5mjBXXU0K3dDlcq1ssSvCgpOx75y0A==
[middle] =>iXy6OWa48kCamViDZFv++K6okIkalC0am3OMPcBwK8sA==
[phone] => eRY3zBhAw2H8tKE
Any ideas?
Ended up with:
function name(){
$data['e_key']=$this->e_key;
$clid = $this->uri->segment(3);
$name = $this->Clients_model->getNameData('*','client_id='.$clid,'');
$nameArray= array();
foreach ($name->result() as $row){
$x = $row;
$keys = array('id','client_id');
$unenc = array();
foreach ($x as $key=>$value){
if(! in_array($key, $keys)){
$unenc[$key]=$this->encrypt->decode($value,$this->e_key);
}else{
$unenc[$key]=$value;
}
}
array_push($nameArray,$unenc);
}
$data['name'] = $nameArray;
$this->load->view('names/name_view',$data);
}
Assuming you know how to decrypt the data, it's but a matter of iterating over the object, decrypting the encrypted fields.
If $YOUR_OBJECT is your object and your function for decryption is decode() then the following code should do the trick.
// The keys corresponding to the encrypted fields
$encoded = array('last', 'first', 'middle', 'phone');
$decoded = array();
foreach($YOUR_OBJECT as $key => $value)
{
if (in_array($key, $encoded))
{
$decoded[$key] = decode($value);
}
}
if it's a particular index, you could decode it like
$result['last'] = base64_decode($result['last']);
or in the model, use mutators and accessors:
public function setUp() {
$this->setTableName('tablename');
$this->actAs('Timestampable');
$this->hasMutator('last', '_encode64');
$this->hasAccessor('last', '_decode64');
}
protected function _encode($value) {
$this->_set('last',base64_encode($value));
}
protected function _decode($value) {
return base64_decode($value); // not sure on this one - might have to
// return $this->set('last', base64_decode($value));
}