php
73 lines · 10 steps
Building a fluent SQL query builder in PHP
A chainable class assembles a parameterized SELECT statement piece by piece, then runs it safely through PDO.
Explained by
highlit
1<?php
2
3namespace App\Database;
4
5use PDO;
6
7class QueryBuilder
8{
9 private string $table;
10 private array $columns = ['*'];
11 private array $wheres = [];
12 private array $bindings = [];
13 private array $orders = [];
14 private ?int $limit = null;
15
16 public function __construct(private PDO $pdo)
17 {
18 }
19
20 public function table(string $table): static
21 {
22 $this->table = $table;
23 return $this;
24 }
25
26 public function select(string ...$columns): static
27 {
28 $this->columns = $columns ?: ['*'];
29 return $this;
30 }
31
32 public function where(string $column, string $operator, mixed $value): static
33 {
34 $this->wheres[] = "{$column} {$operator} ?";
35 $this->bindings[] = $value;
36 return $this;
37 }
38
39 public function orderBy(string $column, string $direction = 'asc'): static
40 {
41 $direction = strtolower($direction) === 'desc' ? 'DESC' : 'ASC';
42 $this->orders[] = "{$column} {$direction}";
43 return $this;
44 }
45
46 public function limit(int $count): static
47 {
48 $this->limit = $count;
49 return $this;
50 }
51
52 public function get(): array
53 {
54 $sql = 'SELECT ' . implode(', ', $this->columns) . ' FROM ' . $this->table;
55
56 if ($this->wheres) {
57 $sql .= ' WHERE ' . implode(' AND ', $this->wheres);
58 }
59
60 if ($this->orders) {
61 $sql .= ' ORDER BY ' . implode(', ', $this->orders);
62 }
63
64 if ($this->limit !== null) {
65 $sql .= ' LIMIT ' . $this->limit;
66 }
67
68 $statement = $this->pdo->prepare($sql);
69 $statement->execute($this->bindings);
70
71 return $statement->fetchAll(PDO::FETCH_ASSOC);
72 }
73}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Returning $this from each setter lets callers chain calls into a readable query expression.
- 2Collecting clauses and bindings separately keeps user values out of the raw SQL string.
- 3Deferring SQL assembly to a single terminal method centralizes how the query is compiled and executed.
Related explainers
php
<?php namespace App\Http\Controllers;
Time-limited signed download links in Laravel
signed urls
authorization
file streaming
Intermediate
6 steps
php
<?php namespace App\Http\Requests;
Normalizing input in a Laravel FormRequest
validation
authorization
input-normalization
Intermediate
6 steps
php
<?php namespace App\Http;
Building an onion middleware pipeline in PHP
middleware
closures
array-reduce
Advanced
9 steps
php
<?php namespace Database\Seeders;
Building blog test data with Laravel factories
seeding
factories
relationships
Intermediate
9 steps
php
<?php namespace App\Notifications;
How a queued weekly digest email works in Laravel
notifications
queues
email
Intermediate
7 steps
php
public function importCustomers(UploadedFile $file): array { $errors = []; $imported = 0;
Validating a CSV import in Laravel
csv-parsing
validation
error-accumulation
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-fluent-sql-query-builder-in-php-explained-php-e618/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.