Squashed commit of the following:

commit e65c74797b
Author: Victor Westerlund <victor@vlw.se>
Date:   Thu Jan 30 08:16:27 2025 +0000

    feat: add ORDER BY statement Enum (#41)

    Importable with:
    ```php
    use vlw\MySQL\Order
    ```
    To be used with the `MySQL->order()` method, for example:
    ```php
    $db->for("table")->order(["column" => Order::ASC])->select("*");
    ```

    Reviewed-on: https://codeberg.org/vlw/php-mysql/pulls/41

commit 64c7bae3cf
Author: Victor Westerlund <victor@vlw.se>
Date:   Thu Jan 16 13:53:30 2025 +0000

    feat: add protected array property for where statement columns (#40)

    This PR adds a compliment for the `MySQL->filter_values` property but for filter columns which can be accessed from a protected scope with `MySQL->filter_columns`

    Reviewed-on: https://codeberg.org/vlw/php-mysql/pulls/40

commit d5f1efb9b9
Author: Victor Westerlund <victor@vlw.se>
Date:   Fri Dec 20 10:59:02 2024 +0000

    feat: expose SQL class properties to protected scope (#39)

    Makes sense to make these accessible when extending the MySQL class.

    Reviewed-on: https://codeberg.org/vlw/php-mysql/pulls/39

commit 619f43b3bf
Author: vlw <victor@vlw.se>
Date:   Wed Sep 25 13:28:15 2024 +0000

    fix(doc): remove reference to removed method `flatten()` from README (#38)

    Closes #14

    Reviewed-on: https://codeberg.org/vlw/php-mysql/pulls/38
    Co-authored-by: vlw <victor@vlw.se>
    Co-committed-by: vlw <victor@vlw.se>

commit 1727247fa7
Author: vlw <victor@vlw.se>
Date:   Wed Sep 25 13:27:55 2024 +0000

    fix: remove `where()` method for database models (#37)

    This PR removes the `where()` method which I don't think is particularly useful and also very untested since I don't use it personally at all. It's also probably better to do in-model checking for table columns **before** sending it off to this library when required anyways.

    Reviewed-on: https://codeberg.org/vlw/php-mysql/pulls/37
    Co-authored-by: vlw <victor@vlw.se>
    Co-committed-by: vlw <victor@vlw.se>

commit a536a3bec4
Author: vlw <victor@vlw.se>
Date:   Wed Sep 25 13:27:20 2024 +0000

    chore: rename of this package to "php-mysql" (#36)

    The name I gave this years ago "libmysqldriver" never sat right with me.. okay it might be considered a library for mysql but it's *definitely* not a driver.

    Reviewed-on: https://codeberg.org/vlw/php-mysql/pulls/36
    Co-authored-by: vlw <victor@vlw.se>
    Co-committed-by: vlw <victor@vlw.se>
This commit is contained in:
Victor Westerlund 2025-01-30 10:14:48 +01:00
parent 1da21b4b33
commit 78887576ef
5 changed files with 24 additions and 124 deletions

View file

@ -1,11 +1,10 @@
# php-libmysqldriver # php-mysql
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. This is a simple abstraction library for MySQL DML operations.
For example: For example:
```php ```php
MySQL->for(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|array|null $limit) ->limit(int|array|null $limit)
@ -17,16 +16,16 @@ SELECT $columns FROM $table WHERE $filter ORDER BY $order_by LIMIT $limit;
``` ```
> [!IMPORTANT] > [!IMPORTANT]
> 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. > This library requires the [`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 victorwesterlund/libmysqldriver composer require vlw/mysql
``` ```
```php ```php
use libmysqldriver/MySQL; use vlw\MySQL\MySQL;
``` ```
# Example / Documentation # Example / Documentation
@ -53,7 +52,7 @@ id|beverage_type|beverage_name|beverage_size
3|tea|black|15 3|tea|black|15
```php ```php
use libmysqldriver\MySQL; use vlw\MySQL\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);
@ -108,24 +107,6 @@ $beverages = MySQL->for("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->for()`](#for) to append a new row to a database table. Chain `MySQL->insert()` anywhere after a [`MySQL->for()`](#for) to append a new row to a database table.
@ -330,9 +311,6 @@ 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
@ -367,13 +345,3 @@ $coffee = MySQL->for("beverages")->limit(3, 2)->select(["beverage_name", "bevera
// ...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": "victorwesterlund/libmysqldriver", "name": "vlw/mysql",
"description": "Abstraction library for common mysqli features", "description": "Abstraction library for common MySQL/MariaDB DML operations with php-mysqli",
"type": "library", "type": "library",
"license": "GPL-3.0-only", "license": "GPL-3.0-or-later",
"authors": [ "authors": [
{ {
"name": "Victor Westerlund", "name": "Victor Westerlund",
"email": "victor.vesterlund@gmail.com" "email": "victor@vlw.se"
} }
], ],
"minimum-stability": "dev", "minimum-stability": "dev",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"libmysqldriver\\": "src/" "vlw\\MySQL\\": "src/"
} }
}, },
"require": {} "require": {}

View file

@ -1,6 +1,6 @@
<?php <?php
namespace libmysqldriver; namespace vlw\MySQL;
use Exception; use Exception;
@ -8,24 +8,21 @@
use mysqli_stmt; use mysqli_stmt;
use mysqli_result; use mysqli_result;
use libmysqldriver\Order; use vlw\MySQL\Operators;
use libmysqldriver\Operators;
require_once "Order.php"; require_once "Order.php";
require_once "Operators.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 {
private string $table;
private ?array $model = null;
private ?string $limit = null;
private ?string $order_by = null;
private array $filter_values = [];
private ?string $filter_sql = null;
// Array of last SELECT-ed columns
public ?array $columns = null; public ?array $columns = null;
protected string $table;
protected ?string $limit = null;
protected ?string $order_by = null;
protected array $filter_columns = [];
protected array $filter_values = [];
protected ?string $filter_sql = null;
// Pass constructor arguments to driver // Pass constructor arguments to driver
function __construct() { function __construct() {
@ -61,14 +58,6 @@
return array_map(fn(mixed $v): string => "`{$v}`", $input); return array_map(fn(mixed $v): string => "`{$v}`", $input);
} }
// Return value(s) that exist in $this->model
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));
}
/* /*
# Definers # Definers
These methods are used to build an SQL query by chaining methods together. These methods are used to build an SQL query by chaining methods together.
@ -86,35 +75,12 @@
return $this; return $this;
} }
// Restrict query to array of column names
public function with(?array $model = null): self {
// Remove table model if empty
if (!$model) {
$this->model = null;
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
public function where(?array ...$conditions): self { public function where(?array ...$conditions): self {
// 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;
@ -134,9 +100,7 @@
// 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 => $operation) {
if ($this->model && !$this->in_model($col)) { $this->filter_columns[] = $col;
continue;
}
// Assume we want an equals comparison if value is not an array // Assume we want an equals comparison if value is not an array
if (!is_array($operation)) { if (!is_array($operation)) {
@ -237,11 +201,6 @@
// Create array of columns from CSV // Create array of columns from CSV
$this->columns = is_array($columns) || is_null($columns) ? $columns : explode(",", $columns); $this->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($this->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 = $this->columns ? implode(",", self::array_wrap_accents($this->columns)) : "NULL";
@ -264,16 +223,6 @@
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);
@ -298,11 +247,6 @@
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.
@ -324,18 +268,6 @@
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();
// Make constraint for table model if defined
if ($this->model) {
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");
}
}
}
}
// Set DELETE WHERE conditions from arguments // Set DELETE WHERE conditions from arguments
$this->where(...$conditions); $this->where(...$conditions);

View file

@ -1,6 +1,6 @@
<?php <?php
namespace libmysqldriver; namespace vlw\MySQL;
enum Operators: string { enum Operators: string {
// Logical // Logical

View file

@ -1,6 +1,6 @@
<?php <?php
namespace libmysqldriver; namespace vlw\MySQL;
enum Order: string { enum Order: string {
case ASC = "ASC"; case ASC = "ASC";