{"id":19751055,"url":"https://github.com/n0nag0n/super-model","last_synced_at":"2025-04-30T10:31:16.160Z","repository":{"id":48824250,"uuid":"258547945","full_name":"n0nag0n/super-model","owner":"n0nag0n","description":"A simple PHP model with no dependencies that will accomplish 90+% of what you want to do with reading or writing data from a database including relational data between tables.","archived":false,"fork":false,"pushed_at":"2024-03-13T05:34:14.000Z","size":66,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-14T00:02:39.793Z","etag":null,"topics":["coverage","database-table","doctrine-orm","illuminate","illuminate-database","models","mysql","no-dependencies","php","php-library","php-model","postgresql","propel","redbean","redbeanphp","simple","sqlite","sqlite3","super"],"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/n0nag0n.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":"2020-04-24T15:16:07.000Z","updated_at":"2024-03-13T05:34:18.000Z","dependencies_parsed_at":"2022-09-22T14:41:20.276Z","dependency_job_id":null,"html_url":"https://github.com/n0nag0n/super-model","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n0nag0n%2Fsuper-model","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n0nag0n%2Fsuper-model/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n0nag0n%2Fsuper-model/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/n0nag0n%2Fsuper-model/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/n0nag0n","download_url":"https://codeload.github.com/n0nag0n/super-model/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224208320,"owners_count":17273674,"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":["coverage","database-table","doctrine-orm","illuminate","illuminate-database","models","mysql","no-dependencies","php","php-library","php-model","postgresql","propel","redbean","redbeanphp","simple","sqlite","sqlite3","super"],"created_at":"2024-11-12T02:42:06.846Z","updated_at":"2024-11-12T02:42:07.685Z","avatar_url":"https://github.com/n0nag0n.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Dependencies](https://david-dm.org/n0nag0n/super-model.svg)\n[![Build Status](https://travis-ci.org/n0nag0n/super-model.png?branch=master)](https://travis-ci.org/n0nag0n/super-model)\n[![codecov](https://codecov.io/gh/n0nag0n/super-model/branch/master/graph/badge.svg)](https://codecov.io/gh/n0nag0n/super-model)\n[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/esta/issues)\n[![HitCount](http://hits.dwyl.com/n0nag0n/super-model.svg)](http://hits.dwyl.com/n0nag0n/super-model)\n\n# Update\n\nIt is highly recommended that you use [flightphp/active-record](https://github.com/flightphp/active-record) instead of this library. No further development will be done with this library, but it does work as it is right now.\n\n# Super Model\n\nSuper model is a very simple ORM type php class to easily interact with tables in a database without writing a ton of SQL code all over the place. \n\nTo prove it, here are the lines of code...\n```\n$ cloc src/\n       1 text file.\n       1 unique file.                              \n       0 files ignored.\n\ngithub.com/AlDanial/cloc v 1.74  T=0.01 s (71.8 files/s, 48768.5 lines/s)\n-------------------------------------------------------------------------------\nLanguage                     files          blank        comment           code\n-------------------------------------------------------------------------------\nPHP                              1             86            246            347\n-------------------------------------------------------------------------------\n```\n\nThis is written with performance in mind. So while it will not satisfy every single requirement in every project that's ever been built, it will work in the majority of cases, and do a kick butt job at it too!\n\n## Basic Usage\nGetting started with Super Model is easy, simply extend the super model class and define a table name. That's about it.\n```php\n\u003c?php\nuse n0nag0n\\Super_Model;\nclass User extends Super_Model {\n\tprotected $table = 'users';\n}\n```\nNow what about some simple examples of how she works?\n\nFirst, lets assume the following table:\n```\nTable: users\n---------------------------------------------------------\n| id\t\t| email\t\t\t| company_id\t| \n| 1\t\t| hi@example.com\t| 50\t\t|\n| 2\t\t| another@example.com\t| 61\t\t|\n| 3\t\t| whatever@example.com\t| 61\t\t|\n---------------------------------------------------------\n```\n\n```php\n\u003c?php\n// somefile.php\n\n$pdo = new PDO('sqlite::memory:', '', '', [ PDO::ATTR_DEFAULT_FETCH_MODE =\u003e PDO::FETCH_ASSOC ]);\n\n$User = new User($pdo);\n\n// WHERE company_id = 50\n$users = $User-\u003egetAllBycompany_id(50);\n\n// same as above\n$users = $User-\u003egetAll([ 'company_id' =\u003e 50 ]);\n\n```\nEasy peasy, lemon squeezy right?\n\n## Docs\n### `getBy*(mixed $value): array [result]`\nThis is a method that returns one row back from the value specified. The `*` part of the method refers to a field in the database. The field name is case-sensitive to whatever your field name is on your database table.\n```php\n\n// get by the id field on the users table\n$User-\u003egetByid(3);\n\n/* \n\t[ \n\t\t'id' =\u003e 3,\n\t\t'email' =\u003e 'whatever@example.com',\n\t\t'company_id' =\u003e 61\n\t]\n*/\n$User-\u003egetBycompany_id(61);\n/* \n\t// it only will pull the first row, not all rows\n\t[ \n\t\t'id' =\u003e 2,\n\t\t'email' =\u003e 'another@example.com',\n\t\t'company_id' =\u003e 61\n\t]\n*/\n```\n\n### `getAllBy*(mixed $value): array [ [result], [result] ]`\nThis is a shortcut filter to return all rows by a given value. The `*` part of the method refers to a field in the database. The field name is case-sensitive to whatever your field name is on your database table.\n```php\n\n// this is pointless, but will still work\n$User-\u003egetAllByid(3);\n\n/* \n\t[\n\t\t[ \n\t\t\t'id' =\u003e 3,\n\t\t\t'email' =\u003e 'whatever@example.com',\n\t\t\t'company_id' =\u003e 61\n\t\t]\n\t]\n*/\n$User-\u003egetAllBycompany_id(61);\n/* \n\t[\n\t\t[ \n\t\t\t'id' =\u003e 2,\n\t\t\t'email' =\u003e 'another@example.com',\n\t\t\t'company_id' =\u003e 61\n\t\t],\n\t\t[ \n\t\t\t'id' =\u003e 3,\n\t\t\t'email' =\u003e 'whatever@example.com',\n\t\t\t'company_id' =\u003e 61\n\t\t]\n\t]\n*/\n```\n\n### `getAll(array $filters, bool $return_one_row = false): array [ [result], [result] ] or [result]`\nThis is the filter where you can add a bunch of customization to filter the data from your table. There are a few unique keys to be aware of and some operators to help you pull your specific data.\n```php\n// Full example\n$filters = [\n\n\t//\n\t// arguments in the WHERE statement\n\t//\n\t'some_field' =\u003e 5, // some_field = ?\n\t'some_field-=' =\u003e 5, // some_field = ?\n\t'another_field' =\u003e 'IS NULL', // some_field IS NULL\n\t'another_field' =\u003e 'IS NOT NULL', // some_field IS NOT NULL\n\t'another_field-\u003e' =\u003e 'Apple', // another_field \u003e ?\n\t'another_field-\u003e=' =\u003e 'Apple', // another_field \u003e= ?\n\t'another_field-\u003c' =\u003e 'Apple', // another_field \u003c ?\n\t'another_field-\u003c=' =\u003e 'Apple', // another_field \u003c= ?\n\t'another_field-!=' =\u003e 'Apple', // another_field != ?\n\t'another_field-\u003c\u003e' =\u003e 'Apple', // another_field \u003c\u003e ?\n\t'another_field-LIKE' =\u003e 'Ap%ple', // another_field LIKE ?\n\t'another_field-NOT LIKE' =\u003e 'Apple%', // another_field NOT LIKE ?\n\t'another_field-IN' =\u003e [ 'Apple', 'Banana', 'Peach' ], // another_field IN(??) double question mark gets parsed as array\n\t'another_field-NOT IN' =\u003e [ 'Apple', 'Banana', 'Peach' ], // another_field NOT IN(??) double question mark gets parsed as array\n\n\t// If you need some custom action\n\t'another_field-RAW-\u003e DATE_SUB(?, INTERVAL 1 DAY)' =\u003e '1980-01-01', // another_field \u003e DATE_SUB(?, INTERVAL 1 DAY)\n\n\t//\n\t// Other parts of the query\n\t//\n\n\t// choose what columns you want to select\n\t'select_fields' =\u003e 'id, first_name',\n\n\t// Get any joins\n\t'joins' =\u003e [ 'LEFT JOIN companies ON companies.id = users.company_id' ],\n\n\t// Group by\n\t'group_by' =\u003e 'company_id',\n\n\t// having\n\t'having' =\u003e 'count \u003e 5',\n\n\t// order by\n\t'order_by' =\u003e 'id DESC',\n\n\t// limit\n\t'limit' =\u003e 15,\n\n\t// offset\n\t'offset' =\u003e 10000,\n];\n\n$users = $User-\u003egetAll($filters);\n```\nThere are also some basic config options with the model properties.\n#### $disallow_wide_open_queries\nIf you have a model that you know will always return a small result set and want to be able to query the entire table, set this property. Otherwise it is a protection so that if no sql params were supplied, you wouldn't retrieve back the entire result set (which could crash and burn many things).\n```php\nuse n0nag0n\\Super_Model;\nclass User extends Super_Model {\n\tprotected $table = 'users';\n\tprotected $disallow_wide_open_queries = false;\n}\n```\n\n### `create(array $data): int [insert id]`\nThis will create a single row on the table, but if you supply a multi-dimensional array, it will insert multiple rows. A primary key of `id` is assumed.\n```php\n$User-\u003ecreate([ 'email' =\u003e 'onemore@example.com', 'company_id' =\u003e 55 ]);\n// returns 4\n\n$User-\u003ecreate([ [ 'email' =\u003e 'ok@example.com', 'company_id' =\u003e 55 ], [ 'email' =\u003e 'thanks@example.com', 'company_id' =\u003e 56 ] ]);\n// returns 6, only the last id will be returned\n```\n\n### `update(array $data, string $update_field = 'id'): int (number of rows updated)`\nThis will create a single row on the table, but if you supply a multi-dimensional array, it will insert multiple rows. A primary key of `id` is assumed.\n```php\n$User-\u003eupdate([ 'id' =\u003e 1, 'email' =\u003e 'whoneedsemail@example.com' ]);\n// returns 1 and will only update the email field\n\n$User-\u003eupdate([ 'email' =\u003e 'whoneedsemail@example.com', 'company_id' =\u003e 61 ], 'email');\n// returns 1\n\n$User-\u003eupdate([ 'company_id' =\u003e 61, 'email' =\u003e 'donotreply@example.com' ], 'company_id');\n// returns 3, not really logical, but it would update all the emails\n```\n\n## FAQ (Advanced Usage)\n\n*What if you want an automated way to alter your result if a specific flag is fired?*\nEasy peasy. There is a method called `processResult()` that will run through every result you pull back. You inject special filters for this method in the `$filters['processResults']` key.\n```php\n\u003c?php\n\tuse n0nag0n\\Super_Model;\n\tclass User extends Super_Model {\n\t\tprotected $table = 'users';\n\n\t\tpublic processResult(array $process_filters, array $result): array {\n\n\t\t\t// add some trigger here and do whatever checks you need\n\t\t\tif(isset($process_filters['set_full_name']) \u0026\u0026 $process_filters['set_full_name'] === true \u0026\u0026 !empty($result['first_name']) \u0026\u0026 !empty($result['last_name'])) {\n\t\t\t\t$result['full_name'] = $result['first_name'].' '.$result['last_name'];\n\t\t\t}\n\n\t\t\treturn $result;\n\t\t}\n\t}\n\n\t// later on in some other file.\n\t$User = new User($pdo);\n\n\t// setting the processResults filter here is the key to connecting the getAll statement with your processResult method\n\t$users = $User-\u003egetAll([ 'company_id' =\u003e 51, 'processResults' =\u003e [ 'set_full_name' =\u003e true ] ]);\n\n\techo $users[0]['full_name']; // Bob Smith\n```\n\n\n*What if you need to do a crazy complex SQL query that doesn't fall in the realm of this class or the `getAll()` filters?*\n\nRemember the point of this class is **NOT** to satisfy every requirement from every project that ever has or will exist, but it will get you 90% the way there. In light of that, there is a simple way to execute the above question. Just use RAW SQL for your one off.\n```php\n\u003c?php\n\tuse n0nag0n\\Super_Model;\n\tclass User extends Super_Model {\n\t\tprotected $table = 'users';\n\n\t\tpublic function processCrazyKukooQuery(/* add whatever required fields you need */): array {\n\t\t\t$db = $this-\u003egetDbConnection();\n\n\t\t\t// shamelessly ripped from StackOverflow\n\t\t\t$statement = $db-\u003eprepare(\"SELECT \n\t\t\t\tDISTINCT\n\t\t\t\tt.id,\n\t\t\t\tt.tag, \n\t\t\t\tc.title AS Category\n\t\t\t\tFROM\n\t\t\t\ttags2Articles t2a \n\t\t\t\tINNER JOIN tags t ON t.id = t2a.idTag\n\t\t\t\tINNER JOIN categories c ON t.tagCategory = c.id\n\t\t\t\tINNER JOIN (\n\t\t\t\t\tSELECT\n\t\t\t\t\ta.id \n\t\t\t\t\tFROM \n\t\t\t\t\tarticles AS a\n\t\t\t\t\tJOIN tags2articles AS ta  ON a.id=ta.idArticle\n\t\t\t\t\tJOIN tags AS tsub ON ta.idTag=tsub.id\n\t\t\t\t\tWHERE \n\t\t\t\t\ttsub.id IN (12,13,16) \n\t\t\t\t\tGROUP BY a.id\n\t\t\t\t\tHAVING COUNT(DISTINCT tsub.id)=3 \n\t\t\t\t) asub ON t2a.idArticle = asub.id\");\n\t\t\t$statement-\u003eexecute();\n\n\t\t\treturn $statement-\u003efetchAll();\n\t\t}\n\t}\n\n\t\n```\n\n## Testing\nSimply run `composer test` to run `phpunit` and `phpstan`. Currently at 100% coverage and that's where I'd like to keep it. \n\n*A note about 100% coverage:* While the code may have 100% coverage, **actual** coverage is different. The goal is to test many different scenarios against the code to think through the code and anticipate unexpected results. I code to \"real\" coverage, not \"did the code run\" coverage. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fn0nag0n%2Fsuper-model","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fn0nag0n%2Fsuper-model","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fn0nag0n%2Fsuper-model/lists"}