From 0d9c524222f7d4f5f31026776b6ccd11a1e26cbf Mon Sep 17 00:00:00 2001 From: Riculum Date: Fri, 15 Apr 2022 11:37:59 +0200 Subject: [PATCH] Initial commit --- LICENSE | 2 +- README.md | 119 ++++++++++- composer.json | 21 ++ composer.lock | 482 ++++++++++++++++++++++++++++++++++++++++++++ config/.env.example | 4 + core/Database.php | 124 ++++++++++++ 6 files changed, 750 insertions(+), 2 deletions(-) create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 config/.env.example create mode 100644 core/Database.php diff --git a/LICENSE b/LICENSE index f436520..dc4a64b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Riculum +Copyright (c) 2021 Riculum Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8b25572..0267e32 100644 --- a/README.md +++ b/README.md @@ -1 +1,118 @@ -# PHP-PDO \ No newline at end of file +# PHP-PDO +A complete database toolkit written in PHP to handle PDO statements + +## Installation +Use the package manager [composer](https://getcomposer.org) to install the library +```bash +composer require riculum/php-pdo +``` + +## Initial setup + +### Credentials +The basic database settings can be set through environment variables. Add a `.env` file in the root of your project. Make sure the `.env` file is added to your `.gitignore` so it is not checked-in the code. By default, the library looks for the following variables: + +* DB_HOST +* DB_NAME +* DB_USERNAME +* DB_PASSWORD + +More information how to use environment variables [here](https://github.com/vlucas/phpdotenv) + +### Configuration +Import vendor/autoload.php and load the `.env` settings +```php +require_once 'vendor/autoload.php'; + +use Database\Core\Database as DB; + +$dotenv = Dotenv\Dotenv::createImmutable(__DIR__); +$dotenv->load(); +``` + +## Statements +The `select` statement is used to return an array containing all the result set rows +```php +try { + $user = DB::select('SELECT * FROM my_table WHERE firstname = ?', ['John']); +} catch (PDOException $e) { + echo $e->getMessage(); +} +``` + +The `single` statement is used to fetch the next row from a result set +```php +try { + $user = DB::single('SELECT * FROM my_table WHERE firstname = ?', ['John']); +} catch (PDOException $e) { + echo $e->getMessage(); +} +``` + +The `insert` statement is used to insert new records in a table +```php +try { + $id = DB::insert('INSERT INTO my_table (firstname, lastname) VALUES (?,?)', ['John', 'Doe']); +} catch (PDOException $e) { + echo $e->getMessage(); +} +``` + +The `insertAssoc` statement is used to insert new records in a table using an associative array +```php +$data = [ + 'firstname' => 'John', + 'lastname' => 'Doe' +]; + +try { + $id = DB::insertAssoc('my_table', $data); +} catch (PDOException $e) { + echo $e->getMessage(); +} +``` + +The `update` statement is used to modify the existing records in a table +```php +try { + DB::update('UPDATE my_table SET firstname = ? WHERE lastname = ?', ['John', 'Doe']); +} catch (PDOException $e) { + echo $e->getMessage(); +} +``` + +The `delete` statement is used to delete existing records in a table +```php +try { + DB::delete('DELETE FROM my_table WHERE firstname = ?', ['John']); +} catch (PDOException $e) { + echo $e->getMessage(); +} +``` + +Use `statement` for other operations not mentioned +```php +try { + DB::statement('DROP TABLE my_table'); +} catch (PDOException $e) { + echo $e->getMessage(); +} +``` + +### Transactions +Transaction are used to run a series of operations within an entity. If an exception is thrown between `beginTransaction` and `commit`, the transaction will automatically be rolled back. +```php +$data = [ + 'firstname' => 'John', + 'lastname' => 'Doe' +]; + +try { + DB::beginTransaction(); + $id = DB::insertAssoc('my_table', $data); + DB::update('UPDATE my_table SET firstname = ?, lastname = ? WHERE id = ?', ['Jane', 'Doe', $id]); + DB::commit(); +} catch (PDOException $e) { + echo $e->getMessage(); +} +``` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..cd03a22 --- /dev/null +++ b/composer.json @@ -0,0 +1,21 @@ +{ + "name": "riculum/php-pdo", + "description": "A complete database toolkit written in PHP to handle MySQL statements", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Riculum", + "email": "riculum@outlook.com" + } + ], + "autoload": { + "psr-4": { + "Database\\": "./" + } + }, + "require": { + "php": ">=7.4.0", + "vlucas/phpdotenv": "^5.3" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..ca5e268 --- /dev/null +++ b/composer.lock @@ -0,0 +1,482 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "44bd068f8653a5edeace39ae61ddff62", + "packages": [ + { + "name": "graham-campbell/result-type", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "0690bde05318336c7221785f2a932467f98b64ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", + "reference": "0690bde05318336c7221785f2a932467f98b64ca", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "phpoption/phpoption": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2021-11-21T21:41:47+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2021-12-04T23:24:31+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "30885182c981ab175d4d034db0f6f469898070ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-20T20:35:02+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-11-30T18:21:41+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", + "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-09-13T13:58:33+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.4.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2021-12-12T23:22:04+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.4.0" + }, + "platform-dev": [], + "plugin-api-version": "2.1.0" +} diff --git a/config/.env.example b/config/.env.example new file mode 100644 index 0000000..844cd23 --- /dev/null +++ b/config/.env.example @@ -0,0 +1,4 @@ +DB_HOST= +DB_NAME= +DB_USERNAME= +DB_PASSWORD= \ No newline at end of file diff --git a/core/Database.php b/core/Database.php new file mode 100644 index 0000000..b573d54 --- /dev/null +++ b/core/Database.php @@ -0,0 +1,124 @@ +setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + self::$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + } + + public static function beginTransaction() + { + self::connect(); + self::$PDO->beginTransaction(); + } + + public static function commit() + { + self::$PDO->commit(); + self::$PDO = null; + } + + public static function delete(string $query, $params = []) + { + self::connect(); + $statement = self::$PDO->prepare($query); + $statement->execute($params); + } + + public static function insert(string $query, $params = []): int + { + self::connect(); + $statement = self::$PDO->prepare($query); + $statement->execute($params); + return self::$PDO->lastInsertId(); + } + + public static function insertAssoc(string $table, $params = []): int + { + self::connect(); + $fields = array_keys($params); + $values = array_values($params); + $fieldList = implode(',', $fields); + $qs = str_repeat("?,", count($fields) - 1); + + $sql = "INSERT INTO $table ($fieldList) values($qs?)"; + $statement = self::$PDO->prepare($sql); + $statement->execute($values); + + return self::$PDO->lastInsertId(); + } + + public static function select(string $query, $params = [], $mode = PDO::FETCH_ASSOC): ?array + { + self::connect(); + $statement = self::$PDO->prepare($query); + $statement->execute($params); + return $statement->fetchAll($mode) ?: []; + } + + public static function single(string $query, $params = [], $mode = PDO::FETCH_ASSOC): ?array + { + self::connect(); + $statement = self::$PDO->prepare($query); + $statement->execute($params); + return $statement->fetch($mode) ?: []; + } + + public static function statement(string $query, $params = []) + { + self::connect(); + $statement = self::$PDO->prepare($query); + $statement->execute($params); + } + + + public static function update(string $query, $params = []) + { + self::connect(); + $statement = self::$PDO->prepare($query); + $statement->execute($params); + } + + public static function updateAssoc(string $table, array &$params, array $conditions) + { + self::connect(); + $qs = ""; + $keys = array_keys($params); + + $i = 0; + foreach ($keys as $key => $value) { + if (++$i !== count($keys)) { + $qs .= $value . " = :" . $value . ", "; + } else { + $qs .= $value . " = :" . $value; + } + } + + $cs = ""; + + if (count($conditions) == count($conditions, COUNT_RECURSIVE)) { + $cs = $conditions['key'] . " " . $conditions['operator'] . " :" . $conditions['key']; + $params[$conditions['key']] = $conditions['value']; + } else { + foreach ($conditions as $condition) { + $cs .= $condition['key'] . " " . $condition['operator'] . " :" . $condition['key'] . " " . $condition['condition'] . " "; + $params[$condition['key']] = $condition['value']; + } + } + + $sql = "UPDATE $table SET $qs WHERE $cs"; + + $statement = self::$PDO->prepare($sql); + $statement->execute($params); + } +}