Compare commits

..

No commits in common. "master" and "3.5.0" have entirely different histories.

5 changed files with 203 additions and 219 deletions

150
README.md
View file

@ -1,36 +1,34 @@
# php-mysql # php-libmysqldriver
This is a simple abstraction library for MySQL DML operations. This library provides abstraction methods for common operations on MySQL-like databases like `SELECT`, `UPDATE`, and `INSERT` using method chaining for the various MySQL features.
For example: For example:
```php ```php
MySQL->from(string $table) MySQL->for(string $table)
->with(?array $model)
->where(?array ...$conditions) ->where(?array ...$conditions)
->order(?array $order_by) ->order(?array $order_by)
->limit(?int $limit = null, ?int $offset = null) ->limit(int|array|null $limit)
->select(string|array|null $columns = null): mysqli_result|bool; ->select(array $columns): array|bool;
``` ```
which would be equivalent to the following in MySQL: which would be equivalent to the following in MySQL:
```sql ```sql
SELECT `columns` FROM `table` WHERE `filter` ORDER BY `order_by` LIMIT `limit`; SELECT $columns FROM $table WHERE $filter ORDER BY $order_by LIMIT $limit;
``` ```
- All methods can be chained in any order (even multiple times) after a [`from()`](#from) as long as a [`select()`](#select), [`insert()`](#insert), [`update()`](#update), or [`delete()`](#delete) is the last method. > [!IMPORTANT]
- Chaining the same method more than once will override its previous value. Passing `null` to any method that accepts it will unset its value completely. > This library is built on top of the PHP [`MySQL Improved`](https://www.php.net/manual/en/book.mysqli.php) extension and requires PHP 8.0 or newer.
## Install from composer ## Install from composer
``` ```
composer require vlw/mysql composer require victorwesterlund/libmysqldriver
``` ```
```php ```php
use vlw\MySQL\MySQL; use libmysqldriver/MySQL;
``` ```
> [!IMPORTANT]
> This library requires the [`MySQL Improved`](https://www.php.net/manual/en/book.mysqli.php) extension and PHP 8.0 or newer.
# Example / Documentation # Example / Documentation
Available statements Available statements
@ -55,7 +53,7 @@ id|beverage_type|beverage_name|beverage_size
3|tea|black|15 3|tea|black|15
```php ```php
use vlw\MySQL\MySQL; use libmysqldriver\MySQL;
// Pass through: https://www.php.net/manual/en/mysqli.construct.php // Pass through: https://www.php.net/manual/en/mysqli.construct.php
$db = new MySQL($host, $user, $pass, $db); $db = new MySQL($host, $user, $pass, $db);
@ -63,30 +61,15 @@ $db = new MySQL($host, $user, $pass, $db);
All executor methods [`select()`](#select), [`update()`](#update), and [`insert()`](#insert) will return a [`mysqli_result`](https://www.php.net/manual/en/class.mysqli-result.php) object or boolean. All executor methods [`select()`](#select), [`update()`](#update), and [`insert()`](#insert) will return a [`mysqli_result`](https://www.php.net/manual/en/class.mysqli-result.php) object or boolean.
# FROM
```php
MySQL->from(
string $table
): self;
```
All queries start by chaining the `from(string $table)` method. This will define which database table the current query should be executed on.
*Example:*
```php
MySQL->from("beverages")->select("beverage_type");
```
# SELECT # SELECT
Chain `MySQL->select()` anywhere after a [`MySQL->from()`](#from) to retrieve columns from a database table. Use `MySQL->select()` to retrieve columns from a database table.
Pass an associative array of strings, CSV string, or null to this method to filter columns. Pass an associative array of strings, CSV string, or null to this method to filter columns.
```php ```php
MySQL->select( MySQL->select(
string|array|null $columns array|string|null $columns
): mysqli_result|bool; ): mysqli_result|bool;
``` ```
@ -94,7 +77,7 @@ In most cases you probably want to select with a constraint. Chain the [`where()
### Example ### Example
```php ```php
$`beverages` = MySQL->from("beverages")->select(["beverage_name", "beverage_size"]); // SELECT `beverage_name`, `beverage_size` FROM beverages $beverages = MySQL->for("beverages")->select(["beverage_name", "beverage_size"]); // SELECT beverage_name, beverage_size FROM beverages
``` ```
``` ```
[ [
@ -110,9 +93,27 @@ $`beverages` = MySQL->from("beverages")->select(["beverage_name", "beverage_size
] ]
``` ```
## Flatten array to single dimension
If you don't want an array of arrays and would instead like to access each key value pair directly. Chain the `MySQL->flatten()` anywhere before `MySQL->select()`.
This will return the key value pairs of the first entry directly.
> **Note**
> This method will not set `LIMIT 1` for you. It is recommended to chain `MySQL->limit(1)` anywhere before `MySQL->select()`. [You can read more about it here](https://github.com/VictorWesterlund/php-libmysqldriver/issues/14)
```php
$coffee = MySQL->for("beverages")->limit(1)->flatten()->select(["beverage_name", "beverage_size"]); // SELECT beverage_name, beverage_size FROM beverages WHERE beverage_type = "coffee" LIMIT 1
```
```php
[
"beverage_name" => "cappuccino",
"beverage_size" => 10
]
```
# INSERT # INSERT
Chain `MySQL->insert()` anywhere after a [`MySQL->from()`](#from) to append a new row to a database table. Use `MySQL->insert()` to append a new row to a database table.
Passing a sequential array to `insert()` will assume that you wish to insert data for all defined columns in the table. Pass an associative array of `[column_name => value]` to INSERT data for specific columns (assuming the other columns have a [DEFAULT](https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html) value defined). Passing a sequential array to `insert()` will assume that you wish to insert data for all defined columns in the table. Pass an associative array of `[column_name => value]` to INSERT data for specific columns (assuming the other columns have a [DEFAULT](https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html) value defined).
@ -120,20 +121,20 @@ Passing a sequential array to `insert()` will assume that you wish to insert dat
MySQL->insert( MySQL->insert(
// Array of values to INSERT // Array of values to INSERT
array $values array $values
): bool ): mysqli_result|bool
// Returns true if row was inserted // Returns true if row was inserted
``` ```
#### Example #### Example
```php ```php
MySQL->from("beverages")->insert([ MySQL->for("beverages")->insert([
null, null,
"coffee", "coffee",
"latte", "latte",
10 10
]); ]);
// INSERT INTO `beverages` VALUES (null, "coffee", "latte", 10); // INSERT INTO beverages VALUES (null, "coffee", "latte", 10);
``` ```
``` ```
true true
@ -141,12 +142,12 @@ true
# DELETE # DELETE
Chain `MySQL->delete()` anywhere after a [`MySQL->from()`](#from) to remove a row or rows from the a database table. Use `MySQL->delete()` to remove a row or rows from the a database table.
```php ```php
MySQL->delete( MySQL->delete(
array ...$conditions array ...$conditions
): bool ): mysqli_result|bool
// Returns true if at least one row was deleted // Returns true if at least one row was deleted
``` ```
@ -155,10 +156,13 @@ This method takes at least one [`MySQL->where()`](#where)-syntaxed argument to d
#### Example #### Example
```php ```php
MySQL->from("beverages")->delete([ MySQL->for("beverages")->insert([
"beverage_name" => "coffee", null,
"coffee",
"latte",
10
]); ]);
// DELETE FROM `beverages` WHERE `beverage_name` = "coffee"; // INSERT INTO beverages VALUES (null, "coffee", "latte", 10);
``` ```
``` ```
true true
@ -166,7 +170,7 @@ true
# UPDATE # UPDATE
Chain `MySQL->update()` anywhere after a [`MySQL->from()`](#from) to modify existing rows in a database table. Modify existing rows with `MySQL->update()`
```php ```php
MySQL->update( MySQL->update(
@ -178,20 +182,18 @@ MySQL->update(
### Example ### Example
```php ```php
MySQL->from("beverages")->update(["beverage_size" => 10]); // UPDATE `beverages` SET `beverage_size` = 10 MySQL->for("beverages")->update(["beverage_size" => 10]); // UPDATE beverages SET beverage_size = 10
``` ```
```php ```php
true true
``` ```
In most cases you probably want to UPDATE against a constaint. Chain a [`where()`](#where) method before [`MySQL->update()`](#update) to set constraints In most cases you probably want to UPDATE against a constaint. Chain a [`where()`](#where) method before `update()` to set constraints
# WHERE # WHERE
Filter a [`MySQL->select()`](#select) or [`MySQL->update()`](#update) method by chaining the `MySQL->where()` method anywhere before it. The [`MySQL->delete()`](#delete) executor method also uses the same syntax for its arguments. Filter a `select()` or `update()` method by chaining the `MySQL->where()` method anywhere before it. The `MySQL->delete()` executor method also uses the same syntax for its arguments.
Each key, value pair will be `AND` constrained against each other.
```php ```php
MySQL->where( MySQL->where(
@ -201,7 +203,7 @@ MySQL->where(
### Example ### Example
```php ```php
$coffee = MySQL->from("beverages")->where(["beverage_type" => "coffee"])->select(["beverage_name", "beverage_size"]); // SELECT `beverage_name`, `beverage_size` FROM `beverages` WHERE (`beverage_type` = "coffee"); $coffee = MySQL->for("beverages")->where(["beverage_type" => "coffee"])->select(["beverage_name", "beverage_size"]); // SELECT beverage_name, beverage_size FROM beverages WHERE (beverage_type = "coffee");
``` ```
```php ```php
[ [
@ -216,7 +218,9 @@ $coffee = MySQL->from("beverages")->where(["beverage_type" => "coffee"])->select
] ]
``` ```
## Capture groups ## Advanced filtering
You can do more detailed filtering by passing more constraints into the same array, or even futher by passing multiple arrays each with filters.
### AND ### AND
@ -229,7 +233,7 @@ MySQL->where([
]); ]);
``` ```
```sql ```sql
WHERE (`beverage_type` = 'coffee' AND `beverage_size` = 15) WHERE (beverage_type = 'coffee' AND beverage_size = 15)
``` ```
### OR ### OR
@ -250,32 +254,13 @@ $filter2 = [
MySQL->where($filter1, $filter2, ...); MySQL->where($filter1, $filter2, ...);
``` ```
```sql ```sql
WHERE (`beverage_type` = 'coffee' AND `beverage_size` = 15) OR (`beverage_type` = 'tea' AND `beverage_name` = 'black') WHERE (beverage_type = 'coffee' AND beverage_size = 15) OR (beverage_type = 'tea' AND beverage_name = 'black')
``` ```
## Define custom operators
By default, all values in an the assoc array passed to `where()` will be treated as an `EQUALS` (=) operator.
```php
MySQL->where(["column" => "euqals_this_value"]);
```
Setting the value of any key to another assoc array will allow for more "advanced" filtering by defining your own [`Operators`](https://github.com/VictorWesterlund/php-libmysqldriver/blob/master/src/Operators.php).
The key of this subarray can be any MySQL operator string, or the **->value** of any case in the [`Operators`](https://github.com/VictorWesterlund/php-libmysqldriver/blob/master/src/Operators.php) enum.
```php
MySQL->where([
"beverage_name" => [
Operators::LIKE->value => "%wildcard_contains%"
]
]);
```
# ORDER BY # ORDER BY
Chain the `MySQL->order()` method before a [`MySQL->select()`](#select) statement to order by a specific column Chain the `order()` method before a `select()` statement to order by a specific column
```php ```php
MySQL->order( MySQL->order(
@ -284,7 +269,7 @@ MySQL->order(
``` ```
```php ```php
$coffee = MySQL->from("beverages")->order(["beverage_name" => "ASC"])->select(["beverage_name", "beverage_size"]); // SELECT `beverage_name`, `beverage_size` FROM `beverages` ORDER BY `beverage_name` ASC $coffee = MySQL->for("beverages")->order(["beverage_name" => "ASC"])->select(["beverage_name", "beverage_size"]); // SELECT beverage_name, beverage_size FROM beverages ORDER BY beverage_name ASC
``` ```
```php ```php
[ [
@ -296,13 +281,13 @@ $coffee = MySQL->from("beverages")->order(["beverage_name" => "ASC"])->select(["
"beverage_name" => "tea", "beverage_name" => "tea",
"beverage_size" => 15 "beverage_size" => 15
], ],
// ...etc for "`beverage_name` = coffee" // ...etc for "beverage_name = coffee"
] ]
``` ```
# LIMIT # LIMIT
Chain the `limit()` method before a [`MySQL->select()`](#select) statement to limit the amount of columns returned Chain the `limit()` method before a `select()` statement to limit the amount of columns returned
```php ```php
MySQL->limit( MySQL->limit(
@ -311,11 +296,14 @@ MySQL->limit(
): self; ): self;
``` ```
> **Note**
> You can also flatten to a single dimensional array from the first entity by chaining [`MySQL->flatten()`](#flatten-array-to-single-dimension)
## Passing a single integer argument ## Passing a single integer argument
This will simply `LIMIT` the results returned to the integer passed This will simply `LIMIT` the results returned to the integer passed
```php ```php
$coffee = MySQL->from("beverages")->limit(1)->select(["beverage_name", "beverage_size"]); // SELECT `beverage_name`, `beverage_size` FROM `beverages` WHERE `beverage_type` = "coffee" LIMIT 1 $coffee = MySQL->for("beverages")->limit(1)->select(["beverage_name", "beverage_size"]); // SELECT beverage_name, beverage_size FROM beverages WHERE beverage_type = "coffee" LIMIT 1
``` ```
```php ```php
[ [
@ -330,7 +318,7 @@ $coffee = MySQL->from("beverages")->limit(1)->select(["beverage_name", "beverage
This will `OFFSET` and `LIMIT` the results returned. The first argument will be the `LIMIT` and the second argument will be its `OFFSET`. This will `OFFSET` and `LIMIT` the results returned. The first argument will be the `LIMIT` and the second argument will be its `OFFSET`.
```php ```php
$coffee = MySQL->from("beverages")->limit(3, 2)->select(["beverage_name", "beverage_size"]); // SELECT `beverage_name`, `beverage_size` FROM `beverages` LIMIT 3 OFFSET 2 $coffee = MySQL->for("beverages")->limit(3, 2)->select(["beverage_name", "beverage_size"]); // SELECT beverage_name, beverage_size FROM beverages LIMIT 3 OFFSET 2
``` ```
```php ```php
[ [
@ -345,3 +333,13 @@ $coffee = MySQL->from("beverages")->limit(3, 2)->select(["beverage_name", "bever
// ...etc // ...etc
] ]
``` ```
----
# Restrict affected/returned database columns to table model
Chain and pass an array to `MySQL->with()` before a `select()`, `update()`, or `insert()` method to limit which columns will be returned/affected. It will use the **values** of the array so it can be either sequential or associative.
**This method will cause `select()`, `update()`, and `insert()` to ignore any columns that are not present in the passed table model.**
You can remove an already set table model by passing `null` to `MySQL->with()`

View file

@ -1,18 +1,18 @@
{ {
"name": "vlw/mysql", "name": "victorwesterlund/libmysqldriver",
"description": "Abstraction library for common MySQL/MariaDB DML operations with php-mysqli", "description": "Abstraction library for common mysqli features",
"type": "library", "type": "library",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-only",
"authors": [ "authors": [
{ {
"name": "Victor Westerlund", "name": "Victor Westerlund",
"email": "victor@vlw.se" "email": "victor.vesterlund@gmail.com"
} }
], ],
"minimum-stability": "dev", "minimum-stability": "dev",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"vlw\\MySQL\\": "src/" "libmysqldriver\\": "src/"
} }
}, },
"require": {} "require": {}

View file

@ -1,29 +1,23 @@
<?php <?php
namespace vlw\MySQL; namespace libmysqldriver;
use Exception; use \Exception;
use mysqli; use \mysqli;
use mysqli_stmt; use \mysqli_stmt;
use mysqli_result; use \mysqli_result;
use vlw\MySQL\Order;
use vlw\MySQL\Operators;
require_once "Order.php";
require_once "Operators.php";
// Interface for MySQL_Driver with abstractions for data manipulation // Interface for MySQL_Driver with abstractions for data manipulation
class MySQL extends mysqli { class MySQL extends mysqli {
public ?array $columns = null; private string $table;
private ?array $model = null;
protected string $table; private bool $flatten = false;
protected ?string $limit = null; private ?string $order_by = null;
protected ?string $order_by = null; private ?string $filter_sql = null;
protected array $filter_columns = []; private array $filter_values = [];
protected array $filter_values = []; private ?string $limit = null;
protected ?string $filter_sql = null;
// Pass constructor arguments to driver // Pass constructor arguments to driver
function __construct() { function __construct() {
@ -35,7 +29,7 @@
*/ */
private function throw_if_no_table() { private function throw_if_no_table() {
if (!isset($this->table)) { if (!$this->table) {
throw new Exception("No table name defined"); throw new Exception("No table name defined");
} }
} }
@ -52,11 +46,15 @@
// Convert all boolean type values to tinyints in array // Convert all boolean type values to tinyints in array
private static function filter_booleans(array $values): array { private static function filter_booleans(array $values): array {
return array_map(fn(mixed $v): mixed => gettype($v) === "boolean" ? self::filter_boolean($v) : $v, $values); return array_map(fn($v): mixed => gettype($v) === "boolean" ? self::filter_boolean($v) : $v, $values);
} }
private static function array_wrap_accents(array $input): array { // Return value(s) that exist in $this->model
return array_map(fn(mixed $v): string => "`{$v}`", $input); private function in_model(string|array $columns): ?array {
// Place string into array
$columns = is_array($columns) ? $columns : [$columns];
// Return columns that exist in table model
return array_filter($columns, fn($col): string => in_array($col, $this->model));
} }
/* /*
@ -66,22 +64,33 @@
*/ */
// Use the following table name // Use the following table name
public function from(string $table): self { public function for(string $table): self {
// Reset all definers when a new query begins
$this->where();
$this->limit();
$this->order();
$this->table = $table; $this->table = $table;
return $this; return $this;
} }
#[\Deprecated( // Restrict query to array of column names
message: "use MySQL->from() instead", public function with(?array $model = null): self {
since: "3.5.7", // Remove table model if empty
)] if (!$model) {
public function for(string $table): self { $this->model = null;
return $this->from($table); return $this;
}
// Reset table model
$this->model = [];
foreach ($model as $k => $v) {
// Column values must be strings
if (!is_string($v)) {
throw new Exception("Key {$k} must have a value of type string");
}
// Append column to model
$this->model[] = $v;
}
return $this;
} }
// Create a WHERE statement from filters // Create a WHERE statement from filters
@ -89,7 +98,6 @@
// Unset filters if null was passed // Unset filters if null was passed
if ($conditions === null) { if ($conditions === null) {
$this->filter_sql = null; $this->filter_sql = null;
$this->filter_columns = null;
$this->filter_values = null; $this->filter_values = null;
return $this; return $this;
@ -108,35 +116,22 @@
} }
// Create SQL string and append values to array for prepared statement // Create SQL string and append values to array for prepared statement
foreach ($condition as $col => $operation) { foreach ($condition as $col => $value) {
$this->filter_columns[] = $col; if ($this->model && !$this->in_model($col)) {
// Assume we want an equals comparison if value is not an array
if (!is_array($operation)) {
$operation = [Operators::EQUALS->value => $operation];
}
// Resolve all operator enum values in inner array
foreach ($operation as $operator => $value) {
// Null values have special syntax
if (is_null($value)) {
// Treat anything that isn't an equals operator as falsy
if ($operator !== Operators::EQUALS->value) {
$filter[] = "`{$col}` IS NOT NULL";
continue; continue;
} }
// Value is null so it does not need to be added to the prepared statement
if (is_null($value)) {
$filter[] = "`{$col}` IS NULL"; $filter[] = "`{$col}` IS NULL";
continue; continue;
} }
// Create SQL for prepared statement // Create SQL for prepared statement
$filter[] = "`{$col}` {$operator} ?"; $filter[] = "`{$col}` = ?";
// Append value to array with all other values // Append value to array with all other values
$values[] = $value; $values[] = $value;
} }
}
// AND together all conditions into a group // AND together all conditions into a group
$filters[] = "(" . implode(" AND ", $filter) . ")"; $filters[] = "(" . implode(" AND ", $filter) . ")";
@ -156,35 +151,50 @@
} }
// SQL LIMIT string // SQL LIMIT string
public function limit(?int $limit = null, ?int $offset = null): self { public function limit(?int $limit, ?int $offset = null): self {
// Unset row limit if null was passed // Unset row limit if null was passed
if ($limit === null) { if ($limit === null) {
$this->limit = null; $this->limit = null;
return $this; return $this;
} }
// Coerce offset to zero if no offset is defined // Set LIMIT without range directly as integer
$offset = $offset ?? 0; if (is_int($limit)) {
$this->limit = $limit;
return $this;
}
// No offset defined, set limit property directly as string
if (is_null($offset)) {
$this->limit = (string) $limit;
return $this;
}
// Set limit and offset as SQL CSV // Set limit and offset as SQL CSV
$this->limit = "{$offset},{$limit}"; $this->limit = "{$offset},{$limit}";
return $this; return $this;
} }
// Flatten returned array to first entity if set
public function flatten(bool $flag = true): self {
$this->flatten = $flag;
return $this;
}
// Return SQL SORT BY string from assoc array of columns and direction // Return SQL SORT BY string from assoc array of columns and direction
public function order(?array $order_by = null): self { public function order(?array $order_by): self {
// Unset row order by if null was passed // Unset row order by if null was passed
if ($order_by === null) { if ($order_by === null) {
$this->order_by = null; $this->order_by = null;
return $this; return $this;
} }
// Assign Order Enum entries from array of arrays<Order|string> // Create CSV from columns
$orders = array_map(fn(Order|string $order): Order => $order instanceof Order ? $order : Order::tryFrom($order), array_values($order_by)); $sql = implode(",", array_keys($order_by));
// Create CSV string with Prepared Statement abbreviations from length of fields array. // Create pipe DSV from values
$sql = array_map(fn(string $column, Order|string $order): string => "`{$column}` " . $order->value, array_keys($order_by), $orders); $sql .= " " . implode("|", array_values($order_by));
$this->order_by = implode(",", $sql); $this->order_by = $sql;
return $this; return $this;
} }
@ -198,10 +208,15 @@
$this->throw_if_no_table(); $this->throw_if_no_table();
// Create array of columns from CSV // Create array of columns from CSV
$this->columns = is_array($columns) || is_null($columns) ? $columns : explode(",", $columns); $columns = is_array($columns) || is_null($columns) ? $columns : explode(",", $columns);
// Filter columns that aren't in the model if defiend
if ($columns && $this->model) {
$columns = $this->in_model($columns);
}
// Create CSV from columns or default to SQL NULL as a string // Create CSV from columns or default to SQL NULL as a string
$columns_sql = $this->columns ? implode(",", self::array_wrap_accents($this->columns)) : "NULL"; $columns_sql = $columns ? implode(",", $columns) : "NULL";
// Create LIMIT statement if argument is defined // Create LIMIT statement if argument is defined
$limit_sql = !is_null($this->limit) ? " LIMIT {$this->limit}" : ""; $limit_sql = !is_null($this->limit) ? " LIMIT {$this->limit}" : "";
@ -213,17 +228,30 @@
$filter_sql = !is_null($this->filter_sql) ? " WHERE {$this->filter_sql}" : ""; $filter_sql = !is_null($this->filter_sql) ? " WHERE {$this->filter_sql}" : "";
// Interpolate components into an SQL SELECT statmenet and execute // Interpolate components into an SQL SELECT statmenet and execute
$sql = "SELECT {$columns_sql} FROM `{$this->table}`{$filter_sql}{$order_by_sql}{$limit_sql}"; $sql = "SELECT {$columns_sql} FROM {$this->table}{$filter_sql}{$order_by_sql}{$limit_sql}";
// Return mysqli_response of matched rows
return $this->execute_query($sql, self::to_list_array($this->filter_values)); // Return array of matched rows
$exec = $this->execute_query($sql, self::to_list_array($this->filter_values));
// Return array if exec was successful. Return as flattened array if flag is set
return empty($exec) || !$this->flatten ? $exec : $exec[0];
} }
// Create Prepared Statement for UPDATE using PRIMARY KEY as anchor // Create Prepared Statement for UPDATE using PRIMARY KEY as anchor
public function update(array $entity): mysqli_result|bool { public function update(array $entity): mysqli_result|bool {
$this->throw_if_no_table(); $this->throw_if_no_table();
// Make constraint for table model if defined
if ($this->model) {
foreach (array_keys($entity) as $col) {
// Throw if column in entity does not exist in defiend table model
if (!in_array($col, $this->model)) {
throw new Exception("Column key '{$col}' does not exist in table model");
}
}
}
// Create CSV string with Prepared Statement abbreviations from length of fields array. // Create CSV string with Prepared Statement abbreviations from length of fields array.
$changes = array_map(fn($column) => "`{$column}` = ?", array_keys($entity)); $changes = array_map(fn($column) => "{$column} = ?", array_keys($entity));
$changes = implode(",", $changes); $changes = implode(",", $changes);
// Get array of SQL WHERE string and filter values // Get array of SQL WHERE string and filter values
@ -238,7 +266,7 @@
} }
// Interpolate components into an SQL UPDATE statement and execute // Interpolate components into an SQL UPDATE statement and execute
$sql = "UPDATE `{$this->table}` SET {$changes} {$filter_sql}"; $sql = "UPDATE {$this->table} SET {$changes} {$filter_sql}";
return $this->execute_query($sql, self::to_list_array($values)); return $this->execute_query($sql, self::to_list_array($values));
} }
@ -246,6 +274,11 @@
public function insert(array $values): mysqli_result|bool { public function insert(array $values): mysqli_result|bool {
$this->throw_if_no_table(); $this->throw_if_no_table();
// A value for each column in table model must be provided
if ($this->model && count($values) !== count($this->model)) {
throw new Exception("Values length does not match columns in model");
}
/* /*
Use array keys from $values as columns to insert if array is associative. Use array keys from $values as columns to insert if array is associative.
Treat statement as an all-columns INSERT if the $values array is sequential. Treat statement as an all-columns INSERT if the $values array is sequential.
@ -259,7 +292,7 @@
$values_stmt = implode(",", array_fill(0, count($values), "?")); $values_stmt = implode(",", array_fill(0, count($values), "?"));
// Interpolate components into an SQL INSERT statement and execute // Interpolate components into an SQL INSERT statement and execute
$sql = "INSERT INTO `{$this->table}` {$columns} VALUES ({$values_stmt})"; $sql = "INSERT INTO {$this->table} {$columns} VALUES ({$values_stmt})";
return $this->execute_query($sql, self::to_list_array($values)); return $this->execute_query($sql, self::to_list_array($values));
} }
@ -267,15 +300,22 @@
public function delete(array ...$conditions): mysqli_result|bool { public function delete(array ...$conditions): mysqli_result|bool {
$this->throw_if_no_table(); $this->throw_if_no_table();
// Set DELETE WHERE conditions from arguments // Make constraint for table model if defined
if ($conditions) { if ($this->model) {
$this->where(...$conditions); foreach ($conditions as $condition) {
foreach (array_keys($condition) as $col) {
// Throw if column in entity does not exist in defiend table model
if (!in_array($col, $this->model)) {
throw new Exception("Column key '{$col}' does not exist in table model");
}
}
}
} }
// Get array of SQL WHERE string and filter values // Set DELETE WHERE conditions from arguments
$filter_sql = !is_null($this->filter_sql) ? " WHERE {$this->filter_sql}" : ""; $this->where(...$conditions);
$sql = "DELETE FROM `{$this->table}`{$filter_sql}"; $sql = "DELETE FROM {$this->table} WHERE {$this->filter_sql}";
return $this->execute_query($sql, self::to_list_array($this->filter_values)); return $this->execute_query($sql, self::to_list_array($this->filter_values));
} }

View file

@ -1,46 +0,0 @@
<?php
namespace vlw\MySQL;
enum Operators: string {
// Logical
case ALL = "ALL";
case AND = "AND";
case ANY = "ANY";
case BETWEEN = "BETWEEN";
case EXISTS = "EXISTS";
case IN = "IN";
case LIKE = "LIKE";
case NOT = "NOT";
case OR = "OR";
case SOME = "SOME";
// Comparison
case EQUALS = "=";
case GT = ">";
case LT = "<";
case GTE = ">=";
case LTE = "<=";
case NOTE = "<>";
// Arithmetic
case ADD = "+";
case SUBTRACT = "-";
case MULTIPLY = "*";
case DIVIDE = "/";
case MODULO = "%";
// Bitwise
case BS_AND = "&";
case BS_OR = "|";
case BS_XOR = "^";
// Compound
case ADDE = "+=";
case SUBE = "-=";
case DIVE = "/=";
case MODE = "%=";
case BS_ANDE = "&=";
case BS_ORE = "|*=";
case BS_XORE = "^-=";
}

View file

@ -1,8 +0,0 @@
<?php
namespace vlw\MySQL;
enum Order: string {
case ASC = "ASC";
case DESC = "DESC";
}