Skip to content

Commit

Permalink
feat: add query builder
Browse files Browse the repository at this point in the history
  • Loading branch information
codisart committed Apr 24, 2024
1 parent 16159ba commit c278a70
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 1 deletion.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"files": [ "src/classnames/function.php" ],
"psr-4": {
"ClassNames\\": "src/classnames",
"MergeCoverage\\": "src/merge-coverage"
"MergeCoverage\\": "src/merge-coverage",
"QueryBuilder\\": "src/query-builder"
}
},
"require-dev": {
Expand Down
3 changes: 3 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
<testsuite name="classnames">
<directory>src/classnames/Tests</directory>
</testsuite>
<testsuite name="query-builder">
<directory>src/query-builder/Tests</directory>
</testsuite>
</testsuites>

<source restrictDeprecations="true" restrictNotices="true" restrictWarnings="true">
Expand Down
20 changes: 20 additions & 0 deletions src/query-builder/Column.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace QueryBuilder;

class Column
{
public function __construct(
private string $expression,
private ?string $alias = null
) {
}

public function __toString()
{
if ($this->alias) {
return $this->expression . ' AS ' . $this->alias;
}
return $this->expression;
}
}
22 changes: 22 additions & 0 deletions src/query-builder/From.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace QueryBuilder;

use QueryBuilder\Join\Equals;

class From
{
public function __construct(private string $expression)
{
}

public function join(string $table, Equals $condition )
{
$this->expression .= PHP_EOL . Query::INDENT . 'JOIN ' . $table . ' ON ' . (string) $condition;
}

public function __toString()
{
return 'FROM ' . $this->expression;
}
}
20 changes: 20 additions & 0 deletions src/query-builder/Join.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace QueryBuilder;

class Join
{
public function __construct(private string $table)
{
$this->table = $table;
}

private function plop($from, $join) {

}

public function __toString()
{
return Query::INDENT . "JOIN " . $this->table . $this->conditions();
}
}
17 changes: 17 additions & 0 deletions src/query-builder/Join/Equals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace QueryBuilder\Join;

use QueryBuilder\Column;

class Equals
{
public function __construct(private Column $left, private Column $right)
{
}

public function __toString()
{
return $this->left . ' = ' . $this->right;
}
}
34 changes: 34 additions & 0 deletions src/query-builder/Query.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace QueryBuilder;

class Query
{
const INDENT = " ";

const FINAL_SEMI_COLON = ";" . PHP_EOL;

private $select;
private $from;
private $where;

public function __construct(Select $select, From $from, Where $where)
{
$this->select = $select;
$this->from = $from;
$this->where = $where;
}

public function __toString()
{
$parts = [
$this->select,
$this->from,
"WHERE " . $this->where,
Query::FINAL_SEMI_COLON,
];

return implode(PHP_EOL, $parts);
}
}

24 changes: 24 additions & 0 deletions src/query-builder/Select.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace QueryBuilder;

class Select
{
private array $columns;

public function __construct(Column ...$columns)
{
$this->columns = $columns;
}

public function add(Column $column){
$this->columns[] = $column;
return $this;
}

public function __toString()
{
$select = implode("," . PHP_EOL . Query::INDENT, $this->columns);
return "SELECT" . PHP_EOL . Query::INDENT . $select;
}
}
21 changes: 21 additions & 0 deletions src/query-builder/Sum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace QueryBuilder;

class Sum extends Column
{
public function __construct(
private string $expression,
private ?string $alias = null
) {
$this->expression = 'SUM(' . $this->expression . ')';
}

public function __toString()
{
if ($this->alias) {
return $this->expression . ' AS ' . $this->alias;
}
return $this->expression;
}
}
31 changes: 31 additions & 0 deletions src/query-builder/Table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace QueryBuilder;

class Table
{
public function __construct(protected string $name, private ?string $alias = null)
{
}

public function from() {
return new From($this->alias ? $this->name . ' ' . $this->alias : $this->name);
}

public function column(string $expression, ?string $alias = null)
{
$table = $this->alias ?? $this->name;
return new Column($table . '.' . $expression, $alias);
}

public function sum(string $expression, ?string $as = null)
{
$table = $this->alias ?? $this->name;
return new Sum($table . '.' . $expression, $as);
}

public function __toString()
{
return $this->alias ?? $this->name;
}
}
91 changes: 91 additions & 0 deletions src/query-builder/Tests/QueryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);

