{"id":19326684,"url":"https://github.com/sroehrl/neoan3-db","last_synced_at":"2025-04-22T20:32:54.035Z","repository":{"id":33721544,"uuid":"161001769","full_name":"sroehrl/neoan3-db","owner":"sroehrl","description":"PHP mySQLi wrapper for rapid \u0026 secure development","archived":false,"fork":false,"pushed_at":"2023-01-18T12:57:27.000Z","size":123,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-02T04:03:05.358Z","etag":null,"topics":["database-wrapper","library","mysql","mysqli","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sroehrl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-09T03:49:06.000Z","updated_at":"2023-02-12T17:01:10.000Z","dependencies_parsed_at":"2023-02-10T15:01:43.015Z","dependency_job_id":null,"html_url":"https://github.com/sroehrl/neoan3-db","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sroehrl%2Fneoan3-db","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sroehrl%2Fneoan3-db/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sroehrl%2Fneoan3-db/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sroehrl%2Fneoan3-db/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sroehrl","download_url":"https://codeload.github.com/sroehrl/neoan3-db/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250318984,"owners_count":21411026,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database-wrapper","library","mysql","mysqli","php"],"created_at":"2024-11-10T02:14:30.244Z","updated_at":"2025-04-22T20:32:53.788Z","avatar_url":"https://github.com/sroehrl.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# neoan3-db\n\nneoan3 app for mysqli connectivity\n\n- opinionated (yet configurable)\n- rapid development\n- secure (prepared statements \u0026 additional security)\n- \"plug \u0026 play\" in any (if any) framework\n\nDesigned for [neoan3](https://github.com/sroehrl/neoan3), but works as standalone\n\n## Prepared Statements\n\nAll queries are performed as prepared statements, running through additional security to sanitize columns.\n \n\u003e Throughout this file you will find representation of SQL-functionality that uses a simplified SQL-logic.\nThese examples are **NOT** the queries actually performed by the wrapper, but meant to provide a solid understanding of\nthe capabilities of this wrapper.\n\n## Quick Start\n\n### Simple SELECT\n```PHP\n// SELECT name FROM user WHERE id = 1\n// returns e.g.: [0=\u003e['name'=\u003e'Adam']]\n\n$user = Db::easy('user.name',['id'=\u003e1]);\n ```\n### Simple INSERT\n```PHP\n// INSERT INTO user(name,email) VALUES('Sam','sam@sam.example')\n\n$insert = ['name'=\u003e'Sam','email'=\u003e'sam@sam.example'];\n$newId = Db::user($insert);\n```\n### Simple UPDATE\n```PHP\n// UPDATE user SET name = 'Sam', email = 'sam@sam.example' WHERE id = 1\n\n$update = ['name'=\u003e'Sam','email'=\u003e'sam@sam.example'];\nDb::user($update, ['id'=\u003e1]);\n```\n\nSee test/test.php for some more quick start examples and/or dive into learning:\n\n\n[![Watch the video](https://img.youtube.com/vi/2kCGESctStg/hqdefault.jpg)](https://youtu.be/2kCGESctStg)\n\n\n## Installation\n`composer require neoan3-apps/db`\n\n```PHP\n\nrequire dirname(__FILE__).'/vendor/autoload.php'\n\nuse Neoan3\\Apps\\Db;\n\nDb::setEnvironment([\n    'name' =\u003e 'your_db',\n    'user' =\u003e 'root',\n    'password' =\u003e 'Som3S3cur3Pa55word'\n])\n\n/*\n*    OR per defines:\n*    define('db_host','localhost');\n*    define('db_name','yourDB');\n*    define('db_user','root');\n*    define('db_password','Som3S3cur3Pa55word');\n*/\n\ntry {\n    $test = Db::ask('\u003eNOW() as now'); \n} catch(DbExeption $e){\n    die($e-\u003egetMessage());\n}\n\n\n/*\n*    $test: [0=\u003e['now'=\u003e'2019-01-01 12:12:12']]\n*/\n\n```\n\n\n\n### Environment variables\n\n| Define | | Default |\n|--------|--------|--------|\n| db_host | usually \"localhost\" | 'localhost' |\n| db_name | Name of your database | not set / required |\n| db_user | Name of db-user | 'root' |\n| db_password | Password for user db_user | '' (empty) |\n| db_assumes_uuid | If true, the app auto-generates uuids | false |\n| db_app_root | Will read the define 'path' if neoan3 is used  | /parent/of/vendor/folder/ |\n| db_file_location | folder of SQL-files relative to app_root  | 'component' |\n| db_filter_characters | filters table-names \u0026 array-keys  | '/[^a-zA-Z\\_\\\\^\\\\.\\s\\*]/' |\n| db_casing | *camel* or *snake* for column names | 'snake' |\n| db_port | (int) port number | 3306 |\n| db_debug | When set to true, SQL is not executed | false |\n| db_dev_errors | When set to true, error reporting exposes handled values | false |\n\nEnvironment variables can either be set as global constants or using Db::setEnvironment()\n\n```PHP\n/*\n* When using Db::setEnvironment() the prepended 'db_' is ommitted.\n*/\n\n// set single variable:\nDb::setEnvironment('name','test_db');\n\n// set multiple variables:\nDb::setEnvironment(['name'=\u003e'test_db','password'=\u003e'FooBar']);\n```\n\n We recommend defining these values in your frame when using neoan3.\n\n### Conventions\n\nThis tool was created with the recommended neoan3 database structure in mind.\nAs such, the following assumptions are made for best usability (auto-joins etc.).\n( See [environment variables](#environment-variables) ):\n\n- tables use snake_case naming\n- columns use snake_case naming OR cameCase naming \n- primary keys are either int(11) auto_incremented OR binary(16)\n- primary keys are called \"id\", foreign relations are referred to as [table_name]_id OR [table_nameId] \n- when handling rows marked as deleted, the field name must be \"delete_date\" OR \"deleteDate\"  (type can be DATE or DATETIME)\n\n## Getting started\n### Db::easy($selectorString [, $conditionArray, $callFunctions, $debug])\nThe easy-function converts a string into a prepared statement and executes it.\nIt returns an array of associative arrays. The easy-markup is a simplified selector-string representing your database-structure.\n\n| example | SQL |\n| --- | --- |\n|`Db::easy('user.first_name user.last_name',['gender'=\u003e'female']);`| `SELECT user.first_name, user.last_name FROM user WHERE gender = ?` 's' 'female' |\n|`Db::easy('user.* user_email.email');` | `SELECT user.*, user_email.email FROM user JOIN user_email on user_email.user_id = user.id` |\n\nSee [operandi](#conditional-modifiers-operandi) \u0026 [selectandi](#value-modifiers-selectandi) for added complexity\n\n### Db::ask($param1 [, $param1, $param1])\nThe ask-function can execute queries based on how it is used. \nIt is best to start understanding the examples below.\nIt returns an array of associative arrays.\n\n| Action | example | SQL \u0026 binding |\n|---| --- | --- |\n|INSERT|`Db::ask('user',['first_name'=\u003e'Richard']);`| `INSERT INTO user(first_name) VALUES(?)` 's' 'Richard' |\n|UPDATE|`Db::ask('user',['first_name'=\u003e'Richard'],['last_name'=\u003e'Hawk']);` | `UPDATE user SET first_name = ? WHERE last_name = ?` 'ss' 'Richard' 'Hawk' |\n|ANY(inline)| `Db::ask('\u003eSELECT * FROM user WHERE first_name = {{first_name}}',['first_name'=\u003e'Richard'])` | `SELECT * FROM user WHERE first_name LIKE ?` 's' 'Richard'|\n|ANY(file)| `Db::ask('/user',['name'=\u003e'Richard Hawk'])` | `SELECT * FROM user WHERE CONCAT_WS(' ',first_name,last_name) LIKE ?` 's' 'Richard Hawk'|\n\n/component/user/user.sql:\n\n```SQL\nSELECT * FROM user WHERE CONCAT_WS(' ',first_name,last_name) LIKE {{name}}\n```\n\nWith the ANY-action, SQL can be managed in editor-friendly formats. \nBy default, the locator expects the neoan3 folder \"component\" (see Environment variables) and understands to following format:\n\n/[file] = '/[db_file_location]/[file]/[file].sql' (like above)\n\nor deeper variations like:\n\n/[folder]/[file] = '/[db_file_location]/[folder]/[file].sql'\n\nThere are no considerations to be made regarding order of parameters. Naming of the target using curly brackets will ensure that only matching array keys are used.\n\nThis makes directly handling user-input save.\n\n### Magic Method Call\n\nSo why does the Db::ask have such a strange name? Because the ask-function is something that you don't **have to** worry about.\nIt is what happens under the hood and can be replaced by calling it using magic methods.\n\n| Ask | Using magic method call | simplified SQL logic |\n|---|---|---|\n|`Db::ask('user',['id'=\u003e'1'])`| `Db::user(['id'=\u003e'1'])` |`INSERT INTO user (id) VALUES(1)`|\n|`Db::ask('user',['name'=\u003e'Sam'],['id'=\u003e1])`| `Db::user(['name'=\u003e'Sam'],['id'=\u003e'1'])` |`UPDATE user SET name = \"Sam\" WHERE id = 1`|\n\n### As-declaration\nHandling field-name modifications.\n\n| Example | simplified SQL logic |\n|---|---|\n|`Db::easy('user.name:username')`|`SELECT user.name as username FROM user`|\n\n### Conditional modifiers (operandi)\nCommon condition modifications can be applied by string-manipulation of the condition-array.\n\n| Example | simplified SQL logic |\n|---|---|\n|`Db::easy('user.*',['delete_date'=\u003e'!'])` | `SELECT * FROM user WHERE delete_date IS NOT NULL`|\n|`Db::easy('user.*',['delete_date'=\u003e''])` or `db::easy('user.*',['^delete_date'])` | `SELECT * FROM user WHERE delete_date IS NULL`|\n|`Db::easy('user.*',['age'=\u003e'\u003e30'])` | `SELECT * FROM user WHERE age \u003e 30`|\n|`Db::easy('user.*',['id'=\u003e'$123s..'])` | `SELECT * FROM user WHERE id = UNHEX(123s..)` (convert hex to binary)|\n|`Db::easy('user.*',['delete_date'=\u003e'.'])` | `SELECT * FROM user WHERE delete_date = NOW()`|\n\n\u003e But what if the value passed to an operandi starts with a modifier?\nYou can prepend the modifier '=' (equal) to prevent operandi from triggering! e.g.: \n\n\u003e`Db::ask('password',['password'=\u003e'='.password_hash('123456', PASSWORD_DEFAULT)])`\n\n### Value modifiers (selectandi)\nCommon value-modifications can be applied by string-manipulation of the select-statement.\nThese modifiers should be used with the \"as-declaration\"\n\n| Example | simplified SQL logic |\n|---|---|\n|`Db::easy('#user.insert_date:inserted')`| `SELECT UNIX_TIMESTAMP(user.insert_date)*1000 as inserted FROM user`|\n|`Db::easy('user.* $user.id:id')` | `SELECT *, HEX(id) as id FROM user`|\n\n\u003e But what if the value passed to a selectandi starts with a modifier?\nYou can prepend the modifier '=' (equal) to prevent operandi from triggering! \nWe do not provide an example as this can only happen if your column names would start with a \ncharacter in violation with a secure [_filter_character_](#environment-variable) setup. \n\n## Db-easy markup\n\nLet's face it: most of the time our queries are rather simple.\nWhenever we need more complex queries, there will always come the point of realizing that pure SQL would be easier to read \nthan endlessly encapsulated arrays. This is where using Db::ask's \"any\"-actions come into play.\n\nDb::easy does not target these scenarios, but rather specializes in the ever-day retrieval of a particular set of data.\nThe selected columns are written in one single string.\n\n### Selecting multiple columns from a table\nMultiple columns of one table are separated by a space:\n\n```PHP\n// SELECT first_name, last_name FROM user\n\nDb::easy('user.first_name user.last_name');\n```\n\n### Simple joins\nJoins are generated based on the order of the occurrence in the string from left to right, respecting the first used table as the \"master\".\nEasy can only perform JOINS if the recommended db structure is used.\nThis means that foreign keys must be in the format \"master\"_id.\n\n```PHP\n// SELECT \n//   user.first_name, \n//   user.last_name, \n//   user_email.email, \n//   user_password.confirm_date\n// FROM user \n// JOIN user_email ON user_email.user_id = user.id\n// JOIN user_password ON user_password.user_id = user.id\n\nDb::easy('user.first_name user.last_name user_email.email user_password.confirm_date');\n```\n\n## OOP \u0026 Testing\nAlthough you want to write your own wrapper depending on the Interface your framework uses,\nthis library comes with a simple wrapper out of the box to encourage dependency injection usage.\n\n```PHP\n$environment = ['name' =\u003e 'my_db'];\n$database = new \\Neoan3\\Apps\\DbOOP($environment);\n$database-\u003eeasy('user.*', ['^delete_date']); //executes \u0026 returns Db::easy \n$database-\u003esmart('user', ['name'=\u003e'sam', 'user_type'=\u003e'admin']); //executes \u0026 returns Db::ask\n\n```\n\n## Heads up\nThe general approach of the db-app has been applied for years. While the difference to common wrappers for mysqli of pdo seems rather big,\ndevelopers are usually surprised of the low learning-curve and possibilities for faster development it offers.\n\n### Deprecation\nThe Db::data-function is considered unsafe without proper escaping and throws a deprecation-notice since 0.0.3\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsroehrl%2Fneoan3-db","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsroehrl%2Fneoan3-db","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsroehrl%2Fneoan3-db/lists"}