Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/soyuka/esql
PHP Extended SQL
https://github.com/soyuka/esql
Last synced: 13 days ago
JSON representation
PHP Extended SQL
- Host: GitHub
- URL: https://github.com/soyuka/esql
- Owner: soyuka
- License: mit
- Created: 2021-01-08T15:31:02.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2022-07-21T16:33:12.000Z (over 2 years ago)
- Last Synced: 2024-10-29T12:29:54.767Z (15 days ago)
- Language: PHP
- Size: 214 KB
- Stars: 91
- Watchers: 4
- Forks: 4
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# PHP Extended SQL
PHP Extended SQL is an alternative to the also-known DQL (Doctrine Query Language). It combines the flexibility of SQL with the powerful Doctrine metadata to give you more control over queries.
```php
getConnection();
$mapper = new ESQLMapper($autoMapper, $managerRegistry);
$esql = new ESQL($managerRegistry, $mapper);
$car = $esql(Car::class);
$model = $car(Model::class);$query = <<columns()}, {$model->columns()} FROM {$car->table()}
INNER JOIN {$model->table()} ON {$car->join(Model::class)}
WHERE {$car->identifier()}
SQL;$stmt = $connection->prepare($query);
$stmt->execute(['id' => 1]);var_dump($esql->map($stmt->fetch()));
```[Jump to the documentation](#documentation) or [read this blog article](https://soyuka.me/esql-alternative-to-doctrine-query-language-why/) to see it in action.
## API Platform bridge
This package comes with an API Platform bridge that supports filters and pagination. To use our bridge, use the `esql` attribute:
```php
automapper->map($data, 'array'); // map your object to an array somehow$query = <<table()} SET {$car->predicates()}
WHERE {$car->identifier()}
SQL;$connection->beginTransaction();
$stmt = $connection->prepare($query);
$stmt->execute($binding);
$connection->commit();
```Same goes for inserting value:
```php
automapper->map($data, 'array'); // map your object to an array somehow
$car = $esql($data)
$query = <<table()} ({$car->columns()}) VALUES ({$car->parameters($binding)});
SQL;$connection->beginTransaction();
$stmt = $connection->prepare($query);
$stmt->execute($binding);
$connection->commit();
```Note that if you used a sequence you'd need to handle that yourself.
## Documentation
- [Doctrine](#doctrine)
- [Mapping](#mapping)
- [Bundle configuration](#bundle-configuration)
- [Paginator](#paginator)
- [Examples](#examples)### Doctrine
An ESQL instance offers a few methods to help you write SQL with the help of Doctrine's metadata. To ease there use inside [HEREDOC](https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc) calling `__invoke($classOrObject)` on the `ESQL` class will return an array with the following closure:
```php
table();// the sql alias
// outputs "car"
echo $car->alias();// Get columns: columns(?array $fields = null, string $output = $car::AS_STRING): string
// columns() outputs "car.id, car.name, car.model_id"
// output can also take: $car::AS_ARRAY | $car::WITHOUT_ALIASES | $car::WITHOUT_JOIN_COLUMNS | $car::IDENTIFIERS
echo $car->columns();// Get a single column: column(string $fieldName): string
// column('id') outputs "car.id"
echo $car->column('id');// Get an identifier predicate: identifier(): string
// identifier() outputs "car.id = :id"
echo $car->identifier();// Get a join predicate: join(string $relationClass): string
// join(Model::class) outputs "car.model_id = model.id"
echo $car->join(Model::class);// All kinds of predicates: predicates(?array $fields = null, string $glue = ', '): string
// predicates() outputs "car.id = :id, car.name = :name"
echo $car->predicates();
```More advanced utilities are available as:
```php
toSQLValue('sold', true);// Given an array of bindings, will output keys prefixed by `:`: parameters(array $bindings): string
// parameters(['id' => 1, 'color' => 'blue']) will output ":id, :color"
$car->parameters();
```This are useful to build filters, write systems or even a custom mapper.
ESQL works using aliases and mapping them to classes and their properties. When working on relation you'll have to use:
```php
alias(); // car
$model = $car(Model::class);
$model->alias(); // car_model
```This way, ESQL knows to map the `Model` to the `Car->model` property. When working with DTOs the relation may not be found and you can alias the relation yourself:
```php
id = 1;
$model->name = 'Volkswagen';$car = new Car();
$car->id = 1;
$car->name = 'Caddy';
$car->model = $model;$car2 = new Car();
$car2->id = 2;
$car2->name = 'Passat';
$car2->model = $model;// Aliases should be generated by ESQL to map properties and relation properly
$this->assertEquals([$car, $car2], $mapper->map([
['car_id' => '1', 'car_name' => 'Caddy', 'model_id' => '1', 'model_name' => 'Volkswagen'],
['car_id' => '2', 'car_name' => 'Passat', 'model_id' => '1', 'model_name' => 'Volkswagen'],
], Car::class));
```There's also a Mapper built with the [`symfony/serializer`](https://symfony.com/doc/current/components/serializer.html).
### Bundle configuration
```yaml
esql:
mapper: Soyuka\ESQL\Bridge\Automapper\ESQLMapper
api-platform:
enabled: true
```### Paginator
API Platform has great defaults for pagination. Using `Soyuka\ESQL\Bridge\ApiPlatform\DataProvider\DataPaginator`, fetching data would look like this:
```php
esql->__invoke(Car::class);
$parameters = [];$query = <<columns()} FROM {$esql->table()}
SQL;if ($paginator = $this->dataPaginator->getPaginator($resourceClass, $operationName)) {
return $paginator($esql, $query, $parameters, $context);
}
```If you want to handle the pagination yourself, we provide a way to do so:
```php
esql->__invoke($resourceClass);
['itemsPerPage' => $itemsPerPage, 'firstResult' => $firstResult, 'nextResult' => $nextResult, 'page' => $page, 'partial' => $isPartialEnabled] = $this->dataPaginator->getPaginationOptions($resourceClass, $operationName);$query = <<columns()} FROM {$esql->table()}
LIMIT $itemsPerPage OFFSET $firstResult
SQL;// fetch data somehow and map
$data = $esql->map($data);$countQuery = <<< SQL
SELECT COUNT(1) as count FROM {$esql->table()}
SQL;// get count results somehow
$count = $countResult['count'];return $isPartialEnabled ? new PartialPaginator($data, $page, $itemsPerPage) : new Paginator($data, $page, $itemsPerPage, $count);
```### Examples
- [Aggregates](https://github.com/soyuka/esql/blob/main/tests/Fixtures/TestBundle/State/StatisticsProvider.php)
- [Product with CTE](https://github.com/soyuka/esql/blob/main/tests/Fixtures/TestBundle/State/ProductProvider.php)