schema = $table; } public function select(string ...$cols): Query { if (!empty($cols)) $this->columns = $cols; foreach ($cols as $col) $this->schema->requireColumn($col); return $this; } public function orderBy(string $col, Order $order): Query { $this->schema->requireColumn($col); $this->orderBy = [ 'name' => $col, 'order' => $order ]; return $this; } public function join(Join $join, Schema $other): Query { $foreignKey = null; if($this->schema->existsReference($other)) $foreignKey = $this->schema->references[$other->name]; if ($other->existsReference($this->schema)) $foreignKey = $other->references[$this->schema->name]; if (is_null($foreignKey)) throw new InvalidArgumentException('Joins can only applied to referencing columns.'); // TODO: Implement include instead of merge $this->schema->include($other); $this->joins[] = [ 'table' => $other->name, 'type' => $join ]; return $this; } protected function isOrdered(): bool { return !empty($this->orderBy); } protected function isJoined(): bool { return !empty($this->joins); } public function toSql(): string { // Merge any aggregations with the standard columns. $selectColumns = $this->columns; if ($this->hasAggregations()) $selectColumns = array_merge($selectColumns, $this->aggregations); // Build the SELECT clause. $columns = implode(", ", $selectColumns); $primaryTable = $this->schema->name; $query = "SELECT $columns FROM $primaryTable"; // Append join clauses, if any. if ($this->isJoined()) foreach ($this->joins as $join) $query .= " " . $join['type']->toString() . " NATURAL JOIN " . $join['table']; // Append the WHERE clause if conditions exist. if ($this->isConditioned()) { $whereClause = $this->combineConditions(); $query .= " WHERE $whereClause"; } // Append the ORDER BY clause if ordering is set. if ($this->isOrdered()) { $orderClause = $this->orderBy['name'] . ' ' . $this->orderBy['order']; $query .= " ORDER BY $orderClause"; } return $query; } public function aggregate(string $col, string $alias, Aggregation $func): Query { if ($col != '*') $this->schema->requireColumn($col); $this->aggregations[] = strtoupper($func->toString()) . "($col) AS $alias"; return $this; } public function hasAggregations(): bool { return !empty($this->aggregations); } public function query(): mixed { global $wpdb; $query = $this->toSql(); $results = $wpdb->get_results($query, ARRAY_A); return $this->formatResults($results); } protected function formatResults(array $results) { $formatted = []; foreach ($results as $row) { // Apply type casting to each column in each row. foreach ($row as $column => &$value) if (isset($this->columnTypes[$column])) $value = $this->columnTypes[$column]->valCast($value); // Use the primary key for row indexing $primaryKey = $this->schema->primaryKey(); $formatted[$row[$primaryKey]] = $row; } if (count($formatted) === 1) { // Unpack single row results $row = array_shift($formatted); if (count($row) === 1) // Unpack single column results return array_shift($row); return $row; } return $formatted; } }