{"id":16446585,"url":"https://github.com/sad-spirit/pg-gateway","last_synced_at":"2026-05-16T03:32:53.078Z","repository":{"id":188272629,"uuid":"678304662","full_name":"sad-spirit/pg-gateway","owner":"sad-spirit","description":"Table Data Gateway implementation for Postgres based on pg_builder, mix and match manually written SQL with query builder","archived":false,"fork":false,"pushed_at":"2023-09-15T07:05:38.000Z","size":262,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-16T18:08:33.761Z","etag":null,"topics":["orm","php","postgres","postgresql","sql","table-data-gateway"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sad-spirit.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","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":"2023-08-14T08:37:34.000Z","updated_at":"2024-05-30T01:37:47.488Z","dependencies_parsed_at":"2024-05-30T01:47:55.314Z","dependency_job_id":null,"html_url":"https://github.com/sad-spirit/pg-gateway","commit_stats":null,"previous_names":["sad-spirit/pg-gateway"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sad-spirit%2Fpg-gateway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sad-spirit%2Fpg-gateway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sad-spirit%2Fpg-gateway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sad-spirit%2Fpg-gateway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sad-spirit","download_url":"https://codeload.github.com/sad-spirit/pg-gateway/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240831380,"owners_count":19864718,"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":["orm","php","postgres","postgresql","sql","table-data-gateway"],"created_at":"2024-10-11T09:48:10.647Z","updated_at":"2026-05-16T03:32:53.073Z","avatar_url":"https://github.com/sad-spirit.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sad_spirit/pg_gateway\n\n[![Continuous Integration](https://github.com/sad-spirit/pg-gateway/actions/workflows/continuous-integration.yml/badge.svg?branch=master)](https://github.com/sad-spirit/pg-gateway/actions/workflows/continuous-integration.yml)\n[![Static Analysis](https://github.com/sad-spirit/pg-gateway/actions/workflows/static-analysis.yml/badge.svg?branch=master)](https://github.com/sad-spirit/pg-gateway/actions/workflows/static-analysis.yml)\n\nThis is a [Table Data Gateway](https://martinfowler.com/eaaCatalog/tableDataGateway.html) implementation built upon\n[pg_wrapper](https://github.com/sad-spirit/pg-wrapper) and [pg_builder](https://github.com/sad-spirit/pg-builder) packages.\n\nUsing those packages immediately allows\n * Transparent conversion of PHP types to Postgres types and back;\n * Writing parts of the query as SQL strings while later processing those parts as Nodes in the Abstract Syntax Tree.\n\n## Installation\n\nRequire the package with composer:\n```\ncomposer require sad_spirit/pg_gateway\n```\n\n## Design goals\n\n * Code generation is not necessary, default gateway implementations are useful as-is.\n * Gateways are aware of the table metadata: columns, primary key, foreign keys.\n * It is possible to cache the generated SQL, skipping the whole parsing/building process.\n * API encourages building parametrized queries.\n * Queries built by several Gateways can be combined via joins / `EXISTS()` / etc.\n\n## Usage example\n\nAssuming the following database schema\n```SQL\n\ncreate schema example;\n\ncreate table example.users (\n    id integer not null generated by default as identity,\n    login text not null,\n    password_hash text not null,\n    \n    constraint users_pkey primary key (id)\n);\n\ncreate table example.roles (\n    id integer not null generated by default as identity,\n    name text not null,\n    description text,\n    \n    constraint roles_pkey primary key (id)\n);\n\ncreate table example.users_roles (\n    user_id integer not null,\n    role_id integer not null,\n    valid_from date,\n    valid_to date,\n    \n    constraint users_roles_pkey primary key (user_id, role_id),\n    constraint roles_users_fkey foreign key (user_id)\n        references example.users (id)\n        on delete cascade on update restrict,\n    constraint users_roles_fkey foreign key (role_id)\n        references example.roles (id)\n        on delete cascade on update restrict\n);\n```\n\nwe can use default gateways and default builders to perform a non-trivial query to the above tables\n\n```PHP\nuse sad_spirit\\pg_gateway\\{\n    TableLocator,\n    builders\\FluentBuilder\n};\nuse sad_spirit\\pg_wrapper\\Connection;\n\n$connection = new Connection('...');\n$locator    = new TableLocator($connection);\n\n$adminRoles = $locator-\u003eselect('example.roles', fn(FluentBuilder $builder) =\u003e $builder\n    -\u003eoperatorCondition('name', '~*', 'admin')\n    -\u003ereturningColumns()\n        -\u003eexcept(['description'])\n        -\u003ereplace('/^/', 'role_'));\n\n$activeAdminRoles = $locator-\u003eselect('example.users_roles', fn(FluentBuilder $builder) =\u003e $builder\n    -\u003esqlCondition(\"current_date between coalesce(self.valid_from, 'yesterday') and coalesce(self.valid_to, 'tomorrow')\")\n    -\u003ejoin($adminRoles) // defaults to joining on foreign key\n    -\u003ereturningColumns(['valid_from', 'valid_to']));\n\n$activeAdminUsers = $locator-\u003eselect('example.users', fn(FluentBuilder $builder) =\u003e $builder\n    -\u003ereturningColumns()\n        -\u003eexcept(['password_hash'])\n        -\u003ereplace('/^/', 'user_')\n    -\u003ejoin($activeAdminRoles) // defaults to joining on foreign key\n    -\u003eorderBy('user_login, role_name')\n    -\u003elimit(5));\n\n// Let's assume we want to output that list with pagination\necho \"Total users with active admin roles: \" . $activeAdminUsers-\u003eexecuteCount() . \"\\n\\n\";\n\nforeach ($activeAdminUsers as $row) {\n    print_r($row);\n}\n\necho $activeAdminUsers-\u003ecreateSelectCountStatement()-\u003egetSql() . \";\\n\\n\";\necho $activeAdminUsers-\u003ecreateSelectStatement()-\u003egetSql() . ';';\n```\n\nwhere the last two `echo` statements will output something similar to\n```SQL\nselect count(self.*)\nfrom example.users as self, example.users_roles as gw_1, example.roles as gw_2\nwhere gw_2.\"name\" ~* $1::\"text\"\n    and gw_1.role_id = gw_2.id\n    and current_date between coalesce(gw_1.valid_from, 'yesterday') and coalesce(gw_1.valid_to, 'tomorrow')\n    and gw_1.user_id = self.id;\n\nselect gw_2.id as role_id, gw_2.\"name\" as role_name, gw_1.valid_from, gw_1.valid_to, self.id as user_id,\n    self.login as user_login\nfrom example.users as self, example.users_roles as gw_1, example.roles as gw_2\nwhere gw_2.\"name\" ~* $1::\"text\"\n    and gw_1.role_id = gw_2.id\n    and current_date between coalesce(gw_1.valid_from, 'yesterday') and coalesce(gw_1.valid_to, 'tomorrow')\n    and gw_1.user_id = self.id\norder by user_login, role_name\nlimit $2;\n```\n\n\n## Documentation\n\nFor in-depth description of package features, visit [pg_gateway manual](https://pg-gateway.readthedocs.io/).\n\n## Requirements\n\n`pg_gateway` requires at least PHP 8.2 with native [pgsql extension](https://php.net/manual/en/book.pgsql.php).\n\nMinimum supported PostgreSQL version is 12.\n\nIt is highly recommended to use [PSR-6 compatible](https://www.php-fig.org/psr/psr-6/) cache in production,\nboth for metadata lookup and for generated queries.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsad-spirit%2Fpg-gateway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsad-spirit%2Fpg-gateway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsad-spirit%2Fpg-gateway/lists"}