prisma-client-php 2.3.6 → 3.0.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  namespace Lib\Prisma\Classes;
4
4
 
5
- use Lib\Validator;
5
+ use PP\Validator;
6
6
  use ReflectionClass;
7
7
  use Exception;
8
8
  use PDO;
@@ -122,8 +122,21 @@ final class PPHPUtility
122
122
  public static function checkFieldsExist(array $select, array $fields, string $modelName)
123
123
  {
124
124
  $virtualFields = ['_count', '_max', '_min', '_avg', '_sum'];
125
+ $logicKeys = ['AND', 'OR', 'NOT'];
125
126
 
126
127
  foreach ($select as $key => $value) {
128
+
129
+ if (is_string($key) && in_array($key, $logicKeys, true)) {
130
+ if (is_array($value)) {
131
+ foreach ($value as $sub) {
132
+ if (is_array($sub)) {
133
+ self::checkFieldsExist($sub, $fields, $modelName);
134
+ }
135
+ }
136
+ }
137
+ continue;
138
+ }
139
+
127
140
  if (is_numeric($key) && is_string($value)) {
128
141
  if (self::fieldExists($key, $fields))
129
142
  throw new Exception("The '$value' is indexed, waiting example: ['$value' => true]");
@@ -131,29 +144,31 @@ final class PPHPUtility
131
144
 
132
145
  if (isset($value) && empty($value) || !is_bool($value)) {
133
146
  if (is_string($key) && !self::fieldExists($key, $fields)) {
134
- if (in_array($key, $virtualFields)) {
147
+ if (in_array($key, $virtualFields, true)) {
135
148
  continue;
136
149
  }
137
150
  throw new Exception("The field '$key' does not exist in the $modelName model.");
138
151
  }
139
152
 
140
153
  if (is_array($value) && !empty($value)) {
141
- $isRelatedModel = false;
154
+ if (self::isOperatorArray($value)) {
155
+ continue;
156
+ }
142
157
 
158
+ $isRelatedModel = false;
143
159
  foreach ($fields as $field) {
144
- $isObject = $field['kind'] === 'object' ? true : false;
145
- $fieldName = $field['name'];
146
-
160
+ $isObject = ($field['kind'] ?? null) === 'object';
161
+ $fieldName = $field['name'] ?? null;
147
162
  if ($isObject && $fieldName === $key) {
148
163
  $isRelatedModel = true;
164
+ break;
149
165
  }
150
166
  }
151
-
152
167
  if ($isRelatedModel) continue;
153
168
 
154
169
  $keys = array_keys($value);
155
170
  foreach ($keys as $fieldName) {
156
- $fieldName = trim($fieldName);
171
+ $fieldName = trim((string)$fieldName);
157
172
  if (!self::fieldExists($fieldName, $fields)) {
158
173
  throw new Exception("The field '$fieldName' does not exist in the $modelName model.");
159
174
  }
@@ -163,10 +178,10 @@ final class PPHPUtility
163
178
  continue;
164
179
  }
165
180
 
166
- foreach (explode(',', $key) as $fieldName) {
181
+ foreach (explode(',', (string)$key) as $fieldName) {
167
182
  $fieldName = trim($fieldName);
168
183
  if (!self::fieldExists($fieldName, $fields)) {
169
- if (in_array($fieldName, $virtualFields)) {
184
+ if (in_array($fieldName, $virtualFields, true)) {
170
185
  continue;
171
186
  }
172
187
  throw new Exception("The field '$fieldName' does not exist in the $modelName model.");
@@ -1010,6 +1025,30 @@ final class PPHPUtility
1010
1025
  $whereCondition = $op[$modelRelatedFieldType];
1011
1026
  $relatedResult = $relatedClass->delete(['where' => $whereCondition]);
1012
1027
  break;
1028
+ case 'deleteMany':
1029
+ if ($isExplicitOneToMany) {
1030
+ foreach ($operations as $opDelete) {
1031
+ if (!isset($opDelete['where'])) {
1032
+ throw new Exception("deleteMany requires 'where' for '{$relatedFieldName}'.");
1033
+ }
1034
+
1035
+ $where = $opDelete['where'];
1036
+
1037
+ if (!empty($childFkFields)) {
1038
+ $parentId = $opDelete[$childFkFields[0]] ?? null;
1039
+ if ($parentId !== null) {
1040
+ $where[$childFkFields[0]] = $parentId;
1041
+ }
1042
+ }
1043
+
1044
+ $relatedClass->deleteMany(['where' => $where]);
1045
+ }
1046
+
1047
+ return [];
1048
+ } else {
1049
+ throw new Exception("deleteMany is only supported for one-to-many relations.");
1050
+ }
1051
+ break;
1013
1052
  case 'disconnect':
1014
1053
  if ($isExplicitOneToMany) {
1015
1054
  foreach ($operations as $opDisc) {
@@ -1037,68 +1076,73 @@ final class PPHPUtility
1037
1076
  break;
1038
1077
  case 'set':
1039
1078
  if ($isExplicitOneToMany) {
1079
+ if (empty($operations)) {
1080
+ return [];
1081
+ }
1082
+
1040
1083
  $parentId = $operations[0][$childFkFields[0]]
1041
1084
  ?? throw new Exception("Missing parent id in 'set' for '{$relatedFieldName}'.");
1042
1085
 
1043
- $keepIds = [];
1044
- foreach ($operations as $opSet) {
1045
- if (isset($opSet[$relatedClass->_primaryKey])) {
1046
- $keepIds[] = $opSet[$relatedClass->_primaryKey];
1047
- }
1048
- }
1086
+ $newIds = [];
1087
+ $keyFields = [];
1088
+
1089
+ $keyFields = array_merge($keyFields, $childFkFields);
1049
1090
 
1050
- $deleteWhere = [
1051
- $childFkFields[0] => $parentId,
1052
- ];
1053
- if ($keepIds) {
1054
- $deleteWhere[$relatedClass->_primaryKey] = ['notIn' => $keepIds];
1091
+ foreach ($relatedClass->_fields as $fieldName => $fieldMeta) {
1092
+ $kind = $fieldMeta['kind'] ?? 'scalar';
1093
+ $isReadOnly = $fieldMeta['isReadOnly'] ?? false;
1094
+
1095
+ if ($kind === 'scalar' && $isReadOnly && $fieldName !== 'id') {
1096
+ $keyFields[] = $fieldName;
1097
+ }
1055
1098
  }
1056
1099
 
1057
- $relatedClass->deleteMany(['where' => $deleteWhere]);
1100
+ $keyFields = array_unique($keyFields);
1058
1101
 
1059
- $newIds = [];
1060
1102
  foreach ($operations as $opSet) {
1061
- $where = array_diff_key($opSet, array_flip($childFkFields));
1062
- $fkUpdate = array_fill_keys($childFkFields, $parentId);
1063
-
1064
- $row = $relatedClass->upsert([
1065
- 'where' => $where,
1066
- 'update' => $fkUpdate,
1067
- 'create' => array_merge($where, $fkUpdate),
1068
- ]);
1103
+ $data = $opSet;
1069
1104
 
1070
- $newIds[] = $row->{$relatedClass->_primaryKey};
1071
- }
1105
+ $where = [];
1106
+ foreach ($keyFields as $field) {
1107
+ if (isset($data[$field])) {
1108
+ $where[$field] = $data[$field];
1109
+ }
1110
+ }
1072
1111
 
1073
- $relatedResult = $relatedClass->findMany([
1074
- 'where' => [
1075
- $relatedClass->_primaryKey => ['in' => $newIds],
1076
- ],
1077
- ]);
1078
- $relatedResult = (array)$relatedResult;
1112
+ if (empty($where)) {
1113
+ throw new Exception("Cannot determine unique identifier for '{$relatedFieldName}'. No key fields found.");
1114
+ }
1079
1115
 
1080
- if (!$requestOption) {
1081
- return $relatedResult;
1082
- }
1083
- if (!$relatedResult) {
1084
- throw new Exception("Failed to process related record for '{$relatedFieldName}'.");
1116
+ $existing = $relatedClass->findFirst(['where' => $where]);
1117
+
1118
+ if ($existing) {
1119
+ $relatedClass->update([
1120
+ 'where' => ['id' => $existing->id],
1121
+ 'data' => $data,
1122
+ ]);
1123
+ $newIds[] = $existing->id;
1124
+ } else {
1125
+ $created = $relatedClass->create(['data' => $data]);
1126
+ $newIds[] = $created->id;
1127
+ }
1085
1128
  }
1086
1129
 
1087
- if ($modelRelatedFieldIsList) {
1088
- return [];
1130
+ if (!empty($newIds)) {
1131
+ $deleteWhere = [
1132
+ $childFkFields[0] => $parentId,
1133
+ 'id' => ['notIn' => $newIds],
1134
+ ];
1135
+ } else {
1136
+ $deleteWhere = [$childFkFields[0] => $parentId];
1089
1137
  }
1090
1138
 
1091
- $bindings = [];
1092
- foreach ($modelRelatedFromFields as $i => $fromField) {
1093
- $toField = $modelRelatedToFields[$i];
1094
- if (!isset($relatedResult[$toField])) {
1095
- throw new Exception("The field '{$toField}' is missing …");
1096
- }
1097
- }
1098
- return $bindings;
1139
+ $relatedClass->deleteMany(['where' => $deleteWhere]);
1140
+
1141
+ return [];
1099
1142
  } elseif (empty($modelRelatedFromFields) && empty($modelRelatedToFields)) {
1100
1143
  $newRelatedIds = [];
1101
1144
  $primaryId = null;
1145
+
1102
1146
  foreach ($operations as $opSet) {
1103
1147
  $relatedFieldData = $opSet[$modelRelatedFieldType];
1104
1148
  $modelFieldData = $opSet[$modelName];
@@ -1144,16 +1188,189 @@ final class PPHPUtility
1144
1188
  $relatedFieldData = $op[$modelRelatedFieldType];
1145
1189
  $relatedResult = $relatedClass->update(['where' => $relatedFieldData['where'], 'data' => $relatedFieldData['data']]);
1146
1190
  break;
1147
- case 'upsert':
1148
- $relatedFieldData = $op[$modelRelatedFieldType];
1149
- $existing = $relatedClass->findUnique(['where' => $relatedFieldData['where']]);
1191
+ case 'updateMany':
1192
+ if ($isExplicitOneToMany) {
1193
+ if (empty($operations)) {
1194
+ return [];
1195
+ }
1196
+
1197
+ $parentId = $operations[0][$childFkFields[0]] ?? null;
1198
+ if ($parentId === null) {
1199
+ throw new Exception("Missing parent id in 'updateMany' for '{$relatedFieldName}'.");
1200
+ }
1201
+
1202
+ $fieldUpdates = [];
1203
+ $ids = [];
1150
1204
 
1151
- if ($existing) {
1152
- $relatedResult = $relatedClass->update(['where' => $relatedFieldData['where'], 'data' => $relatedFieldData['data']]);
1205
+ foreach ($operations as $opUpdate) {
1206
+ $where = $opUpdate['where'];
1207
+ $data = $opUpdate['data'];
1208
+
1209
+ $record = $relatedClass->findFirst([
1210
+ 'where' => array_merge($where, [$childFkFields[0] => $parentId])
1211
+ ]);
1212
+ if (!$record) continue;
1213
+
1214
+ $ids[] = $record->id;
1215
+
1216
+ foreach ($data as $field => $value) {
1217
+ if (!isset($fieldUpdates[$field])) {
1218
+ $fieldUpdates[$field] = [];
1219
+ }
1220
+ $fieldUpdates[$field][$record->id] = $value;
1221
+ }
1222
+ }
1223
+
1224
+ if (empty($ids)) {
1225
+ return [];
1226
+ }
1227
+
1228
+ $tableName = PPHPUtility::quoteColumnName($dbType, $relatedClass->_tableName);
1229
+ $idColumn = PPHPUtility::quoteColumnName($dbType, 'id');
1230
+
1231
+ $setClauses = [];
1232
+ $bindings = [];
1233
+
1234
+ foreach ($fieldUpdates as $field => $updates) {
1235
+ $fieldQuoted = PPHPUtility::quoteColumnName($dbType, $field);
1236
+ $caseWhen = "CASE";
1237
+
1238
+ foreach ($updates as $id => $value) {
1239
+ $placeholder = ":upd_{$field}_" . count($bindings);
1240
+ $idPlaceholder = ":id_case_" . count($bindings);
1241
+ $caseWhen .= " WHEN $idColumn = $idPlaceholder THEN $placeholder";
1242
+ $bindings[$idPlaceholder] = $id;
1243
+ $bindings[$placeholder] = $value;
1244
+ }
1245
+
1246
+ $caseWhen .= " ELSE $fieldQuoted END";
1247
+ $setClauses[] = "$fieldQuoted = $caseWhen";
1248
+ }
1249
+
1250
+ $idPlaceholders = [];
1251
+ foreach ($ids as $id) {
1252
+ $placeholder = ":id_" . count($bindings);
1253
+ $idPlaceholders[] = $placeholder;
1254
+ $bindings[$placeholder] = $id;
1255
+ }
1256
+
1257
+ $sql = "UPDATE $tableName SET " . implode(', ', $setClauses) .
1258
+ " WHERE $idColumn IN (" . implode(', ', $idPlaceholders) . ")";
1259
+
1260
+ $stmt = $pdo->prepare($sql);
1261
+ foreach ($bindings as $key => $value) {
1262
+ $stmt->bindValue($key, $value);
1263
+ }
1264
+ $stmt->execute();
1265
+
1266
+ return [];
1153
1267
  } else {
1154
- $relatedResult = $relatedClass->create(['data' => $op]);
1268
+ throw new Exception("updateMany is only supported for one-to-many relations.");
1269
+ }
1270
+ break;
1271
+ case 'upsert':
1272
+ if ($isExplicitOneToMany) {
1273
+ if (empty($operations)) {
1274
+ return [];
1275
+ }
1276
+
1277
+ $parentId = $operations[0][$childFkFields[0]] ?? null;
1278
+ if ($parentId === null) {
1279
+ throw new Exception("Missing parent id in 'upsert' for '{$relatedFieldName}'.");
1280
+ }
1281
+
1282
+ $tableName = PPHPUtility::quoteColumnName($dbType, $relatedClass->_tableName);
1283
+ $allFields = [];
1284
+ $values = [];
1285
+ $bindings = [];
1286
+
1287
+ $sampleOp = $operations[0];
1288
+ $createData = $sampleOp['create'];
1289
+ $createData[$childFkFields[0]] = $parentId;
1290
+ $fields = array_keys($createData);
1291
+
1292
+ foreach ($fields as $field) {
1293
+ $allFields[] = PPHPUtility::quoteColumnName($dbType, $field);
1294
+ }
1295
+
1296
+ foreach ($operations as $idx => $opUpsert) {
1297
+ $createData = $opUpsert['create'];
1298
+ $createData[$childFkFields[0]] = $parentId;
1299
+
1300
+ $rowPlaceholders = [];
1301
+ foreach ($fields as $field) {
1302
+ $placeholder = ":v{$idx}_{$field}";
1303
+ $rowPlaceholders[] = $placeholder;
1304
+ $bindings[$placeholder] = $createData[$field] ?? null;
1305
+ }
1306
+ $values[] = '(' . implode(', ', $rowPlaceholders) . ')';
1307
+ }
1308
+
1309
+ if ($dbType === 'mysql') {
1310
+ $updateClauses = [];
1311
+ foreach ($fields as $field) {
1312
+ if ($field !== 'id') {
1313
+ $fieldQuoted = PPHPUtility::quoteColumnName($dbType, $field);
1314
+ $updateClauses[] = "$fieldQuoted = VALUES($fieldQuoted)";
1315
+ }
1316
+ }
1317
+
1318
+ $sql = "INSERT INTO $tableName (" . implode(', ', $allFields) . ") VALUES " .
1319
+ implode(', ', $values) .
1320
+ " ON DUPLICATE KEY UPDATE " . implode(', ', $updateClauses);
1321
+ } elseif ($dbType === 'pgsql') {
1322
+ $updateClauses = [];
1323
+ $conflictFields = [];
1324
+
1325
+ foreach ($operations[0]['where'] as $whereField => $whereValue) {
1326
+ $conflictFields[] = PPHPUtility::quoteColumnName($dbType, $whereField);
1327
+ }
1328
+
1329
+ foreach ($fields as $field) {
1330
+ if ($field !== 'id' && !in_array($field, array_keys($operations[0]['where']))) {
1331
+ $fieldQuoted = PPHPUtility::quoteColumnName($dbType, $field);
1332
+ $updateClauses[] = "$fieldQuoted = EXCLUDED.$fieldQuoted";
1333
+ }
1334
+ }
1335
+
1336
+ $sql = "INSERT INTO $tableName (" . implode(', ', $allFields) . ") VALUES " .
1337
+ implode(', ', $values) .
1338
+ " ON CONFLICT (" . implode(', ', $conflictFields) . ") DO UPDATE SET " .
1339
+ implode(', ', $updateClauses);
1340
+ } elseif ($dbType === 'sqlite') {
1341
+ $updateClauses = [];
1342
+ $conflictFields = [];
1343
+
1344
+ foreach ($operations[0]['where'] as $whereField => $whereValue) {
1345
+ $conflictFields[] = PPHPUtility::quoteColumnName($dbType, $whereField);
1346
+ }
1347
+
1348
+ foreach ($fields as $field) {
1349
+ if ($field !== 'id' && !in_array($field, array_keys($operations[0]['where']))) {
1350
+ $fieldQuoted = PPHPUtility::quoteColumnName($dbType, $field);
1351
+ $updateClauses[] = "$fieldQuoted = excluded.$fieldQuoted";
1352
+ }
1353
+ }
1354
+
1355
+ $sql = "INSERT INTO $tableName (" . implode(', ', $allFields) . ") VALUES " .
1356
+ implode(', ', $values) .
1357
+ " ON CONFLICT (" . implode(', ', $conflictFields) . ") DO UPDATE SET " .
1358
+ implode(', ', $updateClauses);
1359
+ } else {
1360
+ throw new Exception("Unsupported database type: $dbType");
1361
+ }
1362
+
1363
+ $stmt = $pdo->prepare($sql);
1364
+ foreach ($bindings as $key => $value) {
1365
+ $stmt->bindValue($key, $value);
1366
+ }
1367
+ $stmt->execute();
1368
+
1369
+ return [];
1155
1370
  }
1156
1371
  break;
1372
+ default:
1373
+ throw new Exception("Unsupported operation '$action' for relation '{$relatedFieldName}'.");
1157
1374
  }
1158
1375
  }
1159
1376
  }
package/dist/utils.js CHANGED
@@ -1,13 +1 @@
1
- import { fileURLToPath } from "url";
2
- import { dirname } from "path";
3
- /**
4
- * Retrieves the file metadata including the filename and directory name.
5
- *
6
- * @param importMetaUrl - The URL of the module's import.meta.url.
7
- * @returns An object containing the filename (`__filename`) and directory name (`__dirname`).
8
- */
9
- export function getFileMeta() {
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
- return { __filename, __dirname };
13
- }
1
+ import{fileURLToPath}from"url";import{dirname}from"path";export function getFileMeta(){const e=fileURLToPath(import.meta.url);return{__filename:e,__dirname:dirname(e)}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "prisma-client-php",
3
3
  "description": "Prisma Client PHP is an auto-generated query builder that enables type-safe database access in PHP.",
4
- "version": "2.3.6",
4
+ "version": "3.0.1",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {