<?php
/**
* @package SP Page Builder
* @author JoomShaper http://www.joomshaper.com
* @copyright Copyright (c) 2010 - 2024 JoomShaper
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 or later
*/
namespace JoomShaper\SPPageBuilder\DynamicContent;
defined('_JEXEC') or die;
use Closure;
use RuntimeException;
use Throwable;
use Joomla\CMS\Factory;
use Joomla\Database\DatabaseDriver;
use JoomShaper\SPPageBuilder\DynamicContent\Supports\Arr;
use JoomShaper\SPPageBuilder\DynamicContent\Concerns\HasEagerLoading;
use JoomShaper\SPPageBuilder\DynamicContent\Constants\Operators;
use JoomShaper\SPPageBuilder\DynamicContent\Supports\Expression;
class QueryBuilder
{
use HasEagerLoading;
/**
* The database driver instance.
*
* @var DatabaseDriver
* @since 5.5.0
*/
private $db;
/**
* The database query instance.
*
* @var DatabaseQuery
* @since 5.5.0
*/
private $query;
/**
* The limit of the query.
*
* @var int
* @since 5.5.0
*/
private $limit = 0;
/**
* The offset of the query.
*
* @var int
* @since 5.5.0
*/
private $offset = 0;
/**
* The columns of the query.
*
* @var array
* @since 5.5.0
*/
private $columns = [];
/**
* The model instance.
*
* @var Model
* @since 5.5.0
*/
protected $model;
/**
* The data of the query.
*
* @var array
* @since 5.5.0
*/
protected $data;
/**
* The static database driver instance.
*
* @var DatabaseDriver
* @since 5.5.0
*/
protected static $staticDb = null;
/**
* The operators that do not require a value.
*
* @var array
* @since 5.5.0
*/
protected const NO_VALUE_OPERATORS = [Operators::IS_NULL, Operators::IS_NOT_NULL];
/**
* The operators that require a value.
*
* @var array
* @since 5.5.0
*/
protected const IN_OPERATORS = [Operators::IN, Operators::NOT_IN];
/**
* The constructor of the class.
*
* @param Model $model
* @since 5.5.0
*/
public function __construct(Model $model)
{
$this->db = Factory::getDbo();
$this->query = $this->db->getQuery(true);
$this->model = $model;
$this->columns = [];
}
public function setModel(Model $model)
{
$this->model = $model;
return $this;
}
/**
* Get the database query instance.
*
* @return DatabaseQuery
* @since 5.5.0
*/
public function getQuery()
{
return $this->query;
}
/**
* Get the database driver instance.
*
* @return DatabaseDriver
* @since 5.5.0
*/
public function getDatabase()
{
return $this->db;
}
/**
* Get the static database driver instance.
*
* @return DatabaseDriver
* @since 5.5.0
*/
protected static function getStaticDb()
{
if (is_null(self::$staticDb)) {
self::$staticDb = Factory::getDbo();
}
return self::$staticDb;
}
/**
* Clear the query.
*
* @param string $clause The clause
* @since 5.5.0
*/
public function clearQuery($clause = null)
{
if (is_null($clause)) {
$this->query->clear();
} elseif (is_array($clause)) {
foreach ($clause as $item) {
$this->query->clear($item);
}
} else {
$this->query->clear($clause);
}
return $this;
}
/**
* Get the prefix of the query.
*
* @return string
* @since 5.5.0
*/
public function getPrefix()
{
return $this->model->getName();
}
/**
* Quote the column name with the prefix.
*
* @param string $column
* @param string $as
*
* @return string
* @since 5.5.0
*/
public function quoteNameWithPrefix($column, $as = null)
{
$hasPrefix = strpos($column, '.') !== false;
if ($hasPrefix) {
return $this->db->quoteName($column, $as);
}
$prefix = $this->getPrefix();
$column = $prefix . '.' . $column;
return $this->db->quoteName($column, $as);
}
/**
* Quote the column name.
*
* @param string $column
* @param string $as
* @return string
* @since 5.5.0
*/
public function quoteName($column, $as = null)
{
return $this->db->quoteName($column, $as);
}
/**
* Quote the value.
*
* @param string $value
* @return string
* @since 5.5.0
*/
public function quote($value)
{
return $this->db->quote($value);
}
/**
* Set the limit of the query for getting a limited number of records.
*
* @param int $limit
*
* @return self
* @since 5.5.0
*/
public function take(int $limit)
{
$this->limit = $limit;
return $this;
}
/**
* Set the offset of the query for skipping a number of records.
*
* @param int $offset
*
* @return self
* @since 5.5.0
*/
public function skip(int $offset = 0)
{
$this->offset = $offset;
return $this;
}
/**
* Set the columns of the query.
*
* @param array $columns
*
* @return self
* @since 5.5.0
*/
public function setColumns(array $columns = [])
{
$this->columns = $columns;
return $this;
}
/**
* Get the columns of the query.
*
* @return array
* @since 5.5.0
*/
public function getColumns()
{
return $this->columns;
}
/**
* Eager load the relations of the query.
*
* @param string|array $relations
*
* @return self
* @since 5.5.0
*/
public function with($relations)
{
return $this->setEagerRelations($relations);
}
/**
* Performing the AND where conditions.
*
* @param array $conditions
*
* @return self
* @since 5.5.0
*/
public function where($column, $operator = Operators::EQUAL, $value = null)
{
$this->query->where($this->buildWhere($column, $operator, $value));
return $this;
}
/**
* Performing the NOT where conditions.
*
* @param string $column
* @param mixed $value
*
* @return self
* @since 5.5.0
*/
public function whereNot($column, $value)
{
return $this->where($column, Operators::NOT_EQUAL, $value);
}
/**
* Performing the greater than where conditions.
*
* @param string $column
* @param mixed $value
*
* @return self
* @since 5.5.0
*/
public function whereGreaterThan($column, $value)
{
return $this->where($column, Operators::GREATER_THAN, $value);
}
/**
* Performing the less than where conditions.
*
* @param string $column
* @param mixed $value
*
* @return self
* @since 5.5.0
*/
public function whereLessThan($column, $value)
{
return $this->where($column, Operators::LESS_THAN, $value);
}
/**
* Performing the greater than or equal where conditions.
*
* @param string $column
* @param mixed $value
*
* @return self
* @since 5.5.0
*/
public function whereGreaterThanOrEqual($column, $value)
{
return $this->where($column, Operators::GREATER_THAN_OR_EQUAL, $value);
}
/**
* Performing the less than or equal where conditions.
*
* @param string $column
* @param mixed $value
*
* @return self
* @since 5.5.0
*/
public function whereLessThanOrEqual($column, $value)
{
return $this->where($column, Operators::LESS_THAN_OR_EQUAL, $value);
}
/**
* Performing the IN where conditions.
*
* @param string $column
* @param array $values
*
* @return self
* @since 5.5.0
*/
public function whereIn($column, array $values)
{
return $this->where($column, Operators::IN, $values);
}
/**
* Performing the NOT IN where conditions.
*
* @param string $column
* @param array $values
*
* @return self
* @since 5.5.0
*/
public function whereNotIn($column, array $values)
{
return $this->where($column, Operators::NOT_IN, $values);
}
/**
* Performing the IS NULL where conditions.
*
* @param string $column
*
* @return self
* @since 5.5.0
*/
public function whereNull($column)
{
return $this->where($column, Operators::IS_NULL);
}
/**
* Performing the IS NOT NULL where conditions.
*
* @param string $column
*
* @return self
* @since 5.5.0
*/
public function whereNotNull($column)
{
return $this->where($column, Operators::IS_NOT_NULL);
}
/**
* Performing the BETWEEN where conditions.
*
* @param string $column
* @param array $values
*
* @return self
* @since 5.5.0
*/
public function whereBetween($column, array $values)
{
return $this->where($column, Operators::BETWEEN, $values);
}
/**
* Performing the LIKE where conditions.
*
* @param string $column
* @param mixed $value
*
* @return self
* @since 5.5.0
*/
public function whereLike($column, $value)
{
return $this->where($column, Operators::LIKE, $value);
}
/**
* Performing the NOT LIKE where conditions.
*
* @param string $column
* @param mixed $value
*
* @return self
* @since 5.5.0
*/
public function whereNotLike($column, $value)
{
return $this->where($column, Operators::NOT_LIKE, $value);
}
/**
* Performing the EXISTS where conditions.
*
* @param Closure $callback
*
* @return self
* @since 5.5.0
*/
public function whereExists(Closure $callback)
{
$db = $this->getDatabase();
$query = $callback($db);
$this->query->where('EXISTS (' . $query->__toString() . ')');
return $this;
}
/**
* Performing the raw where conditions.
*
* @param Closure $callback
*
* @return self
* @since 5.5.0
*/
public function whereRaw(Closure $callback)
{
$db = $this->getDatabase();
$queryString = $callback($db);
$this->query->where($queryString);
return $this;
}
/**
* Performing the OR where conditions.
*
* @param array $conditions
*
* @return self
* @since 5.5.0
*/
public function orWhere($column, $operator = Operators::EQUAL, $value = null)
{
$this->query->orWhere($this->buildWhere($column, $operator, $value));
return $this;
}
/**
* Performing the raw where conditions.
*
* @param Closure $callback
*
* @return self
* @since 5.5.0
*/
public function rawQuery(Closure $callback)
{
return tap($this, $callback);
}
/**
* Set the columns of the query.
*
* @param array $columns
*
* @return self
* @since 5.5.0
*/
public function select($columns)
{
if (!is_array($columns)) {
$columns = [$columns];
}
$columns = array_map(function ($column, $key) {
if (is_numeric($key)) {
return $this->quoteNameWithPrefix($column);
}
return $this->quoteNameWithPrefix($column, $key);
}, $columns, array_keys($columns));
$this->query->select($columns);
return $this;
}
/**
* Find the record by the primary key value.
*
* @param mixed $primaryKeyValue
*
* @return Model
* @since 5.5.0
*/
public function find($primaryKeyValue)
{
return $this->where($this->model->getPrimaryKey(), $primaryKeyValue)->first();
}
/**
* Set the order of the query.
*
* @param string $column
* @param string $direction
*
* @return self
* @since 5.5.0
*/
public function orderBy($column, $direction = 'ASC')
{
$this->query->order($this->db->escape($this->quoteNameWithPrefix($column)) . ' ' . strtoupper($direction));
return $this;
}
/**
* Join the related model to the query.
*
* @param string $related
* @param string $foreignKey
* @param string $localKey
* @param string $type
*
* @return self
* @since 5.5.0
*/
public function join($related, $foreignKey = null, $localKey = null, $type = 'INNER')
{
$relatedModel = new $related();
$relatedQueryProvider = new static($relatedModel);
$foreignKey = $foreignKey ?? $this->model->getName() . '_' . $relatedModel->getPrimaryKey();
$localKey = $localKey ?? $relatedModel->getPrimaryKey();
$this->query->join(
$type,
$this->db->quoteName($relatedModel->getTable(), $relatedModel->getName()) . ' ON (' .
$relatedQueryProvider->quoteNameWithPrefix($foreignKey) . ' = ' . $this->quoteNameWithPrefix($localKey) . ')'
);
return $this;
}
/**
* Join the related model to the query as a left join.
*
* @param string $related
* @param string $foreignKey
* @param string $localKey
*
* @return self
* @since 5.5.0
*/
public function leftJoin($related, $foreignKey = null, $localKey = null)
{
return $this->join($related, $foreignKey, $localKey, 'LEFT');
}
/**
* Join the related model to the query as a right join.
*
* @param string $related
* @param string $foreignKey
* @param string $localKey
*
* @return self
* @since 5.5.0
*/
public function rightJoin($related, $foreignKey = null, $localKey = null)
{
return $this->join($related, $foreignKey, $localKey, 'RIGHT');
}
/**
* Join the related model to the query as a full join.
*
* @param string $related
* @param string $foreignKey
* @param string $localKey
*
* @return self
* @since 5.5.0
*/
public function fullJoin($related, $foreignKey = null, $localKey = null)
{
return $this->join($related, $foreignKey, $localKey, 'FULL');
}
/**
* Join the related model to the query as an inner join.
*
* @param string $related
* @param string $foreignKey
* @param string $localKey
*
* @return self
* @since 5.5.0
*/
public function innerJoin($related, $foreignKey = null, $localKey = null)
{
return $this->join($related, $foreignKey, $localKey, 'INNER');
}
/**
* Get the data records from the database.
*
* @param array $columns
* @return array
* @since 5.5.0
*/
public function get(array $columns = [])
{
$this->setColumns($columns);
$this->prepareQuery();
$results = $this->db->loadObjectList();
if (empty($results)) {
return [];
}
$models = array_map([$this->model, 'newInstance'], $results);
if ($this->hasEagerRelations()) {
return $this->eagerLoadRelations($models);
}
return $models;
}
/**
* Get the first record from the database.
*
* @param array $columns
*
* @return Model
* @since 5.5.0
*/
public function first(array $columns = [])
{
$this->setColumns($columns);
$this->prepareQuery();
$data = $this->db->loadObject();
$this->model->setItem($data);
// No need to load relations eagerly if the model is empty
if ($this->model->isEmpty()) {
return $this->model;
}
if ($this->hasEagerRelations()) {
$this->eagerLoadRelations([$this->model]);
}
return $this->model;
}
/**
* Check if the record exists.
*
* @return bool
* @since 5.5.0
*/
public function exists()
{
return $this->count() > 0;
}
/**
* Get the total number of records from the database.
*
* @return int
* @since 5.5.0
*/
public function count()
{
$this->clearQuery([
'select',
'from',
'limit',
'offset',
]);
$primaryKey = $this->model->getPrimaryKey();
$this->buildSelect("COUNT({$primaryKey}) as total");
$this->buildFrom($this->model->getTable());
try
{
$this->db->setQuery($this->query);
$this->db->execute();
}
catch (Throwable $error)
{
return 0;
}
return $this->db->loadResult();
}
/**
* Get the total number of records from the database for pagination.
*
* @return int
* @since 5.5.0
*/
public function runPaginationCountQuery()
{
$new = $this->newInstance();
$new->query = $this->cloneWithout(['select', 'from', 'limit', 'offset', 'join']);
$primaryKey = $new->model->getPrimaryKey();
$new->buildSelect("COUNT({$primaryKey}) as total");
$new->buildFrom($new->model->getTable());
try
{
$new->db->setQuery($new->query);
$new->db->execute();
}
catch (Throwable $error)
{
return 0;
}
return $new->db->loadResult();
}
/**
* Get the paginated data from the database.
*
* @param int $perPage
* @param int $currentPage
* @param array $columns
*
* @return array
* @since 5.5.0
*/
public function paginate($perPage = 10, $currentPage = 1, array $columns = [])
{
$this->take($perPage);
$this->skip(($currentPage - 1) * $perPage);
$results = $this->get($columns);
$count = $this->runPaginationCountQuery();
return [
'data' => $results,
'total' => $count,
'per_page' => $perPage,
'current_page' => $currentPage,
'last_page' => ceil($count / $perPage),
'total_pages' => ceil($count / $perPage),
];
}
/**
* Create a new record in the database.
*
* @param array $data
*
* @return int
* @since 5.5.0
*/
public function create(array $data)
{
// If the model has an ordering key, set the ordering value to the latest ordering value + 1
if (!empty($this->model->getOrderingKey())) {
$data[$this->model->getOrderingKey()] = $this->getLatestOrderingValue() + 1;
}
$this->query->insert($this->db->quoteName($this->model->getTable()))
->columns($this->getRecordColumns($data))
->values($this->prepareInsertionValue($data));
try
{
$this->db->setQuery($this->query);
$this->db->execute();
$insertId = $this->db->insertId();
if ($this->model->hasAsset()) {
$this->model->newQuery()
->find($insertId)
->manageAsset();
}
return $insertId;
}
catch (Throwable $error)
{
throw $error;
}
}
/**
* Create multiple records in the database.
*
* @param array $data
*
* @return int
* @since 5.5.0
*/
public function createMany(array $data)
{
if (!empty($orderingKey = $this->model->getOrderingKey())) {
$lastOrderingValue = $this->getLatestOrderingValue();
foreach ($data as &$item) {
$item[$orderingKey] = ++$lastOrderingValue;
}
unset($item);
}
$this->query
->insert($this->db->quoteName($this->model->getTable()))
->columns($this->db->quoteName($this->getRecordColumns($data)));
$values = Arr::make($data)->map(function($record) {
return $this->prepareInsertionValue($record);
});
$this->query->values($values->toArray());
try
{
$this->db->setQuery($this->query);
$this->db->execute();
}
catch (Throwable $error)
{
throw $error;
}
return $this->db->getAffectedRows();
}
/**
* Update a record in the database.
*
* @param array|callable $data
*
* @return bool
* @since 5.5.0
*/
public function update($data)
{
if (is_callable($data)) {
$fields = $data($this);
} else {
$fields = $this->prepareUpdateValues($data);
}
$this->query->update(
$this->db->quoteName(
$this->model->getTable(),
$this->model->getName()
)
)->set($fields);
try
{
$this->db->setQuery($this->query);
return $this->db->execute();
}
catch (Throwable $error)
{
throw $error;
}
}
/**
* Delete a record from the database.
*
* @return bool
* @since 5.5.0
*/
public function delete()
{
$this->query->delete(
$this->quoteName(
$this->model->getTable(),
$this->hasJoinClause() ? $this->model->getName() : null
)
);
if (!$this->hasJoinClause()) {
$this->removeAliasFromWhereClauses();
}
try
{
$this->db->setQuery($this->query);
return $this->db->execute();
}
catch (Throwable $error)
{
throw $error;
}
return false;
}
/**
* Begin the transaction.
*
* @return void
* @since 5.5.0
*/
public static function beginTransaction()
{
$db = static::getStaticDb();
$db->transactionStart();
}
/**
* Commit the transaction.
*
* @return void
* @since 5.5.0
*/
public static function commit()
{
$db = static::getStaticDb();
$db->transactionCommit();
}
/**
* Rollback the transaction.
*
* @return void
* @since 5.5.0
*/
public static function rollback()
{
$db = static::getStaticDb();
$db->transactionRollback();
}
/**
* Execute a callback within a transaction.
*
* @param callable $callback
*
* @return void
* @since 5.5.0
*/
public static function transaction(callable $callback)
{
static::beginTransaction();
try
{
$callback();
}
catch (Throwable $error)
{
static::rollback();
throw $error;
}
static::commit();
}
/**
* Create a raw expression.
*
* @param mixed $value
*
* @return Expression
* @since 5.5.0
*/
public static function raw($value)
{
return new Expression($value);
}
/**
* Check if the query has a join clause.
*
* @return bool
* @since 5.5.1
*/
protected function hasJoinClause()
{
return !empty($this->query->join) && count($this->query->join->getElements()) > 0;
}
/**
* Remove the alias from the where clauses.
*
* @return self
* @since 5.5.1
*/
protected function removeAliasFromWhereClauses()
{
$whereElements = $this->query->where->getElements();
$whereElements = Arr::make($whereElements);
$whereElementsWithoutAlias = $whereElements->map(function($element) {
$dotIndex = strpos($element, '.');
if ($dotIndex !== false) {
return substr($element, $dotIndex + 1);
}
return $element;
});
$this->query->clear('where');
foreach ($whereElementsWithoutAlias as $whereClause) {
$this->query->where($whereClause);
}
return $this;
}
/**
* Set the table of the query.
*
* @param string $table
* @since 5.5.0
*/
protected function buildFrom(string $table)
{
$this->query->from(
$this->db->quoteName($table, $this->model->getName())
);
return $this;
}
/**
* Rearrange the select clause.
* Make sure the '*' is always the first element.
*
* @return self
* @since 5.5.0
*/
protected function rearrangeSelectClause()
{
$select = $this->query->select->getElements();
$elements = Arr::make($select);
$starIndex = $elements->findIndex(function ($element) {
return strpos($element, '*') !== false;
});
if ($starIndex > -1) {
$starElement = $elements[$starIndex];
unset($elements[$starIndex]);
$elements->prepend($starElement);
}
$this->clearQuery('select');
$this->query->select($elements->toArray());
return $this;
}
/**
* Build the select query.
*
* @param array $columns
* @since 5.5.0
*/
protected function buildSelect($columns)
{
if (empty($columns)) {
$this->query->select($this->model->getName() . '.*');
return $this->rearrangeSelectClause();
}
if (!is_array($columns)) {
$columns = [$columns];
}
$this->query->select($columns);
return $this->rearrangeSelectClause();
}
/**
* Build the where query using Joomla quoteName and quote methods.
*
* @param array $conditions
* @since 5.5.0
*/
protected function buildWhere($column, $operator = Operators::EQUAL, $value = null, $boolean = 'AND')
{
// Handle array format: ['name' => 'value', 'age' => 20]
if (is_array($column) && !isset($column[0])) {
$conditions = [];
foreach ($column as $key => $item) {
$conditions[] = $this->quoteNameWithPrefix($key) . ' ' . Operators::EQUAL . ' ' . $this->serialize($item);
}
return implode(" $boolean ", $conditions);
}
// Handle operators that do not require a value, e.g. IS NULL, IS NOT NULL
if (in_array(strtoupper($operator), static::NO_VALUE_OPERATORS, true)) {
return $this->quoteNameWithPrefix($column) . ' ' . $operator;
}
// Handle format: where('name', 'value')
if (is_null($value)) {
$value = $operator;
$operator = Operators::EQUAL;
}
// Handle IN or NOT IN conditions
if (in_array(strtoupper($operator), static::IN_OPERATORS, true)) {
if (!is_array($value) || empty($value)) {
throw new RuntimeException(sprintf('The %s operator requires a non-empty array.', strtoupper($operator)));
}
$quotedValues = array_map([$this, 'serialize'], $value);
$value = '(' . implode(',', $quotedValues) . ')';
return $this->quoteNameWithPrefix($column) . " {$operator} " . $value;
}
if (strtoupper($operator) === Operators::BETWEEN) {
if (!is_array($value) || count($value) !== 2) {
throw new RuntimeException('The BETWEEN operator requires an array with two values.');
}
$operator = Operators::BETWEEN;
return $this->quoteNameWithPrefix($column) . " {$operator} " . $this->serialize($value[0]) . ' AND ' . $this->serialize($value[1]);
}
// Handle where('name', 'like', '%value%')
return $this->quoteNameWithPrefix($column) . ' ' . $operator . ' ' . $this->serialize($value);
}
/**
* Prepare the query for getting data from database.
*
* @return self
* @since 5.5.0
*/
protected function prepareQuery()
{
$this->buildSelect($this->getColumns());
$this->buildFrom($this->model->getTable());
$this->query->setLimit($this->limit, $this->offset);
$this->db->setQuery($this->query);
return $this;
}
/**
* Get the record columns.
*
* @param array $data The data
*
* @return array
* @since 5.5.0
*/
protected function getRecordColumns(array $data)
{
return !Arr::isAssociative($data) && !empty($data) ? array_keys($data[0]) : array_keys($data);
}
/**
* Serialize the value.
*
* @param mixed $value The value
*
* @return mixed
* @since 5.5.0
*/
protected function serialize($value)
{
if (is_null($value)) {
return 'NULL';
}
if (is_integer($value) || is_float($value)) {
return $value;
}
if (is_numeric($value)) {
$isFloat = strpos(strval($value), '.') !== false;
return $isFloat ? floatval($value) : intval($value);
}
if (is_bool($value)) {
return (int) $value;
}
return $this->db->quote($value);
}
/**
* Serialize the values.
*
* @param array $data The data
*
* @return Arr
* @since 5.5.0
*/
protected function serializeValues(array $data)
{
return Arr::make($data)->map(function($item) {
return $this->serialize($item);
});
}
/**
* Prepare the insertion value.
*
* @param array $data The data
*
* @return string
* @since 5.5.0
*/
protected function prepareInsertionValue(array $data)
{
return $this->serializeValues($data)->join(',');
}
/**
* Prepare the update values.
*
* @param array $data The data
*
* @return array
* @since 5.5.0
*/
protected function prepareUpdateValues(array $data)
{
$data = Arr::make($data);
return $data->map(function($item, $key) {
$value = $item instanceof Expression ? (string) $item : $this->serialize($item);
return $this->quoteNameWithPrefix($key) . ' = ' . $value;
})->toArray();
}
/**
* Get the latest ordering value.
*
* @return int
* @since 5.5.0
*/
protected function getLatestOrderingValue()
{
$instance = $this->model->newInstance();
$orderingKey = $this->model->getOrderingKey();
return $instance->orderBy($orderingKey, 'DESC')->first([$orderingKey])->$orderingKey ?? 0;
}
/**
* Create a new instance of the class.
*
* @return static
* @since 5.5.0
*/
protected function newInstance()
{
return new static($this->model);
}
/**
* Clone the query without the specified clauses.
*
* @param array $without
* @return DatabaseQuery
* @since 5.5.0
*/
protected function cloneWithout(array $without)
{
$query = clone $this->getQuery();
foreach ($without as $clause) {
$query->clear($clause);
}
return $query;
}
/**
* Convert the query to a string.
*
* @return string
*
* @return mixed
* @since 5.5.0
*/
public function __toString()
{
return $this->query->dump();
}
/**
* Get the query string.
*
* @return string
* @since 5.5.0
*/
public function queryString()
{
return (string) $this->query;
}
}