Completed
Push — master ( fcb1ca...cd9585 )
by Supun
02:16
created

JsonExtractor::toMysqlTables()   B

Complexity

Conditions 8
Paths 14

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 11
nc 14
nop 2
dl 0
loc 20
rs 7.7777
c 0
b 0
f 0
1
<?php
2
3
namespace Lifeeka\JSQL\Extractor;
4
5
use Lifeeka\JSQL\Helpers\Json;
6
7
/**
8
 * Class JsonExtractor.
9
 */
10
class JsonExtractor
11
{
12
    public $json;
13
14
    private $table = [];
15
    private $data = [];
16
17
    public $need_id = true;
18
    public $snake_case_column = true;
19
    public $snake_case_table = true;
20
    public $main_table_name = "main";
21
22
    /**
23
     * JsonExtractor constructor.
24
     *
25
     * @param Json $json
26
     */
27
    public function __construct(Json $json)
28
    {
29
        $this->json = $json;
30
    }
31
32
    /**
33
     * @return array
34
     */
35
    public function getTablesArray()
36
    {
37
        return $this->table;
38
    }
39
40
    /**
41
     * @return array
42
     */
43
    public function getDataArray()
44
    {
45
        return $this->data;
46
    }
47
48
49
    /**
50
     * @param bool $data
51
     * @param string $prefix
52
     * @return string
53
     */
54
    public function toMysqlTables($data = false, $prefix = '')
55
    {
56
        if (!$data) {//if this is not a recursive
57
            $data = $this->json->toObject();
58
        }
59
60
        foreach ($data as $key => $value) {//loop the data
61
62
            $table_name = is_numeric($key) ? $this->main_table_name : $key;
63
64
            if (is_array($value) && is_object($value[0])) {//check whether it's a array and it's firs element is a object
65
66
                $table_data = $this->getTable($prefix . $table_name, $value);//get table sql
67
                $this->table[$table_data['name']] = $table_data['column'];
68
69
                $this->toMysqlTables($this->getHighestColumnArray($value), $prefix . $table_name . '_');//get it inside tables
70
            } elseif (is_array($value) || is_object($value)) {//if it's a array and  firs element is not a object
71
72
                $table_data = $this->getTable($prefix . $table_name, $value);
73
                $this->table[$table_data['name']] = $table_data['column'];
74
            }
75
76
        }
77
    }
78
79
80
    /**
81
     * @param bool $data
82
     * @param string $prefix
83
     */
84
    public function toMysqlData($data = false, $prefix = '')
85
    {
86
87
88
        if (!$data) {//if this is not a recursive
89
            $data = $this->json->toObject();
90
        }
91
        foreach ($data as $table_name => $value) {
92
            if ($this->snake_case_table) {
93
                $table_name = $this->snakeCase($prefix . $table_name);
94
            }
95
            if (is_array($value) && is_object($value[0])) {//if it's a array and  firs element is not a object
96
                $this->toMysqlData($value, $table_name . '_');
0 ignored issues
show
Bug introduced by
$value of type array<mixed,object|mixed> is incompatible with the type boolean expected by parameter $data of Lifeeka\JSQL\Extractor\J...xtractor::toMysqlData(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

96
                $this->toMysqlData(/** @scrutinizer ignore-type */ $value, $table_name . '_');
Loading history...
97
98
            } elseif (is_object($value) || is_array($value)) {
99
                $this->toMysqlData($value, $table_name . '_');
0 ignored issues
show
Bug introduced by
$value of type object|array is incompatible with the type boolean expected by parameter $data of Lifeeka\JSQL\Extractor\J...xtractor::toMysqlData(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

99
                $this->toMysqlData(/** @scrutinizer ignore-type */ $value, $table_name . '_');
Loading history...
100
                $this->getTableData($table_name, $value);
101
102
            }
103
        }
104
105
106
    }
107
108
    /**
109
     * @param $table_name
110
     * @param $value
111
     */
112
    public function getTableData($table_name, $value)
113
    {
114
115
        if (isset($this->table[$table_name])) {
116
117
            $ColumnList = $this->table[$table_name];
118
119
            $DataItem = [];
120
121
            if (is_object($value)) {
122
                foreach ($ColumnList as $key => $ColumnItem) {
123
                    if ($this->isPropertyExist($value, $ColumnItem['name'])) {
124
                        $CurrentItem = $this->snake_case_column ? $this->objectToSnakeCase($value) : $value;
125
                        $column_name = $ColumnItem['name'];
126
                        $DataItem[$column_name] = $CurrentItem->{$ColumnItem['name']};
127
                    } else {
128
                        $column_name = $ColumnItem['name'];
129
                        $DataItem[$column_name] = null;
130
                    }
131
                }
132
                $this->data[$table_name][] = $DataItem;
133
134
            } elseif (is_array($value)) {//reference table
135
                foreach ($value as $item_value)
136
                    $DataItem[] = ['value' => $item_value];
137
138
                $this->data[$table_name] =  array_merge(($this->data[$table_name]??[]),$DataItem);
139
140
            }
141
142
143
        }
144
145
146
    }
147
148
149
    /**
150
     * @param $table
151
     * @param $data
152
     * @return array
153
     */
154
    public function getTable($table, $data)
155
    {
156
        $column = $this->getColumn($this->getHighestColumnArray($data));
157
158
        if ($this->snake_case_table) {
159
            $table = $this->snakeCase($table);
160
        }
161
162
        $last_columns = $column;
163
        if ($this->need_id && array_search('id', array_column($column, 'name')) === false) {
164
            $last_columns[] = [
165
                'name' => "id",
166
                'type' => "int"
167
            ];
168
        }
169
170
        if ($this->snake_case_column)
171
            return [
172
                'name' => $table,
173
                'column' => array_map(function ($item) {
174
                    return [
175
                        'name' => $this->snakeCase($item['name']),
176
                        'type' => $item['type']
177
                    ];
178
179
                }, $last_columns)
180
            ];
181
182
        else
183
            return [
184
                'name' => $table,
185
                'column' => $last_columns
186
            ];
187
    }
188
189
    /**
190
     * @param $data
191
     *
192
     * @return array
193
     */
194
    public function getColumn($data)
195
    {
196
        $Columns = [];
197
198
        if (is_object($data)) {
199
            foreach ($data ?? [] as $Column => $Value) {
200
                if (!is_array($Value) && !is_object($Value) && !empty($Column) && !is_numeric($Column)) {
201
                    $Columns[] = ['name' => $Column, 'type' => gettype($this->getActualDataType($Value, ""))];
202
                }
203
            }
204
        } elseif (is_array($data)) {
205
            $Columns[] = ['name' => 'value', 'type' => gettype($this->getActualDataType($data[0], ""))];
206
        }
207
208
        return $Columns;
209
    }
210
211
    /**
212
     * @param $data
213
     * @param $column
214
     * @return array
215
     */
216
    public function getData($data, $column)
217
    {
218
219
        $values = [];
220
221
        $index = 0;
222
        foreach ($data as $row_item) {
223
            foreach ($column as $column_item) {
224
                switch (is_object($row_item)) {
225
                    case true:
226
                        $values[$index][$column_item['name']] = $this->getActualDataType(($row_item->{$column_item['name']}) ?? null, null);
227
                        break;
228
                    case false:
229
                        $values[$index][$column_item['name']] = $this->getActualDataType(($data->{$column_item['name']}) ?? null, null);
230
                        break;
231
                }
232
            }
233
            $index++;
234
        }
235
236
237
        return $values;
238
    }
239
240
    /**
241
     * @param $input
242
     *
243
     * @return string
244
     */
245
    public static function snakeCase($input)
246
    {
247
        preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
248
        $ret = $matches[0];
249
250
        foreach ($ret as &$match) {
251
            $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
252
        }
253
254
        return implode('_', $ret);
255
    }
256
257
    /**
258
     * @param $Object
259
     * @return \stdClass
260
     */
261
    public static function objectToSnakeCase($Object)
262
    {
263
        $ReturnObject = new \stdClass();
264
265
        foreach ($Object as $ObjectAttribute => $ObjectItem) {
266
            $ReturnObject->{JsonExtractor::snakeCase($ObjectAttribute)} = $ObjectItem;
267
        }
268
        return $ReturnObject;
269
    }
270
271
272
    /**
273
     * @param Object $Object
274
     * @param $Attribute
275
     * @return bool
276
     */
277
    public function isPropertyExist($Object, $Attribute)
278
    {
279
        foreach ($Object as $CurrentAttributeName => $CurrentAttribute) {
280
            if ($this->snake_case_column && $this->snakeCase($CurrentAttributeName) == $Attribute) {
281
                return true;
282
            } elseif ($CurrentAttributeName === $Attribute) {
283
                return true;
284
            }
285
        }
286
287
        return false;
288
    }
289
290
291
    /**
292
     * @param $array
293
     * @return bool|mixed
294
     */
295
    public static function getHighestColumnArray($array)
296
    {
297
        if (is_object($array) || (is_array($array) && !is_object($array[0]) && !is_array($array[0]))) {
298
            return $array;
299
        }
300
301
        $Highest = false;
302
        $ColumnCount = false;
303
        $HighestSubCount = false;
304
305
        foreach ($array as $array_item) {
306
            $current_sub = 0;
307
            //check how many array/object have
308
            foreach ($array_item as $SubTableName => $SubArrayItem) {
309
                if (is_array($SubArrayItem) || is_object($SubArrayItem)) {
310
                    $current_sub = $current_sub + 2;
311
                }
312
            }
313
            //check how many column have
314
            if ($ColumnCount <= count($array_item) || !$Highest) {
315
                if ($current_sub > $HighestSubCount || !$Highest) {
316
                    $Highest = $array_item;
317
                    $HighestSubCount = $current_sub;
318
                    $ColumnCount = count($array_item);
319
                }
320
            }
321
        }
322
323
324
        return $Highest;
325
    }
326
327
    /**
328
     * @param $array
329
     * @return string
330
     */
331
    public static function getStringFromData($array)
332
    {
333
        $String = [];
334
335
        foreach ($array as $item_row) {
336
            $value = [];
337
            foreach ($item_row as $item) {
338
                if (empty($item) && !is_numeric($item)) {
339
                    $value[] = "null";
340
                } elseif (is_numeric($item)) {
341
                    $value[] = $item;
342
                } else {
343
                    $value[] = '"' . addcslashes($item, "W") . '"';
344
                }
345
            }
346
            $String[] = '(' . implode(",", $value) . ')';
347
        }
348
349
        return "values" . implode(", ", $String);
350
    }
351
352
    /**
353
     * @param $array
354
     * @return string
355
     */
356
    public static function getStringFromColumns($array)
357
    {
358
        $String = [];
359
        foreach ($array as $column) {
360
            $String[] = "`" . JsonExtractor::snakeCase($column['name']) . "`";
361
        }
362
363
        return implode(",", $String);
364
    }
365
366
367
    /**
368
     * @param $Data
369
     * @param $empty_val
370
     * @return int|string
371
     */
372
    public static function getActualDataType($Data, $empty_val)
373
    {
374
        $Data = trim($Data);
375
        if (is_numeric($Data)) {
376
            return $Data + 0;
377
        } elseif (empty($Data)) {
378
            return $empty_val;
379
        } else {
380
            return (string)$Data;
381
        }
382
    }
383
}
384