Skip to content

Commit 53f57c5

Browse files
authored
Merge pull request #24
Added InsertBulkQuery
2 parents 6ccece2 + d64eeec commit 53f57c5

12 files changed

+381
-233
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ $result = $repository->getByQuery($query);
157157
## Advanced Topics
158158

159159
* [Active Record](docs/active-record.md)
160+
* [Auto Discovering Relationship](docs/auto-discovering-relationship.md)
160161
* [The Literal Object](docs/the-literal-object.md)
161162
* [Soft Delete](docs/softdelete.md)
162163
* [Caching the Results](docs/cache.md)

docs/updating-the-database.md

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Updating the Database
22

3-
Once you have defined the model, (see [Getting Started](getting-started-model.md)) you can start to interact with the database
4-
and doing queries, updates, and deletes.
3+
Once you have defined the model, (see [Getting Started](getting-started-model.md)) you can start to
4+
interact with the database and doing queries, updates, and deletes.
5+
6+
## Update
57

68
Update a single record is simple as:
79

@@ -14,7 +16,9 @@ $repository->save($users);
1416

1517
This code will update the record with the ID 10 and set the name to "New name".
1618

17-
The idea is to insert a new record. If you don't set the ID, the library will assume that you are inserting a new record.
19+
## Insert
20+
21+
If you don't set the ID, the library will assume that you are inserting a new record.
1822

1923
```php
2024
<?php
@@ -23,9 +27,10 @@ $users->name = "New name";
2327
$repository->save($users);
2428
```
2529

26-
## Advanced Cases
30+
## Using the UpdateQuery for Multiple Records
2731

28-
In some cases you need to update multiples records at once. See an example:
32+
UpdateQuery allows you to update multiple records simultaneously with a single query. This is more efficient than
33+
retrieving and updating individual records one by one when you need to apply the same changes to many records.
2934

3035
```php
3136
<?php
@@ -37,4 +42,80 @@ $updateQuery->set('fld3', 'C');
3742
$updateQuery->where('fld1 > :id', ['id' => 10]);
3843
```
3944

40-
This code will update the table `test` and set the fields `fld1`, `fld2`, and `fld3` to `A`, `B`, and `C` respectively where the `fld1` is greater than 10.
45+
This code will update the table `test` and set the fields `fld1`, `fld2`, and `fld3` to `A`, `B`, and `C`
46+
respectively for all records where `fld1` is greater than 10.
47+
48+
## Insert records with InsertQuery
49+
50+
You can insert records using the `InsertQuery` object. See an example:
51+
52+
```php
53+
<?php
54+
$insertQuery = new \ByJG\MicroOrm\InsertQuery();
55+
$insertQuery->table('test');
56+
$insertQuery->set('fld1', 'A');
57+
$insertQuery->set('fld2', 'B');
58+
$insertQuery->set('fld3', 'C');
59+
```
60+
61+
## Insert records from another select
62+
63+
You can insert records from another select using the `InsertSelectQuery` object. See an example:
64+
65+
```php
66+
<?php
67+
68+
// Define the query to select the records
69+
$query = new QueryBasic();
70+
$query->table('table2');
71+
$query->field('fldA');
72+
$query->field('fldB');
73+
$query->field('fldC');
74+
$query->where('fldA = :valueA', ['valueA' => 1]);
75+
76+
// Define the insert select query
77+
$insertSelectQuery = new \ByJG\MicroOrm\InsertSelectQuery();
78+
$insertSelectQuery->table('test');
79+
$insertSelectQuery->fields(['fld1', 'fld2', 'fld3']);
80+
$insertSelectQuery->fromQuery($query); // The query to select the records
81+
```
82+
83+
## Insert records in batch
84+
85+
You can insert records in batch using the `InsertBulkQuery` object. See an example:
86+
87+
```php
88+
<?php
89+
$insertBulk = new InsertBulkQuery('test', ['fld1', 'fld2']);
90+
$insertBulk->values(['fld1' => 'A', 'fld2' => 'B']);
91+
$insertBulk->values(['fld1' => 'D', 'fld2' => 'E']);
92+
$insertBulk->values(['fld1' => 'G', 'fld2' => 'H']);
93+
```
94+
95+
By default, InsertBulkQuery uses a faster but less secure approach. To use parameterized queries for
96+
better security (especially when handling user input), you can enable safe mode:
97+
98+
```php
99+
<?php
100+
$insertBulk = new InsertBulkQuery('test', ['fld1', 'fld2']);
101+
$insertBulk->withSafeParameters(); // Enable safe parameterized queries
102+
$insertBulk->values(['fld1' => $userInput1, 'fld2' => $userInput2]);
103+
$insertBulk->values(['fld1' => $userInput3, 'fld2' => $userInput4]);
104+
```
105+
106+
> **⚠️ Security Warning:** By default, the `InsertBulkQuery` implementation uses direct value embedding with
107+
> basic escaping rather than parameterized queries. This makes it faster but potentially vulnerable to
108+
> SQL injection attacks with untrusted data. Use the `withSafeParameters()` method when dealing with
109+
> user input for better security, although this may reduce performance for large batch operations.
110+
> For maximum security with user input, consider using the `InsertQuery` for individual inserts.
111+
112+
## Delete records
113+
114+
You can delete records using the `DeleteQuery` object. See an example:
115+
116+
```php
117+
<?php
118+
$deleteQuery = new \ByJG\MicroOrm\DeleteQuery();
119+
$deleteQuery->table('test');
120+
$deleteQuery->where('fld1 = :value', ['value' => 'A']);
121+
```

