{"id":25176005,"url":"https://github.com/gowork/dqo","last_synced_at":"2026-02-16T12:09:53.371Z","repository":{"id":38329976,"uuid":"255697413","full_name":"gowork/dqo","owner":"gowork","description":"Database Query Objects","archived":false,"fork":false,"pushed_at":"2025-01-08T13:10:55.000Z","size":220,"stargazers_count":5,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-14T12:55:05.283Z","etag":null,"topics":["database","dbal","doctrine","objects","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/gowork.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-14T18:50:50.000Z","updated_at":"2025-01-08T13:10:16.000Z","dependencies_parsed_at":"2024-11-13T13:41:09.490Z","dependency_job_id":"c6f90d14-271e-4d58-a006-2c192e3dbe32","html_url":"https://github.com/gowork/dqo","commit_stats":{"total_commits":109,"total_committers":8,"mean_commits":13.625,"dds":0.4678899082568807,"last_synced_commit":"74a7dcfccba6e85b1eb82a92249569a3e4f934c2"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gowork%2Fdqo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gowork%2Fdqo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gowork%2Fdqo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gowork%2Fdqo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gowork","download_url":"https://codeload.github.com/gowork/dqo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252782906,"owners_count":21803418,"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","dbal","doctrine","objects","php"],"created_at":"2025-02-09T13:15:39.181Z","updated_at":"2026-02-16T12:09:48.347Z","avatar_url":"https://github.com/gowork.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Database Query Objects\n\n## Introduction\n\nDQO provides an object representation of SQL database table, row and select query.\n\n#### Features:\n* Each database table can be described as `Table` class\n* Enables column name completion in IDE while writing queries\n* Provides a table columns enumeration as constants\n* Each row returned from SELECT query can be described as `Row` class\n* Table specific deserialization recipes can be added to corresponding `Row` class\n* `Table` and `Row` classes code can be generated with Symfony console command\n* Provides immutable `DatabaseSelectBuilder` for building SELECT queries\n\nDQO is based on Doctrine DBAL and uses Doctrine Types for data deserialization and `Doctrine\\DBAL\\Connection` for query execution.\n\n### `Table` definition\n\nClasses representing specific database tables.\nIt contains enumeration of table columns as constants and simplifies field aliasing.\nMultiple instances can be created with different aliases.\n\n```php\nfinal class UserTable extends GW\\DQO\\Table\n{\n    public const ID = 'id';\n    public const EMAIL = 'email';\n    public const NAME = 'name';\n\n    public function id(): string\n    {\n        return $this-\u003efieldPath(self::ID);\n    }\n\n    public function email(): string\n    {\n        return $this-\u003efieldPath(self::EMAIL);\n    }\n\n    public function name(): string\n    {\n        return $this-\u003efieldPath(self::NAME);\n    }\n    \n    public function createRow(array $raw): UserRow\n    {\n        return new UserRow($raw, $this);\n    }\n}\n\n$userTable = new UserTable('user_alias');\n$userTable-\u003etable(); // \"user\"\n$userTable-\u003ealias(); // \"user_alias\"\n$userTable-\u003eid(); // \"user_alias.id\"\n$userTable-\u003eselectField(UserTable::ID); // \"user_alias.id as user_alias_id\"\n```\n\n### `TableRow` definition\n\nClasses that can be created to unify data extracting and deserializing from corresponding table.\n\n```php\nfinal class UserRow extends ClientRow\n{\n    public function id(): UserId\n    {\n        return $this-\u003egetThroughType('UserId', UserTable::ID);\n    }\n\n    public function name(): string\n    {\n        return $this-\u003egetString(UserTable::NAME);\n    }\n\n    public function email(): Email\n    {\n        return Email::fromString($this-\u003egetString(UserTable::EMAIL));\n    }\n\n    public function optionalSecondEmail(): ?Email\n    {\n        return $this-\u003egetThrough([Email::class, 'fromString'], UserTable::OPTIONAL_SECOND_EMAIL);\n    }\n\n    public function about(): ?string\n    {\n        return $this-\u003egetNullableString(UserTable::NAME);\n    }\n}\n\n$userTable = new UserTable();\n$userRow = new UserRow($rowFromQuery, $userTable);\n```\n\n### Building SELECT query with `DatabaseSelectBuilder`\n\n`DatabaseSelectBuilder` simplifies construction of SELECT statements using `Table` objects.\n\n```php\n/** @var Doctrine\\DBAL\\Connection $connection */\n$builder = new GW\\DQO\\DatabaseSelectBuilder($connection);\n\n$meTable = new UserTable('me');\n$friendTable = new UserTable('friend');\n\n$builder\n    -\u003efrom($meTable)\n    -\u003ejoin($friendTable, \"{$friendTable-\u003eid()} = {$meTable-\u003efriendId()}\")\n    -\u003ewhere(\"{$meTable-\u003eusername()} = :me\", ['me' =\u003e 'John Doe'])\n    -\u003eselect($friend-\u003ename())\n    -\u003eoffsetLimit(0, 10);\n```\n\n#### SELECT column aliases\n\nBy default `TableRow` expects that table column used in SELECT part has alias as follows:  `table_alias.column_name as table_alias_column_name`.\n\nThere are 2 ways to create such alias:\n* Use `Table` methods creating column aliases\n  ```php\n  $table = new UserTable();\n  \n  $builder = $builder-\u003eselect(...$table-\u003eselect(UserTable::ID, UserTable::email));\n  // or\n  $builder = $builder-\u003eselect($table-\u003eselectField(UserTable::ID), $table-\u003eselectField(UserTable::email));\n  // or\n  $builder = $builder-\u003eselect(...$table-\u003eselectAll());\n  ```\n* Use simply `$table-\u003ecolumn()` when `select()` is after `table()` or `join()`\n  ```php\n  $table = new UserTable();\n  \n  // first add $table to builder so it can recognize `user.id`, `user.email` and create valid aliases...\n  $builder = $builder-\u003efrom($table);\n  \n  // ...then simply select\n  $builder = $builder-\u003eselect($table-\u003eid(), $table-\u003eemail());\n  ```\n\n#### Query parameters\n\nQuery parameters can be specified directly in `where/having` method or provided later. \n\n```php\n$builder = $builder-\u003efrom($user)\n    -\u003ewhere(\"{$user-\u003ename()} = :name\", ['name' =\u003e 'John Doe']) \n    -\u003ehaving('orders \u003e :limit', ['limit' =\u003e 10]);\n\n// or \n\n$builder = $builder-\u003efrom($user)\n    -\u003ewhere(\"{$user-\u003ename()} = :name\") \n    -\u003ewithParameter('name', 'John Doe');\n\n// or\n\n$builder = $builder-\u003efrom($user)\n    -\u003ewhere(\"{$user-\u003ename()} = :name\") \n    -\u003ewithParameters(['name' =\u003e 'John Doe']);\n```\n\nQuery parameter types can be specified as `where()` argument.\n\n```php\n$yesterday = new DateTime('yesterday');\n$builder = $builder\n    -\u003efrom($user)\n    -\u003ewhere(\"{$user-\u003eregistered()} \u003e :yesterday\", ['yesterday' =\u003e $yesterday], ['yesterday' =\u003e 'datetime']); \n```\n\nYou can also define mapping of parameter classes to proper Doctrine type.\n\n```php\n$start = new DateTimeImmutable('first day of last month 00:00');\n$end = new DateTimeImmutable('last day of last month 23:59');\n$builder = $builder\n    -\u003ewithTypes([DateTimeImmutable::class =\u003e 'datetime_immutable'])\n    -\u003efrom($user)\n    -\u003ewhere(\"{$user-\u003eregistered()} BETWEEN :start AND :end\", ['start' =\u003e $start, 'end' =\u003e $end]); \n```\n\n\n#### Fetching results\n\n```php\n/** @var array\u003cstring, mixed\u003e|null $result one result row or null when there are no rows */\n$result = $builder-\u003efetch();\n\n/** @var mixed|null $result one column from first result or null when no results */\n$result = $builder-\u003efetchColumn();\n\n/** @var array\u003cint, array\u003cstring, mixed\u003e\u003e $result fetch all result rows */\n$result = $builder-\u003efetchAll();\n\n/** \n * @var ArrayValue\u003carray\u003cstring, mixed\u003e\u003e $result \n * @see https://github.com/gowork/values\n */\n$result = $builder-\u003ewrapAll();\n\n/** @var int $result */\n@result = $builder-\u003ecount();\n```\n\n## Install\n\n```bash\ncomposer require gowork/dqo\n```\n\n## Setup\n\n### Symfony\n\nAdd the DatabaseAccessGeneratorBundle to your application's kernel (only on `dev` environment):\n\n```php\n\u003c?php\npublic function registerBundles(): array\n{\n    $bundles = [\n        // ...\n    ];\n    \n    if ($this-\u003egetEnvironment() === 'dev') {\n        // ...\n        $bundles[] = new GW\\DQO\\Symfony\\DatabaseAccessGeneratorBundle();\n    }\n    ...\n}\n```\n\n## Generate table class\n\n```bash\ndqo:generate-tables src/Database App/Database table_1 table_2\n```\n\n## Table query pattern\n\nAll queries should extends `GW\\DQO\\Query\\AbstractDatabaseQuery`\n\n## TODO\n\n  - [ ] generate queries for tables\n  - [ ] add command to update table/row with new fields\n\n## About\n\nUsed at:\n\n - [gowork.pl](https://www.gowork.pl)\n - [gowork.fr](https://gowork.fr)\n - [gowork.com](https://es.gowork.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgowork%2Fdqo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgowork%2Fdqo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgowork%2Fdqo/lists"}