namespace QueryBuilder\Tests;

use PHPUnit\Framework\TestCase;
use QueryBuilder\From;
use QueryBuilder\Join\Equals;
use QueryBuilder\Query;
use QueryBuilder\Select;
use QueryBuilder\Table;
use QueryBuilder\Where;

class QueryTest extends TestCase
{
public function testQuery()
{
$customers = new class('customers') extends Table {
public function name() {
return $this->column('name');
}
public function id() {
return $this->column('id');
}
public function departement() {
return $this->column('departement');
}
public function departementShouldBe(int $departement) {
return new Where\Equals($this->departement(), (string) $departement);
}

public function joins(Table $orders){
return $orders->from()->join($this->name, new Equals($orders->customerId(), $this->id()));
}
};

$orders = new class('orders') extends Table {
public function customerId() {
return $this->column('customer_id');
}
public function id() {
return $this->column('id');
}
public function valid() {
return $this->column('valid');
}
public function shouldBeValid() {
return new Where\Equals($this->column('valid'), 'TRUE');
}
public function total() {
return $this->sum('price',as: 'total');
}
};

$customerOrders = new class($orders, $customers) extends From {
public function __construct($orders, $customers)
{
parent::__construct('orders');
$this->join('customers', new Equals($orders->customerId(), $customers->id()));
}
};

$select = new Select(
$orders->total(),
$customers->name(),
);

$validDepartements = Where::atLeastOne(
$customers->departementShouldBe(14),
$customers->departementShouldBe(15),
);
$where = Where::everyOnes(
$orders->shouldBeValid(),
$validDepartements,
);

$query = new Query($select, $customerOrders, $where);

$sql = <<<SQL
SELECT
SUM(orders.price) AS total,
customers.name
FROM orders
JOIN customers ON orders.customer_id = customers.id
WHERE orders.valid = TRUE AND (customers.departement = 14 OR customers.departement = 15)
;
SQL;
self::assertSame($sql, (string) $query);
}
}
36 changes: 36 additions & 0 deletions src/query-builder/Where.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php


namespace QueryBuilder;

use QueryBuilder\Where\Ensemble;

class Where
{
public function __construct(private string $expression)
{
}

public static function atLeastOne(self ...$wheres): self {
foreach ($wheres as &$where) {
if ($where instanceof Ensemble) {
$where = '(' . $where . ')';
}
}
return new Ensemble(implode(' OR ', $wheres));
}

public static function everyOnes(self ...$wheres): self {
foreach ($wheres as &$where) {
if ($where instanceof Ensemble) {
$where = '(' . $where . ')';
}
}
return new Ensemble(implode(' AND ', $wheres));
}

public function __toString(): string
{
return $this->expression;
}
}
19 changes: 19 additions & 0 deletions src/query-builder/Where/Ensemble.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace QueryBuilder\Where;

use QueryBuilder\Column;
use QueryBuilder\Where;

class Ensemble extends Where
{
public function __construct(private string $expression)
{
}

public function __toString(): string
{
return $this->expression;
}
}

19 changes: 19 additions & 0 deletions src/query-builder/Where/Equals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace QueryBuilder\Where;

use QueryBuilder\Column;
use QueryBuilder\Where;

class Equals extends Where
{
public function __construct(private Column $left, private string $right)
{
}

public function __toString(): string
{
return $this->left . ' = ' . $this->right;
}
}

0 comments on commit c278a70

Please sign in to comment.