src/InsertBulkQuery.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
namespace ByJG\MicroOrm;
4+
5+
use ByJG\AnyDataset\Db\DbFunctionsInterface;
6+
use ByJG\MicroOrm\Exception\OrmInvalidFieldsException;
7+
use ByJG\MicroOrm\Interface\QueryBuilderInterface;
8+
use ByJG\MicroOrm\Literal\Literal;
9+
use InvalidArgumentException;
10+
11+
class InsertBulkQuery extends Updatable
12+
{
13+
protected array $fields = [];
14+
15+
protected ?QueryBuilderInterface $query = null;
16+
17+
protected ?SqlObject $sqlObject = null;
18+
19+
protected bool $safe = false;
20+
21+
public function __construct(string $table, array $fieldNames)
22+
{
23+
$this->table($table);
24+
25+
foreach ($fieldNames as $fieldname) {
26+
$this->fields[$fieldname] = [];
27+
}
28+
}
29+
30+
31+
public static function getInstance(string $table, array $fieldNames): static
32+
{
33+
return new InsertBulkQuery($table, $fieldNames);
34+
}
35+
36+
public function withSafeParameters(): static
37+
{
38+
$this->safe = true;
39+
return $this;
40+
}
41+
42+
/**
43+
* @throws OrmInvalidFieldsException
44+
*/
45+
public function values(array $values, bool $allowNonMatchFields = true): static
46+
{
47+
if (array_diff(array_keys($this->fields), array_keys($values))) {
48+
throw new OrmInvalidFieldsException('The provided values do not match the expected fields');
49+
}
50+
51+
if (!$allowNonMatchFields && array_diff(array_keys($values), array_keys($this->fields))) {
52+
throw new OrmInvalidFieldsException('The provided values contain more fields than expected');
53+
}
54+
55+
foreach (array_keys($this->fields) as $field) {
56+
$this->fields[$field][] = $values[$field];
57+
}
58+
59+
return $this;
60+
}
61+
62+
/**
63+
* @param DbFunctionsInterface|null $dbHelper
64+
* @return SqlObject
65+
* @throws OrmInvalidFieldsException
66+
*/
67+
public function build(DbFunctionsInterface $dbHelper = null): SqlObject
68+
{
69+
if (empty($this->fields)) {
70+
throw new OrmInvalidFieldsException('You must specify the fields for insert');
71+
}
72+
73+
$tableStr = $this->table;
74+
if (!is_null($dbHelper)) {
75+
$tableStr = $dbHelper->delimiterTable($tableStr);
76+
}
77+
78+
// Extract column names
79+
$columns = array_keys($this->fields);
80+
81+
// Get the number of rows
82+
$rowCount = count(current($this->fields));
83+
84+
// Initialize placeholders and parameters
85+
$placeholders = [];
86+
$params = [];
87+
88+
// Build placeholders and populate $params
89+
for ($i = 0; $i < $rowCount; $i++) {
90+
$rowPlaceholders = [];
91+
foreach ($columns as $j => $col) {
92+
$paramKey = "p{$i}_$j"; // Generate the parameter key
93+
$rowPlaceholders[] = ":$paramKey"; // Add to row placeholders
94+
if ($this->safe) {
95+
$params[$paramKey] = $this->fields[$col][$i];
96+
} else {
97+
$value = str_replace("'", "''", $this->fields[$col][$i]);
98+
if (!is_numeric($value)) {
99+
$value = $dbHelper?->delimiterField($value) ?? "'{$value}'";
100+
}
101+
$params[$paramKey] = new Literal($value); // Map parameter key to value
102+
}
103+
}
104+
$placeholders[] = '(' . implode(', ', $rowPlaceholders) . ')'; // Add row placeholders to query
105+
}
106+
107+
if (!is_null($dbHelper)) {
108+
$columns = $dbHelper->delimiterField($columns);
109+
}
110+
111+
// Construct the final SQL query
112+
$sql = sprintf(
113+
"INSERT INTO %s (%s) VALUES %s",
114+
$tableStr,
115+
implode(', ', $columns),
116+
implode(', ', $placeholders)
117+
);
118+
119+
$sql = ORMHelper::processLiteral($sql, $params);
120+
return new SqlObject($sql, $params);
121+
}
122+
123+
public function convert(?DbFunctionsInterface $dbDriver = null): QueryBuilderInterface
124+
{
125+
throw new InvalidArgumentException('It is not possible to convert an InsertBulkQuery to a Query');
126+
}
127+
}

src/InsertMultipleQuery.php

Lines changed: 0 additions & 101 deletions
This file was deleted.

0 commit comments

Comments
 (0)