<?php<liu21st@gmail.com>declare (strict_types = 1);
namespace think\db;
use think\db\exception\DbException as Exception;
use think\helper\Str;
class Fetch
{
protected $query;
protected $connection;
protected $builder;
public function __construct(Query $query)
{
$this->query = $query;
$this->connection = $query->getConnection();
$this->builder = $this->connection->getBuilder();
}
protected function aggregate(string $aggregate, string $field): string
{
$this->query->parseOptions();
$field = $aggregate . '(' . $this->builder->parseKey($this->query, $field) . ') AS think_' . strtolower($aggregate);
return $this->value($field, 0, false);
}
public function value(string $field, $default = null, bool $one = true): string
{
$options = $this->query->parseOptions();
if (isset($options['field'])) {
$this->query->removeOption('field');
}
$this->query->setOption('field', (array) $field);
$sql = $this->builder->select($this->query, $one);
if (isset($options['field'])) {
$this->query->setOption('field', $options['field']);
} else {
$this->query->removeOption('field');
}
return $this->fetch($sql);
}
public function column(string $field, string $key = ''): string
{
$options = $this->query->parseOptions();
if (isset($options['field'])) {
$this->query->removeOption('field');
}
if ($key && '*' != $field) {
$field = $key . ',' . $field;
}
$field = array_map('trim', explode(',', $field));
$this->query->setOption('field', $field);
$sql = $this->builder->select($this->query);
if (isset($options['field'])) {
$this->query->setOption('field', $options['field']);
} else {
$this->query->removeOption('field');
}
return $this->fetch($sql);
}
public function insert(array $data = []): string
{
$options = $this->query->parseOptions();
if (!empty($data)) {
$this->query->setOption('data', $data);
}
$sql = $this->builder->insert($this->query);
return $this->fetch($sql);
}
public function insertGetId(array $data = []): string
{
return $this->insert($data);
}
public function save(array $data = [], bool $forceInsert = false): string
{
if ($forceInsert) {
return $this->insert($data);
}
$data = array_merge($this->query->getOptions('data') ?: [], $data);
$this->query->setOption('data', $data);
if ($this->query->getOptions('where')) {
$isUpdate = true;
} else {
$isUpdate = $this->query->parseUpdateData($data);
}
return $isUpdate ? $this->update() : $this->insert();
}
public function insertAll(array $dataSet = [], int $limit = null): string
{
$options = $this->query->parseOptions();
if (empty($dataSet)) {
$dataSet = $options['data'];
}
if (empty($limit) && !empty($options['limit'])) {
$limit = $options['limit'];
}
if ($limit) {
$array = array_chunk($dataSet, $limit, true);
$fetchSql = [];
foreach ($array as $item) {
$sql = $this->builder->insertAll($this->query, $item);
$bind = $this->query->getBind();
$fetchSql[] = $this->connection->getRealSql($sql, $bind);
}
return implode(';', $fetchSql);
}
$sql = $this->builder->insertAll($this->query, $dataSet);
return $this->fetch($sql);
}
public function selectInsert(array $fields, string $table): string
{
$this->query->parseOptions();
$sql = $this->builder->selectInsert($this->query, $fields, $table);
return $this->fetch($sql);
}
public function update(array $data = []): string
{
$options = $this->query->parseOptions();
$data = !empty($data) ? $data : $options['data'];
$pk = $this->query->getPk();
if (empty($options['where'])) {
if (is_string($pk) && isset($data[$pk])) {
$this->query->where($pk, '=', $data[$pk]);
unset($data[$pk]);
} elseif (is_array($pk)) {
foreach ($pk as $field) {
if (isset($data[$field])) {
$this->query->where($field, '=', $data[$field]);
} else {
throw new Exception('miss complex primary data');
}
unset($data[$field]);
}
}
if (empty($this->query->getOptions('where'))) {
throw new Exception('miss update condition');
}
}
$this->query->setOption('data', $data);
$sql = $this->builder->update($this->query);
return $this->fetch($sql);
}
public function delete($data = null): string
{
$options = $this->query->parseOptions();
if (!is_null($data) && true !== $data) {
$this->query->parsePkWhere($data);
}
if (!empty($options['soft_delete'])) {
[$field, $condition] = $options['soft_delete'];
if ($condition) {
$this->query->setOption('soft_delete', null);
$this->query->setOption('data', [$field => $condition]);
$sql = $this->builder->delete($this->query);
return $this->fetch($sql);
}
}
$sql = $this->builder->delete($this->query);
return $this->fetch($sql);
}
public function select($data = null): string
{
$this->query->parseOptions();
if (!is_null($data)) {
$this->query->parsePkWhere($data);
}
$sql = $this->builder->select($this->query);
return $this->fetch($sql);
}
public function find($data = null): string
{
$this->query->parseOptions();
if (!is_null($data)) {
$this->query->parsePkWhere($data);
}
$sql = $this->builder->select($this->query, true);
return $this->fetch($sql);
}
public function selectOrFail($data = null): string
{
return $this->select($data);
}
public function findOrFail($data = null): string
{
return $this->find($data);
}
public function findOrEmpty($data = null)
{
return $this->find($data);
}
public function fetch(string $sql): string
{
$bind = $this->query->getBind();
return $this->connection->getRealSql($sql, $bind);
}
public function count(string $field = '*'): string
{
$options = $this->query->parseOptions();
if (!empty($options['group'])) {
$bind = $this->query->getBind();
$subSql = $this->query->options($options)->field('count(' . $field . ') AS think_count')->bind($bind)->buildSql();
$query = $this->query->newQuery()->table([$subSql => '_group_count_']);
return $query->fetchsql()->aggregate('COUNT', '*');
} else {
return $this->aggregate('COUNT', $field);
}
}
public function sum(string $field): string
{
return $this->aggregate('SUM', $field);
}
public function min(string $field): string
{
return $this->aggregate('MIN', $field);
}
public function max(string $field): string
{
return $this->aggregate('MAX', $field);
}
public function avg(string $field): string
{
return $this->aggregate('AVG', $field);
}
public function __call($method, $args)
{
if (strtolower(substr($method, 0, 5)) == 'getby') {
$field = Str::snake(substr($method, 5));
return $this->where($field, '=', $args[0])->find();
} elseif (strtolower(substr($method, 0, 10)) == 'getfieldby') {
$name = Str::snake(substr($method, 10));
return $this->where($name, '=', $args[0])->value($args[1]);
}
$result = call_user_func_array([$this->query, $method], $args);
return $result === $this->query ? $this : $result;
}
}