create-prisma-php-app 1.28.7 → 2.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/composer.json +8 -6
- package/dist/bootstrap.php +652 -682
- package/dist/index.js +51 -30
- package/dist/postcss.config.js +1 -3
- package/dist/prisma/seed.ts +2 -2
- package/dist/prisma-php.js +2 -42
- package/dist/settings/prisma-sdk.ts +4 -0
- package/dist/settings/project-name.ts +26 -2
- package/dist/src/Lib/AI/ChatGPTClient.php +32 -103
- package/dist/src/Lib/CacheHandler.php +121 -0
- package/dist/src/Lib/ErrorHandler.php +123 -0
- package/dist/src/Lib/MainLayout.php +23 -22
- package/dist/src/Lib/PHPX/TemplateCompiler.php +47 -24
- package/dist/src/Lib/PHPX/TwMerge.php +7 -1
- package/dist/src/Lib/Prisma/Classes/PPHPUtility.php +234 -105
- package/dist/src/Lib/Prisma/Model/IModel.php +15 -11
- package/dist/src/Lib/PrismaPHPSettings.php +44 -9
- package/dist/src/Lib/Request.php +1 -1
- package/dist/src/Lib/Validator.php +35 -7
- package/dist/src/app/css/tailwind.css +1 -3
- package/dist/src/app/index.php +2 -2
- package/dist/src/app/js/index.js +12 -1
- package/dist/src/app/layout.php +4 -7
- package/dist/src/app/not-found.php +1 -1
- package/package.json +1 -1
- package/tsconfig.json +5 -6
- package/dist/prisma-client-php/index.enc +0 -1
- package/dist/prisma-client-php/index.js +0 -19
- package/dist/prisma-client-php/key.enc +0 -1
- package/dist/tailwind.config.js +0 -8
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
namespace Lib\Prisma\Classes;
|
|
4
4
|
|
|
5
5
|
use Lib\Validator;
|
|
6
|
+
use ReflectionClass;
|
|
7
|
+
use InvalidArgumentException;
|
|
8
|
+
use DateTime;
|
|
9
|
+
use Brick\Math\BigDecimal;
|
|
10
|
+
use Brick\Math\BigInteger;
|
|
11
|
+
use ReflectionUnionType;
|
|
12
|
+
use ReflectionNamedType;
|
|
13
|
+
use Exception;
|
|
6
14
|
|
|
7
15
|
enum ArrayType: string
|
|
8
16
|
{
|
|
@@ -24,7 +32,7 @@ final class PPHPUtility
|
|
|
24
32
|
* @param string $modelName The name of the model being checked.
|
|
25
33
|
* @param string $timestamp The timestamp field name to be ignored during the check.
|
|
26
34
|
*
|
|
27
|
-
* @throws
|
|
35
|
+
* @throws Exception If a field does not exist in the model or if the selection format is incorrect.
|
|
28
36
|
*/
|
|
29
37
|
public static function checkFieldsExistWithReferences(
|
|
30
38
|
array $select,
|
|
@@ -41,17 +49,17 @@ final class PPHPUtility
|
|
|
41
49
|
|
|
42
50
|
if (is_numeric($key) && is_string($value)) {
|
|
43
51
|
if (array_key_exists($value, $fields))
|
|
44
|
-
throw new
|
|
52
|
+
throw new Exception("The '$value' is indexed, waiting example: ['$value' => true]");
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
if (isset($value) && empty($value) || !is_bool($value)) {
|
|
48
56
|
if (is_string($key) && !array_key_exists($key, $fields)) {
|
|
49
|
-
throw new
|
|
57
|
+
throw new Exception("The field '$key' does not exist in the $modelName model.");
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
if (is_string($key) && array_key_exists($key, $fields)) {
|
|
53
61
|
if (!is_bool($value) && !is_array($value)) {
|
|
54
|
-
throw new
|
|
62
|
+
throw new Exception("The '$key' is indexed, waiting example: ['$key' => true]");
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
|
|
@@ -67,7 +75,7 @@ final class PPHPUtility
|
|
|
67
75
|
$relatedEntityFields[$key] = [$key];
|
|
68
76
|
} else {
|
|
69
77
|
if (!is_bool($value) || empty($value)) {
|
|
70
|
-
throw new
|
|
78
|
+
throw new Exception("The '$key' is indexed, waiting example: ['$key' => true] or ['$key' => ['select' => ['field1' => true, 'field2' => true]]]");
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
}
|
|
@@ -78,7 +86,7 @@ final class PPHPUtility
|
|
|
78
86
|
|
|
79
87
|
if (!array_key_exists($fieldName, $fields)) {
|
|
80
88
|
$availableFields = implode(', ', array_keys($fields));
|
|
81
|
-
throw new
|
|
89
|
+
throw new Exception("The field '$fieldName' does not exist in the $modelName model. Available fields are: $availableFields");
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
if (
|
|
@@ -116,19 +124,19 @@ final class PPHPUtility
|
|
|
116
124
|
* @param array $fields The array of fields available in the model.
|
|
117
125
|
* @param string $modelName The name of the model being checked.
|
|
118
126
|
*
|
|
119
|
-
* @throws
|
|
127
|
+
* @throws Exception If a field in the select array does not exist in the fields array.
|
|
120
128
|
*/
|
|
121
129
|
public static function checkFieldsExist(array $select, array $fields, string $modelName)
|
|
122
130
|
{
|
|
123
131
|
foreach ($select as $key => $value) {
|
|
124
132
|
if (is_numeric($key) && is_string($value)) {
|
|
125
|
-
if (
|
|
126
|
-
throw new
|
|
133
|
+
if (self::fieldExists($key, $fields))
|
|
134
|
+
throw new Exception("The '$value' is indexed, waiting example: ['$value' => true]");
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
if (isset($value) && empty($value) || !is_bool($value)) {
|
|
130
|
-
if (is_string($key) && !
|
|
131
|
-
throw new
|
|
138
|
+
if (is_string($key) && !self::fieldExists($key, $fields)) {
|
|
139
|
+
throw new Exception("The field '$key' does not exist in the $modelName model.");
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
if (is_array($value) && !empty($value)) {
|
|
@@ -136,11 +144,12 @@ final class PPHPUtility
|
|
|
136
144
|
$isRelatedModel = false;
|
|
137
145
|
|
|
138
146
|
foreach ($fields as $field) {
|
|
139
|
-
$
|
|
140
|
-
$
|
|
141
|
-
$implicitRelation = $field['decorators']['implicitRelation'] ?? null;
|
|
147
|
+
$isObject = $field['kind'] === 'object' ? true : false;
|
|
148
|
+
$fieldName = $field['name'];
|
|
142
149
|
|
|
143
|
-
if (
|
|
150
|
+
if ($isObject && $fieldName === $key) {
|
|
151
|
+
$isRelatedModel = true;
|
|
152
|
+
}
|
|
144
153
|
}
|
|
145
154
|
|
|
146
155
|
if ($isRelatedModel) continue;
|
|
@@ -148,8 +157,8 @@ final class PPHPUtility
|
|
|
148
157
|
$keys = array_keys($value);
|
|
149
158
|
foreach ($keys as $fieldName) {
|
|
150
159
|
$fieldName = trim($fieldName);
|
|
151
|
-
if (!
|
|
152
|
-
throw new
|
|
160
|
+
if (!self::fieldExists($fieldName, $fields)) {
|
|
161
|
+
throw new Exception("The field '$fieldName' does not exist in the $modelName model.");
|
|
153
162
|
}
|
|
154
163
|
}
|
|
155
164
|
}
|
|
@@ -159,13 +168,23 @@ final class PPHPUtility
|
|
|
159
168
|
|
|
160
169
|
foreach (explode(',', $key) as $fieldName) {
|
|
161
170
|
$fieldName = trim($fieldName);
|
|
162
|
-
if (!
|
|
163
|
-
throw new
|
|
171
|
+
if (!self::fieldExists($fieldName, $fields)) {
|
|
172
|
+
throw new Exception("The field '$fieldName' does not exist in the $modelName model.");
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
175
|
}
|
|
167
176
|
}
|
|
168
177
|
|
|
178
|
+
private static function fieldExists(string $key, array $fields): bool
|
|
179
|
+
{
|
|
180
|
+
foreach ($fields as $field) {
|
|
181
|
+
if (isset($field['name']) && $field['name'] === $key) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
169
188
|
/**
|
|
170
189
|
* Checks the contents of an array and determines its type.
|
|
171
190
|
*
|
|
@@ -202,7 +221,7 @@ final class PPHPUtility
|
|
|
202
221
|
* @param array $fields The array of fields in the model.
|
|
203
222
|
* @param string $modelName The name of the model being processed.
|
|
204
223
|
*
|
|
205
|
-
* @throws
|
|
224
|
+
* @throws Exception If an include value is indexed incorrectly or if a field does not exist in the model.
|
|
206
225
|
*/
|
|
207
226
|
public static function checkIncludes(array $include, array &$relatedEntityFields, array &$includes, array $fields, string $modelName)
|
|
208
227
|
{
|
|
@@ -215,7 +234,7 @@ final class PPHPUtility
|
|
|
215
234
|
self::processIncludeValue($key, $value, $relatedEntityFields, $fields, $modelName, $key);
|
|
216
235
|
|
|
217
236
|
if (is_numeric($key) && is_string($value)) {
|
|
218
|
-
throw new
|
|
237
|
+
throw new Exception("The '$value' is indexed, waiting example: ['$value' => true]");
|
|
219
238
|
}
|
|
220
239
|
|
|
221
240
|
if (isset($value) && empty($value) || !is_bool($value)) {
|
|
@@ -223,7 +242,7 @@ final class PPHPUtility
|
|
|
223
242
|
}
|
|
224
243
|
|
|
225
244
|
if (!array_key_exists($key, $fields)) {
|
|
226
|
-
throw new
|
|
245
|
+
throw new Exception("The field '$key' does not exist in the $modelName model.");
|
|
227
246
|
}
|
|
228
247
|
|
|
229
248
|
$includes[$key] = $value;
|
|
@@ -249,7 +268,7 @@ final class PPHPUtility
|
|
|
249
268
|
}
|
|
250
269
|
} else {
|
|
251
270
|
if (!is_bool($value) || empty($value)) {
|
|
252
|
-
throw new
|
|
271
|
+
throw new Exception("The '$value' is indexed, waiting example: ['$value' => true] or ['$value' => ['select' => ['field1' => true, 'field2' => true]]]");
|
|
253
272
|
}
|
|
254
273
|
}
|
|
255
274
|
}
|
|
@@ -357,7 +376,7 @@ final class PPHPUtility
|
|
|
357
376
|
break;
|
|
358
377
|
default:
|
|
359
378
|
// Handle other conditions or log an error/warning for unsupported conditions
|
|
360
|
-
throw new
|
|
379
|
+
throw new Exception("Unsupported condition: $condition");
|
|
361
380
|
break;
|
|
362
381
|
}
|
|
363
382
|
}
|
|
@@ -392,13 +411,13 @@ final class PPHPUtility
|
|
|
392
411
|
* @param array $fields The array of allowed field names.
|
|
393
412
|
* @param string $modelName The name of the model being checked.
|
|
394
413
|
*
|
|
395
|
-
* @throws
|
|
414
|
+
* @throws Exception If an invalid key is found in the data array.
|
|
396
415
|
*/
|
|
397
416
|
public static function checkForInvalidKeys(array $data, array $fields, string $modelName)
|
|
398
417
|
{
|
|
399
418
|
foreach ($data as $key => $value) {
|
|
400
419
|
if (!empty($key) && !in_array($key, $fields)) {
|
|
401
|
-
throw new
|
|
420
|
+
throw new Exception("The field '$key' does not exist in the $modelName model. Accepted fields: " . implode(', ', $fields));
|
|
402
421
|
}
|
|
403
422
|
}
|
|
404
423
|
}
|
|
@@ -569,7 +588,7 @@ final class PPHPUtility
|
|
|
569
588
|
* @param string $dbType The type of the database (e.g., 'mysql', 'pgsql').
|
|
570
589
|
* @param object|null $model The model object containing metadata about the relations.
|
|
571
590
|
*
|
|
572
|
-
* @throws
|
|
591
|
+
* @throws Exception If relation metadata is not defined or if required fields/references are missing.
|
|
573
592
|
*/
|
|
574
593
|
public static function buildJoinsRecursively(
|
|
575
594
|
array $include,
|
|
@@ -579,18 +598,19 @@ final class PPHPUtility
|
|
|
579
598
|
mixed $pdo,
|
|
580
599
|
string $dbType,
|
|
581
600
|
?object $model = null,
|
|
582
|
-
string $defaultJoinType = 'INNER JOIN'
|
|
601
|
+
string $defaultJoinType = 'INNER JOIN',
|
|
602
|
+
string $pathPrefix = ''
|
|
583
603
|
) {
|
|
584
604
|
foreach ($include as $relationName => $relationOptions) {
|
|
585
|
-
|
|
586
605
|
$joinType = isset($relationOptions['join.type'])
|
|
587
606
|
? strtoupper($relationOptions['join.type']) . ' JOIN'
|
|
588
607
|
: $defaultJoinType;
|
|
589
608
|
|
|
590
609
|
if (!in_array($joinType, ['INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN'], true)) {
|
|
591
|
-
throw new
|
|
610
|
+
throw new Exception("Invalid join type: $joinType (expected 'INNER JOIN', 'LEFT JOIN', or 'RIGHT JOIN')");
|
|
592
611
|
}
|
|
593
|
-
|
|
612
|
+
|
|
613
|
+
// Extract nested includes
|
|
594
614
|
$nestedInclude = [];
|
|
595
615
|
if (is_array($relationOptions) && isset($relationOptions['include']) && is_array($relationOptions['include'])) {
|
|
596
616
|
$nestedInclude = $relationOptions['include'];
|
|
@@ -598,87 +618,69 @@ final class PPHPUtility
|
|
|
598
618
|
$isNested = !empty($nestedInclude);
|
|
599
619
|
|
|
600
620
|
// 1. Fetch metadata
|
|
601
|
-
if (!isset($model->fields[$relationName]
|
|
602
|
-
throw new
|
|
603
|
-
"Relation metadata not defined for '$relationName' in " . get_class($model)
|
|
604
|
-
);
|
|
621
|
+
if (!isset($model->fields[$relationName])) {
|
|
622
|
+
throw new Exception("Relation metadata not defined for '$relationName' in " . get_class($model));
|
|
605
623
|
}
|
|
606
|
-
$decorator = $model->fields[$relationName]['decorators'];
|
|
607
624
|
|
|
608
|
-
// 2.
|
|
609
|
-
$
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (!$joinTable) {
|
|
614
|
-
throw new \Exception("No valid table name found for relation '$relationName'.");
|
|
625
|
+
// 2. Identify related class
|
|
626
|
+
$relatedClassName = "Lib\\Prisma\\Classes\\" . $model->fields[$relationName]['type'] ?? null;
|
|
627
|
+
$relatedClass = new $relatedClassName($pdo);
|
|
628
|
+
if (!$relatedClass) {
|
|
629
|
+
throw new Exception("Could not instantiate class for relation '$relationName'.");
|
|
615
630
|
}
|
|
616
631
|
|
|
617
|
-
// 3.
|
|
618
|
-
$
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
// Decide the alias separator: OneToOne/ManyToOne => '.', else '._.'
|
|
623
|
-
$separator = ($relationType === 'OneToOne' || $relationType === 'ManyToOne')
|
|
624
|
-
? '.'
|
|
625
|
-
: '._.';
|
|
632
|
+
// 3. Determine DB table
|
|
633
|
+
$joinTable = $relatedClass->tableName ?? null;
|
|
634
|
+
if (!$joinTable) {
|
|
635
|
+
throw new Exception("No valid table name found for relation '$relationName'.");
|
|
636
|
+
}
|
|
626
637
|
|
|
627
638
|
$newAliasQuoted = PPHPUtility::quoteColumnName($dbType, $relationName);
|
|
628
639
|
|
|
629
|
-
//
|
|
630
|
-
$
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
$
|
|
640
|
+
// 5. Build the ON condition
|
|
641
|
+
$joinConditions = [];
|
|
642
|
+
$fieldsRelatedWithKeys = $model->fieldsRelatedWithKeys[$relationName] ?? null;
|
|
643
|
+
if ($fieldsRelatedWithKeys) {
|
|
644
|
+
$relationToFields = $fieldsRelatedWithKeys['relationToFields'] ?? [];
|
|
645
|
+
$relationFromFields = $fieldsRelatedWithKeys['relationFromFields'] ?? [];
|
|
634
646
|
|
|
635
|
-
if (
|
|
636
|
-
throw new
|
|
647
|
+
if (count($relationToFields) !== count($relationFromFields)) {
|
|
648
|
+
throw new Exception("Mismatched 'references' and 'fields' for '$relationName'.");
|
|
637
649
|
}
|
|
638
650
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
$newAliasQuoted,
|
|
645
|
-
PPHPUtility::quoteColumnName($dbType, $relationField)
|
|
646
|
-
);
|
|
647
|
-
} elseif (isset($decorator['inverseRelation'])) {
|
|
648
|
-
$relationField = $decorator['inverseRelation']['fields'][0] ?? null;
|
|
649
|
-
$referenceKey = $decorator['inverseRelation']['references'][0] ?? null;
|
|
651
|
+
foreach ($relationToFields as $index => $toField) {
|
|
652
|
+
$fromField = $relationFromFields[$index] ?? null;
|
|
653
|
+
if (!$toField || !$fromField) {
|
|
654
|
+
throw new Exception("Missing references/fields for '$relationName' at index $index.");
|
|
655
|
+
}
|
|
650
656
|
|
|
651
|
-
|
|
652
|
-
throw new \Exception("Missing 'fields' or 'references' for inverseRelation '$relationName'.");
|
|
653
|
-
}
|
|
657
|
+
$fromFieldExists = array_key_exists($fromField, $model->fields);
|
|
654
658
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
659
|
+
if ($fromFieldExists) {
|
|
660
|
+
$joinConditions[] = sprintf(
|
|
661
|
+
'%s.%s = %s.%s',
|
|
662
|
+
$parentAlias,
|
|
663
|
+
PPHPUtility::quoteColumnName($dbType, $fromField),
|
|
664
|
+
$newAliasQuoted,
|
|
665
|
+
PPHPUtility::quoteColumnName($dbType, $toField)
|
|
666
|
+
);
|
|
667
|
+
} else {
|
|
668
|
+
$joinConditions[] = sprintf(
|
|
669
|
+
'%s.%s = %s.%s',
|
|
670
|
+
$parentAlias,
|
|
671
|
+
PPHPUtility::quoteColumnName($dbType, $toField),
|
|
672
|
+
$newAliasQuoted,
|
|
673
|
+
PPHPUtility::quoteColumnName($dbType, $fromField)
|
|
674
|
+
);
|
|
675
|
+
}
|
|
668
676
|
}
|
|
669
|
-
|
|
670
|
-
$joinCondition = sprintf(
|
|
671
|
-
'%s.%s = %s.%s',
|
|
672
|
-
$parentAlias,
|
|
673
|
-
PPHPUtility::quoteColumnName($dbType, $referenceKey),
|
|
674
|
-
$newAliasQuoted,
|
|
675
|
-
PPHPUtility::quoteColumnName($dbType, $relationField)
|
|
676
|
-
);
|
|
677
677
|
} else {
|
|
678
|
-
throw new
|
|
678
|
+
throw new Exception("Relation '$relationName' not properly defined.");
|
|
679
679
|
}
|
|
680
680
|
|
|
681
|
-
|
|
681
|
+
$joinCondition = implode(' AND ', $joinConditions);
|
|
682
|
+
|
|
683
|
+
// 6. Add the JOIN statement
|
|
682
684
|
$joinTableQuoted = PPHPUtility::quoteColumnName($dbType, $joinTable);
|
|
683
685
|
$joins[] = sprintf(
|
|
684
686
|
'%s %s AS %s ON %s',
|
|
@@ -688,18 +690,19 @@ final class PPHPUtility
|
|
|
688
690
|
$joinCondition
|
|
689
691
|
);
|
|
690
692
|
|
|
691
|
-
//
|
|
692
|
-
|
|
693
|
-
if
|
|
694
|
-
|
|
695
|
-
|
|
693
|
+
// 7. ADD COLUMNS (with the *full path prefix*).
|
|
694
|
+
// e.g. if pathPrefix="" and relationName="post", then childPathPrefix="post".
|
|
695
|
+
// if pathPrefix="post" and relationName="categories", => "post.categories".
|
|
696
|
+
$childPathPrefix = $pathPrefix
|
|
697
|
+
? $pathPrefix . '.' . $relationName
|
|
698
|
+
: $relationName;
|
|
696
699
|
|
|
697
|
-
// 7. Add columns from the joined table
|
|
698
700
|
$fieldsOnly = $relatedClass->fieldsOnly ?? [];
|
|
699
701
|
foreach ($fieldsOnly as $field) {
|
|
700
702
|
$quotedField = PPHPUtility::quoteColumnName($dbType, $field);
|
|
701
|
-
$columnAlias =
|
|
703
|
+
$columnAlias = $childPathPrefix . '.' . $field; // e.g. "post.categories.id"
|
|
702
704
|
$columnAliasQuoted = PPHPUtility::quoteColumnName($dbType, $columnAlias);
|
|
705
|
+
|
|
703
706
|
$selectFields[] = sprintf(
|
|
704
707
|
'%s.%s AS %s',
|
|
705
708
|
$newAliasQuoted,
|
|
@@ -712,14 +715,140 @@ final class PPHPUtility
|
|
|
712
715
|
if ($isNested) {
|
|
713
716
|
self::buildJoinsRecursively(
|
|
714
717
|
$nestedInclude,
|
|
715
|
-
$newAliasQuoted,
|
|
718
|
+
$newAliasQuoted, // use this for the next level's JOIN
|
|
716
719
|
$joins,
|
|
717
720
|
$selectFields,
|
|
718
721
|
$pdo,
|
|
719
722
|
$dbType,
|
|
720
|
-
$relatedClass
|
|
723
|
+
$relatedClass,
|
|
724
|
+
$defaultJoinType,
|
|
725
|
+
$childPathPrefix // pass down the updated path
|
|
721
726
|
);
|
|
722
727
|
}
|
|
723
728
|
}
|
|
724
729
|
}
|
|
730
|
+
|
|
731
|
+
public static function arrayToObjectRecursive($data)
|
|
732
|
+
{
|
|
733
|
+
// If it's not an array, there's nothing to convert; just return as-is
|
|
734
|
+
if (!is_array($data)) {
|
|
735
|
+
return $data;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Convert each item in the array and then convert the array itself
|
|
739
|
+
foreach ($data as $key => $value) {
|
|
740
|
+
$data[$key] = self::arrayToObjectRecursive($value);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return (object) $data;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
public static function arrayToClassRecursive(array $data, string $class)
|
|
747
|
+
{
|
|
748
|
+
if (!class_exists($class)) {
|
|
749
|
+
throw new InvalidArgumentException("Class {$class} does not exist.");
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
$reflection = new ReflectionClass($class);
|
|
753
|
+
$instance = $reflection->newInstanceWithoutConstructor();
|
|
754
|
+
$properties = $reflection->getProperties();
|
|
755
|
+
|
|
756
|
+
foreach ($properties as $property) {
|
|
757
|
+
$propertyName = $property->getName();
|
|
758
|
+
|
|
759
|
+
if (array_key_exists($propertyName, $data)) {
|
|
760
|
+
$propertyType = $property->getType();
|
|
761
|
+
$typeNames = [];
|
|
762
|
+
|
|
763
|
+
if ($propertyType instanceof ReflectionUnionType) {
|
|
764
|
+
$typeNames = array_map(fn($t) => $t->getName(), $propertyType->getTypes());
|
|
765
|
+
} elseif ($propertyType instanceof ReflectionNamedType) {
|
|
766
|
+
$typeNames[] = $propertyType->getName();
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (in_array(DateTime::class, $typeNames)) {
|
|
770
|
+
$instance->$propertyName = (new DateTime($data[$propertyName]))->format('Y-m-d H:i:s');
|
|
771
|
+
} elseif (in_array(BigDecimal::class, $typeNames)) {
|
|
772
|
+
$instance->$propertyName = BigDecimal::of($data[$propertyName]);
|
|
773
|
+
} elseif (in_array(BigInteger::class, $typeNames)) {
|
|
774
|
+
$instance->$propertyName = BigInteger::of($data[$propertyName]);
|
|
775
|
+
} elseif (count(array_intersect($typeNames, ['int', 'float', 'string', 'bool'])) > 0) {
|
|
776
|
+
$instance->$propertyName = $data[$propertyName];
|
|
777
|
+
} elseif (in_array('array', $typeNames) && isset($data[$propertyName]) && is_array($data[$propertyName])) {
|
|
778
|
+
// Check array type
|
|
779
|
+
$arrayType = self::checkArrayContents($data[$propertyName]);
|
|
780
|
+
|
|
781
|
+
// Handle array-to-object conversion
|
|
782
|
+
$docComment = $property->getDocComment();
|
|
783
|
+
if ($docComment && preg_match('/@var\s+([^\s\[\]]+)\[]/', $docComment, $matches)) {
|
|
784
|
+
$elementType = $matches[1];
|
|
785
|
+
if (class_exists($elementType)) {
|
|
786
|
+
if ($arrayType === ArrayType::Indexed) {
|
|
787
|
+
$instance->$propertyName = array_map(
|
|
788
|
+
fn($item) => self::arrayToClassRecursive($item, $elementType),
|
|
789
|
+
$data[$propertyName]
|
|
790
|
+
);
|
|
791
|
+
} else {
|
|
792
|
+
// If associative, keep as array
|
|
793
|
+
$instance->$propertyName = $data[$propertyName];
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
$instance->$propertyName = $data[$propertyName]; // Default to raw array
|
|
797
|
+
}
|
|
798
|
+
} else {
|
|
799
|
+
$instance->$propertyName = $data[$propertyName]; // Default to raw array
|
|
800
|
+
}
|
|
801
|
+
} else {
|
|
802
|
+
foreach ($typeNames as $typeName) {
|
|
803
|
+
if (class_exists($typeName)) {
|
|
804
|
+
if (is_array($data[$propertyName])) {
|
|
805
|
+
$arrayType = self::checkArrayContents($data[$propertyName]);
|
|
806
|
+
|
|
807
|
+
if ($arrayType === ArrayType::Associative) {
|
|
808
|
+
$instance->$propertyName = self::arrayToClassRecursive($data[$propertyName], $typeName);
|
|
809
|
+
} elseif ($arrayType === ArrayType::Indexed) {
|
|
810
|
+
$instance->$propertyName = array_map(
|
|
811
|
+
fn($item) => self::arrayToClassRecursive($item, $typeName),
|
|
812
|
+
$data[$propertyName] ?? []
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
} elseif ($data[$propertyName] instanceof $typeName) {
|
|
816
|
+
$instance->$propertyName = $data[$propertyName];
|
|
817
|
+
}
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
return $instance;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Recursively sets a value in a multi-dimensional array
|
|
830
|
+
* based on an array of keys. E.g.:
|
|
831
|
+
* setNestedValue($arr, ['post','categories','id'], 'some-id')
|
|
832
|
+
* becomes
|
|
833
|
+
* $arr['post']['categories']['id'] = 'some-id';
|
|
834
|
+
*/
|
|
835
|
+
public static function setNestedValue(array &$array, array $keys, $value)
|
|
836
|
+
{
|
|
837
|
+
// Take the first key from the array
|
|
838
|
+
$key = array_shift($keys);
|
|
839
|
+
|
|
840
|
+
// If this key doesn't exist yet, initialize it
|
|
841
|
+
if (!isset($array[$key])) {
|
|
842
|
+
// Decide if you want an empty array or some default
|
|
843
|
+
$array[$key] = [];
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// If there are no more keys left, this is where we set the final value
|
|
847
|
+
if (empty($keys)) {
|
|
848
|
+
$array[$key] = $value;
|
|
849
|
+
} else {
|
|
850
|
+
// Otherwise, we recurse deeper
|
|
851
|
+
self::setNestedValue($array[$key], $keys, $value);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
725
854
|
}
|
|
@@ -4,17 +4,21 @@ namespace Lib\Prisma\Model;
|
|
|
4
4
|
|
|
5
5
|
interface IModel
|
|
6
6
|
{
|
|
7
|
+
// public function aggregate(array $operation);
|
|
8
|
+
// public function createMany(array $data);
|
|
9
|
+
// public function createManyAndReturn(array $data);
|
|
7
10
|
public function create(array $data);
|
|
8
|
-
public function
|
|
11
|
+
// public function deleteMany(array $criteria);
|
|
12
|
+
// public function delete(array $criteria);
|
|
13
|
+
// public function findFirst(array $criteria);
|
|
14
|
+
// public function findFirstOrThrow(array $criteria);
|
|
15
|
+
// public function findMany(array $criteria);
|
|
9
16
|
public function findUnique(array $criteria);
|
|
10
|
-
public function
|
|
11
|
-
public function
|
|
12
|
-
public function
|
|
13
|
-
public function
|
|
14
|
-
public function
|
|
15
|
-
public function
|
|
16
|
-
public function
|
|
17
|
-
public function aggregate(array $operation);
|
|
18
|
-
public function groupBy(array $by);
|
|
19
|
-
public function count(array $criteria);
|
|
17
|
+
// public function findUniqueOrThrow(array $criteria);
|
|
18
|
+
// public function groupBy(array $by);
|
|
19
|
+
// public function updateMany(array $data);
|
|
20
|
+
// public function updateManyAndReturn(array $data);
|
|
21
|
+
// public function update(array $data);
|
|
22
|
+
// public function upsert(array $data);
|
|
23
|
+
// public function count(array $criteria);
|
|
20
24
|
}
|
|
@@ -40,7 +40,6 @@ class PrismaSettings
|
|
|
40
40
|
$this->projectRootPath = $data['projectRootPath'] ?? '';
|
|
41
41
|
$this->phpEnvironment = $data['phpEnvironment'] ?? '';
|
|
42
42
|
$this->phpRootPathExe = $data['phpRootPathExe'] ?? '';
|
|
43
|
-
$this->phpGenerateClassPath = $data['phpGenerateClassPath'] ?? '';
|
|
44
43
|
$this->bsTarget = $data['bsTarget'] ?? '';
|
|
45
44
|
$this->bsPathRewrite = new BSPathRewrite($data['bsPathRewrite'] ?? []);
|
|
46
45
|
$this->backendOnly = $data['backendOnly'] ?? false;
|
|
@@ -84,12 +83,20 @@ class PrismaPHPSettings
|
|
|
84
83
|
*/
|
|
85
84
|
public static array $includeFiles = [];
|
|
86
85
|
|
|
86
|
+
/**
|
|
87
|
+
* The local storage key for the app state.
|
|
88
|
+
*
|
|
89
|
+
* @var string
|
|
90
|
+
*/
|
|
91
|
+
public static string $localStoreKey;
|
|
92
|
+
|
|
87
93
|
public static function init(): void
|
|
88
94
|
{
|
|
89
95
|
self::$option = self::getPrismaSettings();
|
|
90
96
|
self::$routeFiles = self::getRoutesFileList();
|
|
91
97
|
self::$classLogFiles = self::getClassesLogFiles();
|
|
92
98
|
self::$includeFiles = self::getIncludeFiles();
|
|
99
|
+
self::$localStoreKey = self::getLocalStorageKey();
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
/**
|
|
@@ -119,26 +126,54 @@ class PrismaPHPSettings
|
|
|
119
126
|
private static function getRoutesFileList(): array
|
|
120
127
|
{
|
|
121
128
|
$jsonFileName = SETTINGS_PATH . '/files-list.json';
|
|
122
|
-
|
|
129
|
+
if (!file_exists($jsonFileName)) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
$jsonContent = file_get_contents($jsonFileName);
|
|
134
|
+
if ($jsonContent === false || empty(trim($jsonContent))) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
123
137
|
|
|
124
|
-
|
|
138
|
+
$routeFiles = json_decode($jsonContent, true);
|
|
139
|
+
return is_array($routeFiles) ? $routeFiles : [];
|
|
125
140
|
}
|
|
126
141
|
|
|
127
142
|
private static function getClassesLogFiles(): array
|
|
128
143
|
{
|
|
129
144
|
$jsonFileName = SETTINGS_PATH . '/class-imports.json';
|
|
130
|
-
|
|
145
|
+
if (!file_exists($jsonFileName)) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
131
148
|
|
|
132
|
-
|
|
149
|
+
$jsonContent = file_get_contents($jsonFileName);
|
|
150
|
+
if ($jsonContent === false || empty(trim($jsonContent))) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
$classLogFiles = json_decode($jsonContent, true);
|
|
155
|
+
return is_array($classLogFiles) ? $classLogFiles : [];
|
|
133
156
|
}
|
|
134
157
|
|
|
135
158
|
private static function getIncludeFiles(): array
|
|
136
159
|
{
|
|
137
160
|
$jsonFileName = SETTINGS_PATH . "/request-data.json";
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
161
|
+
if (!file_exists($jsonFileName)) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
$jsonContent = file_get_contents($jsonFileName);
|
|
166
|
+
if ($jsonContent === false || empty(trim($jsonContent))) {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
$includeFiles = json_decode($jsonContent, true);
|
|
171
|
+
return is_array($includeFiles) ? $includeFiles : [];
|
|
172
|
+
}
|
|
141
173
|
|
|
142
|
-
|
|
174
|
+
private static function getLocalStorageKey(): string
|
|
175
|
+
{
|
|
176
|
+
$localStorageKey = $_ENV['LOCALSTORE_KEY'] ?? 'pphp_local_store_59e13';
|
|
177
|
+
return strtolower(preg_replace('/\s+/', '_', trim($localStorageKey)));
|
|
143
178
|
}
|
|
144
179
|
}
|