2

Im trying to create a dynamic WHERE clause builder with PHP OOP. And Im not sure what Im doing is right.

So I created a class named QueryBuilder where I have a private property named $filters (this is an array property for store both keys and values for filters) and two methods:

1, addFilter() -> This is for adding "filters" to the query

2, getFilters() -> This is for getting the WHERE clause string and the params for PDO execute()

Here is my code:

class QueryBuilder {

 private array $filters;

 public function addFilter(array $filterDetails): void
 {
    $this->filters[] = $filterDetails;

 }

public function getFilters(): array
 {
    $whereClause = " WHERE 1";
    if (!empty($this->filters)) {
        foreach ($this->filters as $filter) {
            $whereClause .= " AND (" . $filter["searchKey"] . " " . $filter["operator"] . " :" . $filter["searchKey"] . ")";
            $params[":" . $filter["searchKey"]] = $filter["searchValue"];
        }
        return ["whereClause" => $whereClause,
            "params"              => $params];
    }
    return ["whereClause" => $whereClause,
        "params"              => null];
  }

 }

And this is how I run it:

$QueryBuilder = new QueryBuilder();
$sql = "SELECT * FROM mytable";
$name = "Test Name";
$age = 12;
$QueryBuilder->addFilter(["operator" => "LIKE",
    "searchKey"                            => "name",
    "searchValue"                          => $name]);
$QueryBuilder->addFilter(["operator" => "=",
    "searchKey"                            => "age",
    "searchValue"                          => $age]);


$whereClause = $QueryBuilder->getFilters();
echo $sql . $whereClause["whereClause"];
echo "</br>";
echo "<pre>";
print_r($whereClause["params"]);
echo "</pre>";

The results seems to be fine:

SELECT * FROM mytable WHERE 1 AND (name LIKE :name) AND (age = :age)
Array
(
    [:name] => Test Name
    [:age] => 12
)

But now Im trying to create a WHERE clause where is a BETWEEN operator and I don't have any idea how can I implement into this code.

Anyway what do you think this is a good approach to create a query (WHERE CLAUSE) builder? Or should I make some modifications to the code?

Thank you!

kviktor1230
  • 101
  • 7
  • 1
    Just a note: This only can be a very very simple builder. Because: What if you have to 'LIKE' filters for the same field (you can not set two times the same name e.g. `:name`). Or what if you want to do an `AND (x=1 OR x=2)` query. Keep that in mind. – Foobar Dec 15 '22 at 11:06

1 Answers1

3

Instead of having just one addFilter() method you could have specific methods for different things:

public function addWhereLike($columnName, $value);
public function addWhereEqual($columnName, $value);
public function addWhereBetween($columnName, $minimumValue, $maximumValue);

And so on. You should now process the method arguments inside these methods, and store the resulting strings in the QueryBuilder object, not process them all together in the getFilters() method. addWhereLike() and addWhereEqual() could both use a private method addWhereWithOperator() that takes the operator as an extra argument.

I would also rename getFilters() into getWherePartOfQuery(). The name should reflect what it does.

I'm also sure that, in the end, you want your QueryBuilder() to actually build whole queries, and not just the WHERE bit, so keep that in mind.

NOTE: Just to be clear, this answer answers your question, but I'm not saying this is the best solution for a query builder. There have been many attempts at this, some more successful than others. The term you probably want to look for is: ORM. Although technically this term does not always relate to databases, it often does.

KIKO Software
  • 15,283
  • 3
  • 18
  • 33
  • Thank you! I just created a quick example: [link](https://phpize.online/sql/mariadb/d3daf738ce948bda29f0b14dcf6f4b12/php/php81/d7fbc9d71f9013f245521738ca05279d/) What do you think about this approach? Thank you for your help! – kviktor1230 Dec 15 '22 at 12:28
  • 1
    @kviktor1230 Yes, that was the general idea. I do have some remarks. As said in my answer `addWhereEqual()` and `addWhereLike()` are very similar and could both call `addWhereWithOperator()` that takes the operator as an extra argument. See: [DRY](https://deviq.com/principles/dont-repeat-yourself). The other thing is the parameter names. You use the column names for this, but that is not smart. Sometimes you want to use the same column in different conditions, and that won't work. Each parameter should have a guaranteed unique name. – KIKO Software Dec 15 '22 at 13:01
  • 1
    @kviktor1230 You now use fixed numbers for the parameter in `addWhereBetween()`, but you could use a similar system for all parameters and just add a new _dynamic_ number to every one. That way a parameter will always be unique. – KIKO Software Dec 15 '22 at 13:03