Skip to content

Commit 04736b8

Browse files
committed
sql2p
1 parent 6588da9 commit 04736b8

File tree

3 files changed

+321
-0
lines changed

3 files changed

+321
-0
lines changed

packages/naming.php

+1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@
1818
'workflow.md' => 'Workflow',
1919
'writer.md' => 'Writer',
2020
'xrdebug.md' => 'xrDebug',
21+
'sql2p.md' => 'SQL2P',
2122
];

packages/sql2p.md

+245
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
# SQL2P
2+
3+
![SQL2P](../src/packages/sql2p/sql2p-logo.svg)
4+
5+
## Summary
6+
7+
SQL2P generates [Parameter(s)](https://chevere.org/library/parameter) for MySQL schemas. It represents database tables and its columns using the Parameter package.
8+
9+
## Installing
10+
11+
SQL2P is available through [Packagist](https://packagist.org/packages/chevere/sql2p) and the repository source is at [chevere/sql2p](https://github.com/chevere/sql2p).
12+
13+
```sh
14+
composer require chevere/sql2p
15+
```
16+
17+
## How it works?
18+
19+
From a `CREATE TABLE` statement like this one below.
20+
21+
```sql
22+
CREATE TABLE `invoice` (
23+
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
24+
`client_id` INT UNSIGNED NOT NULL,
25+
`datetime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
26+
`details` LONGTEXT NULL,
27+
`quantity` INT UNSIGNED NOT NULL,
28+
`rate` DECIMAL(10,2) NOT NULL,
29+
`total` DECIMAL(19,4) GENERATED ALWAYS AS (quantity*rate),
30+
PRIMARY KEY (`id`)
31+
) ENGINE = InnoDB;
32+
```
33+
34+
SQL2P generates the following PHP code.
35+
36+
```php
37+
use Chevere\Parameter\Interfaces\ArrayParameterInterface;
38+
use function Chevere\Parameter\arrayp;
39+
use function Chevere\Parameter\datetime;
40+
use function Chevere\Parameter\float;
41+
use function Chevere\Parameter\int;
42+
use function Chevere\Parameter\null;
43+
use function Chevere\Parameter\string;
44+
use function Chevere\Parameter\union;
45+
46+
function invoiceTable(): ArrayParameterInterface
47+
{
48+
return arrayp(
49+
id: int(min: 0),
50+
client_id: int(min: 0),
51+
datetime: datetime(),
52+
details: union(
53+
null(),
54+
string()
55+
),
56+
quantity: int(min: 0),
57+
rate: float(),
58+
total: float()
59+
);
60+
}
61+
```
62+
63+
The `invoiceTable()` function returns table `invoice` schema in PHP code using [Array](https://chevere.org/packages/parameter#array) Parameter where each column is represented by another Parameter.
64+
65+
From this you can add your own validation rules on top of generated code.
66+
67+
For example, you limit `quantity` to a range of `100, 200` by adding `max` and `min` arguments. Add a regex to `details` to validate string shape.
68+
69+
```diff
70+
details: union(
71+
null(),
72+
- string()
73+
+ string('/^(?!\s*$)./')
74+
),
75+
-quantity: int(min: 0),
76+
+quantity: int(max: 200, min: 100),
77+
```
78+
79+
Array Parameter object returned by this function can be also used to dynamic interact with one or more of these columns. See [Array Composing](#array-composing) to learn more.
80+
81+
## Creating SQL2P
82+
83+
Create a `SQL2P` instance by passing the SQL and a [Writer](https://chevere.org/packages/writer) instance. On instance creation the SQL is parsed and the writer is used to write the generated code.
84+
85+
```php
86+
use Chevere\SQL2P\SQL2P;
87+
use Chevere\Writer\StreamWriter;
88+
use function Chevere\Filesystem\fileForPath;
89+
use function Chevere\Writer\streamFor;
90+
91+
$schema = __DIR__ . '/schema.sql';
92+
$output = __DIR__ . '/sql2p.php';
93+
$header = <<<PHP
94+
namespace MyNamespace;
95+
PHP;
96+
$sql = file_get_contents($schema);
97+
file_put_contents($output, '');
98+
$stream = streamFor($output, 'w');
99+
$writer = new StreamWriter($stream);
100+
$sql2p = new SQL2P($sql, $writer, $header);
101+
$count = count($sql2p);
102+
echo <<<PLAIN
103+
[{$count} tables] {$output->path()}
104+
105+
PLAIN;
106+
```
107+
108+
## Data validation
109+
110+
Use SQL2P to validate data against table Parameter schema.
111+
112+
For example, on a single fetch result you may get the following array for a database row.
113+
114+
```sql
115+
SELECT * FROM invoice WHERE id = 1
116+
```
117+
118+
```php
119+
$fetch = [
120+
'id' => 1,
121+
'client_id' => 1234,
122+
'datetime' => '2023-10-22 19:58:44',
123+
'details' => null,
124+
'quantity' => 100,
125+
'rate' => 16.5,
126+
'total' => 1650,
127+
];
128+
```
129+
130+
Function `invoiceTable()` can be used to validate `$fetch` by invoking it.
131+
132+
```php
133+
$table = invoiceTable();
134+
$table($fetch); // validation
135+
```
136+
137+
Use `arrayFrom` function to create an array taking only the columns you need.
138+
139+
```sql
140+
SELECT id, total FROM invoice WHERE id = 1
141+
```
142+
143+
```php
144+
use function Chevere\Parameter\arrayFrom;
145+
146+
$fetch = [
147+
'id' => 1,
148+
'total' => 1650,
149+
];
150+
$table = arrayFrom(invoiceTable(), 'id', 'total');
151+
$table($fetch);
152+
```
153+
154+
Use `arguments` function to get typed access to fetched array members.
155+
156+
```php
157+
use function Chevere\Parameter\arguments;
158+
159+
$invoice = arguments($table, $fetch);
160+
$total = $invoice->required('total')->int(); // 1650
161+
```
162+
163+
When fetching multiple rows wrap Array table with [iterable](https://chevere.org/packages/parameter.html#iterable) function.
164+
165+
```sql
166+
SELECT id, total FROM invoice WHERE id > 0
167+
```
168+
169+
```php
170+
$fetchAll = [
171+
0 => [
172+
'id' => 1,
173+
'total' => 1650,
174+
],
175+
1 => [
176+
'id' => 2,
177+
'total' => 1820,
178+
],
179+
];
180+
$iterable = iterable($table);
181+
$iterable($fetchAll);
182+
```
183+
184+
Note that `arguments` function supports iterable.
185+
186+
```php
187+
$invoices = arguments($iterable, $fetchAll);
188+
$secondRow = $invoices->required('1')->array();
189+
```
190+
191+
## Array composing
192+
193+
Parameter provides a set of tools to work with arrays, enabling to dynamically add, remove or modify values. It also enables to compose arrays from other arrays.
194+
195+
For example to add a `total_usd` virtual column to `invoiceTable()`.
196+
197+
```sql
198+
SELECT
199+
id,
200+
total,
201+
total/100 total_usd
202+
FROM invoice WHERE id = 1
203+
```
204+
205+
```php
206+
$fetch = [
207+
'id' => 1,
208+
'total' => 1650,
209+
'total_usd' => 16.5,
210+
];
211+
$table = arrayFrom(invoiceTable(), 'id', 'total');
212+
$table = $table
213+
->withRequired(
214+
total_usd: float(),
215+
);
216+
$table($fetch);
217+
```
218+
219+
When `JOIN` tables you may want to take columns based on joined tables. Use `takeFrom` function to create a iterable with `column => parameter` pairs.
220+
221+
```sql
222+
SELECT
223+
invoice.id,
224+
invoice.total,
225+
client.name,
226+
client.email
227+
FROM invoice
228+
JOIN client ON client.id = invoice.client_id
229+
WHERE invoice.id = 1
230+
```
231+
232+
```php
233+
$fetch = [
234+
'id' => 1,
235+
'total' => 1650,
236+
'name' => 'Rodolfo',
237+
'email' => '[email protected]'
238+
];
239+
$invoice = arrayFrom(invoiceTable(), 'id', 'total');
240+
$client = takeFrom(clientTable(), 'name', 'email');
241+
$table = $invoice->withRequired(...$client);
242+
$table($fetch);
243+
```
244+
245+
For this code `$client` is assigned to an iterable containing `name` and `email` column pairs from `clientTable()`. Then by calling `withRequired` on `$invoice` it gets these columns on spread.

0 commit comments

Comments
 (0)