chore: import upstream snapshot with attribution
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# Handling forms with a variable number of fields
|
||||
|
||||
This example shows how to handle forms with a number of fields
|
||||
that is not known in advance, but depends on the contents of the database.
|
||||
|
||||
## The model
|
||||
|
||||
We have a database with a table
|
||||
`products` that contains the products,
|
||||
a table `orders` that contains the orders,
|
||||
and a table `order_items` that contains the items of each order.
|
||||
|
||||
We want to display a form to create a new order, with a field for each product.
|
||||
|
||||
We cannot know in advance how many products there are,
|
||||
so we cannot write a simple insert statement for `orders_items`,
|
||||
like
|
||||
|
||||
```sql
|
||||
INSERT INTO order_items (product_id, quantity)
|
||||
VALUES (:product_1, :product_1_quantity);
|
||||
```
|
||||
|
||||
Instead, we use a single POST variable of type array to store the
|
||||
product IDs and quantities, like so:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
'product_quantity[]' AS name,
|
||||
'Quantity of ' || name AS label
|
||||
FROM products
|
||||
```
|
||||
|
||||
And then we parse the array in SQLite with the [JSON_EACH](https://www.sqlite.org/json1.html#jeach) function.
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||
@@ -0,0 +1,8 @@
|
||||
services:
|
||||
web:
|
||||
image: lovasoa/sqlpage:main
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- .:/var/www
|
||||
- ./sqlpage:/etc/sqlpage
|
||||
@@ -0,0 +1,15 @@
|
||||
SELECT 'shell' AS component, 'Products' AS title;
|
||||
|
||||
SELECT 'list' AS component, 'Products' AS title;
|
||||
SELECT 'Add a new product' AS title,
|
||||
'red' AS color,
|
||||
'new_product_form.sql' AS link,
|
||||
TRUE AS active;
|
||||
SELECT 'Pass an order' AS title,
|
||||
'blue' AS color,
|
||||
'order_form.sql' AS link,
|
||||
TRUE AS active;
|
||||
SELECT
|
||||
name AS title,
|
||||
price || '€' AS description
|
||||
FROM products;
|
||||
@@ -0,0 +1,8 @@
|
||||
SELECT 'shell' AS component, 'New product' AS title;
|
||||
|
||||
SELECT 'form' as component,
|
||||
'New product' as title,
|
||||
'Create product' as validate,
|
||||
'new_product_insert.sql' as action;
|
||||
SELECT 'Name' as name;
|
||||
SELECT 'Price' as name, 'number' as type;
|
||||
@@ -0,0 +1,6 @@
|
||||
INSERT INTO products (name, price)
|
||||
VALUES (:Name, :Price)
|
||||
RETURNING
|
||||
'redirect' AS component,
|
||||
'index.sql' AS link
|
||||
;
|
||||
@@ -0,0 +1,18 @@
|
||||
SELECT 'shell' AS component, 'Order' AS title;
|
||||
|
||||
SELECT 'form' as component,
|
||||
'Pass an order' as title,
|
||||
'Order' as validate,
|
||||
'order_insert.sql' as action;
|
||||
|
||||
SELECT 'Name' as name, 'Your full name' AS placeholder;
|
||||
SELECT 'Email' as name, 'Your email address' AS placeholder;
|
||||
SELECT id AS name,
|
||||
'Quantity of ' || name || '' AS label,
|
||||
'Number of ' || name || ' you wish to order, for ' || price || ' € each.' AS description,
|
||||
'number' AS type,
|
||||
1 AS step,
|
||||
0 as min,
|
||||
0 as value
|
||||
FROM products
|
||||
ORDER BY id;
|
||||
@@ -0,0 +1,12 @@
|
||||
INSERT INTO orders(customer_name, customer_email)
|
||||
VALUES (:Name, :Email);
|
||||
|
||||
INSERT INTO order_items(order_id, quantity, product_id)
|
||||
SELECT
|
||||
last_insert_rowid() AS order_id, -- The id of the order we just inserted. Requires SQLPage v0.9.0 or later.
|
||||
CAST(value AS INTEGER) AS quantity,
|
||||
CAST(key AS INTEGER) AS product_id -- extracts converts the field name into a number
|
||||
FROM JSON_EACH(sqlpage.variables('post')) -- Iterates on all posted variables. Requires SQLPage v0.15.0 or later
|
||||
WHERE product_id > 0 -- we used the product id as a variable name in the product quatntity form fields
|
||||
and CAST(value AS INTEGER) > 0
|
||||
RETURNING 'redirect' as component, 'orders.sql?id=' || order_id as link;
|
||||
@@ -0,0 +1,28 @@
|
||||
SELECT 'alert' as component,
|
||||
format('Thanks %s!', customer_name) as title,
|
||||
'analyze' as icon,
|
||||
'teal' as color,
|
||||
format('Your order is being processed.
|
||||
You will shortly receive an email on %s with a receipt.',
|
||||
customer_email) as description
|
||||
from orders where id = $id;
|
||||
|
||||
SELECT 'index.sql' as link,
|
||||
'Back to homepage' as title;
|
||||
|
||||
SELECT 'list' AS component,
|
||||
'Order summary' AS title;
|
||||
SELECT
|
||||
quantity || ' x ' || name AS title,
|
||||
'Subtotal: ' || quantity || ' x ' || price || ' € = ' || (quantity * price) || ' €' AS description
|
||||
FROM order_items
|
||||
INNER JOIN products ON products.id = order_items.product_id
|
||||
WHERE order_id = $id;
|
||||
|
||||
SELECT
|
||||
'Total: ' || SUM(quantity * price) || ' €' AS title,
|
||||
'red' AS color,
|
||||
TRUE AS active
|
||||
FROM order_items
|
||||
INNER JOIN products ON products.id = order_items.product_id
|
||||
WHERE order_id = $id;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
@@ -0,0 +1,21 @@
|
||||
CREATE TABLE products (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
price DECIMAL(10, 2) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE orders (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
customer_name VARCHAR(255) NOT NULL,
|
||||
customer_email VARCHAR(255) NOT NULL,
|
||||
date_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE order_items (
|
||||
order_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
quantity INTEGER NOT NULL,
|
||||
FOREIGN KEY (order_id) REFERENCES orders (id),
|
||||
FOREIGN KEY (product_id) REFERENCES products (id),
|
||||
PRIMARY KEY (order_id, product_id)
|
||||
);
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- Insert statements to populate the 'products' table with unusual products
|
||||
INSERT INTO products (name, price) VALUES ('Invisible Umbrella', 99.99);
|
||||
INSERT INTO products (name, price) VALUES ('Silent Alarm Clock', 49.95);
|
||||
INSERT INTO products (name, price) VALUES ('Pet Rock 2.0', 19.99);
|
||||
INSERT INTO products (name, price) VALUES ('Chocolate Teapot', 39.99);
|
||||
INSERT INTO products (name, price) VALUES ('Square Wheels for Your Bicycle', 29.99);
|
||||
@@ -0,0 +1,34 @@
|
||||
GET http://localhost:8080/order_form.sql
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body contains "Pass an order"
|
||||
body contains "Quantity of Invisible Umbrella"
|
||||
body contains "Quantity of Silent Alarm Clock"
|
||||
body contains "Quantity of Pet Rock 2.0"
|
||||
body not contains "An error occurred"
|
||||
|
||||
POST http://localhost:8080/order_insert.sql
|
||||
[FormParams]
|
||||
Name: Ada Lovelace
|
||||
Email: ada@example.com
|
||||
1: 2
|
||||
2: 1
|
||||
3: 0
|
||||
HTTP 302
|
||||
[Captures]
|
||||
location: header "Location"
|
||||
[Asserts]
|
||||
header "Location" matches "^orders\\.sql\\?id=\\d+$"
|
||||
|
||||
GET http://localhost:8080/{{location}}
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
body contains "Thanks Ada Lovelace!"
|
||||
body contains "2 x Invisible Umbrella"
|
||||
body contains "Subtotal: 2 x 99.99"
|
||||
body contains "199.98"
|
||||
body contains "1 x Silent Alarm Clock"
|
||||
body contains "Subtotal: 1 x 49.95"
|
||||
body contains "Total: 249.93 €"
|
||||
body not contains "Pet Rock 2.0"
|
||||
body not contains "An error occurred"
|
||||
Reference in New Issue
Block a user