v.0.1
This commit is contained in:
227
vendor/illuminate/database/Concerns/BuildsQueries.php
vendored
Normal file
227
vendor/illuminate/database/Concerns/BuildsQueries.php
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Database\Concerns;
|
||||
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
|
||||
trait BuildsQueries
|
||||
{
|
||||
/**
|
||||
* Chunk the results of the query.
|
||||
*
|
||||
* @param int $count
|
||||
* @param callable $callback
|
||||
* @return bool
|
||||
*/
|
||||
public function chunk($count, callable $callback)
|
||||
{
|
||||
$this->enforceOrderBy();
|
||||
|
||||
$page = 1;
|
||||
|
||||
do {
|
||||
// We'll execute the query for the given page and get the results. If there are
|
||||
// no results we can just break and return from here. When there are results
|
||||
// we will call the callback with the current chunk of these results here.
|
||||
$results = $this->forPage($page, $count)->get();
|
||||
|
||||
$countResults = $results->count();
|
||||
|
||||
if ($countResults == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// On each chunk result set, we will pass them to the callback and then let the
|
||||
// developer take care of everything within the callback, which allows us to
|
||||
// keep the memory low for spinning through large result sets for working.
|
||||
if ($callback($results, $page) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($results);
|
||||
|
||||
$page++;
|
||||
} while ($countResults == $count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a callback over each item while chunking.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param int $count
|
||||
* @return bool
|
||||
*/
|
||||
public function each(callable $callback, $count = 1000)
|
||||
{
|
||||
return $this->chunk($count, function ($results) use ($callback) {
|
||||
foreach ($results as $key => $value) {
|
||||
if ($callback($value, $key) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk the results of a query by comparing IDs.
|
||||
*
|
||||
* @param int $count
|
||||
* @param callable $callback
|
||||
* @param string|null $column
|
||||
* @param string|null $alias
|
||||
* @return bool
|
||||
*/
|
||||
public function chunkById($count, callable $callback, $column = null, $alias = null)
|
||||
{
|
||||
$column = $column ?? $this->defaultKeyName();
|
||||
|
||||
$alias = $alias ?? $column;
|
||||
|
||||
$lastId = null;
|
||||
|
||||
do {
|
||||
$clone = clone $this;
|
||||
|
||||
// We'll execute the query for the given page and get the results. If there are
|
||||
// no results we can just break and return from here. When there are results
|
||||
// we will call the callback with the current chunk of these results here.
|
||||
$results = $clone->forPageAfterId($count, $lastId, $column)->get();
|
||||
|
||||
$countResults = $results->count();
|
||||
|
||||
if ($countResults == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// On each chunk result set, we will pass them to the callback and then let the
|
||||
// developer take care of everything within the callback, which allows us to
|
||||
// keep the memory low for spinning through large result sets for working.
|
||||
if ($callback($results) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$lastId = $results->last()->{$alias};
|
||||
|
||||
unset($results);
|
||||
} while ($countResults == $count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a callback over each item while chunking by ID.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param int $count
|
||||
* @param string|null $column
|
||||
* @param string|null $alias
|
||||
* @return bool
|
||||
*/
|
||||
public function eachById(callable $callback, $count = 1000, $column = null, $alias = null)
|
||||
{
|
||||
return $this->chunkById($count, function ($results) use ($callback) {
|
||||
foreach ($results as $key => $value) {
|
||||
if ($callback($value, $key) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}, $column, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query and get the first result.
|
||||
*
|
||||
* @param array|string $columns
|
||||
* @return \Illuminate\Database\Eloquent\Model|object|static|null
|
||||
*/
|
||||
public function first($columns = ['*'])
|
||||
{
|
||||
return $this->take(1)->get($columns)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the callback's query changes if the given "value" is true.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param callable $callback
|
||||
* @param callable|null $default
|
||||
* @return mixed|$this
|
||||
*/
|
||||
public function when($value, $callback, $default = null)
|
||||
{
|
||||
if ($value) {
|
||||
return $callback($this, $value) ?: $this;
|
||||
} elseif ($default) {
|
||||
return $default($this, $value) ?: $this;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the query to a given callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function tap($callback)
|
||||
{
|
||||
return $this->when(true, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the callback's query changes if the given "value" is false.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param callable $callback
|
||||
* @param callable|null $default
|
||||
* @return mixed|$this
|
||||
*/
|
||||
public function unless($value, $callback, $default = null)
|
||||
{
|
||||
if (! $value) {
|
||||
return $callback($this, $value) ?: $this;
|
||||
} elseif ($default) {
|
||||
return $default($this, $value) ?: $this;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new length-aware paginator instance.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $items
|
||||
* @param int $total
|
||||
* @param int $perPage
|
||||
* @param int $currentPage
|
||||
* @param array $options
|
||||
* @return \Illuminate\Pagination\LengthAwarePaginator
|
||||
*/
|
||||
protected function paginator($items, $total, $perPage, $currentPage, $options)
|
||||
{
|
||||
return Container::getInstance()->makeWith(LengthAwarePaginator::class, compact(
|
||||
'items', 'total', 'perPage', 'currentPage', 'options'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new simple paginator instance.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $items
|
||||
* @param int $perPage
|
||||
* @param int $currentPage
|
||||
* @param array $options
|
||||
* @return \Illuminate\Pagination\Paginator
|
||||
*/
|
||||
protected function simplePaginator($items, $perPage, $currentPage, $options)
|
||||
{
|
||||
return Container::getInstance()->makeWith(Paginator::class, compact(
|
||||
'items', 'perPage', 'currentPage', 'options'
|
||||
));
|
||||
}
|
||||
}
|
292
vendor/illuminate/database/Concerns/ManagesTransactions.php
vendored
Normal file
292
vendor/illuminate/database/Concerns/ManagesTransactions.php
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
namespace Illuminate\Database\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Throwable;
|
||||
|
||||
trait ManagesTransactions
|
||||
{
|
||||
/**
|
||||
* Execute a Closure within a transaction.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @param int $attempts
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function transaction(Closure $callback, $attempts = 1)
|
||||
{
|
||||
for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
|
||||
$this->beginTransaction();
|
||||
|
||||
// We'll simply execute the given callback within a try / catch block and if we
|
||||
// catch any exception we can rollback this transaction so that none of this
|
||||
// gets actually persisted to a database or stored in a permanent fashion.
|
||||
try {
|
||||
$callbackResult = $callback($this);
|
||||
}
|
||||
|
||||
// If we catch an exception we'll rollback this transaction and try again if we
|
||||
// are not out of attempts. If we are out of attempts we will just throw the
|
||||
// exception back out and let the developer handle an uncaught exceptions.
|
||||
catch (Throwable $e) {
|
||||
$this->handleTransactionException(
|
||||
$e, $currentAttempt, $attempts
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->transactions == 1) {
|
||||
$this->getPdo()->commit();
|
||||
}
|
||||
|
||||
$this->transactions = max(0, $this->transactions - 1);
|
||||
} catch (Throwable $e) {
|
||||
$this->handleCommitTransactionException(
|
||||
$e, $currentAttempt, $attempts
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->fireConnectionEvent('committed');
|
||||
|
||||
return $callbackResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an exception encountered when running a transacted statement.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @param int $currentAttempt
|
||||
* @param int $maxAttempts
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function handleTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
|
||||
{
|
||||
// On a deadlock, MySQL rolls back the entire transaction so we can't just
|
||||
// retry the query. We have to throw this exception all the way out and
|
||||
// let the developer handle it in another way. We will decrement too.
|
||||
if ($this->causedByConcurrencyError($e) &&
|
||||
$this->transactions > 1) {
|
||||
$this->transactions--;
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// If there was an exception we will rollback this transaction and then we
|
||||
// can check if we have exceeded the maximum attempt count for this and
|
||||
// if we haven't we will return and try this query again in our loop.
|
||||
$this->rollBack();
|
||||
|
||||
if ($this->causedByConcurrencyError($e) &&
|
||||
$currentAttempt < $maxAttempts) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new database transaction.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->createTransaction();
|
||||
|
||||
$this->transactions++;
|
||||
|
||||
$this->fireConnectionEvent('beganTransaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a transaction within the database.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function createTransaction()
|
||||
{
|
||||
if ($this->transactions == 0) {
|
||||
$this->reconnectIfMissingConnection();
|
||||
|
||||
try {
|
||||
$this->getPdo()->beginTransaction();
|
||||
} catch (Throwable $e) {
|
||||
$this->handleBeginTransactionException($e);
|
||||
}
|
||||
} elseif ($this->transactions >= 1 && $this->queryGrammar->supportsSavepoints()) {
|
||||
$this->createSavepoint();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a save point within the database.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function createSavepoint()
|
||||
{
|
||||
$this->getPdo()->exec(
|
||||
$this->queryGrammar->compileSavepoint('trans'.($this->transactions + 1))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an exception from a transaction beginning.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function handleBeginTransactionException(Throwable $e)
|
||||
{
|
||||
if ($this->causedByLostConnection($e)) {
|
||||
$this->reconnect();
|
||||
|
||||
$this->getPdo()->beginTransaction();
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the active database transaction.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
if ($this->transactions == 1) {
|
||||
$this->getPdo()->commit();
|
||||
}
|
||||
|
||||
$this->transactions = max(0, $this->transactions - 1);
|
||||
|
||||
$this->fireConnectionEvent('committed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an exception encountered when committing a transaction.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @param int $currentAttempt
|
||||
* @param int $maxAttempts
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function handleCommitTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
|
||||
{
|
||||
$this->transactions = max(0, $this->transactions - 1);
|
||||
|
||||
if ($this->causedByConcurrencyError($e) &&
|
||||
$currentAttempt < $maxAttempts) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->causedByLostConnection($e)) {
|
||||
$this->transactions = 0;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback the active database transaction.
|
||||
*
|
||||
* @param int|null $toLevel
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function rollBack($toLevel = null)
|
||||
{
|
||||
// We allow developers to rollback to a certain transaction level. We will verify
|
||||
// that this given transaction level is valid before attempting to rollback to
|
||||
// that level. If it's not we will just return out and not attempt anything.
|
||||
$toLevel = is_null($toLevel)
|
||||
? $this->transactions - 1
|
||||
: $toLevel;
|
||||
|
||||
if ($toLevel < 0 || $toLevel >= $this->transactions) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Next, we will actually perform this rollback within this database and fire the
|
||||
// rollback event. We will also set the current transaction level to the given
|
||||
// level that was passed into this method so it will be right from here out.
|
||||
try {
|
||||
$this->performRollBack($toLevel);
|
||||
} catch (Throwable $e) {
|
||||
$this->handleRollBackException($e);
|
||||
}
|
||||
|
||||
$this->transactions = $toLevel;
|
||||
|
||||
$this->fireConnectionEvent('rollingBack');
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a rollback within the database.
|
||||
*
|
||||
* @param int $toLevel
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function performRollBack($toLevel)
|
||||
{
|
||||
if ($toLevel == 0) {
|
||||
$this->getPdo()->rollBack();
|
||||
} elseif ($this->queryGrammar->supportsSavepoints()) {
|
||||
$this->getPdo()->exec(
|
||||
$this->queryGrammar->compileSavepointRollBack('trans'.($toLevel + 1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an exception from a rollback.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function handleRollBackException(Throwable $e)
|
||||
{
|
||||
if ($this->causedByLostConnection($e)) {
|
||||
$this->transactions = 0;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of active transactions.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function transactionLevel()
|
||||
{
|
||||
return $this->transactions;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user