diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 7e54581c2a..0000000000
--- a/.gitattributes
+++ /dev/null
@@ -1,2 +0,0 @@
-/.github export-ignore
-.gitattributes export-ignore
diff --git a/.github/workflows/close-pull-request.yml b/.github/workflows/close-pull-request.yml
deleted file mode 100644
index 6cbfcf0671..0000000000
--- a/.github/workflows/close-pull-request.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: Close Pull Request
-
-on:
- pull_request_target:
- types: [opened]
-
-jobs:
- run:
- runs-on: ubuntu-24.04
- steps:
- - uses: superbrothers/close-pull-request@v3
- with:
- comment: "Thank you for your pull request. However, you have submitted this PR on the Illuminate organization which is a read-only sub split of `laravel/framework`. Please submit your PR on the https://github.com/laravel/framework repository.
Thanks!"
diff --git a/Capsule/Manager.php b/Capsule/Manager.php
index ddcc85dcf7..b82a792ce7 100755
--- a/Capsule/Manager.php
+++ b/Capsule/Manager.php
@@ -2,13 +2,13 @@
namespace Illuminate\Database\Capsule;
+use PDO;
use Illuminate\Container\Container;
-use Illuminate\Contracts\Events\Dispatcher;
-use Illuminate\Database\Connectors\ConnectionFactory;
use Illuminate\Database\DatabaseManager;
-use Illuminate\Database\Eloquent\Model as Eloquent;
+use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Traits\CapsuleManagerTrait;
-use PDO;
+use Illuminate\Database\Eloquent\Model as Eloquent;
+use Illuminate\Database\Connectors\ConnectionFactory;
class Manager
{
@@ -25,8 +25,9 @@ class Manager
* Create a new database capsule manager.
*
* @param \Illuminate\Container\Container|null $container
+ * @return void
*/
- public function __construct(?Container $container = null)
+ public function __construct(Container $container = null)
{
$this->setupContainer($container ?: new Container);
@@ -65,7 +66,7 @@ protected function setupManager()
/**
* Get a connection instance from the global manager.
*
- * @param string|null $connection
+ * @param string $connection
* @return \Illuminate\Database\Connection
*/
public static function connection($connection = null)
@@ -76,20 +77,19 @@ public static function connection($connection = null)
/**
* Get a fluent query builder instance.
*
- * @param \Closure|\Illuminate\Database\Query\Builder|string $table
- * @param string|null $as
- * @param string|null $connection
+ * @param string $table
+ * @param string $connection
* @return \Illuminate\Database\Query\Builder
*/
- public static function table($table, $as = null, $connection = null)
+ public static function table($table, $connection = null)
{
- return static::$instance->connection($connection)->table($table, $as);
+ return static::$instance->connection($connection)->table($table);
}
/**
* Get a schema builder instance.
*
- * @param string|null $connection
+ * @param string $connection
* @return \Illuminate\Database\Schema\Builder
*/
public static function schema($connection = null)
@@ -100,7 +100,7 @@ public static function schema($connection = null)
/**
* Get a registered connection instance.
*
- * @param string|null $name
+ * @param string $name
* @return \Illuminate\Database\Connection
*/
public function getConnection($name = null)
@@ -111,7 +111,7 @@ public function getConnection($name = null)
/**
* Register a connection with the manager.
*
- * @param array $config
+ * @param array $config
* @param string $name
* @return void
*/
@@ -191,7 +191,7 @@ public function setEventDispatcher(Dispatcher $dispatcher)
* Dynamically pass methods to the default connection.
*
* @param string $method
- * @param array $parameters
+ * @param array $parameters
* @return mixed
*/
public static function __callStatic($method, $parameters)
diff --git a/ClassMorphViolationException.php b/ClassMorphViolationException.php
deleted file mode 100644
index 6594d2d902..0000000000
--- a/ClassMorphViolationException.php
+++ /dev/null
@@ -1,29 +0,0 @@
-model = $class;
- }
-}
diff --git a/Concerns/BuildsQueries.php b/Concerns/BuildsQueries.php
index 537594e085..e56e020e82 100644
--- a/Concerns/BuildsQueries.php
+++ b/Concerns/BuildsQueries.php
@@ -2,194 +2,26 @@
namespace Illuminate\Database\Concerns;
-use Illuminate\Container\Container;
-use Illuminate\Database\Eloquent\Builder;
-use Illuminate\Database\MultipleRecordsFoundException;
-use Illuminate\Database\Query\Expression;
-use Illuminate\Database\RecordNotFoundException;
-use Illuminate\Database\RecordsNotFoundException;
-use Illuminate\Pagination\Cursor;
-use Illuminate\Pagination\CursorPaginator;
-use Illuminate\Pagination\LengthAwarePaginator;
-use Illuminate\Pagination\Paginator;
-use Illuminate\Support\Collection;
-use Illuminate\Support\LazyCollection;
-use Illuminate\Support\Str;
-use Illuminate\Support\Traits\Conditionable;
-use InvalidArgumentException;
-use RuntimeException;
-
-/**
- * @template TValue
- *
- * @mixin \Illuminate\Database\Query\Builder
- */
trait BuildsQueries
{
- use Conditionable;
-
/**
* Chunk the results of the query.
*
* @param int $count
- * @param callable(\Illuminate\Support\Collection, int): mixed $callback
+ * @param callable $callback
* @return bool
*/
public function chunk($count, callable $callback)
{
$this->enforceOrderBy();
- $skip = $this->getOffset();
- $remaining = $this->getLimit();
-
$page = 1;
do {
- $offset = (($page - 1) * $count) + (int) $skip;
-
- $limit = is_null($remaining) ? $count : min($count, $remaining);
-
- if ($limit == 0) {
- break;
- }
-
- $results = $this->offset($offset)->limit($limit)->get();
-
- $countResults = $results->count();
-
- if ($countResults == 0) {
- break;
- }
-
- if (! is_null($remaining)) {
- $remaining = max($remaining - $countResults, 0);
- }
-
- if ($callback($results, $page) === false) {
- return false;
- }
-
- unset($results);
-
- $page++;
- } while ($countResults == $count);
-
- return true;
- }
-
- /**
- * Run a map over each item while chunking.
- *
- * @template TReturn
- *
- * @param callable(TValue): TReturn $callback
- * @param int $count
- * @return \Illuminate\Support\Collection
- */
- public function chunkMap(callable $callback, $count = 1000)
- {
- $collection = new Collection;
-
- $this->chunk($count, function ($items) use ($collection, $callback) {
- $items->each(function ($item) use ($collection, $callback) {
- $collection->push($callback($item));
- });
- });
-
- return $collection;
- }
-
- /**
- * Execute a callback over each item while chunking.
- *
- * @param callable(TValue, int): mixed $callback
- * @param int $count
- * @return bool
- *
- * @throws \RuntimeException
- */
- 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(\Illuminate\Support\Collection, int): mixed $callback
- * @param string|null $column
- * @param string|null $alias
- * @return bool
- */
- public function chunkById($count, callable $callback, $column = null, $alias = null)
- {
- return $this->orderedChunkById($count, $callback, $column, $alias);
- }
-
- /**
- * Chunk the results of a query by comparing IDs in descending order.
- *
- * @param int $count
- * @param callable(\Illuminate\Support\Collection, int): mixed $callback
- * @param string|null $column
- * @param string|null $alias
- * @return bool
- */
- public function chunkByIdDesc($count, callable $callback, $column = null, $alias = null)
- {
- return $this->orderedChunkById($count, $callback, $column, $alias, descending: true);
- }
-
- /**
- * Chunk the results of a query by comparing IDs in a given order.
- *
- * @param int $count
- * @param callable(\Illuminate\Support\Collection, int): mixed $callback
- * @param string|null $column
- * @param string|null $alias
- * @param bool $descending
- * @return bool
- *
- * @throws \RuntimeException
- */
- public function orderedChunkById($count, callable $callback, $column = null, $alias = null, $descending = false)
- {
- $column ??= $this->defaultKeyName();
- $alias ??= $column;
- $lastId = null;
- $skip = $this->getOffset();
- $remaining = $this->getLimit();
-
- $page = 1;
-
- do {
- $clone = clone $this;
-
- if ($skip && $page > 1) {
- $clone->offset(0);
- }
-
- $limit = is_null($remaining) ? $count : min($count, $remaining);
-
- if ($limit == 0) {
- break;
- }
-
// 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.
- if ($descending) {
- $results = $clone->forPageBeforeId($limit, $lastId, $column)->get();
- } else {
- $results = $clone->forPageAfterId($limit, $lastId, $column)->get();
- }
+ $results = $this->forPage($page, $count)->get();
$countResults = $results->count();
@@ -197,25 +29,13 @@ public function orderedChunkById($count, callable $callback, $column = null, $al
break;
}
- if (! is_null($remaining)) {
- $remaining = max($remaining - $countResults, 0);
- }
-
// 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) {
+ if ($callback($results) === false) {
return false;
}
- $lastId = data_get($results->last(), $alias);
-
- if ($lastId === null) {
- throw new RuntimeException("The chunkById operation was aborted because the [{$alias}] column is not present in the query result.");
- }
-
- unset($results);
-
$page++;
} while ($countResults == $count);
@@ -223,393 +43,50 @@ public function orderedChunkById($count, callable $callback, $column = null, $al
}
/**
- * Execute a callback over each item while chunking by ID.
+ * Execute a callback over each item while chunking.
*
- * @param callable(TValue, int): mixed $callback
+ * @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)
+ public function each(callable $callback, $count = 1000)
{
- return $this->chunkById($count, function ($results, $page) use ($callback, $count) {
+ return $this->chunk($count, function ($results) use ($callback) {
foreach ($results as $key => $value) {
- if ($callback($value, (($page - 1) * $count) + $key) === false) {
+ if ($callback($value, $key) === false) {
return false;
}
}
- }, $column, $alias);
- }
-
- /**
- * Query lazily, by chunks of the given size.
- *
- * @param int $chunkSize
- * @return \Illuminate\Support\LazyCollection
- *
- * @throws \InvalidArgumentException
- */
- public function lazy($chunkSize = 1000)
- {
- if ($chunkSize < 1) {
- throw new InvalidArgumentException('The chunk size should be at least 1');
- }
-
- $this->enforceOrderBy();
-
- return new LazyCollection(function () use ($chunkSize) {
- $page = 1;
-
- while (true) {
- $results = $this->forPage($page++, $chunkSize)->get();
-
- foreach ($results as $result) {
- yield $result;
- }
-
- if ($results->count() < $chunkSize) {
- return;
- }
- }
- });
- }
-
- /**
- * Query lazily, by chunking the results of a query by comparing IDs.
- *
- * @param int $chunkSize
- * @param string|null $column
- * @param string|null $alias
- * @return \Illuminate\Support\LazyCollection
- *
- * @throws \InvalidArgumentException
- */
- public function lazyById($chunkSize = 1000, $column = null, $alias = null)
- {
- return $this->orderedLazyById($chunkSize, $column, $alias);
- }
-
- /**
- * Query lazily, by chunking the results of a query by comparing IDs in descending order.
- *
- * @param int $chunkSize
- * @param string|null $column
- * @param string|null $alias
- * @return \Illuminate\Support\LazyCollection
- *
- * @throws \InvalidArgumentException
- */
- public function lazyByIdDesc($chunkSize = 1000, $column = null, $alias = null)
- {
- return $this->orderedLazyById($chunkSize, $column, $alias, true);
- }
-
- /**
- * Query lazily, by chunking the results of a query by comparing IDs in a given order.
- *
- * @param int $chunkSize
- * @param string|null $column
- * @param string|null $alias
- * @param bool $descending
- * @return \Illuminate\Support\LazyCollection
- *
- * @throws \InvalidArgumentException
- * @throws \RuntimeException
- */
- protected function orderedLazyById($chunkSize = 1000, $column = null, $alias = null, $descending = false)
- {
- if ($chunkSize < 1) {
- throw new InvalidArgumentException('The chunk size should be at least 1');
- }
-
- $column ??= $this->defaultKeyName();
-
- $alias ??= $column;
-
- return new LazyCollection(function () use ($chunkSize, $column, $alias, $descending) {
- $lastId = null;
-
- while (true) {
- $clone = clone $this;
-
- if ($descending) {
- $results = $clone->forPageBeforeId($chunkSize, $lastId, $column)->get();
- } else {
- $results = $clone->forPageAfterId($chunkSize, $lastId, $column)->get();
- }
-
- foreach ($results as $result) {
- yield $result;
- }
-
- if ($results->count() < $chunkSize) {
- return;
- }
-
- $lastId = $results->last()->{$alias};
-
- if ($lastId === null) {
- throw new RuntimeException("The lazyById operation was aborted because the [{$alias}] column is not present in the query result.");
- }
- }
});
}
/**
* Execute the query and get the first result.
*
- * @param array|string $columns
- * @return TValue|null
+ * @param array $columns
+ * @return mixed
*/
public function first($columns = ['*'])
{
- return $this->limit(1)->get($columns)->first();
- }
-
- /**
- * Execute the query and get the first result or throw an exception.
- *
- * @param array|string $columns
- * @param string|null $message
- * @return TValue
- *
- * @throws \Illuminate\Database\RecordNotFoundException
- */
- public function firstOrFail($columns = ['*'], $message = null)
- {
- if (! is_null($result = $this->first($columns))) {
- return $result;
- }
-
- throw new RecordNotFoundException($message ?: 'No record found for the given query.');
- }
-
- /**
- * Execute the query and get the first result if it's the sole matching record.
- *
- * @param array|string $columns
- * @return TValue
- *
- * @throws \Illuminate\Database\RecordsNotFoundException
- * @throws \Illuminate\Database\MultipleRecordsFoundException
- */
- public function sole($columns = ['*'])
- {
- $result = $this->limit(2)->get($columns);
-
- $count = $result->count();
-
- if ($count === 0) {
- throw new RecordsNotFoundException;
- }
-
- if ($count > 1) {
- throw new MultipleRecordsFoundException($count);
- }
-
- return $result->first();
- }
-
- /**
- * Paginate the given query using a cursor paginator.
- *
- * @param int $perPage
- * @param array|string $columns
- * @param string $cursorName
- * @param \Illuminate\Pagination\Cursor|string|null $cursor
- * @return \Illuminate\Contracts\Pagination\CursorPaginator
- */
- protected function paginateUsingCursor($perPage, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
- {
- if (! $cursor instanceof Cursor) {
- $cursor = is_string($cursor)
- ? Cursor::fromEncoded($cursor)
- : CursorPaginator::resolveCurrentCursor($cursorName, $cursor);
- }
-
- $orders = $this->ensureOrderForCursorPagination(! is_null($cursor) && $cursor->pointsToPreviousItems());
-
- if (! is_null($cursor)) {
- // Reset the union bindings so we can add the cursor where in the correct position...
- $this->setBindings([], 'union');
-
- $addCursorConditions = function (self $builder, $previousColumn, $originalColumn, $i) use (&$addCursorConditions, $cursor, $orders) {
- $unionBuilders = $builder->getUnionBuilders();
-
- if (! is_null($previousColumn)) {
- $originalColumn ??= $this->getOriginalColumnNameForCursorPagination($this, $previousColumn);
-
- $builder->where(
- Str::contains($originalColumn, ['(', ')']) ? new Expression($originalColumn) : $originalColumn,
- '=',
- $cursor->parameter($previousColumn)
- );
-
- $unionBuilders->each(function ($unionBuilder) use ($previousColumn, $cursor) {
- $unionBuilder->where(
- $this->getOriginalColumnNameForCursorPagination($unionBuilder, $previousColumn),
- '=',
- $cursor->parameter($previousColumn)
- );
-
- $this->addBinding($unionBuilder->getRawBindings()['where'], 'union');
- });
- }
-
- $builder->where(function (self $secondBuilder) use ($addCursorConditions, $cursor, $orders, $i, $unionBuilders) {
- ['column' => $column, 'direction' => $direction] = $orders[$i];
-
- $originalColumn = $this->getOriginalColumnNameForCursorPagination($this, $column);
-
- $secondBuilder->where(
- Str::contains($originalColumn, ['(', ')']) ? new Expression($originalColumn) : $originalColumn,
- $direction === 'asc' ? '>' : '<',
- $cursor->parameter($column)
- );
-
- if ($i < $orders->count() - 1) {
- $secondBuilder->orWhere(function (self $thirdBuilder) use ($addCursorConditions, $column, $originalColumn, $i) {
- $addCursorConditions($thirdBuilder, $column, $originalColumn, $i + 1);
- });
- }
-
- $unionBuilders->each(function ($unionBuilder) use ($column, $direction, $cursor, $i, $orders, $addCursorConditions) {
- $unionWheres = $unionBuilder->getRawBindings()['where'];
-
- $originalColumn = $this->getOriginalColumnNameForCursorPagination($unionBuilder, $column);
- $unionBuilder->where(function ($unionBuilder) use ($column, $direction, $cursor, $i, $orders, $addCursorConditions, $originalColumn, $unionWheres) {
- $unionBuilder->where(
- $originalColumn,
- $direction === 'asc' ? '>' : '<',
- $cursor->parameter($column)
- );
-
- if ($i < $orders->count() - 1) {
- $unionBuilder->orWhere(function (self $fourthBuilder) use ($addCursorConditions, $column, $originalColumn, $i) {
- $addCursorConditions($fourthBuilder, $column, $originalColumn, $i + 1);
- });
- }
-
- $this->addBinding($unionWheres, 'union');
- $this->addBinding($unionBuilder->getRawBindings()['where'], 'union');
- });
- });
- });
- };
-
- $addCursorConditions($this, null, null, 0);
- }
-
- $this->limit($perPage + 1);
-
- return $this->cursorPaginator($this->get($columns), $perPage, $cursor, [
- 'path' => Paginator::resolveCurrentPath(),
- 'cursorName' => $cursorName,
- 'parameters' => $orders->pluck('column')->toArray(),
- ]);
+ return $this->take(1)->get($columns)->first();
}
/**
- * Get the original column name of the given column, without any aliasing.
+ * Apply the callback's query changes if the given "value" is true.
*
- * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder<*> $builder
- * @param string $parameter
- * @return string
+ * @param mixed $value
+ * @param \Closure $callback
+ * @param \Closure $default
+ * @return mixed
*/
- protected function getOriginalColumnNameForCursorPagination($builder, string $parameter)
+ public function when($value, $callback, $default = null)
{
- $columns = $builder instanceof Builder ? $builder->getQuery()->getColumns() : $builder->getColumns();
-
- if (! is_null($columns)) {
- foreach ($columns as $column) {
- if (($position = strripos($column, ' as ')) !== false) {
- $original = substr($column, 0, $position);
-
- $alias = substr($column, $position + 4);
-
- if ($parameter === $alias || $builder->getGrammar()->wrap($parameter) === $alias) {
- return $original;
- }
- }
- }
+ if ($value) {
+ return $callback($this, $value) ?: $this;
+ } elseif ($default) {
+ return $default($this, $value) ?: $this;
}
- return $parameter;
- }
-
- /**
- * 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'
- ));
- }
-
- /**
- * Create a new cursor paginator instance.
- *
- * @param \Illuminate\Support\Collection $items
- * @param int $perPage
- * @param \Illuminate\Pagination\Cursor $cursor
- * @param array $options
- * @return \Illuminate\Pagination\CursorPaginator
- */
- protected function cursorPaginator($items, $perPage, $cursor, $options)
- {
- return Container::getInstance()->makeWith(CursorPaginator::class, compact(
- 'items', 'perPage', 'cursor', 'options'
- ));
- }
-
- /**
- * Pass the query to a given callback and then return it.
- *
- * @param callable($this): mixed $callback
- * @return $this
- */
- public function tap($callback)
- {
- $callback($this);
-
return $this;
}
-
- /**
- * Pass the query to a given callback and return the result.
- *
- * @template TReturn
- *
- * @param (callable($this): TReturn) $callback
- * @return (TReturn is null|void ? $this : TReturn)
- */
- public function pipe($callback)
- {
- return $callback($this) ?? $this;
- }
}
diff --git a/Concerns/BuildsWhereDateClauses.php b/Concerns/BuildsWhereDateClauses.php
deleted file mode 100644
index 06da844273..0000000000
--- a/Concerns/BuildsWhereDateClauses.php
+++ /dev/null
@@ -1,249 +0,0 @@
-wherePastOrFuture($columns, '<', 'and');
- }
-
- /**
- * Add a where clause to determine if a "date" column is in the past or now to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function whereNowOrPast($columns)
- {
- return $this->wherePastOrFuture($columns, '<=', 'and');
- }
-
- /**
- * Add an "or where" clause to determine if a "date" column is in the past to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWherePast($columns)
- {
- return $this->wherePastOrFuture($columns, '<', 'or');
- }
-
- /**
- * Add a where clause to determine if a "date" column is in the past or now to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWhereNowOrPast($columns)
- {
- return $this->wherePastOrFuture($columns, '<=', 'or');
- }
-
- /**
- * Add a where clause to determine if a "date" column is in the future to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function whereFuture($columns)
- {
- return $this->wherePastOrFuture($columns, '>', 'and');
- }
-
- /**
- * Add a where clause to determine if a "date" column is in the future or now to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function whereNowOrFuture($columns)
- {
- return $this->wherePastOrFuture($columns, '>=', 'and');
- }
-
- /**
- * Add an "or where" clause to determine if a "date" column is in the future to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWhereFuture($columns)
- {
- return $this->wherePastOrFuture($columns, '>', 'or');
- }
-
- /**
- * Add an "or where" clause to determine if a "date" column is in the future or now to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWhereNowOrFuture($columns)
- {
- return $this->wherePastOrFuture($columns, '>=', 'or');
- }
-
- /**
- * Add an "where" clause to determine if a "date" column is in the past or future.
- *
- * @param array|string $columns
- * @param string $operator
- * @param string $boolean
- * @return $this
- */
- protected function wherePastOrFuture($columns, $operator, $boolean)
- {
- $type = 'Basic';
- $value = Carbon::now();
-
- foreach (Arr::wrap($columns) as $column) {
- $this->wheres[] = compact('type', 'column', 'boolean', 'operator', 'value');
-
- $this->addBinding($value);
- }
-
- return $this;
- }
-
- /**
- * Add a "where date" clause to determine if a "date" column is today to the query.
- *
- * @param array|string $columns
- * @param string $boolean
- * @return $this
- */
- public function whereToday($columns, $boolean = 'and')
- {
- return $this->whereTodayBeforeOrAfter($columns, '=', $boolean);
- }
-
- /**
- * Add a "where date" clause to determine if a "date" column is before today.
- *
- * @param array|string $columns
- * @return $this
- */
- public function whereBeforeToday($columns)
- {
- return $this->whereTodayBeforeOrAfter($columns, '<', 'and');
- }
-
- /**
- * Add a "where date" clause to determine if a "date" column is today or before to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function whereTodayOrBefore($columns)
- {
- return $this->whereTodayBeforeOrAfter($columns, '<=', 'and');
- }
-
- /**
- * Add a "where date" clause to determine if a "date" column is after today.
- *
- * @param array|string $columns
- * @return $this
- */
- public function whereAfterToday($columns)
- {
- return $this->whereTodayBeforeOrAfter($columns, '>', 'and');
- }
-
- /**
- * Add a "where date" clause to determine if a "date" column is today or after to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function whereTodayOrAfter($columns)
- {
- return $this->whereTodayBeforeOrAfter($columns, '>=', 'and');
- }
-
- /**
- * Add an "or where date" clause to determine if a "date" column is today to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWhereToday($columns)
- {
- return $this->whereToday($columns, 'or');
- }
-
- /**
- * Add an "or where date" clause to determine if a "date" column is before today.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWhereBeforeToday($columns)
- {
- return $this->whereTodayBeforeOrAfter($columns, '<', 'or');
- }
-
- /**
- * Add an "or where date" clause to determine if a "date" column is today or before to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWhereTodayOrBefore($columns)
- {
- return $this->whereTodayBeforeOrAfter($columns, '<=', 'or');
- }
-
- /**
- * Add an "or where date" clause to determine if a "date" column is after today.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWhereAfterToday($columns)
- {
- return $this->whereTodayBeforeOrAfter($columns, '>', 'or');
- }
-
- /**
- * Add an "or where date" clause to determine if a "date" column is today or after to the query.
- *
- * @param array|string $columns
- * @return $this
- */
- public function orWhereTodayOrAfter($columns)
- {
- return $this->whereTodayBeforeOrAfter($columns, '>=', 'or');
- }
-
- /**
- * Add a "where date" clause to determine if a "date" column is today or after to the query.
- *
- * @param array|string $columns
- * @param string $operator
- * @param string $boolean
- * @return $this
- */
- protected function whereTodayBeforeOrAfter($columns, $operator, $boolean)
- {
- $value = Carbon::today()->format('Y-m-d');
-
- foreach (Arr::wrap($columns) as $column) {
- $this->addDateBasedWhere('Date', $column, $operator, $value, $boolean);
- }
-
- return $this;
- }
-}
diff --git a/Concerns/CompilesJsonPaths.php b/Concerns/CompilesJsonPaths.php
deleted file mode 100644
index fb62914374..0000000000
--- a/Concerns/CompilesJsonPaths.php
+++ /dev/null
@@ -1,65 +0,0 @@
-', $column, 2);
-
- $field = $this->wrap($parts[0]);
-
- $path = count($parts) > 1 ? ', '.$this->wrapJsonPath($parts[1], '->') : '';
-
- return [$field, $path];
- }
-
- /**
- * Wrap the given JSON path.
- *
- * @param string $value
- * @param string $delimiter
- * @return string
- */
- protected function wrapJsonPath($value, $delimiter = '->')
- {
- $value = preg_replace("/([\\\\]+)?\\'/", "''", $value);
-
- $jsonPath = (new Collection(explode($delimiter, $value)))
- ->map(fn ($segment) => $this->wrapJsonPathSegment($segment))
- ->join('.');
-
- return "'$".(str_starts_with($jsonPath, '[') ? '' : '.').$jsonPath."'";
- }
-
- /**
- * Wrap the given JSON path segment.
- *
- * @param string $segment
- * @return string
- */
- protected function wrapJsonPathSegment($segment)
- {
- if (preg_match('/(\[[^\]]+\])+$/', $segment, $parts)) {
- $key = Str::beforeLast($segment, $parts[0]);
-
- if (! empty($key)) {
- return '"'.$key.'"'.$parts[0];
- }
-
- return $parts[0];
- }
-
- return '"'.$segment.'"';
- }
-}
diff --git a/Concerns/ExplainsQueries.php b/Concerns/ExplainsQueries.php
deleted file mode 100644
index 7168de1e55..0000000000
--- a/Concerns/ExplainsQueries.php
+++ /dev/null
@@ -1,24 +0,0 @@
-toSql();
-
- $bindings = $this->getBindings();
-
- $explanation = $this->getConnection()->select('EXPLAIN '.$sql, $bindings);
-
- return new Collection($explanation);
- }
-}
diff --git a/Concerns/ManagesTransactions.php b/Concerns/ManagesTransactions.php
index 9874727d26..9b1dcd16ca 100644
--- a/Concerns/ManagesTransactions.php
+++ b/Concerns/ManagesTransactions.php
@@ -3,25 +3,19 @@
namespace Illuminate\Database\Concerns;
use Closure;
-use Illuminate\Database\DeadlockException;
-use RuntimeException;
+use Exception;
use Throwable;
-/**
- * @mixin \Illuminate\Database\Connection
- */
trait ManagesTransactions
{
/**
- * @template TReturn of mixed
- *
* Execute a Closure within a transaction.
*
- * @param (\Closure(static): TReturn) $callback
+ * @param \Closure $callback
* @param int $attempts
- * @return TReturn
+ * @return mixed
*
- * @throws \Throwable
+ * @throws \Exception|\Throwable
*/
public function transaction(Closure $callback, $attempts = 1)
{
@@ -32,73 +26,46 @@ public function transaction(Closure $callback, $attempts = 1)
// 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);
+ return tap($callback($this), function ($result) {
+ $this->commit();
+ });
}
// 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 exception.
- catch (Throwable $e) {
+ // exception back out and let the developer handle an uncaught exceptions.
+ catch (Exception $e) {
$this->handleTransactionException(
$e, $currentAttempt, $attempts
);
-
- continue;
- }
-
- $levelBeingCommitted = $this->transactions;
-
- try {
- if ($this->transactions == 1) {
- $this->fireConnectionEvent('committing');
- $this->getPdo()->commit();
- }
-
- $this->transactions = max(0, $this->transactions - 1);
} catch (Throwable $e) {
- $this->handleCommitTransactionException(
- $e, $currentAttempt, $attempts
- );
+ $this->rollBack();
- continue;
+ throw $e;
}
-
- $this->transactionsManager?->commit(
- $this->getName(),
- $levelBeingCommitted,
- $this->transactions
- );
-
- $this->fireConnectionEvent('committed');
-
- return $callbackResult;
}
}
/**
* Handle an exception encountered when running a transacted statement.
*
- * @param \Throwable $e
+ * @param \Exception $e
* @param int $currentAttempt
* @param int $maxAttempts
* @return void
*
- * @throws \Throwable
+ * @throws \Exception
*/
- protected function handleTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
+ protected function handleTransactionException($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) &&
+ if ($this->causedByDeadlock($e) &&
$this->transactions > 1) {
- $this->transactions--;
-
- $this->transactionsManager?->rollback(
- $this->getName(), $this->transactions
- );
+ --$this->transactions;
- throw new DeadlockException($e->getMessage(), is_int($e->getCode()) ? $e->getCode() : 0, $e);
+ throw $e;
}
// If there was an exception we will rollback this transaction and then we
@@ -106,7 +73,7 @@ protected function handleTransactionException(Throwable $e, $currentAttempt, $ma
// if we haven't we will return and try this query again in our loop.
$this->rollBack();
- if ($this->causedByConcurrencyError($e) &&
+ if ($this->causedByDeadlock($e) &&
$currentAttempt < $maxAttempts) {
return;
}
@@ -118,22 +85,13 @@ protected function handleTransactionException(Throwable $e, $currentAttempt, $ma
* Start a new database transaction.
*
* @return void
- *
- * @throws \Throwable
+ * @throws \Exception
*/
public function beginTransaction()
{
- foreach ($this->beforeStartingTransaction as $callback) {
- $callback($this);
- }
-
$this->createTransaction();
- $this->transactions++;
-
- $this->transactionsManager?->begin(
- $this->getName(), $this->transactions
- );
+ ++$this->transactions;
$this->fireConnectionEvent('beganTransaction');
}
@@ -142,17 +100,13 @@ public function beginTransaction()
* Create a transaction within the database.
*
* @return void
- *
- * @throws \Throwable
*/
protected function createTransaction()
{
if ($this->transactions == 0) {
- $this->reconnectIfMissingConnection();
-
try {
- $this->executeBeginTransactionStatement();
- } catch (Throwable $e) {
+ $this->getPdo()->beginTransaction();
+ } catch (Exception $e) {
$this->handleBeginTransactionException($e);
}
} elseif ($this->transactions >= 1 && $this->queryGrammar->supportsSavepoints()) {
@@ -164,8 +118,6 @@ protected function createTransaction()
* Create a save point within the database.
*
* @return void
- *
- * @throws \Throwable
*/
protected function createSavepoint()
{
@@ -177,17 +129,17 @@ protected function createSavepoint()
/**
* Handle an exception from a transaction beginning.
*
- * @param \Throwable $e
+ * @param \Exception $e
* @return void
*
- * @throws \Throwable
+ * @throws \Exception
*/
- protected function handleBeginTransactionException(Throwable $e)
+ protected function handleBeginTransactionException($e)
{
if ($this->causedByLostConnection($e)) {
$this->reconnect();
- $this->executeBeginTransactionStatement();
+ $this->pdo->beginTransaction();
} else {
throw $e;
}
@@ -197,51 +149,16 @@ protected function handleBeginTransactionException(Throwable $e)
* Commit the active database transaction.
*
* @return void
- *
- * @throws \Throwable
*/
public function commit()
{
- if ($this->transactionLevel() == 1) {
- $this->fireConnectionEvent('committing');
+ if ($this->transactions == 1) {
$this->getPdo()->commit();
}
- [$levelBeingCommitted, $this->transactions] = [
- $this->transactions,
- max(0, $this->transactions - 1),
- ];
-
- $this->transactionsManager?->commit(
- $this->getName(), $levelBeingCommitted, $this->transactions
- );
-
- $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;
+ $this->fireConnectionEvent('committed');
}
/**
@@ -249,8 +166,6 @@ protected function handleCommitTransactionException(Throwable $e, $currentAttemp
*
* @param int|null $toLevel
* @return void
- *
- * @throws \Throwable
*/
public function rollBack($toLevel = null)
{
@@ -258,8 +173,8 @@ public function rollBack($toLevel = null)
// 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;
+ ? $this->transactions - 1
+ : $toLevel;
if ($toLevel < 0 || $toLevel >= $this->transactions) {
return;
@@ -268,18 +183,10 @@ public function rollBack($toLevel = null)
// 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->performRollBack($toLevel);
$this->transactions = $toLevel;
- $this->transactionsManager?->rollback(
- $this->getName(), $this->transactions
- );
-
$this->fireConnectionEvent('rollingBack');
}
@@ -288,17 +195,11 @@ public function rollBack($toLevel = null)
*
* @param int $toLevel
* @return void
- *
- * @throws \Throwable
*/
protected function performRollBack($toLevel)
{
if ($toLevel == 0) {
- $pdo = $this->getPdo();
-
- if ($pdo->inTransaction()) {
- $pdo->rollBack();
- }
+ $this->getPdo()->rollBack();
} elseif ($this->queryGrammar->supportsSavepoints()) {
$this->getPdo()->exec(
$this->queryGrammar->compileSavepointRollBack('trans'.($toLevel + 1))
@@ -306,27 +207,6 @@ protected function performRollBack($toLevel)
}
}
- /**
- * 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;
-
- $this->transactionsManager?->rollback(
- $this->getName(), $this->transactions
- );
- }
-
- throw $e;
- }
-
/**
* Get the number of active transactions.
*
@@ -336,38 +216,4 @@ public function transactionLevel()
{
return $this->transactions;
}
-
- /**
- * Execute the callback after a transaction commits.
- *
- * @param callable $callback
- * @return void
- *
- * @throws \RuntimeException
- */
- public function afterCommit($callback)
- {
- if ($this->transactionsManager) {
- return $this->transactionsManager->addCallback($callback);
- }
-
- throw new RuntimeException('Transactions Manager has not been set.');
- }
-
- /**
- * Execute the callback after a transaction rolls back.
- *
- * @param callable $callback
- * @return void
- *
- * @throws \RuntimeException
- */
- public function afterRollBack($callback)
- {
- if ($this->transactionsManager) {
- return $this->transactionsManager->addCallbackForRollback($callback);
- }
-
- throw new RuntimeException('Transactions Manager has not been set.');
- }
}
diff --git a/Concerns/ParsesSearchPath.php b/Concerns/ParsesSearchPath.php
deleted file mode 100644
index e822c722b7..0000000000
--- a/Concerns/ParsesSearchPath.php
+++ /dev/null
@@ -1,25 +0,0 @@
-getCode() === 40001 || $e->getCode() === '40001')) {
- return true;
- }
-
- $message = $e->getMessage();
-
- return Str::contains($message, [
- 'Deadlock found when trying to get lock',
- 'deadlock detected',
- 'The database file is locked',
- 'database is locked',
- 'database table is locked',
- 'A table in the database is locked',
- 'has been chosen as the deadlock victim',
- 'Lock wait timeout exceeded; try restarting transaction',
- 'WSREP detected deadlock/conflict and aborted the transaction. Try restarting the transaction',
- 'Record has changed since last read in table',
- ]);
- }
-}
diff --git a/ConfigurationUrlParser.php b/ConfigurationUrlParser.php
deleted file mode 100644
index bc7c624a28..0000000000
--- a/ConfigurationUrlParser.php
+++ /dev/null
@@ -1,10 +0,0 @@
-): mixed)}[]
- */
- protected $queryDurationHandlers = [];
-
/**
* Indicates if the connection is in a "dry run".
*
@@ -190,40 +130,27 @@ class Connection implements ConnectionInterface
protected $pretending = false;
/**
- * All of the callbacks that should be invoked before a transaction is started.
+ * The instance of Doctrine connection.
*
- * @var \Closure[]
+ * @var \Doctrine\DBAL\Connection
*/
- protected $beforeStartingTransaction = [];
-
- /**
- * All of the callbacks that should be invoked before a query is executed.
- *
- * @var (\Closure(string, array, \Illuminate\Database\Connection): mixed)[]
- */
- protected $beforeExecutingCallbacks = [];
+ protected $doctrineConnection;
/**
* The connection resolvers.
*
- * @var \Closure[]
+ * @var array
*/
protected static $resolvers = [];
- /**
- * The last retrieved PDO read / write type.
- *
- * @var null|'read'|'write'
- */
- protected $latestPdoTypeRetrieved = null;
-
/**
* Create a new database connection instance.
*
- * @param \PDO|(\Closure(): \PDO) $pdo
- * @param string $database
- * @param string $tablePrefix
- * @param array $config
+ * @param \PDO|\Closure $pdo
+ * @param string $database
+ * @param string $tablePrefix
+ * @param array $config
+ * @return void
*/
public function __construct($pdo, $database = '', $tablePrefix = '', array $config = [])
{
@@ -263,7 +190,7 @@ public function useDefaultQueryGrammar()
*/
protected function getDefaultQueryGrammar()
{
- return new QueryGrammar($this);
+ return new QueryGrammar;
}
/**
@@ -279,7 +206,7 @@ public function useDefaultSchemaGrammar()
/**
* Get the default schema grammar instance.
*
- * @return \Illuminate\Database\Schema\Grammars\Grammar|null
+ * @return \Illuminate\Database\Schema\Grammars\Grammar
*/
protected function getDefaultSchemaGrammar()
{
@@ -323,13 +250,12 @@ public function getSchemaBuilder()
/**
* Begin a fluent query against a database table.
*
- * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Contracts\Database\Query\Expression|\UnitEnum|string $table
- * @param string|null $as
+ * @param string $table
* @return \Illuminate\Database\Query\Builder
*/
- public function table($table, $as = null)
+ public function table($table)
{
- return $this->query()->from(enum_value($table), $as);
+ return $this->query()->from($table);
}
/**
@@ -348,7 +274,7 @@ public function query()
* Run a select statement and return a single result.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @param bool $useReadPdo
* @return mixed
*/
@@ -359,38 +285,11 @@ public function selectOne($query, $bindings = [], $useReadPdo = true)
return array_shift($records);
}
- /**
- * Run a select statement and return the first column of the first row.
- *
- * @param string $query
- * @param array $bindings
- * @param bool $useReadPdo
- * @return mixed
- *
- * @throws \Illuminate\Database\MultipleColumnsSelectedException
- */
- public function scalar($query, $bindings = [], $useReadPdo = true)
- {
- $record = $this->selectOne($query, $bindings, $useReadPdo);
-
- if (is_null($record)) {
- return null;
- }
-
- $record = (array) $record;
-
- if (count($record) > 1) {
- throw new MultipleColumnsSelectedException;
- }
-
- return array_first($record);
- }
-
/**
* Run a select statement against the database.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return array
*/
public function selectFromWriteConnection($query, $bindings = [])
@@ -404,12 +303,11 @@ public function selectFromWriteConnection($query, $bindings = [])
* @param string $query
* @param array $bindings
* @param bool $useReadPdo
- * @param array $fetchUsing
* @return array
*/
- public function select($query, $bindings = [], $useReadPdo = true, array $fetchUsing = [])
+ public function select($query, $bindings = [], $useReadPdo = true)
{
- return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo, $fetchUsing) {
+ return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
if ($this->pretending()) {
return [];
}
@@ -417,49 +315,14 @@ public function select($query, $bindings = [], $useReadPdo = true, array $fetchU
// For select statements, we'll simply execute the query and return an array
// of the database result set. Each element in the array will be a single
// row from the database table, and will either be an array or objects.
- $statement = $this->prepared(
- $this->getPdoForSelect($useReadPdo)->prepare($query)
- );
-
- $this->bindValues($statement, $this->prepareBindings($bindings));
-
- $statement->execute();
-
- return $statement->fetchAll(...$fetchUsing);
- });
- }
-
- /**
- * Run a select statement against the database and returns all of the result sets.
- *
- * @param string $query
- * @param array $bindings
- * @param bool $useReadPdo
- * @param array $fetchUsing
- * @return array
- */
- public function selectResultSets($query, $bindings = [], $useReadPdo = true, array $fetchUsing = [])
- {
- return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo, $fetchUsing) {
- if ($this->pretending()) {
- return [];
- }
-
- $statement = $this->prepared(
- $this->getPdoForSelect($useReadPdo)->prepare($query)
- );
+ $statement = $this->prepared($this->getPdoForSelect($useReadPdo)
+ ->prepare($query));
$this->bindValues($statement, $this->prepareBindings($bindings));
$statement->execute();
- $sets = [];
-
- do {
- $sets[] = $statement->fetchAll(...$fetchUsing);
- } while ($statement->nextRowset());
-
- return $sets;
+ return $statement->fetchAll();
});
}
@@ -469,10 +332,9 @@ public function selectResultSets($query, $bindings = [], $useReadPdo = true, arr
* @param string $query
* @param array $bindings
* @param bool $useReadPdo
- * @param array $fetchUsing
- * @return \Generator
+ * @return \Generator
*/
- public function cursor($query, $bindings = [], $useReadPdo = true, array $fetchUsing = [])
+ public function cursor($query, $bindings = [], $useReadPdo = true)
{
$statement = $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
if ($this->pretending()) {
@@ -483,7 +345,7 @@ public function cursor($query, $bindings = [], $useReadPdo = true, array $fetchU
// mode and prepare the bindings for the query. Once that's done we will be
// ready to execute the query against the database and return the cursor.
$statement = $this->prepared($this->getPdoForSelect($useReadPdo)
- ->prepare($query));
+ ->prepare($query));
$this->bindValues(
$statement, $this->prepareBindings($bindings)
@@ -497,7 +359,7 @@ public function cursor($query, $bindings = [], $useReadPdo = true, array $fetchU
return $statement;
});
- while ($record = $statement->fetch(...$fetchUsing)) {
+ while ($record = $statement->fetch()) {
yield $record;
}
}
@@ -512,7 +374,9 @@ protected function prepared(PDOStatement $statement)
{
$statement->setFetchMode($this->fetchMode);
- $this->event(new StatementPrepared($this, $statement));
+ $this->event(new Events\StatementPrepared(
+ $this, $statement
+ ));
return $statement;
}
@@ -532,7 +396,7 @@ protected function getPdoForSelect($useReadPdo = true)
* Run an insert statement against the database.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return bool
*/
public function insert($query, $bindings = [])
@@ -544,7 +408,7 @@ public function insert($query, $bindings = [])
* Run an update statement against the database.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return int
*/
public function update($query, $bindings = [])
@@ -556,7 +420,7 @@ public function update($query, $bindings = [])
* Run a delete statement against the database.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return int
*/
public function delete($query, $bindings = [])
@@ -568,7 +432,7 @@ public function delete($query, $bindings = [])
* Execute an SQL statement and return the boolean result.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return bool
*/
public function statement($query, $bindings = [])
@@ -582,8 +446,6 @@ public function statement($query, $bindings = [])
$this->bindValues($statement, $this->prepareBindings($bindings));
- $this->recordsHaveBeenModified();
-
return $statement->execute();
});
}
@@ -592,7 +454,7 @@ public function statement($query, $bindings = [])
* Run an SQL statement and get the number of rows affected.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return int
*/
public function affectingStatement($query, $bindings = [])
@@ -611,11 +473,7 @@ public function affectingStatement($query, $bindings = [])
$statement->execute();
- $this->recordsHaveBeenModified(
- ($count = $statement->rowCount()) > 0
- );
-
- return $count;
+ return $statement->rowCount();
});
}
@@ -632,76 +490,37 @@ public function unprepared($query)
return true;
}
- $this->recordsHaveBeenModified(
- $change = $this->getPdo()->exec($query) !== false
- );
-
- return $change;
+ return (bool) $this->getPdo()->exec($query);
});
}
- /**
- * Get the number of open connections for the database.
- *
- * @return int|null
- */
- public function threadCount()
- {
- $query = $this->getQueryGrammar()->compileThreadCount();
-
- return $query ? $this->scalar($query) : null;
- }
-
/**
* Execute the given callback in "dry run" mode.
*
- * @param (\Closure(\Illuminate\Database\Connection): mixed) $callback
- * @return array{query: string, bindings: array, time: float|null}[]
+ * @param \Closure $callback
+ * @return array
*/
public function pretend(Closure $callback)
{
return $this->withFreshQueryLog(function () use ($callback) {
$this->pretending = true;
- try {
- // Basically to make the database connection "pretend", we will just return
- // the default values for all the query methods, then we will return an
- // array of queries that were "executed" within the Closure callback.
- $callback($this);
-
- return $this->queryLog;
- } finally {
- $this->pretending = false;
- }
- });
- }
-
- /**
- * Execute the given callback without "pretending".
- *
- * @param \Closure $callback
- * @return mixed
- */
- public function withoutPretending(Closure $callback)
- {
- if (! $this->pretending) {
- return $callback();
- }
+ // Basically to make the database connection "pretend", we will just return
+ // the default values for all the query methods, then we will return an
+ // array of queries that were "executed" within the Closure callback.
+ $callback($this);
- $this->pretending = false;
+ $this->pretending = false;
- try {
- return $callback();
- } finally {
- $this->pretending = true;
- }
+ return $this->queryLog;
+ });
}
/**
* Execute the given callback in "dry run" mode.
*
- * @param (\Closure(): array{query: string, bindings: array, time: float|null}[]) $callback
- * @return array{query: string, bindings: array, time: float|null}[]
+ * @param \Closure $callback
+ * @return array
*/
protected function withFreshQueryLog($callback)
{
@@ -716,7 +535,7 @@ protected function withFreshQueryLog($callback)
// Now we'll execute this callback and capture the result. Once it has been
// executed we will restore the value of query logging and give back the
- // value of the callback so the original callers can have the results.
+ // value of hte callback so the original callers can have the results.
$result = $callback();
$this->loggingQueries = $loggingQueries;
@@ -727,7 +546,7 @@ protected function withFreshQueryLog($callback)
/**
* Bind values to their parameters in the given statement.
*
- * @param \PDOStatement $statement
+ * @param \PDOStatement $statement
* @param array $bindings
* @return void
*/
@@ -735,13 +554,8 @@ public function bindValues($statement, $bindings)
{
foreach ($bindings as $key => $value) {
$statement->bindValue(
- is_string($key) ? $key : $key + 1,
- $value,
- match (true) {
- is_int($value) => PDO::PARAM_INT,
- is_resource($value) => PDO::PARAM_LOB,
- default => PDO::PARAM_STR
- },
+ is_string($key) ? $key : $key + 1, $value,
+ is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
);
}
}
@@ -762,8 +576,8 @@ public function prepareBindings(array $bindings)
// so we'll just ask the grammar for the format to get from the date.
if ($value instanceof DateTimeInterface) {
$bindings[$key] = $value->format($grammar->getDateFormat());
- } elseif (is_bool($value)) {
- $bindings[$key] = (int) $value;
+ } elseif ($value === false) {
+ $bindings[$key] = 0;
}
}
@@ -773,8 +587,8 @@ public function prepareBindings(array $bindings)
/**
* Run a SQL statement and log its execution context.
*
- * @param string $query
- * @param array $bindings
+ * @param string $query
+ * @param array $bindings
* @param \Closure $callback
* @return mixed
*
@@ -782,10 +596,6 @@ public function prepareBindings(array $bindings)
*/
protected function run($query, $bindings, Closure $callback)
{
- foreach ($this->beforeExecutingCallbacks as $beforeExecutingCallback) {
- $beforeExecutingCallback($query, $bindings, $this);
- }
-
$this->reconnectIfMissingConnection();
$start = microtime(true);
@@ -814,8 +624,8 @@ protected function run($query, $bindings, Closure $callback)
/**
* Run a SQL statement.
*
- * @param string $query
- * @param array $bindings
+ * @param string $query
+ * @param array $bindings
* @param \Closure $callback
* @return mixed
*
@@ -827,68 +637,42 @@ protected function runQueryCallback($query, $bindings, Closure $callback)
// run the SQL against the PDO connection. Then we can calculate the time it
// took to execute and log the query SQL, bindings and time in our memory.
try {
- return $callback($query, $bindings);
+ $result = $callback($query, $bindings);
}
// If an exception occurs when attempting to run a query, we'll format the error
// message to include the bindings with SQL, which will make this exception a
// lot more helpful to the developer instead of just the database's errors.
catch (Exception $e) {
- $exceptionType = $this->isUniqueConstraintError($e)
- ? UniqueConstraintViolationException::class
- : QueryException::class;
-
- throw new $exceptionType(
- $this->getNameWithReadWriteType(),
- $query,
- $this->prepareBindings($bindings),
- $e,
- $this->getConnectionDetails(),
- $this->latestReadWriteTypeUsed(),
+ throw new QueryException(
+ $query, $this->prepareBindings($bindings), $e
);
}
- }
- /**
- * Determine if the given database exception was caused by a unique constraint violation.
- *
- * @param \Exception $exception
- * @return bool
- */
- protected function isUniqueConstraintError(Exception $exception)
- {
- return false;
+ return $result;
}
/**
* Log a query in the connection's query log.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @param float|null $time
* @return void
*/
public function logQuery($query, $bindings, $time = null)
{
- $this->totalQueryDuration += $time ?? 0.0;
-
- $readWriteType = $this->latestReadWriteTypeUsed();
-
- $this->event(new QueryExecuted($query, $bindings, $time, $this, $readWriteType));
-
- $query = $this->pretending === true
- ? $this->queryGrammar?->substituteBindingsIntoRawSql($query, $bindings) ?? $query
- : $query;
+ $this->event(new QueryExecuted($query, $bindings, $time, $this));
if ($this->loggingQueries) {
- $this->queryLog[] = compact('query', 'bindings', 'time', 'readWriteType');
+ $this->queryLog[] = compact('query', 'bindings', 'time');
}
}
/**
- * Get the elapsed time in milliseconds since a given starting point.
+ * Get the elapsed time since a given starting point.
*
- * @param float $start
+ * @param int $start
* @return float
*/
protected function getElapsedTime($start)
@@ -896,83 +680,16 @@ protected function getElapsedTime($start)
return round((microtime(true) - $start) * 1000, 2);
}
- /**
- * Register a callback to be invoked when the connection queries for longer than a given amount of time.
- *
- * @param \DateTimeInterface|\Carbon\CarbonInterval|float|int $threshold
- * @param (callable(\Illuminate\Database\Connection, \Illuminate\Database\Events\QueryExecuted): mixed) $handler
- * @return void
- */
- public function whenQueryingForLongerThan($threshold, $handler)
- {
- $threshold = $threshold instanceof DateTimeInterface
- ? $this->secondsUntil($threshold) * 1000
- : $threshold;
-
- $threshold = $threshold instanceof CarbonInterval
- ? $threshold->totalMilliseconds
- : $threshold;
-
- $this->queryDurationHandlers[] = [
- 'has_run' => false,
- 'handler' => $handler,
- ];
-
- $key = count($this->queryDurationHandlers) - 1;
-
- $this->listen(function ($event) use ($threshold, $handler, $key) {
- if (! $this->queryDurationHandlers[$key]['has_run'] && $this->totalQueryDuration() > $threshold) {
- $handler($this, $event);
-
- $this->queryDurationHandlers[$key]['has_run'] = true;
- }
- });
- }
-
- /**
- * Allow all the query duration handlers to run again, even if they have already run.
- *
- * @return void
- */
- public function allowQueryDurationHandlersToRunAgain()
- {
- foreach ($this->queryDurationHandlers as $key => $queryDurationHandler) {
- $this->queryDurationHandlers[$key]['has_run'] = false;
- }
- }
-
- /**
- * Get the duration of all run queries in milliseconds.
- *
- * @return float
- */
- public function totalQueryDuration()
- {
- return $this->totalQueryDuration;
- }
-
- /**
- * Reset the duration of all run queries.
- *
- * @return void
- */
- public function resetTotalQueryDuration()
- {
- $this->totalQueryDuration = 0.0;
- }
-
/**
* Handle a query exception.
*
- * @param \Illuminate\Database\QueryException $e
+ * @param \Exception $e
* @param string $query
* @param array $bindings
* @param \Closure $callback
* @return mixed
- *
- * @throws \Illuminate\Database\QueryException
*/
- protected function handleQueryException(QueryException $e, $query, $bindings, Closure $callback)
+ protected function handleQueryException($e, $query, $bindings, Closure $callback)
{
if ($this->transactions >= 1) {
throw $e;
@@ -987,8 +704,8 @@ protected function handleQueryException(QueryException $e, $query, $bindings, Cl
* Handle a query exception that occurred during query execution.
*
* @param \Illuminate\Database\QueryException $e
- * @param string $query
- * @param array $bindings
+ * @param string $query
+ * @param array $bindings
* @param \Closure $callback
* @return mixed
*
@@ -1008,9 +725,9 @@ protected function tryAgainIfCausedByLostConnection(QueryException $e, $query, $
/**
* Reconnect to the database.
*
- * @return mixed|false
+ * @return void
*
- * @throws \Illuminate\Database\LostConnectionException
+ * @throws \LogicException
*/
public function reconnect()
{
@@ -1018,7 +735,7 @@ public function reconnect()
return call_user_func($this->reconnector, $this);
}
- throw new LostConnectionException('Lost connection and no reconnector available.');
+ throw new LogicException('Lost connection and no reconnector available.');
}
/**
@@ -1026,7 +743,7 @@ public function reconnect()
*
* @return void
*/
- public function reconnectIfMissingConnection()
+ protected function reconnectIfMissingConnection()
{
if (is_null($this->pdo)) {
$this->reconnect();
@@ -1043,58 +760,39 @@ public function disconnect()
$this->setPdo(null)->setReadPdo(null);
}
- /**
- * Register a hook to be run just before a database transaction is started.
- *
- * @param \Closure $callback
- * @return $this
- */
- public function beforeStartingTransaction(Closure $callback)
- {
- $this->beforeStartingTransaction[] = $callback;
-
- return $this;
- }
-
- /**
- * Register a hook to be run just before a database query is executed.
- *
- * @param \Closure $callback
- * @return $this
- */
- public function beforeExecuting(Closure $callback)
- {
- $this->beforeExecutingCallbacks[] = $callback;
-
- return $this;
- }
-
/**
* Register a database query listener with the connection.
*
- * @param \Closure(\Illuminate\Database\Events\QueryExecuted) $callback
+ * @param \Closure $callback
* @return void
*/
public function listen(Closure $callback)
{
- $this->events?->listen(Events\QueryExecuted::class, $callback);
+ if (isset($this->events)) {
+ $this->events->listen(Events\QueryExecuted::class, $callback);
+ }
}
/**
* Fire an event for this connection.
*
* @param string $event
- * @return array|null
+ * @return void
*/
protected function fireConnectionEvent($event)
{
- return $this->events?->dispatch(match ($event) {
- 'beganTransaction' => new TransactionBeginning($this),
- 'committed' => new TransactionCommitted($this),
- 'committing' => new TransactionCommitting($this),
- 'rollingBack' => new TransactionRolledBack($this),
- default => null,
- });
+ if (! isset($this->events)) {
+ return;
+ }
+
+ switch ($event) {
+ case 'beganTransaction':
+ return $this->events->dispatch(new Events\TransactionBeginning($this));
+ case 'committed':
+ return $this->events->dispatch(new Events\TransactionCommitted($this));
+ case 'rollingBack':
+ return $this->events->dispatch(new Events\TransactionRolledBack($this));
+ }
}
/**
@@ -1105,14 +803,16 @@ protected function fireConnectionEvent($event)
*/
protected function event($event)
{
- $this->events?->dispatch($event);
+ if (isset($this->events)) {
+ $this->events->dispatch($event);
+ }
}
/**
* Get a new raw query expression.
*
* @param mixed $value
- * @return \Illuminate\Contracts\Database\Query\Expression
+ * @return \Illuminate\Database\Query\Expression
*/
public function raw($value)
{
@@ -1120,131 +820,55 @@ public function raw($value)
}
/**
- * Escape a value for safe SQL embedding.
- *
- * @param string|float|int|bool|null $value
- * @param bool $binary
- * @return string
- *
- * @throws \RuntimeException
- */
- public function escape($value, $binary = false)
- {
- if ($value === null) {
- return 'null';
- } elseif ($binary) {
- return $this->escapeBinary($value);
- } elseif (is_int($value) || is_float($value)) {
- return (string) $value;
- } elseif (is_bool($value)) {
- return $this->escapeBool($value);
- } elseif (is_array($value)) {
- throw new RuntimeException('The database connection does not support escaping arrays.');
- } else {
- if (str_contains($value, "\00")) {
- throw new RuntimeException('Strings with null bytes cannot be escaped. Use the binary escape option.');
- }
-
- if (preg_match('//u', $value) === false) {
- throw new RuntimeException('Strings with invalid UTF-8 byte sequences cannot be escaped.');
- }
-
- return $this->escapeString($value);
- }
- }
-
- /**
- * Escape a string value for safe SQL embedding.
- *
- * @param string $value
- * @return string
- */
- protected function escapeString($value)
- {
- return $this->getReadPdo()->quote($value);
- }
-
- /**
- * Escape a boolean value for safe SQL embedding.
- *
- * @param bool $value
- * @return string
- */
- protected function escapeBool($value)
- {
- return $value ? '1' : '0';
- }
-
- /**
- * Escape a binary value for safe SQL embedding.
- *
- * @param string $value
- * @return string
- *
- * @throws \RuntimeException
- */
- protected function escapeBinary($value)
- {
- throw new RuntimeException('The database connection does not support escaping binary values.');
- }
-
- /**
- * Determine if the database connection has modified any database records.
+ * Is Doctrine available?
*
* @return bool
*/
- public function hasModifiedRecords()
+ public function isDoctrineAvailable()
{
- return $this->recordsModified;
+ return class_exists('Doctrine\DBAL\Connection');
}
/**
- * Indicate if any records have been modified.
+ * Get a Doctrine Schema Column instance.
*
- * @param bool $value
- * @return void
+ * @param string $table
+ * @param string $column
+ * @return \Doctrine\DBAL\Schema\Column
*/
- public function recordsHaveBeenModified($value = true)
+ public function getDoctrineColumn($table, $column)
{
- if (! $this->recordsModified) {
- $this->recordsModified = $value;
- }
- }
+ $schema = $this->getDoctrineSchemaManager();
- /**
- * Set the record modification state.
- *
- * @param bool $value
- * @return $this
- */
- public function setRecordModificationState(bool $value)
- {
- $this->recordsModified = $value;
-
- return $this;
+ return $schema->listTableDetails($table)->getColumn($column);
}
/**
- * Reset the record modification state.
+ * Get the Doctrine DBAL schema manager for the connection.
*
- * @return void
+ * @return \Doctrine\DBAL\Schema\AbstractSchemaManager
*/
- public function forgetRecordModificationState()
+ public function getDoctrineSchemaManager()
{
- $this->recordsModified = false;
+ return $this->getDoctrineDriver()->getSchemaManager($this->getDoctrineConnection());
}
/**
- * Indicate that the connection should use the write PDO connection for reads.
+ * Get the Doctrine DBAL database connection instance.
*
- * @param bool $value
- * @return $this
+ * @return \Doctrine\DBAL\Connection
*/
- public function useWriteConnectionWhenReading($value = true)
+ public function getDoctrineConnection()
{
- $this->readOnWriteConnection = $value;
+ if (is_null($this->doctrineConnection)) {
+ $data = ['pdo' => $this->getPdo(), 'dbname' => $this->getConfig('database')];
- return $this;
+ $this->doctrineConnection = new DoctrineConnection(
+ $data, $this->getDoctrineDriver()
+ );
+ }
+
+ return $this->doctrineConnection;
}
/**
@@ -1254,8 +878,6 @@ public function useWriteConnectionWhenReading($value = true)
*/
public function getPdo()
{
- $this->latestPdoTypeRetrieved = 'write';
-
if ($this->pdo instanceof Closure) {
return $this->pdo = call_user_func($this->pdo);
}
@@ -1263,16 +885,6 @@ public function getPdo()
return $this->pdo;
}
- /**
- * Get the current PDO connection parameter without executing any reconnect logic.
- *
- * @return \PDO|\Closure|null
- */
- public function getRawPdo()
- {
- return $this->pdo;
- }
-
/**
* Get the current PDO connection used for reading.
*
@@ -1280,17 +892,10 @@ public function getRawPdo()
*/
public function getReadPdo()
{
- if ($this->transactions > 0) {
- return $this->getPdo();
- }
-
- if ($this->readOnWriteConnection ||
- ($this->recordsModified && $this->getConfig('sticky'))) {
+ if ($this->transactions >= 1) {
return $this->getPdo();
}
- $this->latestPdoTypeRetrieved = 'read';
-
if ($this->readPdo instanceof Closure) {
return $this->readPdo = call_user_func($this->readPdo);
}
@@ -1298,20 +903,10 @@ public function getReadPdo()
return $this->readPdo ?: $this->getPdo();
}
- /**
- * Get the current read PDO connection parameter without executing any reconnect logic.
- *
- * @return \PDO|\Closure|null
- */
- public function getRawReadPdo()
- {
- return $this->readPdo;
- }
-
/**
* Set the PDO connection.
*
- * @param \PDO|\Closure|null $pdo
+ * @param \PDO|null $pdo
* @return $this
*/
public function setPdo($pdo)
@@ -1326,7 +921,7 @@ public function setPdo($pdo)
/**
* Set the PDO connection used for reading.
*
- * @param \PDO|\Closure|null $pdo
+ * @param \PDO|null $pdo
* @return $this
*/
public function setReadPdo($pdo)
@@ -1336,23 +931,10 @@ public function setReadPdo($pdo)
return $this;
}
- /**
- * Set the read PDO connection configuration.
- *
- * @param array $config
- * @return $this
- */
- public function setReadPdoConfig(array $config)
- {
- $this->readPdoConfig = $config;
-
- return $this;
- }
-
/**
* Set the reconnect instance on the connection.
*
- * @param (callable(\Illuminate\Database\Connection): mixed) $reconnector
+ * @param callable $reconnector
* @return $this
*/
public function setReconnector(callable $reconnector)
@@ -1372,18 +954,6 @@ public function getName()
return $this->getConfig('name');
}
- /**
- * Get the database connection with its read / write type.
- *
- * @return string|null
- */
- public function getNameWithReadWriteType()
- {
- $name = $this->getName().($this->readWriteType ? '::'.$this->readWriteType : '');
-
- return empty($name) ? null : $name;
- }
-
/**
* Get an option from the configuration options.
*
@@ -1395,27 +965,6 @@ public function getConfig($option = null)
return Arr::get($this->config, $option);
}
- /**
- * Get the basic connection information as an array for debugging.
- *
- * @return array
- */
- protected function getConnectionDetails()
- {
- $config = $this->latestReadWriteTypeUsed() === 'read'
- ? $this->readPdoConfig
- : $this->config;
-
- return [
- 'driver' => $this->getDriverName(),
- 'name' => $this->getNameWithReadWriteType(),
- 'host' => $config['host'] ?? null,
- 'port' => $config['port'] ?? null,
- 'database' => $config['database'] ?? null,
- 'unix_socket' => $config['unix_socket'] ?? null,
- ];
- }
-
/**
* Get the PDO driver name.
*
@@ -1426,16 +975,6 @@ public function getDriverName()
return $this->getConfig('driver');
}
- /**
- * Get a human-readable name for the given connection driver.
- *
- * @return string
- */
- public function getDriverTitle()
- {
- return $this->getDriverName();
- }
-
/**
* Get the query grammar used by the connection.
*
@@ -1450,13 +989,11 @@ public function getQueryGrammar()
* Set the query grammar used by the connection.
*
* @param \Illuminate\Database\Query\Grammars\Grammar $grammar
- * @return $this
+ * @return void
*/
public function setQueryGrammar(Query\Grammars\Grammar $grammar)
{
$this->queryGrammar = $grammar;
-
- return $this;
}
/**
@@ -1473,13 +1010,11 @@ public function getSchemaGrammar()
* Set the schema grammar used by the connection.
*
* @param \Illuminate\Database\Schema\Grammars\Grammar $grammar
- * @return $this
+ * @return void
*/
public function setSchemaGrammar(Schema\Grammars\Grammar $grammar)
{
$this->schemaGrammar = $grammar;
-
- return $this;
}
/**
@@ -1496,19 +1031,17 @@ public function getPostProcessor()
* Set the query post processor used by the connection.
*
* @param \Illuminate\Database\Query\Processors\Processor $processor
- * @return $this
+ * @return void
*/
public function setPostProcessor(Processor $processor)
{
$this->postProcessor = $processor;
-
- return $this;
}
/**
* Get the event dispatcher used by the connection.
*
- * @return \Illuminate\Contracts\Events\Dispatcher|null
+ * @return \Illuminate\Contracts\Events\Dispatcher
*/
public function getEventDispatcher()
{
@@ -1519,60 +1052,15 @@ public function getEventDispatcher()
* Set the event dispatcher instance on the connection.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
- * @return $this
+ * @return void
*/
public function setEventDispatcher(Dispatcher $events)
{
$this->events = $events;
-
- return $this;
- }
-
- /**
- * Unset the event dispatcher for this connection.
- *
- * @return void
- */
- public function unsetEventDispatcher()
- {
- $this->events = null;
- }
-
- /**
- * Run the statement to start a new transaction.
- *
- * @return void
- */
- protected function executeBeginTransactionStatement()
- {
- $this->getPdo()->beginTransaction();
}
/**
- * Set the transaction manager instance on the connection.
- *
- * @param \Illuminate\Database\DatabaseTransactionsManager $manager
- * @return $this
- */
- public function setTransactionManager($manager)
- {
- $this->transactionsManager = $manager;
-
- return $this;
- }
-
- /**
- * Unset the transaction manager for this connection.
- *
- * @return void
- */
- public function unsetTransactionManager()
- {
- $this->transactionsManager = null;
- }
-
- /**
- * Determine if the connection is in a "dry run".
+ * Determine if the connection in a "dry run".
*
* @return bool
*/
@@ -1584,29 +1072,13 @@ public function pretending()
/**
* Get the connection query log.
*
- * @return array{query: string, bindings: array, time: float|null}[]
+ * @return array
*/
public function getQueryLog()
{
return $this->queryLog;
}
- /**
- * Get the connection query log with embedded bindings.
- *
- * @return array
- */
- public function getRawQueryLog()
- {
- return array_map(fn (array $log) => [
- 'raw_query' => $this->queryGrammar->substituteBindingsIntoRawSql(
- $log['query'],
- $this->prepareBindings($log['bindings'])
- ),
- 'time' => $log['time'],
- ], $this->getQueryLog());
- }
-
/**
* Clear the query log.
*
@@ -1661,36 +1133,11 @@ public function getDatabaseName()
* Set the name of the connected database.
*
* @param string $database
- * @return $this
+ * @return string
*/
public function setDatabaseName($database)
{
$this->database = $database;
-
- return $this;
- }
-
- /**
- * Set the read / write type of the connection.
- *
- * @param string|null $readWriteType
- * @return $this
- */
- public function setReadWriteType($readWriteType)
- {
- $this->readWriteType = $readWriteType;
-
- return $this;
- }
-
- /**
- * Retrieve the latest read / write type used.
- *
- * @return 'read'|'write'|null
- */
- protected function latestReadWriteTypeUsed()
- {
- return $this->readWriteType ?? $this->latestPdoTypeRetrieved;
}
/**
@@ -1707,42 +1154,26 @@ public function getTablePrefix()
* Set the table prefix in use by the connection.
*
* @param string $prefix
- * @return $this
+ * @return void
*/
public function setTablePrefix($prefix)
{
$this->tablePrefix = $prefix;
- return $this;
+ $this->getQueryGrammar()->setTablePrefix($prefix);
}
/**
- * Execute the given callback without table prefix.
+ * Set the table prefix and return the grammar.
*
- * @param \Closure $callback
- * @return mixed
+ * @param \Illuminate\Database\Grammar $grammar
+ * @return \Illuminate\Database\Grammar
*/
- public function withoutTablePrefix(Closure $callback): mixed
+ public function withTablePrefix(Grammar $grammar)
{
- $tablePrefix = $this->getTablePrefix();
+ $grammar->setTablePrefix($this->tablePrefix);
- $this->setTablePrefix('');
-
- try {
- return $callback($this);
- } finally {
- $this->setTablePrefix($tablePrefix);
- }
- }
-
- /**
- * Get the server version for the connection.
- *
- * @return string
- */
- public function getServerVersion(): string
- {
- return $this->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
+ return $grammar;
}
/**
@@ -1761,25 +1192,11 @@ public static function resolverFor($driver, Closure $callback)
* Get the connection resolver for the given driver.
*
* @param string $driver
- * @return \Closure|null
+ * @return mixed
*/
public static function getResolver($driver)
{
- return static::$resolvers[$driver] ?? null;
- }
-
- /**
- * Prepare the instance for cloning.
- *
- * @return void
- */
- public function __clone()
- {
- // When cloning, re-initialize grammars to reference cloned connection...
- $this->useDefaultQueryGrammar();
-
- if (! is_null($this->schemaGrammar)) {
- $this->useDefaultSchemaGrammar();
- }
+ return isset(static::$resolvers[$driver]) ?
+ static::$resolvers[$driver] : null;
}
}
diff --git a/ConnectionInterface.php b/ConnectionInterface.php
index 69e1fafa38..9262d6fdfe 100755
--- a/ConnectionInterface.php
+++ b/ConnectionInterface.php
@@ -9,17 +9,16 @@ interface ConnectionInterface
/**
* Begin a fluent query against a database table.
*
- * @param \Closure|\Illuminate\Database\Query\Builder|\UnitEnum|string $table
- * @param string|null $as
+ * @param string $table
* @return \Illuminate\Database\Query\Builder
*/
- public function table($table, $as = null);
+ public function table($table);
/**
* Get a new raw query expression.
*
* @param mixed $value
- * @return \Illuminate\Contracts\Database\Query\Expression
+ * @return \Illuminate\Database\Query\Expression
*/
public function raw($value);
@@ -27,51 +26,25 @@ public function raw($value);
* Run a select statement and return a single result.
*
* @param string $query
- * @param array $bindings
- * @param bool $useReadPdo
+ * @param array $bindings
* @return mixed
*/
- public function selectOne($query, $bindings = [], $useReadPdo = true);
-
- /**
- * Run a select statement and return the first column of the first row.
- *
- * @param string $query
- * @param array $bindings
- * @param bool $useReadPdo
- * @return mixed
- *
- * @throws \Illuminate\Database\MultipleColumnsSelectedException
- */
- public function scalar($query, $bindings = [], $useReadPdo = true);
+ public function selectOne($query, $bindings = []);
/**
* Run a select statement against the database.
*
* @param string $query
- * @param array $bindings
- * @param bool $useReadPdo
- * @param array $fetchUsing
+ * @param array $bindings
* @return array
*/
- public function select($query, $bindings = [], $useReadPdo = true, array $fetchUsing = []);
-
- /**
- * Run a select statement against the database and returns a generator.
- *
- * @param string $query
- * @param array $bindings
- * @param bool $useReadPdo
- * @param array $fetchUsing
- * @return \Generator
- */
- public function cursor($query, $bindings = [], $useReadPdo = true, array $fetchUsing = []);
+ public function select($query, $bindings = []);
/**
* Run an insert statement against the database.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return bool
*/
public function insert($query, $bindings = []);
@@ -80,7 +53,7 @@ public function insert($query, $bindings = []);
* Run an update statement against the database.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return int
*/
public function update($query, $bindings = []);
@@ -89,7 +62,7 @@ public function update($query, $bindings = []);
* Run a delete statement against the database.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return int
*/
public function delete($query, $bindings = []);
@@ -98,7 +71,7 @@ public function delete($query, $bindings = []);
* Execute an SQL statement and return the boolean result.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return bool
*/
public function statement($query, $bindings = []);
@@ -107,7 +80,7 @@ public function statement($query, $bindings = []);
* Run an SQL statement and get the number of rows affected.
*
* @param string $query
- * @param array $bindings
+ * @param array $bindings
* @return int
*/
public function affectingStatement($query, $bindings = []);
@@ -174,11 +147,4 @@ public function transactionLevel();
* @return array
*/
public function pretend(Closure $callback);
-
- /**
- * Get the name of the connected database.
- *
- * @return string
- */
- public function getDatabaseName();
}
diff --git a/ConnectionResolver.php b/ConnectionResolver.php
index b7b6279e1f..425ab6bce6 100755
--- a/ConnectionResolver.php
+++ b/ConnectionResolver.php
@@ -7,7 +7,7 @@ class ConnectionResolver implements ConnectionResolverInterface
/**
* All of the registered connections.
*
- * @var \Illuminate\Database\ConnectionInterface[]
+ * @var array
*/
protected $connections = [];
@@ -21,7 +21,8 @@ class ConnectionResolver implements ConnectionResolverInterface
/**
* Create a new connection resolver instance.
*
- * @param array $connections
+ * @param array $connections
+ * @return void
*/
public function __construct(array $connections = [])
{
@@ -33,7 +34,7 @@ public function __construct(array $connections = [])
/**
* Get a database connection instance.
*
- * @param string|null $name
+ * @param string $name
* @return \Illuminate\Database\ConnectionInterface
*/
public function connection($name = null)
diff --git a/ConnectionResolverInterface.php b/ConnectionResolverInterface.php
index 47161d37d6..eb0397a5d7 100755
--- a/ConnectionResolverInterface.php
+++ b/ConnectionResolverInterface.php
@@ -7,7 +7,7 @@ interface ConnectionResolverInterface
/**
* Get a database connection instance.
*
- * @param \UnitEnum|string|null $name
+ * @param string $name
* @return \Illuminate\Database\ConnectionInterface
*/
public function connection($name = null);
diff --git a/Connectors/ConnectionFactory.php b/Connectors/ConnectionFactory.php
index 7017c0aa5c..709fa3aa9a 100755
--- a/Connectors/ConnectionFactory.php
+++ b/Connectors/ConnectionFactory.php
@@ -2,16 +2,16 @@
namespace Illuminate\Database\Connectors;
-use Illuminate\Contracts\Container\Container;
+use PDOException;
+use Illuminate\Support\Arr;
+use InvalidArgumentException;
use Illuminate\Database\Connection;
-use Illuminate\Database\MariaDbConnection;
use Illuminate\Database\MySqlConnection;
-use Illuminate\Database\PostgresConnection;
use Illuminate\Database\SQLiteConnection;
+use Illuminate\Database\PostgresConnection;
use Illuminate\Database\SqlServerConnection;
-use Illuminate\Support\Arr;
-use InvalidArgumentException;
-use PDOException;
+use Illuminate\Contracts\Container\Container;
+use Illuminate\Contracts\Debug\ExceptionHandler;
class ConnectionFactory
{
@@ -26,6 +26,7 @@ class ConnectionFactory
* Create a new connection factory instance.
*
* @param \Illuminate\Contracts\Container\Container $container
+ * @return void
*/
public function __construct(Container $container)
{
@@ -35,8 +36,8 @@ public function __construct(Container $container)
/**
* Establish a PDO connection based on the configuration.
*
- * @param array $config
- * @param string|null $name
+ * @param array $config
+ * @param string $name
* @return \Illuminate\Database\Connection
*/
public function make(array $config, $name = null)
@@ -53,7 +54,7 @@ public function make(array $config, $name = null)
/**
* Parse and prepare the database configuration.
*
- * @param array $config
+ * @param array $config
* @param string $name
* @return array
*/
@@ -78,7 +79,7 @@ protected function createSingleConnection(array $config)
}
/**
- * Create a read / write database connection instance.
+ * Create a single database connection instance.
*
* @param array $config
* @return \Illuminate\Database\Connection
@@ -87,9 +88,7 @@ protected function createReadWriteConnection(array $config)
{
$connection = $this->createSingleConnection($this->getWriteConfig($config));
- return $connection
- ->setReadPdo($this->createReadPdo($config))
- ->setReadPdoConfig($this->getReadConfig($config));
+ return $connection->setReadPdo($this->createReadPdo($config));
}
/**
@@ -117,7 +116,7 @@ protected function getReadConfig(array $config)
}
/**
- * Get the write configuration for a read / write connection.
+ * Get the read configuration for a read / write connection.
*
* @param array $config
* @return array
@@ -132,15 +131,15 @@ protected function getWriteConfig(array $config)
/**
* Get a read / write level configuration.
*
- * @param array $config
+ * @param array $config
* @param string $type
* @return array
*/
protected function getReadWriteConfig(array $config, $type)
{
return isset($config[$type][0])
- ? Arr::random($config[$type])
- : $config[$type];
+ ? $config[$type][array_rand($config[$type])]
+ : $config[$type];
}
/**
@@ -164,8 +163,8 @@ protected function mergeReadWriteConfig(array $config, array $merge)
protected function createPdoResolver(array $config)
{
return array_key_exists('host', $config)
- ? $this->createPdoResolverWithHosts($config)
- : $this->createPdoResolverWithoutHosts($config);
+ ? $this->createPdoResolverWithHosts($config)
+ : $this->createPdoResolverWithoutHosts($config);
}
/**
@@ -173,25 +172,23 @@ protected function createPdoResolver(array $config)
*
* @param array $config
* @return \Closure
- *
- * @throws \PDOException
*/
protected function createPdoResolverWithHosts(array $config)
{
return function () use ($config) {
- foreach (Arr::shuffle($this->parseHosts($config)) as $host) {
+ foreach (Arr::shuffle($hosts = $this->parseHosts($config)) as $key => $host) {
$config['host'] = $host;
try {
return $this->createConnector($config)->connect($config);
} catch (PDOException $e) {
- continue;
+ if (count($hosts) - 1 === $key && $this->container->bound(ExceptionHandler::class)) {
+ $this->container->make(ExceptionHandler::class)->report($e);
+ }
}
}
- if (isset($e)) {
- throw $e;
- }
+ throw $e;
};
}
@@ -200,8 +197,6 @@ protected function createPdoResolverWithHosts(array $config)
*
* @param array $config
* @return array
- *
- * @throws \InvalidArgumentException
*/
protected function parseHosts(array $config)
{
@@ -222,7 +217,9 @@ protected function parseHosts(array $config)
*/
protected function createPdoResolverWithoutHosts(array $config)
{
- return fn () => $this->createConnector($config)->connect($config);
+ return function () use ($config) {
+ return $this->createConnector($config)->connect($config);
+ };
}
/**
@@ -243,24 +240,28 @@ public function createConnector(array $config)
return $this->container->make($key);
}
- return match ($config['driver']) {
- 'mysql' => new MySqlConnector,
- 'mariadb' => new MariaDbConnector,
- 'pgsql' => new PostgresConnector,
- 'sqlite' => new SQLiteConnector,
- 'sqlsrv' => new SqlServerConnector,
- default => throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]."),
- };
+ switch ($config['driver']) {
+ case 'mysql':
+ return new MySqlConnector;
+ case 'pgsql':
+ return new PostgresConnector;
+ case 'sqlite':
+ return new SQLiteConnector;
+ case 'sqlsrv':
+ return new SqlServerConnector;
+ }
+
+ throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]");
}
/**
* Create a new connection instance.
*
- * @param string $driver
- * @param \PDO|\Closure $connection
- * @param string $database
- * @param string $prefix
- * @param array $config
+ * @param string $driver
+ * @param \PDO|\Closure $connection
+ * @param string $database
+ * @param string $prefix
+ * @param array $config
* @return \Illuminate\Database\Connection
*
* @throws \InvalidArgumentException
@@ -271,13 +272,17 @@ protected function createConnection($driver, $connection, $database, $prefix = '
return $resolver($connection, $database, $prefix, $config);
}
- return match ($driver) {
- 'mysql' => new MySqlConnection($connection, $database, $prefix, $config),
- 'mariadb' => new MariaDbConnection($connection, $database, $prefix, $config),
- 'pgsql' => new PostgresConnection($connection, $database, $prefix, $config),
- 'sqlite' => new SQLiteConnection($connection, $database, $prefix, $config),
- 'sqlsrv' => new SqlServerConnection($connection, $database, $prefix, $config),
- default => throw new InvalidArgumentException("Unsupported driver [{$driver}]."),
- };
+ switch ($driver) {
+ case 'mysql':
+ return new MySqlConnection($connection, $database, $prefix, $config);
+ case 'pgsql':
+ return new PostgresConnection($connection, $database, $prefix, $config);
+ case 'sqlite':
+ return new SQLiteConnection($connection, $database, $prefix, $config);
+ case 'sqlsrv':
+ return new SqlServerConnection($connection, $database, $prefix, $config);
+ }
+
+ throw new InvalidArgumentException("Unsupported driver [$driver]");
}
}
diff --git a/Connectors/Connector.php b/Connectors/Connector.php
index 8d0af49a9a..a56fe95a47 100755
--- a/Connectors/Connector.php
+++ b/Connectors/Connector.php
@@ -2,10 +2,11 @@
namespace Illuminate\Database\Connectors;
+use PDO;
use Exception;
+use Illuminate\Support\Arr;
+use Doctrine\DBAL\Driver\PDOConnection;
use Illuminate\Database\DetectsLostConnections;
-use PDO;
-use Throwable;
class Connector
{
@@ -28,16 +29,14 @@ class Connector
* Create a new PDO connection.
*
* @param string $dsn
- * @param array $config
- * @param array $options
+ * @param array $config
+ * @param array $options
* @return \PDO
- *
- * @throws \Exception
*/
public function createConnection($dsn, array $config, array $options)
{
- [$username, $password] = [
- $config['username'] ?? null, $config['password'] ?? null,
+ list($username, $password) = [
+ Arr::get($config, 'username'), Arr::get($config, 'password'),
];
try {
@@ -60,26 +59,40 @@ public function createConnection($dsn, array $config, array $options)
* @param array $options
* @return \PDO
*/
- protected function createPdoConnection($dsn, $username, #[\SensitiveParameter] $password, $options)
+ protected function createPdoConnection($dsn, $username, $password, $options)
{
- return version_compare(PHP_VERSION, '8.4.0', '<')
- ? new PDO($dsn, $username, $password, $options)
- : PDO::connect($dsn, $username, $password, $options); /** @phpstan-ignore staticMethod.notFound (PHP 8.4) */
+ if (class_exists(PDOConnection::class) && ! $this->isPersistentConnection($options)) {
+ return new PDOConnection($dsn, $username, $password, $options);
+ }
+
+ return new PDO($dsn, $username, $password, $options);
+ }
+
+ /**
+ * Determine if the connection is persistent.
+ *
+ * @param array $options
+ * @return bool
+ */
+ protected function isPersistentConnection($options)
+ {
+ return isset($options[PDO::ATTR_PERSISTENT]) &&
+ $options[PDO::ATTR_PERSISTENT];
}
/**
* Handle an exception that occurred during connect execution.
*
- * @param \Throwable $e
+ * @param \Exception $e
* @param string $dsn
* @param string $username
* @param string $password
- * @param array $options
+ * @param array $options
* @return \PDO
*
- * @throws \Throwable
+ * @throws \Exception
*/
- protected function tryAgainIfCausedByLostConnection(Throwable $e, $dsn, $username, #[\SensitiveParameter] $password, $options)
+ protected function tryAgainIfCausedByLostConnection(Exception $e, $dsn, $username, $password, $options)
{
if ($this->causedByLostConnection($e)) {
return $this->createPdoConnection($dsn, $username, $password, $options);
@@ -96,7 +109,7 @@ protected function tryAgainIfCausedByLostConnection(Throwable $e, $dsn, $usernam
*/
public function getOptions(array $config)
{
- $options = $config['options'] ?? [];
+ $options = Arr::get($config, 'options', []);
return array_diff_key($this->options, $options) + $options;
}
diff --git a/Connectors/MariaDbConnector.php b/Connectors/MariaDbConnector.php
deleted file mode 100755
index b7203f87ae..0000000000
--- a/Connectors/MariaDbConnector.php
+++ /dev/null
@@ -1,32 +0,0 @@
-createConnection($dsn, $config, $options);
- if (! empty($config['database']) &&
- (! isset($config['use_db_after_connecting']) ||
- $config['use_db_after_connecting'])) {
+ if (! empty($config['database'])) {
$connection->exec("use `{$config['database']}`;");
}
- $this->configureConnection($connection, $config);
+ $this->configureEncoding($connection, $config);
+
+ // Next, we will check to see if a timezone has been specified in this config
+ // and if it has we will issue a statement to modify the timezone with the
+ // database. Setting this DB timezone is an optional configuration item.
+ $this->configureTimezone($connection, $config);
+
+ $this->setModes($connection, $config);
return $connection;
}
+ /**
+ * Set the connection character set and collation.
+ *
+ * @param \PDO $connection
+ * @param array $config
+ * @return void
+ */
+ protected function configureEncoding($connection, array $config)
+ {
+ if (! isset($config['charset'])) {
+ return $connection;
+ }
+
+ $connection->prepare(
+ "set names '{$config['charset']}'".$this->getCollation($config)
+ )->execute();
+ }
+
+ /**
+ * Get the collation for the configuration.
+ *
+ * @param array $config
+ * @return string
+ */
+ protected function getCollation(array $config)
+ {
+ return ! is_null($config['collation']) ? " collate '{$config['collation']}'" : '';
+ }
+
+ /**
+ * Set the timezone on the connection.
+ *
+ * @param \PDO $connection
+ * @param array $config
+ * @return void
+ */
+ protected function configureTimezone($connection, array $config)
+ {
+ if (isset($config['timezone'])) {
+ $connection->prepare('set time_zone="'.$config['timezone'].'"')->execute();
+ }
+ }
+
/**
* Create a DSN string from a configuration.
*
* Chooses socket or host/port based on the 'unix_socket' config value.
*
- * @param array $config
+ * @param array $config
* @return string
*/
protected function getDsn(array $config)
{
return $this->hasSocket($config)
- ? $this->getSocketDsn($config)
- : $this->getHostDsn($config);
+ ? $this->getSocketDsn($config)
+ : $this->getHostDsn($config);
}
/**
@@ -79,76 +127,54 @@ protected function getSocketDsn(array $config)
*/
protected function getHostDsn(array $config)
{
- return isset($config['port'])
- ? "mysql:host={$config['host']};port={$config['port']};dbname={$config['database']}"
- : "mysql:host={$config['host']};dbname={$config['database']}";
+ extract($config, EXTR_SKIP);
+
+ return isset($port)
+ ? "mysql:host={$host};port={$port};dbname={$database}"
+ : "mysql:host={$host};dbname={$database}";
}
/**
- * Configure the given PDO connection.
+ * Set the modes for the connection.
*
* @param \PDO $connection
* @param array $config
* @return void
*/
- protected function configureConnection(PDO $connection, array $config)
+ protected function setModes(PDO $connection, array $config)
{
- if (isset($config['isolation_level'])) {
- $connection->exec(sprintf('SET SESSION TRANSACTION ISOLATION LEVEL %s;', $config['isolation_level']));
- }
-
- $statements = [];
-
- if (isset($config['charset'])) {
- if (isset($config['collation'])) {
- $statements[] = sprintf("NAMES '%s' COLLATE '%s'", $config['charset'], $config['collation']);
+ if (isset($config['modes'])) {
+ $this->setCustomModes($connection, $config);
+ } elseif (isset($config['strict'])) {
+ if ($config['strict']) {
+ $connection->prepare($this->strictMode())->execute();
} else {
- $statements[] = sprintf("NAMES '%s'", $config['charset']);
+ $connection->prepare("set session sql_mode='NO_ENGINE_SUBSTITUTION'")->execute();
}
}
-
- if (isset($config['timezone'])) {
- $statements[] = sprintf("time_zone='%s'", $config['timezone']);
- }
-
- $sqlMode = $this->getSqlMode($connection, $config);
-
- if ($sqlMode !== null) {
- $statements[] = sprintf("SESSION sql_mode='%s'", $sqlMode);
- }
-
- if ($statements !== []) {
- $connection->exec(sprintf('SET %s;', implode(', ', $statements)));
- }
}
/**
- * Get the sql_mode value.
+ * Set the custom modes on the connection.
*
* @param \PDO $connection
* @param array $config
- * @return string|null
+ * @return void
*/
- protected function getSqlMode(PDO $connection, array $config)
+ protected function setCustomModes(PDO $connection, array $config)
{
- if (isset($config['modes'])) {
- return implode(',', $config['modes']);
- }
-
- if (! isset($config['strict'])) {
- return null;
- }
-
- if (! $config['strict']) {
- return 'NO_ENGINE_SUBSTITUTION';
- }
-
- $version = $config['version'] ?? $connection->getAttribute(PDO::ATTR_SERVER_VERSION);
+ $modes = implode(',', $config['modes']);
- if (version_compare($version, '8.0.11', '>=')) {
- return 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
- }
+ $connection->prepare("set session sql_mode='{$modes}'")->execute();
+ }
- return 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
+ /**
+ * Get the query to enable strict mode.
+ *
+ * @return string
+ */
+ protected function strictMode()
+ {
+ return "set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'";
}
}
diff --git a/Connectors/PostgresConnector.php b/Connectors/PostgresConnector.php
index 31d2ff4732..0581b8b2aa 100755
--- a/Connectors/PostgresConnector.php
+++ b/Connectors/PostgresConnector.php
@@ -2,13 +2,10 @@
namespace Illuminate\Database\Connectors;
-use Illuminate\Database\Concerns\ParsesSearchPath;
use PDO;
class PostgresConnector extends Connector implements ConnectorInterface
{
- use ParsesSearchPath;
-
/**
* The default PDO connection options.
*
@@ -36,152 +33,142 @@ public function connect(array $config)
$this->getDsn($config), $config, $this->getOptions($config)
);
- $this->configureIsolationLevel($connection, $config);
+ $this->configureEncoding($connection, $config);
// Next, we will check to see if a timezone has been specified in this config
// and if it has we will issue a statement to modify the timezone with the
// database. Setting this DB timezone is an optional configuration item.
$this->configureTimezone($connection, $config);
- $this->configureSearchPath($connection, $config);
+ $this->configureSchema($connection, $config);
- $this->configureSynchronousCommit($connection, $config);
+ // Postgres allows an application_name to be set by the user and this name is
+ // used to when monitoring the application with pg_stat_activity. So we'll
+ // determine if the option has been specified and run a statement if so.
+ $this->configureApplicationName($connection, $config);
return $connection;
}
/**
- * Create a DSN string from a configuration.
+ * Set the connection character set and collation.
*
+ * @param \PDO $connection
* @param array $config
- * @return string
+ * @return void
*/
- protected function getDsn(array $config)
+ protected function configureEncoding($connection, $config)
{
- // First we will create the basic DSN setup as well as the port if it is in
- // in the configuration options. This will give us the basic DSN we will
- // need to establish the PDO connections and return them back for use.
- extract($config, EXTR_SKIP);
-
- $host = isset($host) ? "host={$host};" : '';
-
- // Sometimes - users may need to connect to a database that has a different
- // name than the database used for "information_schema" queries. This is
- // typically the case if using "pgbouncer" type software when pooling.
- $database = $connect_via_database ?? $database ?? null;
- $port = $connect_via_port ?? $port ?? null;
-
- $dsn = "pgsql:{$host}dbname='{$database}'";
-
- // If a port was specified, we will add it to this Postgres DSN connections
- // format. Once we have done that we are ready to return this connection
- // string back out for usage, as this has been fully constructed here.
- if (! is_null($port)) {
- $dsn .= ";port={$port}";
- }
+ $charset = $config['charset'];
- if (isset($charset)) {
- $dsn .= ";client_encoding='{$charset}'";
- }
-
- // Postgres allows an application_name to be set by the user and this name is
- // used to when monitoring the application with pg_stat_activity. So we'll
- // determine if the option has been specified and run a statement if so.
- if (isset($application_name)) {
- $dsn .= ";application_name='".str_replace("'", "\'", $application_name)."'";
- }
-
- return $this->addSslOptions($dsn, $config);
+ $connection->prepare("set names '$charset'")->execute();
}
/**
- * Add the SSL options to the DSN.
+ * Set the timezone on the connection.
*
- * @param string $dsn
+ * @param \PDO $connection
* @param array $config
- * @return string
+ * @return void
*/
- protected function addSslOptions($dsn, array $config)
+ protected function configureTimezone($connection, array $config)
{
- foreach (['sslmode', 'sslcert', 'sslkey', 'sslrootcert'] as $option) {
- if (isset($config[$option])) {
- $dsn .= ";{$option}={$config[$option]}";
- }
- }
+ if (isset($config['timezone'])) {
+ $timezone = $config['timezone'];
- return $dsn;
+ $connection->prepare("set time zone '{$timezone}'")->execute();
+ }
}
/**
- * Set the connection transaction isolation level.
+ * Set the schema on the connection.
*
* @param \PDO $connection
* @param array $config
* @return void
*/
- protected function configureIsolationLevel($connection, array $config)
+ protected function configureSchema($connection, $config)
{
- if (isset($config['isolation_level'])) {
- $connection->prepare("set session characteristics as transaction isolation level {$config['isolation_level']}")->execute();
+ if (isset($config['schema'])) {
+ $schema = $this->formatSchema($config['schema']);
+
+ $connection->prepare("set search_path to {$schema}")->execute();
}
}
/**
- * Set the timezone on the connection.
+ * Format the schema for the DSN.
*
- * @param \PDO $connection
- * @param array $config
- * @return void
+ * @param array|string $schema
+ * @return string
*/
- protected function configureTimezone($connection, array $config)
+ protected function formatSchema($schema)
{
- if (isset($config['timezone'])) {
- $timezone = $config['timezone'];
-
- $connection->prepare("set time zone '{$timezone}'")->execute();
+ if (is_array($schema)) {
+ return '"'.implode('", "', $schema).'"';
+ } else {
+ return '"'.$schema.'"';
}
}
/**
- * Set the "search_path" on the database connection.
+ * Set the schema on the connection.
*
* @param \PDO $connection
* @param array $config
* @return void
*/
- protected function configureSearchPath($connection, $config)
+ protected function configureApplicationName($connection, $config)
{
- if (isset($config['search_path']) || isset($config['schema'])) {
- $searchPath = $this->quoteSearchPath(
- $this->parseSearchPath($config['search_path'] ?? $config['schema'])
- );
+ if (isset($config['application_name'])) {
+ $applicationName = $config['application_name'];
- $connection->prepare("set search_path to {$searchPath}")->execute();
+ $connection->prepare("set application_name to '$applicationName'")->execute();
}
}
/**
- * Format the search path for the DSN.
+ * Create a DSN string from a configuration.
*
- * @param array $searchPath
+ * @param array $config
* @return string
*/
- protected function quoteSearchPath($searchPath)
+ protected function getDsn(array $config)
{
- return count($searchPath) === 1 ? '"'.$searchPath[0].'"' : '"'.implode('", "', $searchPath).'"';
+ // First we will create the basic DSN setup as well as the port if it is in
+ // in the configuration options. This will give us the basic DSN we will
+ // need to establish the PDO connections and return them back for use.
+ extract($config, EXTR_SKIP);
+
+ $host = isset($host) ? "host={$host};" : '';
+
+ $dsn = "pgsql:{$host}dbname={$database}";
+
+ // If a port was specified, we will add it to this Postgres DSN connections
+ // format. Once we have done that we are ready to return this connection
+ // string back out for usage, as this has been fully constructed here.
+ if (isset($config['port'])) {
+ $dsn .= ";port={$port}";
+ }
+
+ return $this->addSslOptions($dsn, $config);
}
/**
- * Configure the synchronous_commit setting.
+ * Add the SSL options to the DSN.
*
- * @param \PDO $connection
+ * @param string $dsn
* @param array $config
- * @return void
+ * @return string
*/
- protected function configureSynchronousCommit($connection, array $config)
+ protected function addSslOptions($dsn, array $config)
{
- if (isset($config['synchronous_commit'])) {
- $connection->prepare("set synchronous_commit to '{$config['synchronous_commit']}'")->execute();
+ foreach (['sslmode', 'sslcert', 'sslkey', 'sslrootcert'] as $option) {
+ if (isset($config[$option])) {
+ $dsn .= ";{$option}={$config[$option]}";
+ }
}
+
+ return $dsn;
}
}
diff --git a/Connectors/SQLiteConnector.php b/Connectors/SQLiteConnector.php
index 858549ec55..28f90915b5 100755
--- a/Connectors/SQLiteConnector.php
+++ b/Connectors/SQLiteConnector.php
@@ -2,7 +2,7 @@
namespace Illuminate\Database\Connectors;
-use Illuminate\Database\SQLiteDatabaseDoesNotExistException;
+use InvalidArgumentException;
class SQLiteConnector extends Connector implements ConnectorInterface
{
@@ -11,139 +11,29 @@ class SQLiteConnector extends Connector implements ConnectorInterface
*
* @param array $config
* @return \PDO
+ *
+ * @throws \InvalidArgumentException
*/
public function connect(array $config)
{
$options = $this->getOptions($config);
- $path = $this->parseDatabasePath($config['database']);
-
- $connection = $this->createConnection("sqlite:{$path}", $config, $options);
-
- $this->configurePragmas($connection, $config);
- $this->configureForeignKeyConstraints($connection, $config);
- $this->configureBusyTimeout($connection, $config);
- $this->configureJournalMode($connection, $config);
- $this->configureSynchronous($connection, $config);
-
- return $connection;
- }
-
- /**
- * Get the absolute database path.
- *
- * @param string $path
- * @return string
- *
- * @throws \Illuminate\Database\SQLiteDatabaseDoesNotExistException
- */
- protected function parseDatabasePath(string $path): string
- {
- $database = $path;
-
// SQLite supports "in-memory" databases that only last as long as the owning
// connection does. These are useful for tests or for short lifetime store
- // querying. In-memory databases shall be anonymous (:memory:) or named.
- if ($path === ':memory:' ||
- str_contains($path, '?mode=memory') ||
- str_contains($path, '&mode=memory')
- ) {
- return $path;
+ // querying. In-memory databases may only have a single open connection.
+ if ($config['database'] == ':memory:') {
+ return $this->createConnection('sqlite::memory:', $config, $options);
}
- $path = realpath($path) ?: realpath(base_path($path));
+ $path = realpath($config['database']);
// Here we'll verify that the SQLite database exists before going any further
// as the developer probably wants to know if the database exists and this
// SQLite driver will not throw any exception if it does not by default.
if ($path === false) {
- throw new SQLiteDatabaseDoesNotExistException($database);
- }
-
- return $path;
- }
-
- /**
- * Set miscellaneous user-configured pragmas.
- *
- * @param \PDO $connection
- * @param array $config
- * @return void
- */
- protected function configurePragmas($connection, array $config): void
- {
- if (! isset($config['pragmas'])) {
- return;
- }
-
- foreach ($config['pragmas'] as $pragma => $value) {
- $connection->prepare("pragma {$pragma} = {$value}")->execute();
- }
- }
-
- /**
- * Enable or disable foreign key constraints if configured.
- *
- * @param \PDO $connection
- * @param array $config
- * @return void
- */
- protected function configureForeignKeyConstraints($connection, array $config): void
- {
- if (! isset($config['foreign_key_constraints'])) {
- return;
- }
-
- $foreignKeys = $config['foreign_key_constraints'] ? 1 : 0;
-
- $connection->prepare("pragma foreign_keys = {$foreignKeys}")->execute();
- }
-
- /**
- * Set the busy timeout if configured.
- *
- * @param \PDO $connection
- * @param array $config
- * @return void
- */
- protected function configureBusyTimeout($connection, array $config): void
- {
- if (! isset($config['busy_timeout'])) {
- return;
- }
-
- $connection->prepare("pragma busy_timeout = {$config['busy_timeout']}")->execute();
- }
-
- /**
- * Set the journal mode if configured.
- *
- * @param \PDO $connection
- * @param array $config
- * @return void
- */
- protected function configureJournalMode($connection, array $config): void
- {
- if (! isset($config['journal_mode'])) {
- return;
- }
-
- $connection->prepare("pragma journal_mode = {$config['journal_mode']}")->execute();
- }
-
- /**
- * Set the synchronous mode if configured.
- *
- * @param \PDO $connection
- * @param array $config
- * @return void
- */
- protected function configureSynchronous($connection, array $config): void
- {
- if (! isset($config['synchronous'])) {
- return;
+ throw new InvalidArgumentException("Database (${config['database']}) does not exist.");
}
- $connection->prepare("pragma synchronous = {$config['synchronous']}")->execute();
+ return $this->createConnection("sqlite:{$path}", $config, $options);
}
}
diff --git a/Connectors/SqlServerConnector.php b/Connectors/SqlServerConnector.php
index 14cb72dbbf..a1b8447ab6 100755
--- a/Connectors/SqlServerConnector.php
+++ b/Connectors/SqlServerConnector.php
@@ -2,8 +2,8 @@
namespace Illuminate\Database\Connectors;
-use Illuminate\Support\Arr;
use PDO;
+use Illuminate\Support\Arr;
class SqlServerConnector extends Connector implements ConnectorInterface
{
@@ -29,37 +29,13 @@ public function connect(array $config)
{
$options = $this->getOptions($config);
- $connection = $this->createConnection($this->getDsn($config), $config, $options);
-
- $this->configureIsolationLevel($connection, $config);
-
- return $connection;
- }
-
- /**
- * Set the connection transaction isolation level.
- *
- * https://learn.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql
- *
- * @param \PDO $connection
- * @param array $config
- * @return void
- */
- protected function configureIsolationLevel($connection, array $config)
- {
- if (! isset($config['isolation_level'])) {
- return;
- }
-
- $connection->prepare(
- "SET TRANSACTION ISOLATION LEVEL {$config['isolation_level']}"
- )->execute();
+ return $this->createConnection($this->getDsn($config), $config, $options);
}
/**
* Create a DSN string from a configuration.
*
- * @param array $config
+ * @param array $config
* @return string
*/
protected function getDsn(array $config)
@@ -67,14 +43,12 @@ protected function getDsn(array $config)
// First we will create the basic DSN setup as well as the port if it is in
// in the configuration options. This will give us the basic DSN we will
// need to establish the PDO connections and return them back for use.
- if ($this->prefersOdbc($config)) {
+ if (in_array('dblib', $this->getAvailableDrivers())) {
+ return $this->getDblibDsn($config);
+ } elseif ($this->prefersOdbc($config)) {
return $this->getOdbcDsn($config);
- }
-
- if (in_array('sqlsrv', $this->getAvailableDrivers())) {
- return $this->getSqlSrvDsn($config);
} else {
- return $this->getDblibDsn($config);
+ return $this->getSqlSrvDsn($config);
}
}
@@ -87,7 +61,7 @@ protected function getDsn(array $config)
protected function prefersOdbc(array $config)
{
return in_array('odbc', $this->getAvailableDrivers()) &&
- ($config['odbc'] ?? null) === true;
+ array_get($config, 'odbc') === true;
}
/**
@@ -101,7 +75,7 @@ protected function getDblibDsn(array $config)
return $this->buildConnectString('dblib', array_merge([
'host' => $this->buildHostString($config, ':'),
'dbname' => $config['database'],
- ], Arr::only($config, ['appname', 'charset', 'version'])));
+ ], Arr::only($config, ['appname', 'charset'])));
}
/**
@@ -113,8 +87,7 @@ protected function getDblibDsn(array $config)
protected function getOdbcDsn(array $config)
{
return isset($config['odbc_datasource_name'])
- ? 'odbc:'.$config['odbc_datasource_name']
- : '';
+ ? 'odbc:'.$config['odbc_datasource_name'] : '';
}
/**
@@ -153,42 +126,6 @@ protected function getSqlSrvDsn(array $config)
$arguments['TrustServerCertificate'] = $config['trust_server_certificate'];
}
- if (isset($config['multiple_active_result_sets']) && $config['multiple_active_result_sets'] === false) {
- $arguments['MultipleActiveResultSets'] = 'false';
- }
-
- if (isset($config['transaction_isolation'])) {
- $arguments['TransactionIsolation'] = $config['transaction_isolation'];
- }
-
- if (isset($config['multi_subnet_failover'])) {
- $arguments['MultiSubnetFailover'] = $config['multi_subnet_failover'];
- }
-
- if (isset($config['column_encryption'])) {
- $arguments['ColumnEncryption'] = $config['column_encryption'];
- }
-
- if (isset($config['key_store_authentication'])) {
- $arguments['KeyStoreAuthentication'] = $config['key_store_authentication'];
- }
-
- if (isset($config['key_store_principal_id'])) {
- $arguments['KeyStorePrincipalId'] = $config['key_store_principal_id'];
- }
-
- if (isset($config['key_store_secret'])) {
- $arguments['KeyStoreSecret'] = $config['key_store_secret'];
- }
-
- if (isset($config['login_timeout'])) {
- $arguments['LoginTimeout'] = $config['login_timeout'];
- }
-
- if (isset($config['authentication'])) {
- $arguments['Authentication'] = $config['authentication'];
- }
-
return $this->buildConnectString('sqlsrv', $arguments);
}
@@ -215,11 +152,11 @@ protected function buildConnectString($driver, array $arguments)
*/
protected function buildHostString(array $config, $separator)
{
- if (empty($config['port'])) {
+ if (isset($config['port']) && ! empty($config['port'])) {
+ return $config['host'].$separator.$config['port'];
+ } else {
return $config['host'];
}
-
- return $config['host'].$separator.$config['port'];
}
/**
diff --git a/Console/DatabaseInspectionCommand.php b/Console/DatabaseInspectionCommand.php
deleted file mode 100644
index 8faab04147..0000000000
--- a/Console/DatabaseInspectionCommand.php
+++ /dev/null
@@ -1,50 +0,0 @@
-getDriverTitle();
- }
-
- /**
- * Get the number of open connections for a database.
- *
- * @param \Illuminate\Database\ConnectionInterface $connection
- * @return int|null
- *
- * @deprecated
- */
- protected function getConnectionCount(ConnectionInterface $connection)
- {
- return $connection->threadCount();
- }
-
- /**
- * Get the connection configuration details for the given connection.
- *
- * @param string|null $database
- * @return array
- */
- protected function getConfigFromDatabase($database)
- {
- $database ??= config('database.default');
-
- return Arr::except(config('database.connections.'.$database), ['password']);
- }
-}
diff --git a/Console/DbCommand.php b/Console/DbCommand.php
deleted file mode 100644
index 3017607355..0000000000
--- a/Console/DbCommand.php
+++ /dev/null
@@ -1,257 +0,0 @@
-getConnection();
-
- if (! isset($connection['host']) && $connection['driver'] !== 'sqlite') {
- $this->components->error('No host specified for this database connection.');
- $this->line(' Use the [--read]> and [--write]> options to specify a read or write connection.');
- $this->newLine();
-
- return Command::FAILURE;
- }
-
- try {
- (new Process(
- array_merge([$command = $this->getCommand($connection)], $this->commandArguments($connection)),
- null,
- $this->commandEnvironment($connection)
- ))->setTimeout(null)->setTty(true)->mustRun(function ($type, $buffer) {
- $this->output->write($buffer);
- });
- } catch (ProcessFailedException $e) {
- throw_unless($e->getProcess()->getExitCode() === 127, $e);
-
- $this->error("{$command} not found in path.");
-
- return Command::FAILURE;
- }
-
- return 0;
- }
-
- /**
- * Get the database connection configuration.
- *
- * @return array
- *
- * @throws \UnexpectedValueException
- */
- public function getConnection()
- {
- $connection = $this->laravel['config']['database.connections.'.
- (($db = $this->argument('connection')) ?? $this->laravel['config']['database.default'])
- ];
-
- if (empty($connection)) {
- throw new UnexpectedValueException("Invalid database connection [{$db}].");
- }
-
- if (! empty($connection['url'])) {
- $connection = (new ConfigurationUrlParser)->parseConfiguration($connection);
- }
-
- if ($this->option('read')) {
- if (is_array($connection['read']['host'])) {
- $connection['read']['host'] = $connection['read']['host'][0];
- }
-
- $connection = array_merge($connection, $connection['read']);
- } elseif ($this->option('write')) {
- if (is_array($connection['write']['host'])) {
- $connection['write']['host'] = $connection['write']['host'][0];
- }
-
- $connection = array_merge($connection, $connection['write']);
- }
-
- return $connection;
- }
-
- /**
- * Get the arguments for the database client command.
- *
- * @param array $connection
- * @return array
- */
- public function commandArguments(array $connection)
- {
- $driver = ucfirst($connection['driver']);
-
- return $this->{"get{$driver}Arguments"}($connection);
- }
-
- /**
- * Get the environment variables for the database client command.
- *
- * @param array $connection
- * @return array|null
- */
- public function commandEnvironment(array $connection)
- {
- $driver = ucfirst($connection['driver']);
-
- if (method_exists($this, "get{$driver}Environment")) {
- return $this->{"get{$driver}Environment"}($connection);
- }
-
- return null;
- }
-
- /**
- * Get the database client command to run.
- *
- * @param array $connection
- * @return string
- */
- public function getCommand(array $connection)
- {
- return [
- 'mysql' => 'mysql',
- 'mariadb' => 'mariadb',
- 'pgsql' => 'psql',
- 'sqlite' => 'sqlite3',
- 'sqlsrv' => 'sqlcmd',
- ][$connection['driver']];
- }
-
- /**
- * Get the arguments for the MySQL CLI.
- *
- * @param array $connection
- * @return array
- */
- protected function getMysqlArguments(array $connection)
- {
- $optionalArguments = [
- 'password' => '--password='.$connection['password'],
- 'unix_socket' => '--socket='.($connection['unix_socket'] ?? ''),
- 'charset' => '--default-character-set='.($connection['charset'] ?? ''),
- ];
-
- if (! $connection['password']) {
- unset($optionalArguments['password']);
- }
-
- return array_merge([
- '--host='.$connection['host'],
- '--port='.$connection['port'],
- '--user='.$connection['username'],
- ], $this->getOptionalArguments($optionalArguments, $connection), [$connection['database']]);
- }
-
- /**
- * Get the arguments for the MariaDB CLI.
- *
- * @param array $connection
- * @return array
- */
- protected function getMariaDbArguments(array $connection)
- {
- return $this->getMysqlArguments($connection);
- }
-
- /**
- * Get the arguments for the Postgres CLI.
- *
- * @param array $connection
- * @return array
- */
- protected function getPgsqlArguments(array $connection)
- {
- return [$connection['database']];
- }
-
- /**
- * Get the arguments for the SQLite CLI.
- *
- * @param array $connection
- * @return array
- */
- protected function getSqliteArguments(array $connection)
- {
- return [$connection['database']];
- }
-
- /**
- * Get the arguments for the SQL Server CLI.
- *
- * @param array $connection
- * @return array
- */
- protected function getSqlsrvArguments(array $connection)
- {
- return array_merge(...$this->getOptionalArguments([
- 'database' => ['-d', $connection['database']],
- 'username' => ['-U', $connection['username']],
- 'password' => ['-P', $connection['password']],
- 'host' => ['-S', 'tcp:'.$connection['host']
- .($connection['port'] ? ','.$connection['port'] : ''), ],
- 'trust_server_certificate' => ['-C'],
- ], $connection));
- }
-
- /**
- * Get the environment variables for the Postgres CLI.
- *
- * @param array $connection
- * @return array|null
- */
- protected function getPgsqlEnvironment(array $connection)
- {
- return array_merge(...$this->getOptionalArguments([
- 'username' => ['PGUSER' => $connection['username']],
- 'host' => ['PGHOST' => $connection['host']],
- 'port' => ['PGPORT' => $connection['port']],
- 'password' => ['PGPASSWORD' => $connection['password']],
- ], $connection));
- }
-
- /**
- * Get the optional arguments based on the connection configuration.
- *
- * @param array $args
- * @param array $connection
- * @return array
- */
- protected function getOptionalArguments(array $args, array $connection)
- {
- return array_values(array_filter($args, function ($key) use ($connection) {
- return ! empty($connection[$key]);
- }, ARRAY_FILTER_USE_KEY));
- }
-}
diff --git a/Console/DumpCommand.php b/Console/DumpCommand.php
deleted file mode 100644
index fea8fc0554..0000000000
--- a/Console/DumpCommand.php
+++ /dev/null
@@ -1,104 +0,0 @@
-isProhibited()) {
- return Command::FAILURE;
- }
-
- $connection = $connections->connection($database = $this->input->getOption('database'));
-
- $this->schemaState($connection)->dump(
- $connection, $path = $this->path($connection)
- );
-
- $dispatcher->dispatch(new SchemaDumped($connection, $path));
-
- $info = 'Database schema dumped';
-
- if ($this->option('prune')) {
- (new Filesystem)->deleteDirectory(
- $path = database_path('migrations'), preserve: false
- );
-
- $info .= ' and pruned';
-
- $dispatcher->dispatch(new MigrationsPruned($connection, $path));
- }
-
- $this->components->info($info.' successfully.');
- }
-
- /**
- * Create a schema state instance for the given connection.
- *
- * @param \Illuminate\Database\Connection $connection
- * @return mixed
- */
- protected function schemaState(Connection $connection)
- {
- $migrations = Config::get('database.migrations', 'migrations');
-
- $migrationTable = is_array($migrations) ? ($migrations['table'] ?? 'migrations') : $migrations;
-
- return $connection->getSchemaState()
- ->withMigrationTable($migrationTable)
- ->handleOutputUsing(function ($type, $buffer) {
- $this->output->write($buffer);
- });
- }
-
- /**
- * Get the path that the dump should be written to.
- *
- * @param \Illuminate\Database\Connection $connection
- */
- protected function path(Connection $connection)
- {
- return tap($this->option('path') ?: database_path('schema/'.$connection->getName().'-schema.sql'), function ($path) {
- (new Filesystem)->ensureDirectoryExists(dirname($path));
- });
- }
-}
diff --git a/Console/Factories/FactoryMakeCommand.php b/Console/Factories/FactoryMakeCommand.php
deleted file mode 100644
index 6d080a1439..0000000000
--- a/Console/Factories/FactoryMakeCommand.php
+++ /dev/null
@@ -1,144 +0,0 @@
-resolveStubPath('/stubs/factory.stub');
- }
-
- /**
- * Resolve the fully-qualified path to the stub.
- *
- * @param string $stub
- * @return string
- */
- protected function resolveStubPath($stub)
- {
- return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
- ? $customPath
- : __DIR__.$stub;
- }
-
- /**
- * Build the class with the given name.
- *
- * @param string $name
- * @return string
- */
- protected function buildClass($name)
- {
- $factory = class_basename(Str::ucfirst(str_replace('Factory', '', $name)));
-
- $namespaceModel = $this->option('model')
- ? $this->qualifyModel($this->option('model'))
- : $this->qualifyModel($this->guessModelName($name));
-
- $model = class_basename($namespaceModel);
-
- $namespace = $this->getNamespace(
- Str::replaceFirst($this->rootNamespace(), 'Database\\Factories\\', $this->qualifyClass($this->getNameInput()))
- );
-
- $replace = [
- '{{ factoryNamespace }}' => $namespace,
- 'NamespacedDummyModel' => $namespaceModel,
- '{{ namespacedModel }}' => $namespaceModel,
- '{{namespacedModel}}' => $namespaceModel,
- 'DummyModel' => $model,
- '{{ model }}' => $model,
- '{{model}}' => $model,
- '{{ factory }}' => $factory,
- '{{factory}}' => $factory,
- ];
-
- return str_replace(
- array_keys($replace), array_values($replace), parent::buildClass($name)
- );
- }
-
- /**
- * Get the destination class path.
- *
- * @param string $name
- * @return string
- */
- protected function getPath($name)
- {
- $name = (new Stringable($name))->replaceFirst($this->rootNamespace(), '')->finish('Factory')->value();
-
- return $this->laravel->databasePath().'/factories/'.str_replace('\\', '/', $name).'.php';
- }
-
- /**
- * Guess the model name from the Factory name or return a default model name.
- *
- * @param string $name
- * @return string
- */
- protected function guessModelName($name)
- {
- if (str_ends_with($name, 'Factory')) {
- $name = substr($name, 0, -7);
- }
-
- $modelName = $this->qualifyModel(Str::after($name, $this->rootNamespace()));
-
- if (class_exists($modelName)) {
- return $modelName;
- }
-
- if (is_dir(app_path('Models/'))) {
- return $this->rootNamespace().'Models\Model';
- }
-
- return $this->rootNamespace().'Model';
- }
-
- /**
- * Get the console command options.
- *
- * @return array
- */
- protected function getOptions()
- {
- return [
- ['model', 'm', InputOption::VALUE_OPTIONAL, 'The name of the model'],
- ];
- }
-}
diff --git a/Console/Factories/stubs/factory.stub b/Console/Factories/stubs/factory.stub
deleted file mode 100644
index f931493a27..0000000000
--- a/Console/Factories/stubs/factory.stub
+++ /dev/null
@@ -1,23 +0,0 @@
-
- */
-class {{ factory }}Factory extends Factory
-{
- /**
- * Define the model's default state.
- *
- * @return array
- */
- public function definition(): array
- {
- return [
- //
- ];
- }
-}
diff --git a/Console/Migrations/BaseCommand.php b/Console/Migrations/BaseCommand.php
index a250d2945f..81ca3cc75e 100755
--- a/Console/Migrations/BaseCommand.php
+++ b/Console/Migrations/BaseCommand.php
@@ -3,14 +3,13 @@
namespace Illuminate\Database\Console\Migrations;
use Illuminate\Console\Command;
-use Illuminate\Support\Collection;
class BaseCommand extends Command
{
/**
* Get all of the migration paths.
*
- * @return string[]
+ * @return array
*/
protected function getMigrationPaths()
{
@@ -18,28 +17,16 @@ protected function getMigrationPaths()
// use the path relative to the root of the installation folder so our database
// migrations may be run for any customized path from within the application.
if ($this->input->hasOption('path') && $this->option('path')) {
- return (new Collection($this->option('path')))->map(function ($path) {
- return ! $this->usingRealPath()
- ? $this->laravel->basePath().'/'.$path
- : $path;
+ return collect($this->option('path'))->map(function ($path) {
+ return $this->laravel->basePath().'/'.$path;
})->all();
}
return array_merge(
- $this->migrator->paths(), [$this->getMigrationPath()]
+ [$this->getMigrationPath()], $this->migrator->paths()
);
}
- /**
- * Determine if the given path(s) are pre-resolved "real" paths.
- *
- * @return bool
- */
- protected function usingRealPath()
- {
- return $this->input->hasOption('realpath') && $this->option('realpath');
- }
-
/**
* Get the path to the migration directory.
*
diff --git a/Console/Migrations/FreshCommand.php b/Console/Migrations/FreshCommand.php
index 723d3c2298..68e8d2d68f 100644
--- a/Console/Migrations/FreshCommand.php
+++ b/Console/Migrations/FreshCommand.php
@@ -4,17 +4,11 @@
use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
-use Illuminate\Console\Prohibitable;
-use Illuminate\Contracts\Events\Dispatcher;
-use Illuminate\Database\Events\DatabaseRefreshed;
-use Illuminate\Database\Migrations\Migrator;
-use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
-#[AsCommand(name: 'migrate:fresh')]
class FreshCommand extends Command
{
- use ConfirmableTrait, Prohibitable;
+ use ConfirmableTrait;
/**
* The console command name.
@@ -30,74 +24,45 @@ class FreshCommand extends Command
*/
protected $description = 'Drop all tables and re-run all migrations';
- /**
- * The migrator instance.
- *
- * @var \Illuminate\Database\Migrations\Migrator
- */
- protected $migrator;
-
- /**
- * Create a new fresh command instance.
- *
- * @param \Illuminate\Database\Migrations\Migrator $migrator
- */
- public function __construct(Migrator $migrator)
- {
- parent::__construct();
-
- $this->migrator = $migrator;
- }
-
/**
* Execute the console command.
*
- * @return int
+ * @return void
*/
- public function handle()
+ public function fire()
{
- if ($this->isProhibited() ||
- ! $this->confirmToProceed()) {
- return Command::FAILURE;
+ if (! $this->confirmToProceed()) {
+ return;
}
- $database = $this->input->getOption('database');
-
- $this->migrator->usingConnection($database, function () use ($database) {
- if ($this->migrator->repositoryExists()) {
- $this->newLine();
+ $this->dropAllTables(
+ $database = $this->input->getOption('database')
+ );
- $this->components->task('Dropping all tables', fn () => $this->callSilent('db:wipe', array_filter([
- '--database' => $database,
- '--drop-views' => $this->option('drop-views'),
- '--drop-types' => $this->option('drop-types'),
- '--force' => true,
- ])) == 0);
- }
- });
+ $this->info('Dropped all tables successfully.');
- $this->newLine();
-
- $this->call('migrate', array_filter([
+ $this->call('migrate', [
'--database' => $database,
'--path' => $this->input->getOption('path'),
- '--realpath' => $this->input->getOption('realpath'),
- '--schema-path' => $this->input->getOption('schema-path'),
- '--force' => true,
- '--step' => $this->option('step'),
- ]));
-
- if ($this->laravel->bound(Dispatcher::class)) {
- $this->laravel[Dispatcher::class]->dispatch(
- new DatabaseRefreshed($database, $this->needsSeeding())
- );
- }
+ '--force' => $this->input->getOption('force'),
+ ]);
if ($this->needsSeeding()) {
$this->runSeeder($database);
}
+ }
- return 0;
+ /**
+ * Drop all of the database tables.
+ *
+ * @param string $database
+ * @return void
+ */
+ protected function dropAllTables($database)
+ {
+ $this->laravel['db']->connection($database)
+ ->getSchemaBuilder()
+ ->dropAllTables();
}
/**
@@ -118,11 +83,11 @@ protected function needsSeeding()
*/
protected function runSeeder($database)
{
- $this->call('db:seed', array_filter([
+ $this->call('db:seed', [
'--database' => $database,
- '--class' => $this->option('seeder') ?: 'Database\\Seeders\\DatabaseSeeder',
- '--force' => true,
- ]));
+ '--class' => $this->option('seeder') ?: 'DatabaseSeeder',
+ '--force' => $this->option('force'),
+ ]);
}
/**
@@ -133,16 +98,15 @@ protected function runSeeder($database)
protected function getOptions()
{
return [
- ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
- ['drop-views', null, InputOption::VALUE_NONE, 'Drop all tables and views'],
- ['drop-types', null, InputOption::VALUE_NONE, 'Drop all tables and types (Postgres only)'],
- ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
- ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'],
- ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
- ['schema-path', null, InputOption::VALUE_OPTIONAL, 'The path to a schema dump file'],
- ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run'],
- ['seeder', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder'],
- ['step', null, InputOption::VALUE_NONE, 'Force the migrations to be run so they can be rolled back individually'],
+ ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'],
+
+ ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'],
+
+ ['path', null, InputOption::VALUE_OPTIONAL, 'The path of migrations files to be executed.'],
+
+ ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run.'],
+
+ ['seeder', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder.'],
];
}
}
diff --git a/Console/Migrations/InstallCommand.php b/Console/Migrations/InstallCommand.php
index b89cd4b4e8..103dcaa928 100755
--- a/Console/Migrations/InstallCommand.php
+++ b/Console/Migrations/InstallCommand.php
@@ -3,11 +3,9 @@
namespace Illuminate\Database\Console\Migrations;
use Illuminate\Console\Command;
-use Illuminate\Database\Migrations\MigrationRepositoryInterface;
-use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
+use Illuminate\Database\Migrations\MigrationRepositoryInterface;
-#[AsCommand(name: 'migrate:install')]
class InstallCommand extends Command
{
/**
@@ -35,6 +33,7 @@ class InstallCommand extends Command
* Create a new migration install command instance.
*
* @param \Illuminate\Database\Migrations\MigrationRepositoryInterface $repository
+ * @return void
*/
public function __construct(MigrationRepositoryInterface $repository)
{
@@ -48,15 +47,13 @@ public function __construct(MigrationRepositoryInterface $repository)
*
* @return void
*/
- public function handle()
+ public function fire()
{
$this->repository->setSource($this->input->getOption('database'));
- if (! $this->repository->repositoryExists()) {
- $this->repository->createRepository();
- }
+ $this->repository->createRepository();
- $this->components->info('Migration table created successfully.');
+ $this->info('Migration table created successfully.');
}
/**
@@ -67,7 +64,7 @@ public function handle()
protected function getOptions()
{
return [
- ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
+ ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'],
];
}
}
diff --git a/Console/Migrations/MigrateCommand.php b/Console/Migrations/MigrateCommand.php
index e3116ca3d1..092a7d2426 100755
--- a/Console/Migrations/MigrateCommand.php
+++ b/Console/Migrations/MigrateCommand.php
@@ -3,22 +3,9 @@
namespace Illuminate\Database\Console\Migrations;
use Illuminate\Console\ConfirmableTrait;
-use Illuminate\Contracts\Console\Isolatable;
-use Illuminate\Contracts\Events\Dispatcher;
-use Illuminate\Database\Events\SchemaLoaded;
use Illuminate\Database\Migrations\Migrator;
-use Illuminate\Database\SQLiteDatabaseDoesNotExistException;
-use Illuminate\Database\SqlServerConnection;
-use Illuminate\Support\Str;
-use PDOException;
-use RuntimeException;
-use Symfony\Component\Console\Attribute\AsCommand;
-use Throwable;
-use function Laravel\Prompts\confirm;
-
-#[AsCommand(name: 'migrate')]
-class MigrateCommand extends BaseCommand implements Isolatable
+class MigrateCommand extends BaseCommand
{
use ConfirmableTrait;
@@ -27,16 +14,12 @@ class MigrateCommand extends BaseCommand implements Isolatable
*
* @var string
*/
- protected $signature = 'migrate {--database= : The database connection to use}
- {--force : Force the operation to run when in production}
- {--path=* : The path(s) to the migrations files to be executed}
- {--realpath : Indicate any provided migration file paths are pre-resolved absolute paths}
- {--schema-path= : The path to a schema dump file}
- {--pretend : Dump the SQL queries that would be run}
- {--seed : Indicates if the seed task should be re-run}
- {--seeder= : The class name of the root seeder}
- {--step : Force the migrations to be run so they can be rolled back individually}
- {--graceful : Return a successful exit code even if an error occurs}';
+ protected $signature = 'migrate {--database= : The database connection to use.}
+ {--force : Force the operation to run when in production.}
+ {--path= : The path of migrations files to be executed.}
+ {--pretend : Dump the SQL queries that would be run.}
+ {--seed : Indicates if the seed task should be re-run.}
+ {--step : Force the migrations to be run so they can be rolled back individually.}';
/**
* The console command description.
@@ -52,84 +35,53 @@ class MigrateCommand extends BaseCommand implements Isolatable
*/
protected $migrator;
- /**
- * The event dispatcher instance.
- *
- * @var \Illuminate\Contracts\Events\Dispatcher
- */
- protected $dispatcher;
-
/**
* Create a new migration command instance.
*
* @param \Illuminate\Database\Migrations\Migrator $migrator
- * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
+ * @return void
*/
- public function __construct(Migrator $migrator, Dispatcher $dispatcher)
+ public function __construct(Migrator $migrator)
{
parent::__construct();
$this->migrator = $migrator;
- $this->dispatcher = $dispatcher;
}
/**
* Execute the console command.
*
- * @return int
- *
- * @throws \Throwable
+ * @return void
*/
- public function handle()
+ public function fire()
{
if (! $this->confirmToProceed()) {
- return 1;
+ return;
}
- try {
- $this->runMigrations();
- } catch (Throwable $e) {
- if ($this->option('graceful')) {
- $this->components->warn($e->getMessage());
+ $this->prepareDatabase();
- return 0;
- }
+ // Next, we will check to see if a path option has been defined. If it has
+ // we will use the path relative to the root of this installation folder
+ // so that migrations may be run for any path within the applications.
+ $this->migrator->run($this->getMigrationPaths(), [
+ 'pretend' => $this->option('pretend'),
+ 'step' => $this->option('step'),
+ ]);
- throw $e;
+ // Once the migrator has run we will grab the note output and send it out to
+ // the console screen, since the migrator itself functions without having
+ // any instances of the OutputInterface contract passed into the class.
+ foreach ($this->migrator->getNotes() as $note) {
+ $this->output->writeln($note);
}
- return 0;
- }
-
- /**
- * Run the pending migrations.
- *
- * @return void
- */
- protected function runMigrations()
- {
- $this->migrator->usingConnection($this->option('database'), function () {
- $this->prepareDatabase();
-
- // Next, we will check to see if a path option has been defined. If it has
- // we will use the path relative to the root of this installation folder
- // so that migrations may be run for any path within the applications.
- $this->migrator->setOutput($this->output)
- ->run($this->getMigrationPaths(), [
- 'pretend' => $this->option('pretend'),
- 'step' => $this->option('step'),
- ]);
-
- // Finally, if the "seed" option has been given, we will re-run the database
- // seed task to re-populate the database, which is convenient when adding
- // a migration and a seed at the same time, as it is only this command.
- if ($this->option('seed') && ! $this->option('pretend')) {
- $this->call('db:seed', [
- '--class' => $this->option('seeder') ?: 'Database\\Seeders\\DatabaseSeeder',
- '--force' => true,
- ]);
- }
- });
+ // Finally, if the "seed" option has been given, we will re-run the database
+ // seed task to re-populate the database, which is convenient when adding
+ // a migration and a seed at the same time, as it is only this command.
+ if ($this->option('seed')) {
+ $this->call('db:seed', ['--force' => true]);
+ }
}
/**
@@ -139,205 +91,12 @@ protected function runMigrations()
*/
protected function prepareDatabase()
{
- if (! $this->repositoryExists()) {
- $this->components->info('Preparing database.');
-
- $this->components->task('Creating migration table', function () {
- return $this->callSilent('migrate:install', array_filter([
- '--database' => $this->option('database'),
- ])) == 0;
- });
-
- $this->newLine();
- }
-
- if (! $this->migrator->hasRunAnyMigrations() && ! $this->option('pretend')) {
- $this->loadSchemaState();
- }
- }
-
- /**
- * Determine if the migrator repository exists.
- *
- * @return bool
- */
- protected function repositoryExists()
- {
- return retry(2, fn () => $this->migrator->repositoryExists(), 0, function ($e) {
- try {
- return $this->handleMissingDatabase($e->getPrevious());
- } catch (Throwable) {
- return false;
- }
- });
- }
-
- /**
- * Attempt to create the database if it is missing.
- *
- * @param \Throwable $e
- * @return bool
- */
- protected function handleMissingDatabase(Throwable $e)
- {
- if ($e instanceof SQLiteDatabaseDoesNotExistException) {
- return $this->createMissingSqliteDatabase($e->path);
- }
-
- $connection = $this->migrator->resolveConnection($this->option('database'));
+ $this->migrator->setConnection($this->option('database'));
- if (! $e instanceof PDOException) {
- return false;
- }
-
- if (($e->getCode() === 1049 && in_array($connection->getDriverName(), ['mysql', 'mariadb'])) ||
- (($e->errorInfo[0] ?? null) == '08006' &&
- $connection->getDriverName() == 'pgsql' &&
- Str::contains($e->getMessage(), '"'.$connection->getDatabaseName().'"'))) {
- return $this->createMissingMySqlOrPgsqlDatabase($connection);
- }
-
- return false;
- }
-
- /**
- * Create a missing SQLite database.
- *
- * @param string $path
- * @return bool
- *
- * @throws \RuntimeException
- */
- protected function createMissingSqliteDatabase($path)
- {
- if ($this->option('force')) {
- return touch($path);
- }
-
- if ($this->option('no-interaction')) {
- return false;
- }
-
- $this->components->warn('The SQLite database configured for this application does not exist: '.$path);
-
- if (! confirm('Would you like to create it?', default: true)) {
- $this->components->info('Operation cancelled. No database was created.');
-
- throw new RuntimeException('Database was not created. Aborting migration.');
- }
-
- return touch($path);
- }
-
- /**
- * Create a missing MySQL or Postgres database.
- *
- * @param \Illuminate\Database\Connection $connection
- * @return bool
- *
- * @throws \RuntimeException
- */
- protected function createMissingMySqlOrPgsqlDatabase($connection)
- {
- if ($this->laravel['config']->get("database.connections.{$connection->getName()}.database") !== $connection->getDatabaseName()) {
- return false;
- }
-
- if (! $this->option('force') && $this->option('no-interaction')) {
- return false;
- }
-
- if (! $this->option('force') && ! $this->option('no-interaction')) {
- $this->components->warn("The database '{$connection->getDatabaseName()}' does not exist on the '{$connection->getName()}' connection.");
-
- if (! confirm('Would you like to create it?', default: true)) {
- $this->components->info('Operation cancelled. No database was created.');
-
- throw new RuntimeException('Database was not created. Aborting migration.');
- }
- }
- try {
- $this->laravel['config']->set(
- "database.connections.{$connection->getName()}.database",
- match ($connection->getDriverName()) {
- 'mysql', 'mariadb' => null,
- 'pgsql' => 'postgres',
- },
+ if (! $this->migrator->repositoryExists()) {
+ $this->call(
+ 'migrate:install', ['--database' => $this->option('database')]
);
-
- $this->laravel['db']->purge();
-
- $freshConnection = $this->migrator->resolveConnection($this->option('database'));
-
- return tap($freshConnection->unprepared(
- match ($connection->getDriverName()) {
- 'mysql', 'mariadb' => "CREATE DATABASE IF NOT EXISTS `{$connection->getDatabaseName()}`",
- 'pgsql' => 'CREATE DATABASE "'.$connection->getDatabaseName().'"',
- }
- ), function () {
- $this->laravel['db']->purge();
- });
- } finally {
- $this->laravel['config']->set("database.connections.{$connection->getName()}.database", $connection->getDatabaseName());
}
}
-
- /**
- * Load the schema state to seed the initial database schema structure.
- *
- * @return void
- */
- protected function loadSchemaState()
- {
- $connection = $this->migrator->resolveConnection($this->option('database'));
-
- // First, we will make sure that the connection supports schema loading and that
- // the schema file exists before we proceed any further. If not, we will just
- // continue with the standard migration operation as normal without errors.
- if ($connection instanceof SqlServerConnection ||
- ! is_file($path = $this->schemaPath($connection))) {
- return;
- }
-
- $this->components->info('Loading stored database schemas.');
-
- $this->components->task($path, function () use ($connection, $path) {
- // Since the schema file will create the "migrations" table and reload it to its
- // proper state, we need to delete it here so we don't get an error that this
- // table already exists when the stored database schema file gets executed.
- $this->migrator->deleteRepository();
-
- $connection->getSchemaState()->handleOutputUsing(function ($type, $buffer) {
- $this->output->write($buffer);
- })->load($path);
- });
-
- $this->newLine();
-
- // Finally, we will fire an event that this schema has been loaded so developers
- // can perform any post schema load tasks that are necessary in listeners for
- // this event, which may seed the database tables with some necessary data.
- $this->dispatcher->dispatch(
- new SchemaLoaded($connection, $path)
- );
- }
-
- /**
- * Get the path to the stored schema for the given connection.
- *
- * @param \Illuminate\Database\Connection $connection
- * @return string
- */
- protected function schemaPath($connection)
- {
- if ($this->option('schema-path')) {
- return $this->option('schema-path');
- }
-
- if (file_exists($path = database_path('schema/'.$connection->getName().'-schema.dump'))) {
- return $path;
- }
-
- return database_path('schema/'.$connection->getName().'-schema.sql');
- }
}
diff --git a/Console/Migrations/MigrateMakeCommand.php b/Console/Migrations/MigrateMakeCommand.php
index ac5077f58d..fa2024afd5 100644
--- a/Console/Migrations/MigrateMakeCommand.php
+++ b/Console/Migrations/MigrateMakeCommand.php
@@ -2,26 +2,20 @@
namespace Illuminate\Database\Console\Migrations;
-use Illuminate\Contracts\Console\PromptsForMissingInput;
-use Illuminate\Database\Migrations\MigrationCreator;
use Illuminate\Support\Composer;
-use Illuminate\Support\Str;
-use Symfony\Component\Console\Attribute\AsCommand;
+use Illuminate\Database\Migrations\MigrationCreator;
-#[AsCommand(name: 'make:migration')]
-class MigrateMakeCommand extends BaseCommand implements PromptsForMissingInput
+class MigrateMakeCommand extends BaseCommand
{
/**
* The console command signature.
*
* @var string
*/
- protected $signature = 'make:migration {name : The name of the migration}
- {--create= : The table to be created}
- {--table= : The table to migrate}
- {--path= : The location where the migration file should be created}
- {--realpath : Indicate any provided migration file paths are pre-resolved absolute paths}
- {--fullpath : Output the full path of the migration (Deprecated)}';
+ protected $signature = 'make:migration {name : The name of the migration.}
+ {--create= : The table to be created.}
+ {--table= : The table to migrate.}
+ {--path= : The location where the migration file should be created.}';
/**
* The console command description.
@@ -41,8 +35,6 @@ class MigrateMakeCommand extends BaseCommand implements PromptsForMissingInput
* The Composer instance.
*
* @var \Illuminate\Support\Composer
- *
- * @deprecated Will be removed in a future Laravel version.
*/
protected $composer;
@@ -51,6 +43,7 @@ class MigrateMakeCommand extends BaseCommand implements PromptsForMissingInput
*
* @param \Illuminate\Database\Migrations\MigrationCreator $creator
* @param \Illuminate\Support\Composer $composer
+ * @return void
*/
public function __construct(MigrationCreator $creator, Composer $composer)
{
@@ -65,12 +58,12 @@ public function __construct(MigrationCreator $creator, Composer $composer)
*
* @return void
*/
- public function handle()
+ public function fire()
{
// It's possible for the developer to specify the tables to modify in this
// schema operation. The developer may also specify if this table needs
// to be freshly created so we can create the appropriate migrations.
- $name = Str::snake(trim($this->input->getArgument('name')));
+ $name = trim($this->input->getArgument('name'));
$table = $this->input->getOption('table');
@@ -85,17 +78,12 @@ public function handle()
$create = true;
}
- // Next, we will attempt to guess the table name if this the migration has
- // "create" in the name. This will allow us to provide a convenient way
- // of creating migrations that create new tables for the application.
- if (! $table) {
- [$table, $create] = TableGuesser::guess($name);
- }
-
// Now we are ready to write the migration out to disk. Once we've written
// the migration out, we will dump-autoload for the entire framework to
// make sure that the migrations are registered by the class loaders.
$this->writeMigration($name, $table, $create);
+
+ $this->composer->dumpAutoloads();
}
/**
@@ -103,20 +91,16 @@ public function handle()
*
* @param string $name
* @param string $table
- * @param bool $create
- * @return void
+ * @param bool $create
+ * @return string
*/
protected function writeMigration($name, $table, $create)
{
- $file = $this->creator->create(
+ $file = pathinfo($this->creator->create(
$name, $this->getMigrationPath(), $table, $create
- );
+ ), PATHINFO_FILENAME);
- if (windows_os()) {
- $file = str_replace('/', '\\', $file);
- }
-
- $this->components->info(sprintf('Migration [%s] created successfully.', $file));
+ $this->line("Created Migration: {$file}");
}
/**
@@ -127,23 +111,9 @@ protected function writeMigration($name, $table, $create)
protected function getMigrationPath()
{
if (! is_null($targetPath = $this->input->getOption('path'))) {
- return ! $this->usingRealPath()
- ? $this->laravel->basePath().'/'.$targetPath
- : $targetPath;
+ return $this->laravel->basePath().'/'.$targetPath;
}
return parent::getMigrationPath();
}
-
- /**
- * Prompt for missing input arguments using the returned questions.
- *
- * @return array
- */
- protected function promptForMissingArgumentsUsing()
- {
- return [
- 'name' => ['What should the migration be named?', 'E.g. create_flights_table'],
- ];
- }
}
diff --git a/Console/Migrations/RefreshCommand.php b/Console/Migrations/RefreshCommand.php
index 7d74f5b38c..4d2772b970 100755
--- a/Console/Migrations/RefreshCommand.php
+++ b/Console/Migrations/RefreshCommand.php
@@ -4,16 +4,11 @@
use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
-use Illuminate\Console\Prohibitable;
-use Illuminate\Contracts\Events\Dispatcher;
-use Illuminate\Database\Events\DatabaseRefreshed;
-use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
-#[AsCommand(name: 'migrate:refresh')]
class RefreshCommand extends Command
{
- use ConfirmableTrait, Prohibitable;
+ use ConfirmableTrait;
/**
* The console command name.
@@ -32,13 +27,12 @@ class RefreshCommand extends Command
/**
* Execute the console command.
*
- * @return int
+ * @return void
*/
- public function handle()
+ public function fire()
{
- if ($this->isProhibited() ||
- ! $this->confirmToProceed()) {
- return Command::FAILURE;
+ if (! $this->confirmToProceed()) {
+ return;
}
// Next we'll gather some of the options so that we can have the right options
@@ -48,38 +42,31 @@ public function handle()
$path = $this->input->getOption('path');
+ $force = $this->input->getOption('force');
+
// If the "step" option is specified it means we only want to rollback a small
// number of migrations before migrating again. For example, the user might
// only rollback and remigrate the latest four migrations instead of all.
$step = $this->input->getOption('step') ?: 0;
if ($step > 0) {
- $this->runRollback($database, $path, $step);
+ $this->runRollback($database, $path, $step, $force);
} else {
- $this->runReset($database, $path);
+ $this->runReset($database, $path, $force);
}
// The refresh command is essentially just a brief aggregate of a few other of
// the migration commands and just provides a convenient wrapper to execute
// them in succession. We'll also see if we need to re-seed the database.
- $this->call('migrate', array_filter([
+ $this->call('migrate', [
'--database' => $database,
'--path' => $path,
- '--realpath' => $this->input->getOption('realpath'),
- '--force' => true,
- ]));
-
- if ($this->laravel->bound(Dispatcher::class)) {
- $this->laravel[Dispatcher::class]->dispatch(
- new DatabaseRefreshed($database, $this->needsSeeding())
- );
- }
+ '--force' => $force,
+ ]);
if ($this->needsSeeding()) {
$this->runSeeder($database);
}
-
- return 0;
}
/**
@@ -87,18 +74,18 @@ public function handle()
*
* @param string $database
* @param string $path
- * @param int $step
+ * @param bool $step
+ * @param bool $force
* @return void
*/
- protected function runRollback($database, $path, $step)
+ protected function runRollback($database, $path, $step, $force)
{
- $this->call('migrate:rollback', array_filter([
+ $this->call('migrate:rollback', [
'--database' => $database,
'--path' => $path,
- '--realpath' => $this->input->getOption('realpath'),
'--step' => $step,
- '--force' => true,
- ]));
+ '--force' => $force,
+ ]);
}
/**
@@ -106,16 +93,16 @@ protected function runRollback($database, $path, $step)
*
* @param string $database
* @param string $path
+ * @param bool $force
* @return void
*/
- protected function runReset($database, $path)
+ protected function runReset($database, $path, $force)
{
- $this->call('migrate:reset', array_filter([
+ $this->call('migrate:reset', [
'--database' => $database,
'--path' => $path,
- '--realpath' => $this->input->getOption('realpath'),
- '--force' => true,
- ]));
+ '--force' => $force,
+ ]);
}
/**
@@ -136,11 +123,11 @@ protected function needsSeeding()
*/
protected function runSeeder($database)
{
- $this->call('db:seed', array_filter([
+ $this->call('db:seed', [
'--database' => $database,
- '--class' => $this->option('seeder') ?: 'Database\\Seeders\\DatabaseSeeder',
- '--force' => true,
- ]));
+ '--class' => $this->option('seeder') ?: 'DatabaseSeeder',
+ '--force' => $this->option('force'),
+ ]);
}
/**
@@ -151,13 +138,17 @@ protected function runSeeder($database)
protected function getOptions()
{
return [
- ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
- ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
- ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'],
- ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
- ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run'],
- ['seeder', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder'],
- ['step', null, InputOption::VALUE_OPTIONAL, 'The number of migrations to be reverted & re-run'],
+ ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'],
+
+ ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'],
+
+ ['path', null, InputOption::VALUE_OPTIONAL, 'The path of migrations files to be executed.'],
+
+ ['seed', null, InputOption::VALUE_NONE, 'Indicates if the seed task should be re-run.'],
+
+ ['seeder', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder.'],
+
+ ['step', null, InputOption::VALUE_OPTIONAL, 'The number of migrations to be reverted & re-run.'],
];
}
}
diff --git a/Console/Migrations/ResetCommand.php b/Console/Migrations/ResetCommand.php
index 787801bab2..3b8dab3ea6 100755
--- a/Console/Migrations/ResetCommand.php
+++ b/Console/Migrations/ResetCommand.php
@@ -2,17 +2,13 @@
namespace Illuminate\Database\Console\Migrations;
-use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
-use Illuminate\Console\Prohibitable;
use Illuminate\Database\Migrations\Migrator;
-use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
-#[AsCommand(name: 'migrate:reset')]
class ResetCommand extends BaseCommand
{
- use ConfirmableTrait, Prohibitable;
+ use ConfirmableTrait;
/**
* The console command name.
@@ -39,6 +35,7 @@ class ResetCommand extends BaseCommand
* Create a new migration rollback command instance.
*
* @param \Illuminate\Database\Migrations\Migrator $migrator
+ * @return void
*/
public function __construct(Migrator $migrator)
{
@@ -50,27 +47,33 @@ public function __construct(Migrator $migrator)
/**
* Execute the console command.
*
- * @return int
+ * @return void
*/
- public function handle()
+ public function fire()
{
- if ($this->isProhibited() ||
- ! $this->confirmToProceed()) {
- return Command::FAILURE;
+ if (! $this->confirmToProceed()) {
+ return;
}
- return $this->migrator->usingConnection($this->option('database'), function () {
- // First, we'll make sure that the migration table actually exists before we
- // start trying to rollback and re-run all of the migrations. If it's not
- // present we'll just bail out with an info message for the developers.
- if (! $this->migrator->repositoryExists()) {
- return $this->components->warn('Migration table not found.');
- }
-
- $this->migrator->setOutput($this->output)->reset(
- $this->getMigrationPaths(), $this->option('pretend')
- );
- });
+ $this->migrator->setConnection($this->option('database'));
+
+ // First, we'll make sure that the migration table actually exists before we
+ // start trying to rollback and re-run all of the migrations. If it's not
+ // present we'll just bail out with an info message for the developers.
+ if (! $this->migrator->repositoryExists()) {
+ return $this->comment('Migration table not found.');
+ }
+
+ $this->migrator->reset(
+ $this->getMigrationPaths(), $this->option('pretend')
+ );
+
+ // Once the migrator has run we will grab the note output and send it out to
+ // the console screen, since the migrator itself functions without having
+ // any instances of the OutputInterface contract passed into the class.
+ foreach ($this->migrator->getNotes() as $note) {
+ $this->output->writeln($note);
+ }
}
/**
@@ -81,15 +84,13 @@ public function handle()
protected function getOptions()
{
return [
- ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
-
- ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
+ ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'],
- ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'],
+ ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'],
- ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
+ ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) of migrations files to be executed.'],
- ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run'],
+ ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run.'],
];
}
}
diff --git a/Console/Migrations/RollbackCommand.php b/Console/Migrations/RollbackCommand.php
index 9c3543ec5b..4b1a75faed 100755
--- a/Console/Migrations/RollbackCommand.php
+++ b/Console/Migrations/RollbackCommand.php
@@ -2,17 +2,13 @@
namespace Illuminate\Database\Console\Migrations;
-use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
-use Illuminate\Console\Prohibitable;
use Illuminate\Database\Migrations\Migrator;
-use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
-#[AsCommand('migrate:rollback')]
class RollbackCommand extends BaseCommand
{
- use ConfirmableTrait, Prohibitable;
+ use ConfirmableTrait;
/**
* The console command name.
@@ -39,6 +35,7 @@ class RollbackCommand extends BaseCommand
* Create a new migration rollback command instance.
*
* @param \Illuminate\Database\Migrations\Migrator $migrator
+ * @return void
*/
public function __construct(Migrator $migrator)
{
@@ -50,26 +47,29 @@ public function __construct(Migrator $migrator)
/**
* Execute the console command.
*
- * @return int
+ * @return void
*/
- public function handle()
+ public function fire()
{
- if ($this->isProhibited() ||
- ! $this->confirmToProceed()) {
- return Command::FAILURE;
+ if (! $this->confirmToProceed()) {
+ return;
}
- $this->migrator->usingConnection($this->option('database'), function () {
- $this->migrator->setOutput($this->output)->rollback(
- $this->getMigrationPaths(), [
- 'pretend' => $this->option('pretend'),
- 'step' => (int) $this->option('step'),
- 'batch' => (int) $this->option('batch'),
- ]
- );
- });
-
- return 0;
+ $this->migrator->setConnection($this->option('database'));
+
+ $this->migrator->rollback(
+ $this->getMigrationPaths(), [
+ 'pretend' => $this->option('pretend'),
+ 'step' => (int) $this->option('step'),
+ ]
+ );
+
+ // Once the migrator has run we will grab the note output and send it out to
+ // the console screen, since the migrator itself functions without having
+ // any instances of the OutputInterface contract passed into the class.
+ foreach ($this->migrator->getNotes() as $note) {
+ $this->output->writeln($note);
+ }
}
/**
@@ -80,13 +80,15 @@ public function handle()
protected function getOptions()
{
return [
- ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
- ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
- ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'],
- ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
- ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run'],
- ['step', null, InputOption::VALUE_OPTIONAL, 'The number of migrations to be reverted'],
- ['batch', null, InputOption::VALUE_REQUIRED, 'The batch of migrations (identified by their batch number) to be reverted'],
+ ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'],
+
+ ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'],
+
+ ['path', null, InputOption::VALUE_OPTIONAL, 'The path of migrations files to be executed.'],
+
+ ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run.'],
+
+ ['step', null, InputOption::VALUE_OPTIONAL, 'The number of migrations to be reverted.'],
];
}
}
diff --git a/Console/Migrations/StatusCommand.php b/Console/Migrations/StatusCommand.php
index cbb16a133c..34b98f27b2 100644
--- a/Console/Migrations/StatusCommand.php
+++ b/Console/Migrations/StatusCommand.php
@@ -2,13 +2,10 @@
namespace Illuminate\Database\Console\Migrations;
-use Illuminate\Database\Migrations\Migrator;
use Illuminate\Support\Collection;
-use Illuminate\Support\Stringable;
-use Symfony\Component\Console\Attribute\AsCommand;
+use Illuminate\Database\Migrations\Migrator;
use Symfony\Component\Console\Input\InputOption;
-#[AsCommand(name: 'migrate:status')]
class StatusCommand extends BaseCommand
{
/**
@@ -35,7 +32,8 @@ class StatusCommand extends BaseCommand
/**
* Create a new migration rollback command instance.
*
- * @param \Illuminate\Database\Migrations\Migrator $migrator
+ * @param \Illuminate\Database\Migrations\Migrator $migrator
+ * @return \Illuminate\Database\Console\Migrations\StatusCommand
*/
public function __construct(Migrator $migrator)
{
@@ -47,72 +45,41 @@ public function __construct(Migrator $migrator)
/**
* Execute the console command.
*
- * @return int|null
+ * @return void
*/
- public function handle()
+ public function fire()
{
- return $this->migrator->usingConnection($this->option('database'), function () {
- if (! $this->migrator->repositoryExists()) {
- $this->components->error('Migration table not found.');
-
- return 1;
- }
-
- $ran = $this->migrator->getRepository()->getRan();
-
- $batches = $this->migrator->getRepository()->getMigrationBatches();
-
- $migrations = $this->getStatusFor($ran, $batches)
- ->when($this->option('pending') !== false, fn ($collection) => $collection->filter(function ($migration) {
- return (new Stringable($migration[1]))->contains('Pending');
- }));
+ $this->migrator->setConnection($this->option('database'));
- if (count($migrations) > 0) {
- $this->newLine();
+ if (! $this->migrator->repositoryExists()) {
+ return $this->error('No migrations found.');
+ }
- $this->components->twoColumnDetail('Migration name>', 'Batch / Status>');
+ $ran = $this->migrator->getRepository()->getRan();
- $migrations
- ->each(
- fn ($migration) => $this->components->twoColumnDetail($migration[0], $migration[1])
- );
-
- $this->newLine();
- } elseif ($this->option('pending') !== false) {
- $this->components->info('No pending migrations');
- } else {
- $this->components->info('No migrations found');
- }
-
- if ($this->option('pending') && $migrations->some(fn ($m) => (new Stringable($m[1]))->contains('Pending'))) {
- return $this->option('pending');
- }
- });
+ if (count($migrations = $this->getStatusFor($ran)) > 0) {
+ $this->table(['Ran?', 'Migration'], $migrations);
+ } else {
+ $this->error('No migrations found');
+ }
}
/**
- * Get the status for the given run migrations.
+ * Get the status for the given ran migrations.
*
* @param array $ran
- * @param array $batches
* @return \Illuminate\Support\Collection
*/
- protected function getStatusFor(array $ran, array $batches)
+ protected function getStatusFor(array $ran)
{
- return (new Collection($this->getAllMigrationFiles()))
- ->map(function ($migration) use ($ran, $batches) {
- $migrationName = $this->migrator->getMigrationName($migration);
-
- $status = in_array($migrationName, $ran)
- ? 'Ran>'
- : 'Pending>';
-
- if (in_array($migrationName, $ran)) {
- $status = '['.$batches[$migrationName].'] '.$status;
- }
-
- return [$migrationName, $status];
- });
+ return Collection::make($this->getAllMigrationFiles())
+ ->map(function ($migration) use ($ran) {
+ $migrationName = $this->migrator->getMigrationName($migration);
+
+ return in_array($migrationName, $ran)
+ ? ['Y', $migrationName]
+ : ['N', $migrationName];
+ });
}
/**
@@ -133,10 +100,9 @@ protected function getAllMigrationFiles()
protected function getOptions()
{
return [
- ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
- ['pending', null, InputOption::VALUE_OPTIONAL, 'Only list pending migrations', false],
- ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to use'],
- ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'],
+ ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use.'],
+
+ ['path', null, InputOption::VALUE_OPTIONAL, 'The path of migrations files to use.'],
];
}
}
diff --git a/Console/Migrations/TableGuesser.php b/Console/Migrations/TableGuesser.php
deleted file mode 100644
index baf19485ee..0000000000
--- a/Console/Migrations/TableGuesser.php
+++ /dev/null
@@ -1,37 +0,0 @@
-connection = $connection;
- $this->events = $events;
- }
-
- /**
- * Execute the console command.
- *
- * @return void
- */
- public function handle()
- {
- $databases = $this->parseDatabases($this->option('databases'));
-
- $this->displayConnections($databases);
-
- if ($this->option('max')) {
- $this->dispatchEvents($databases);
- }
- }
-
- /**
- * Parse the database into an array of the connections.
- *
- * @param string $databases
- * @return \Illuminate\Support\Collection
- */
- protected function parseDatabases($databases)
- {
- return (new Collection(explode(',', $databases)))->map(function ($database) {
- if (! $database) {
- $database = $this->laravel['config']['database.default'];
- }
-
- $maxConnections = $this->option('max');
-
- $connections = $this->connection->connection($database)->threadCount();
-
- return [
- 'database' => $database,
- 'connections' => $connections,
- 'status' => $maxConnections && $connections >= $maxConnections ? 'ALERT>' : 'OK>',
- ];
- });
- }
-
- /**
- * Display the databases and their connection counts in the console.
- *
- * @param \Illuminate\Support\Collection $databases
- * @return void
- */
- protected function displayConnections($databases)
- {
- $this->newLine();
-
- $this->components->twoColumnDetail('Database name>', 'Connections>');
-
- $databases->each(function ($database) {
- $status = '['.$database['connections'].'] '.$database['status'];
-
- $this->components->twoColumnDetail($database['database'], $status);
- });
-
- $this->newLine();
- }
-
- /**
- * Dispatch the database monitoring events.
- *
- * @param \Illuminate\Support\Collection $databases
- * @return void
- */
- protected function dispatchEvents($databases)
- {
- $databases->each(function ($database) {
- if ($database['status'] === 'OK>') {
- return;
- }
-
- $this->events->dispatch(
- new DatabaseBusy(
- $database['database'],
- $database['connections']
- )
- );
- });
- }
-}
diff --git a/Console/PruneCommand.php b/Console/PruneCommand.php
deleted file mode 100644
index 527ab70c6f..0000000000
--- a/Console/PruneCommand.php
+++ /dev/null
@@ -1,199 +0,0 @@
-models();
-
- if ($models->isEmpty()) {
- $this->components->info('No prunable models found.');
-
- return;
- }
-
- if ($this->option('pretend')) {
- $models->each(function ($model) {
- $this->pretendToPrune($model);
- });
-
- return;
- }
-
- $pruning = [];
-
- $events->listen(ModelsPruned::class, function ($event) use (&$pruning) {
- if (! in_array($event->model, $pruning)) {
- $pruning[] = $event->model;
-
- $this->newLine();
-
- $this->components->info(sprintf('Pruning [%s] records.', $event->model));
- }
-
- $this->components->twoColumnDetail($event->model, "{$event->count} records");
- });
-
- $events->dispatch(new ModelPruningStarting($models->all()));
-
- $models->each(function ($model) {
- $this->pruneModel($model);
- });
-
- $events->dispatch(new ModelPruningFinished($models->all()));
-
- $events->forget(ModelsPruned::class);
- }
-
- /**
- * Prune the given model.
- *
- * @param string $model
- * @return void
- */
- protected function pruneModel(string $model)
- {
- $instance = new $model;
-
- $chunkSize = property_exists($instance, 'prunableChunkSize')
- ? $instance->prunableChunkSize
- : $this->option('chunk');
-
- $total = $model::isPrunable()
- ? $instance->pruneAll($chunkSize)
- : 0;
-
- if ($total == 0) {
- $this->components->info("No prunable [$model] records found.");
- }
- }
-
- /**
- * Determine the models that should be pruned.
- *
- * @return \Illuminate\Support\Collection
- *
- * @throws \InvalidArgumentException
- */
- protected function models()
- {
- $models = $this->option('model');
- $except = $this->option('except');
-
- if ($models && $except) {
- throw new InvalidArgumentException('The --models and --except options cannot be combined.');
- }
-
- if ($models) {
- return (new Collection($models))
- ->filter(static fn (string $model) => class_exists($model))
- ->values();
- }
-
- return (new Collection(Finder::create()->in($this->getPath())->files()->name('*.php')))
- ->map(function ($model) {
- $namespace = $this->laravel->getNamespace();
-
- return $namespace.str_replace(
- ['/', '.php'],
- ['\\', ''],
- Str::after($model->getRealPath(), realpath(app_path()).DIRECTORY_SEPARATOR)
- );
- })
- ->when(! empty($except), fn ($models) => $models->reject(fn ($model) => in_array($model, $except)))
- ->filter(fn ($model) => $this->isPrunable($model))
- ->values();
- }
-
- /**
- * Get the path where models are located.
- *
- * @return string[]|string
- */
- protected function getPath()
- {
- if (! empty($path = $this->option('path'))) {
- return (new Collection($path))
- ->map(fn ($path) => base_path($path))
- ->all();
- }
-
- return app_path('Models');
- }
-
- /**
- * Display how many models will be pruned.
- *
- * @param class-string $model
- * @return void
- */
- protected function pretendToPrune($model)
- {
- $instance = new $model;
-
- $count = $instance->prunable()
- ->when($model::isSoftDeletable(), function ($query) {
- $query->withTrashed();
- })->count();
-
- if ($count === 0) {
- $this->components->info("No prunable [$model] records found.");
- } else {
- $this->components->info("{$count} [{$model}] records will be pruned.");
- }
- }
-
- /**
- * Determine if the given model is prunable.
- *
- * @param string $model
- * @return bool
- */
- protected function isPrunable(string $model)
- {
- return class_exists($model)
- && is_a($model, Model::class, true)
- && ! (new \ReflectionClass($model))->isAbstract()
- && $model::isPrunable();
- }
-}
diff --git a/Console/Seeds/SeedCommand.php b/Console/Seeds/SeedCommand.php
index 515ff410b3..cf866e0457 100644
--- a/Console/Seeds/SeedCommand.php
+++ b/Console/Seeds/SeedCommand.php
@@ -3,18 +3,14 @@
namespace Illuminate\Database\Console\Seeds;
use Illuminate\Console\Command;
-use Illuminate\Console\ConfirmableTrait;
-use Illuminate\Console\Prohibitable;
-use Illuminate\Database\ConnectionResolverInterface as Resolver;
use Illuminate\Database\Eloquent\Model;
-use Symfony\Component\Console\Attribute\AsCommand;
-use Symfony\Component\Console\Input\InputArgument;
+use Illuminate\Console\ConfirmableTrait;
use Symfony\Component\Console\Input\InputOption;
+use Illuminate\Database\ConnectionResolverInterface as Resolver;
-#[AsCommand(name: 'db:seed')]
class SeedCommand extends Command
{
- use ConfirmableTrait, Prohibitable;
+ use ConfirmableTrait;
/**
* The console command name.
@@ -41,6 +37,7 @@ class SeedCommand extends Command
* Create a new database seed command instance.
*
* @param \Illuminate\Database\ConnectionResolverInterface $resolver
+ * @return void
*/
public function __construct(Resolver $resolver)
{
@@ -52,30 +49,19 @@ public function __construct(Resolver $resolver)
/**
* Execute the console command.
*
- * @return int
+ * @return void
*/
- public function handle()
+ public function fire()
{
- if ($this->isProhibited() ||
- ! $this->confirmToProceed()) {
- return Command::FAILURE;
+ if (! $this->confirmToProceed()) {
+ return;
}
- $this->components->info('Seeding database.');
-
- $previousConnection = $this->resolver->getDefaultConnection();
-
$this->resolver->setDefaultConnection($this->getDatabase());
Model::unguarded(function () {
$this->getSeeder()->__invoke();
});
-
- if ($previousConnection) {
- $this->resolver->setDefaultConnection($previousConnection);
- }
-
- return 0;
}
/**
@@ -85,20 +71,9 @@ public function handle()
*/
protected function getSeeder()
{
- $class = $this->input->getArgument('class') ?? $this->input->getOption('class');
-
- if (! str_contains($class, '\\')) {
- $class = 'Database\\Seeders\\'.$class;
- }
-
- if ($class === 'Database\\Seeders\\DatabaseSeeder' &&
- ! class_exists($class)) {
- $class = 'DatabaseSeeder';
- }
+ $class = $this->laravel->make($this->input->getOption('class'));
- return $this->laravel->make($class)
- ->setContainer($this->laravel)
- ->setCommand($this);
+ return $class->setContainer($this->laravel)->setCommand($this);
}
/**
@@ -113,18 +88,6 @@ protected function getDatabase()
return $database ?: $this->laravel['config']['database.default'];
}
- /**
- * Get the console command arguments.
- *
- * @return array
- */
- protected function getArguments()
- {
- return [
- ['class', InputArgument::OPTIONAL, 'The class name of the root seeder', null],
- ];
- }
-
/**
* Get the console command options.
*
@@ -133,9 +96,11 @@ protected function getArguments()
protected function getOptions()
{
return [
- ['class', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder', 'Database\\Seeders\\DatabaseSeeder'],
+ ['class', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder', 'DatabaseSeeder'],
+
['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to seed'],
- ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
+
+ ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production.'],
];
}
}
diff --git a/Console/Seeds/SeederMakeCommand.php b/Console/Seeds/SeederMakeCommand.php
index c021bbbe54..89b793122e 100644
--- a/Console/Seeds/SeederMakeCommand.php
+++ b/Console/Seeds/SeederMakeCommand.php
@@ -2,11 +2,10 @@
namespace Illuminate\Database\Console\Seeds;
+use Illuminate\Support\Composer;
+use Illuminate\Filesystem\Filesystem;
use Illuminate\Console\GeneratorCommand;
-use Illuminate\Support\Str;
-use Symfony\Component\Console\Attribute\AsCommand;
-#[AsCommand(name: 'make:seeder')]
class SeederMakeCommand extends GeneratorCommand
{
/**
@@ -31,36 +30,46 @@ class SeederMakeCommand extends GeneratorCommand
protected $type = 'Seeder';
/**
- * Execute the console command.
+ * The Composer instance.
+ *
+ * @var \Illuminate\Support\Composer
+ */
+ protected $composer;
+
+ /**
+ * Create a new command instance.
*
+ * @param \Illuminate\Filesystem\Filesystem $files
+ * @param \Illuminate\Support\Composer $composer
* @return void
*/
- public function handle()
+ public function __construct(Filesystem $files, Composer $composer)
{
- parent::handle();
+ parent::__construct($files);
+
+ $this->composer = $composer;
}
/**
- * Get the stub file for the generator.
+ * Execute the console command.
*
- * @return string
+ * @return void
*/
- protected function getStub()
+ public function fire()
{
- return $this->resolveStubPath('/stubs/seeder.stub');
+ parent::fire();
+
+ $this->composer->dumpAutoloads();
}
/**
- * Resolve the fully-qualified path to the stub.
+ * Get the stub file for the generator.
*
- * @param string $stub
* @return string
*/
- protected function resolveStubPath($stub)
+ protected function getStub()
{
- return is_file($customPath = $this->laravel->basePath(trim($stub, '/')))
- ? $customPath
- : __DIR__.$stub;
+ return __DIR__.'/stubs/seeder.stub';
}
/**
@@ -71,22 +80,17 @@ protected function resolveStubPath($stub)
*/
protected function getPath($name)
{
- $name = str_replace('\\', '/', Str::replaceFirst($this->rootNamespace(), '', $name));
-
- if (is_dir($this->laravel->databasePath().'/seeds')) {
- return $this->laravel->databasePath().'/seeds/'.$name.'.php';
- }
-
- return $this->laravel->databasePath().'/seeders/'.$name.'.php';
+ return $this->laravel->databasePath().'/seeds/'.$name.'.php';
}
/**
- * Get the root namespace for the class.
+ * Parse the class name and format according to the root namespace.
*
+ * @param string $name
* @return string
*/
- protected function rootNamespace()
+ protected function qualifyClass($name)
{
- return 'Database\Seeders\\';
+ return $name;
}
}
diff --git a/Console/Seeds/WithoutModelEvents.php b/Console/Seeds/WithoutModelEvents.php
deleted file mode 100644
index acd9ec3f20..0000000000
--- a/Console/Seeds/WithoutModelEvents.php
+++ /dev/null
@@ -1,19 +0,0 @@
- Model::withoutEvents($callback);
- }
-}
diff --git a/Console/Seeds/stubs/seeder.stub b/Console/Seeds/stubs/seeder.stub
index 8b5403f38d..4aa3845422 100644
--- a/Console/Seeds/stubs/seeder.stub
+++ b/Console/Seeds/stubs/seeder.stub
@@ -1,16 +1,15 @@
Note: This can be slow on large databases >}
- {--views : Show the database views Note: This can be slow on large databases >}
- {--types : Show the user defined types}';
-
- /**
- * The console command description.
- *
- * @var string
- */
- protected $description = 'Display information about the given database';
-
- /**
- * Execute the console command.
- *
- * @param \Illuminate\Database\ConnectionResolverInterface $connections
- * @return int
- */
- public function handle(ConnectionResolverInterface $connections)
- {
- $connection = $connections->connection($database = $this->input->getOption('database'));
-
- $schema = $connection->getSchemaBuilder();
-
- $data = [
- 'platform' => [
- 'config' => $this->getConfigFromDatabase($database),
- 'name' => $connection->getDriverTitle(),
- 'connection' => $connection->getName(),
- 'version' => $connection->getServerVersion(),
- 'open_connections' => $connection->threadCount(),
- ],
- 'tables' => $this->tables($connection, $schema),
- ];
-
- if ($this->option('views')) {
- $data['views'] = $this->views($connection, $schema);
- }
-
- if ($this->option('types')) {
- $data['types'] = $this->types($connection, $schema);
- }
-
- $this->display($data);
-
- return 0;
- }
-
- /**
- * Get information regarding the tables within the database.
- *
- * @param \Illuminate\Database\ConnectionInterface $connection
- * @param \Illuminate\Database\Schema\Builder $schema
- * @return \Illuminate\Support\Collection
- */
- protected function tables(ConnectionInterface $connection, Builder $schema)
- {
- return (new Collection($schema->getTables()))->map(fn ($table) => [
- 'table' => $table['name'],
- 'schema' => $table['schema'],
- 'schema_qualified_name' => $table['schema_qualified_name'],
- 'size' => $table['size'],
- 'rows' => $this->option('counts')
- ? $connection->withoutTablePrefix(fn ($connection) => $connection->table($table['schema_qualified_name'])->count())
- : null,
- 'engine' => $table['engine'],
- 'collation' => $table['collation'],
- 'comment' => $table['comment'],
- ]);
- }
-
- /**
- * Get information regarding the views within the database.
- *
- * @param \Illuminate\Database\ConnectionInterface $connection
- * @param \Illuminate\Database\Schema\Builder $schema
- * @return \Illuminate\Support\Collection
- */
- protected function views(ConnectionInterface $connection, Builder $schema)
- {
- return (new Collection($schema->getViews()))
- ->map(fn ($view) => [
- 'view' => $view['name'],
- 'schema' => $view['schema'],
- 'rows' => $connection->withoutTablePrefix(fn ($connection) => $connection->table($view['schema_qualified_name'])->count()),
- ]);
- }
-
- /**
- * Get information regarding the user-defined types within the database.
- *
- * @param \Illuminate\Database\ConnectionInterface $connection
- * @param \Illuminate\Database\Schema\Builder $schema
- * @return \Illuminate\Support\Collection
- */
- protected function types(ConnectionInterface $connection, Builder $schema)
- {
- return (new Collection($schema->getTypes()))
- ->map(fn ($type) => [
- 'name' => $type['name'],
- 'schema' => $type['schema'],
- 'type' => $type['type'],
- 'category' => $type['category'],
- ]);
- }
-
- /**
- * Render the database information.
- *
- * @param array $data
- * @return void
- */
- protected function display(array $data)
- {
- $this->option('json') ? $this->displayJson($data) : $this->displayForCli($data);
- }
-
- /**
- * Render the database information as JSON.
- *
- * @param array $data
- * @return void
- */
- protected function displayJson(array $data)
- {
- $this->output->writeln(json_encode($data));
- }
-
- /**
- * Render the database information formatted for the CLI.
- *
- * @param array $data
- * @return void
- */
- protected function displayForCli(array $data)
- {
- $platform = $data['platform'];
- $tables = $data['tables'];
- $views = $data['views'] ?? null;
- $types = $data['types'] ?? null;
-
- $this->newLine();
-
- $this->components->twoColumnDetail(''.$platform['name'].'>', $platform['version']);
- $this->components->twoColumnDetail('Connection', $platform['connection']);
- $this->components->twoColumnDetail('Database', Arr::get($platform['config'], 'database'));
- $this->components->twoColumnDetail('Host', Arr::get($platform['config'], 'host'));
- $this->components->twoColumnDetail('Port', Arr::get($platform['config'], 'port'));
- $this->components->twoColumnDetail('Username', Arr::get($platform['config'], 'username'));
- $this->components->twoColumnDetail('URL', Arr::get($platform['config'], 'url'));
- $this->components->twoColumnDetail('Open Connections', $platform['open_connections']);
- $this->components->twoColumnDetail('Tables', $tables->count());
-
- if ($tableSizeSum = $tables->sum('size')) {
- $this->components->twoColumnDetail('Total Size', Number::fileSize($tableSizeSum, 2));
- }
-
- $this->newLine();
-
- if ($tables->isNotEmpty()) {
- $hasSchema = ! is_null($tables->first()['schema']);
-
- $this->components->twoColumnDetail(
- ($hasSchema ? 'Schema> /> ' : '').'Table>',
- 'Size'.($this->option('counts') ? ' /> Rows>' : '')
- );
-
- $tables->each(function ($table) {
- $tableSize = is_null($table['size']) ? null : Number::fileSize($table['size'], 2);
-
- $this->components->twoColumnDetail(
- ($table['schema'] ? $table['schema'].' /> ' : '').$table['table'].($this->output->isVerbose() ? ' '.$table['engine'].'>' : null),
- ($tableSize ?? '—').($this->option('counts') ? ' /> '.Number::format($table['rows']).'>' : '')
- );
-
- if ($this->output->isVerbose()) {
- if ($table['comment']) {
- $this->components->bulletList([
- $table['comment'],
- ]);
- }
- }
- });
-
- $this->newLine();
- }
-
- if ($views && $views->isNotEmpty()) {
- $hasSchema = ! is_null($views->first()['schema']);
-
- $this->components->twoColumnDetail(
- ($hasSchema ? 'Schema> /> ' : '').'View>',
- 'Rows>'
- );
-
- $views->each(fn ($view) => $this->components->twoColumnDetail(
- ($view['schema'] ? $view['schema'].' /> ' : '').$view['view'],
- Number::format($view['rows'])
- ));
-
- $this->newLine();
- }
-
- if ($types && $types->isNotEmpty()) {
- $hasSchema = ! is_null($types->first()['schema']);
-
- $this->components->twoColumnDetail(
- ($hasSchema ? 'Schema> /> ' : '').'Type>',
- 'Type> /> Category>'
- );
-
- $types->each(fn ($type) => $this->components->twoColumnDetail(
- ($type['schema'] ? $type['schema'].' /> ' : '').$type['name'],
- $type['type'].' /> '.$type['category']
- ));
-
- $this->newLine();
- }
- }
-}
diff --git a/Console/ShowModelCommand.php b/Console/ShowModelCommand.php
deleted file mode 100644
index 463b0b73e6..0000000000
--- a/Console/ShowModelCommand.php
+++ /dev/null
@@ -1,191 +0,0 @@
-inspect(
- $this->argument('model'),
- $this->option('database')
- );
- } catch (BindingResolutionException $e) {
- $this->components->error($e->getMessage());
-
- return 1;
- }
-
- $this->display($info);
-
- return 0;
- }
-
- /**
- * Render the model information.
- *
- * @return void
- */
- protected function display(ModelInfo $modelData)
- {
- $this->option('json')
- ? $this->displayJson($modelData)
- : $this->displayCli($modelData);
- }
-
- /**
- * Render the model information as JSON.
- *
- * @return void
- */
- protected function displayJson(ModelInfo $modelData)
- {
- $this->output->writeln(
- (new Collection($modelData))->toJson()
- );
- }
-
- /**
- * Render the model information for the CLI.
- *
- * @return void
- */
- protected function displayCli(ModelInfo $modelData)
- {
- $this->newLine();
-
- $this->components->twoColumnDetail(''.$modelData->class.'>');
- $this->components->twoColumnDetail('Database', $modelData->database);
- $this->components->twoColumnDetail('Table', $modelData->table);
-
- if ($policy = $modelData->policy ?? false) {
- $this->components->twoColumnDetail('Policy', $policy);
- }
-
- $this->newLine();
-
- $this->components->twoColumnDetail(
- 'Attributes>',
- 'type /> cast>',
- );
-
- foreach ($modelData->attributes as $attribute) {
- $first = trim(sprintf(
- '%s %s',
- $attribute['name'],
- (new Collection(['increments', 'unique', 'nullable', 'fillable', 'hidden', 'appended']))
- ->filter(fn ($property) => $attribute[$property])
- ->map(fn ($property) => sprintf('%s>', $property))
- ->implode(',> ')
- ));
-
- $second = (new Collection([
- $attribute['type'],
- $attribute['cast'] ? ''.$attribute['cast'].'>' : null,
- ]))->filter()->implode(' /> ');
-
- $this->components->twoColumnDetail($first, $second);
-
- if ($attribute['default'] !== null) {
- $this->components->bulletList(
- [sprintf('default: %s', $attribute['default'])],
- OutputInterface::VERBOSITY_VERBOSE
- );
- }
- }
-
- $this->newLine();
-
- $this->components->twoColumnDetail('Relations>');
-
- foreach ($modelData->relations as $relation) {
- $this->components->twoColumnDetail(
- sprintf('%s %s>', $relation['name'], $relation['type']),
- $relation['related']
- );
- }
-
- $this->newLine();
-
- $this->components->twoColumnDetail('Events>');
-
- if ($modelData->events->count()) {
- foreach ($modelData->events as $event) {
- $this->components->twoColumnDetail(
- sprintf('%s', $event['event']),
- sprintf('%s', $event['class']),
- );
- }
- }
-
- $this->newLine();
-
- $this->components->twoColumnDetail('Observers>');
-
- if ($modelData->observers->count()) {
- foreach ($modelData->observers as $observer) {
- $this->components->twoColumnDetail(
- sprintf('%s', $observer['event']),
- implode(', ', $observer['observer'])
- );
- }
- }
-
- $this->newLine();
- }
-
- /**
- * Prompt for missing input arguments using the returned questions.
- *
- * @return array
- */
- protected function promptForMissingArgumentsUsing(): array
- {
- return [
- 'model' => fn (): string => suggest('Which model would you like to show?', $this->findAvailableModels()),
- ];
- }
-}
diff --git a/Console/TableCommand.php b/Console/TableCommand.php
deleted file mode 100644
index ecfa00a9e1..0000000000
--- a/Console/TableCommand.php
+++ /dev/null
@@ -1,283 +0,0 @@
-connection($this->input->getOption('database'));
- $tables = (new Collection($connection->getSchemaBuilder()->getTables()))
- ->keyBy('schema_qualified_name')->all();
-
- $tableNames = (new Collection($tables))->keys();
-
- $tableName = $this->argument('table') ?: search(
- 'Which table would you like to inspect?',
- fn (string $query) => $tableNames
- ->filter(fn ($table) => str_contains(strtolower($table), strtolower($query)))
- ->values()
- ->all()
- );
-
- $table = $tables[$tableName] ?? (new Collection($tables))->when(
- Arr::wrap($connection->getSchemaBuilder()->getCurrentSchemaListing()
- ?? $connection->getSchemaBuilder()->getCurrentSchemaName()),
- fn (Collection $collection, array $currentSchemas) => $collection->sortBy(
- function (array $table) use ($currentSchemas) {
- $index = array_search($table['schema'], $currentSchemas);
-
- return $index === false ? PHP_INT_MAX : $index;
- }
- )
- )->firstWhere('name', $tableName);
-
- if (! $table) {
- $this->components->warn("Table [{$tableName}] doesn't exist.");
-
- return 1;
- }
-
- [$columns, $indexes, $foreignKeys] = $connection->withoutTablePrefix(function ($connection) use ($table) {
- $schema = $connection->getSchemaBuilder();
- $tableName = $table['schema_qualified_name'];
-
- return [
- $this->columns($schema, $tableName),
- $this->indexes($schema, $tableName),
- $this->foreignKeys($schema, $tableName),
- ];
- });
-
- $data = [
- 'table' => [
- 'schema' => $table['schema'],
- 'name' => $table['name'],
- 'schema_qualified_name' => $table['schema_qualified_name'],
- 'columns' => count($columns),
- 'size' => $table['size'],
- 'comment' => $table['comment'],
- 'collation' => $table['collation'],
- 'engine' => $table['engine'],
- ],
- 'columns' => $columns,
- 'indexes' => $indexes,
- 'foreign_keys' => $foreignKeys,
- ];
-
- $this->display($data);
-
- return 0;
- }
-
- /**
- * Get the information regarding the table's columns.
- *
- * @param \Illuminate\Database\Schema\Builder $schema
- * @param string $table
- * @return \Illuminate\Support\Collection
- */
- protected function columns(Builder $schema, string $table)
- {
- return (new Collection($schema->getColumns($table)))->map(fn ($column) => [
- 'column' => $column['name'],
- 'attributes' => $this->getAttributesForColumn($column),
- 'default' => $column['default'],
- 'type' => $column['type'],
- ]);
- }
-
- /**
- * Get the attributes for a table column.
- *
- * @param array $column
- * @return \Illuminate\Support\Collection
- */
- protected function getAttributesForColumn($column)
- {
- return (new Collection([
- $column['type_name'],
- $column['generation'] ? $column['generation']['type'] : null,
- $column['auto_increment'] ? 'autoincrement' : null,
- $column['nullable'] ? 'nullable' : null,
- $column['collation'],
- ]))->filter();
- }
-
- /**
- * Get the information regarding the table's indexes.
- *
- * @param \Illuminate\Database\Schema\Builder $schema
- * @param string $table
- * @return \Illuminate\Support\Collection
- */
- protected function indexes(Builder $schema, string $table)
- {
- return (new Collection($schema->getIndexes($table)))->map(fn ($index) => [
- 'name' => $index['name'],
- 'columns' => new Collection($index['columns']),
- 'attributes' => $this->getAttributesForIndex($index),
- ]);
- }
-
- /**
- * Get the attributes for a table index.
- *
- * @param array $index
- * @return \Illuminate\Support\Collection
- */
- protected function getAttributesForIndex($index)
- {
- return (new Collection([
- $index['type'],
- count($index['columns']) > 1 ? 'compound' : null,
- $index['unique'] && ! $index['primary'] ? 'unique' : null,
- $index['primary'] ? 'primary' : null,
- ]))->filter();
- }
-
- /**
- * Get the information regarding the table's foreign keys.
- *
- * @param \Illuminate\Database\Schema\Builder $schema
- * @param string $table
- * @return \Illuminate\Support\Collection
- */
- protected function foreignKeys(Builder $schema, string $table)
- {
- return (new Collection($schema->getForeignKeys($table)))->map(fn ($foreignKey) => [
- 'name' => $foreignKey['name'],
- 'columns' => new Collection($foreignKey['columns']),
- 'foreign_schema' => $foreignKey['foreign_schema'],
- 'foreign_table' => $foreignKey['foreign_table'],
- 'foreign_columns' => new Collection($foreignKey['foreign_columns']),
- 'on_update' => $foreignKey['on_update'],
- 'on_delete' => $foreignKey['on_delete'],
- ]);
- }
-
- /**
- * Render the table information.
- *
- * @param array $data
- * @return void
- */
- protected function display(array $data)
- {
- $this->option('json') ? $this->displayJson($data) : $this->displayForCli($data);
- }
-
- /**
- * Render the table information as JSON.
- *
- * @param array $data
- * @return void
- */
- protected function displayJson(array $data)
- {
- $this->output->writeln(json_encode($data));
- }
-
- /**
- * Render the table information formatted for the CLI.
- *
- * @param array $data
- * @return void
- */
- protected function displayForCli(array $data)
- {
- [$table, $columns, $indexes, $foreignKeys] = [
- $data['table'], $data['columns'], $data['indexes'], $data['foreign_keys'],
- ];
-
- $this->newLine();
-
- $this->components->twoColumnDetail(''.$table['schema_qualified_name'].'>', $table['comment'] ? ''.$table['comment'].'>' : null);
- $this->components->twoColumnDetail('Columns', $table['columns']);
-
- if (! is_null($table['size'])) {
- $this->components->twoColumnDetail('Size', Number::fileSize($table['size'], 2));
- }
-
- if ($table['engine']) {
- $this->components->twoColumnDetail('Engine', $table['engine']);
- }
-
- if ($table['collation']) {
- $this->components->twoColumnDetail('Collation', $table['collation']);
- }
-
- $this->newLine();
-
- if ($columns->isNotEmpty()) {
- $this->components->twoColumnDetail('Column>', 'Type');
-
- $columns->each(function ($column) {
- $this->components->twoColumnDetail(
- $column['column'].' '.$column['attributes']->implode(', ').'>',
- (! is_null($column['default']) ? ''.$column['default'].'> ' : '').$column['type']
- );
- });
-
- $this->newLine();
- }
-
- if ($indexes->isNotEmpty()) {
- $this->components->twoColumnDetail('Index>');
-
- $indexes->each(function ($index) {
- $this->components->twoColumnDetail(
- $index['name'].' '.$index['columns']->implode(', ').'>',
- $index['attributes']->implode(', ')
- );
- });
-
- $this->newLine();
- }
-
- if ($foreignKeys->isNotEmpty()) {
- $this->components->twoColumnDetail('Foreign Key>', 'On Update / On Delete');
-
- $foreignKeys->each(function ($foreignKey) {
- $this->components->twoColumnDetail(
- $foreignKey['name'].' '.$foreignKey['columns']->implode(', ').' references '.$foreignKey['foreign_columns']->implode(', ').' on '.$foreignKey['foreign_table'].'>',
- $foreignKey['on_update'].' / '.$foreignKey['on_delete'],
- );
- });
-
- $this->newLine();
- }
- }
-}
diff --git a/Console/WipeCommand.php b/Console/WipeCommand.php
deleted file mode 100644
index d638db41d0..0000000000
--- a/Console/WipeCommand.php
+++ /dev/null
@@ -1,129 +0,0 @@
-isProhibited() ||
- ! $this->confirmToProceed()) {
- return Command::FAILURE;
- }
-
- $database = $this->input->getOption('database');
-
- if ($this->option('drop-views')) {
- $this->dropAllViews($database);
-
- $this->components->info('Dropped all views successfully.');
- }
-
- $this->dropAllTables($database);
-
- $this->components->info('Dropped all tables successfully.');
-
- if ($this->option('drop-types')) {
- $this->dropAllTypes($database);
-
- $this->components->info('Dropped all types successfully.');
- }
-
- $this->flushDatabaseConnection($database);
-
- return 0;
- }
-
- /**
- * Drop all of the database tables.
- *
- * @param string $database
- * @return void
- */
- protected function dropAllTables($database)
- {
- $this->laravel['db']->connection($database)
- ->getSchemaBuilder()
- ->dropAllTables();
- }
-
- /**
- * Drop all of the database views.
- *
- * @param string $database
- * @return void
- */
- protected function dropAllViews($database)
- {
- $this->laravel['db']->connection($database)
- ->getSchemaBuilder()
- ->dropAllViews();
- }
-
- /**
- * Drop all of the database types.
- *
- * @param string $database
- * @return void
- */
- protected function dropAllTypes($database)
- {
- $this->laravel['db']->connection($database)
- ->getSchemaBuilder()
- ->dropAllTypes();
- }
-
- /**
- * Flush the given database connection.
- *
- * @param string $database
- * @return void
- */
- protected function flushDatabaseConnection($database)
- {
- $this->laravel['db']->connection($database)->disconnect();
- }
-
- /**
- * Get the console command options.
- *
- * @return array
- */
- protected function getOptions()
- {
- return [
- ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'],
- ['drop-views', null, InputOption::VALUE_NONE, 'Drop all tables and views'],
- ['drop-types', null, InputOption::VALUE_NONE, 'Drop all tables and types (Postgres only)'],
- ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'],
- ];
- }
-}
diff --git a/DatabaseManager.php b/DatabaseManager.php
index 2d8edbc533..cc9c8dbe6e 100755
--- a/DatabaseManager.php
+++ b/DatabaseManager.php
@@ -2,32 +2,18 @@
namespace Illuminate\Database;
-use Illuminate\Database\Connectors\ConnectionFactory;
-use Illuminate\Database\Events\ConnectionEstablished;
+use PDO;
use Illuminate\Support\Arr;
-use Illuminate\Support\Collection;
-use Illuminate\Support\ConfigurationUrlParser;
use Illuminate\Support\Str;
-use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
-use PDO;
-use RuntimeException;
-
-use function Illuminate\Support\enum_value;
+use Illuminate\Database\Connectors\ConnectionFactory;
-/**
- * @mixin \Illuminate\Database\Connection
- */
class DatabaseManager implements ConnectionResolverInterface
{
- use Macroable {
- __call as macroCall;
- }
-
/**
* The application instance.
*
- * @var \Illuminate\Contracts\Foundation\Application
+ * @var \Illuminate\Foundation\Application
*/
protected $app;
@@ -41,130 +27,54 @@ class DatabaseManager implements ConnectionResolverInterface
/**
* The active connection instances.
*
- * @var array
+ * @var array
*/
protected $connections = [];
- /**
- * The dynamically configured (DB::build) connection configurations.
- *
- * @var array
- */
- protected $dynamicConnectionConfigurations = [];
-
/**
* The custom connection resolvers.
*
- * @var array
+ * @var array
*/
protected $extensions = [];
- /**
- * The callback to be executed to reconnect to a database.
- *
- * @var callable
- */
- protected $reconnector;
-
/**
* Create a new database manager instance.
*
- * @param \Illuminate\Contracts\Foundation\Application $app
+ * @param \Illuminate\Foundation\Application $app
* @param \Illuminate\Database\Connectors\ConnectionFactory $factory
+ * @return void
*/
public function __construct($app, ConnectionFactory $factory)
{
$this->app = $app;
$this->factory = $factory;
-
- $this->reconnector = function ($connection) {
- $connection->setPdo(
- $this->reconnect($connection->getNameWithReadWriteType())->getRawPdo()
- );
- };
}
/**
* Get a database connection instance.
*
- * @param \UnitEnum|string|null $name
+ * @param string $name
* @return \Illuminate\Database\Connection
*/
public function connection($name = null)
{
- [$database, $type] = $this->parseConnectionName($name = enum_value($name) ?: $this->getDefaultConnection());
+ list($database, $type) = $this->parseConnectionName($name);
+
+ $name = $name ?: $database;
// If we haven't created this connection, we'll create it based on the config
// provided in the application. Once we've created the connections we will
// set the "fetch mode" for PDO which determines the query return types.
if (! isset($this->connections[$name])) {
$this->connections[$name] = $this->configure(
- $this->makeConnection($database), $type
+ $connection = $this->makeConnection($database), $type
);
-
- $this->dispatchConnectionEstablishedEvent($this->connections[$name]);
}
return $this->connections[$name];
}
- /**
- * Build a database connection instance from the given configuration.
- *
- * @param array $config
- * @return \Illuminate\Database\ConnectionInterface
- */
- public function build(array $config)
- {
- $config['name'] ??= static::calculateDynamicConnectionName($config);
-
- $this->dynamicConnectionConfigurations[$config['name']] = $config;
-
- return $this->connectUsing($config['name'], $config, true);
- }
-
- /**
- * Calculate the dynamic connection name for an on-demand connection based on its configuration.
- *
- * @param array $config
- * @return string
- */
- public static function calculateDynamicConnectionName(array $config)
- {
- return 'dynamic_'.md5((new Collection($config))->map(function ($value, $key) {
- return $key.(is_string($value) || is_int($value) ? $value : '');
- })->implode(''));
- }
-
- /**
- * Get a database connection instance from the given configuration.
- *
- * @param \UnitEnum|string $name
- * @param array $config
- * @param bool $force
- * @return \Illuminate\Database\ConnectionInterface
- *
- * @throws \RuntimeException
- */
- public function connectUsing(string $name, array $config, bool $force = false)
- {
- if ($force) {
- $this->purge($name = enum_value($name));
- }
-
- if (isset($this->connections[$name])) {
- throw new RuntimeException("Cannot establish connection [$name] because another connection with that name already exists.");
- }
-
- $connection = $this->configure(
- $this->factory->make($config, $name), null
- );
-
- $this->dispatchConnectionEstablishedEvent($connection);
-
- return tap($connection, fn ($connection) => $this->connections[$name] = $connection);
- }
-
/**
* Parse the connection into an array of the name and read / write type.
*
@@ -173,9 +83,10 @@ public function connectUsing(string $name, array $config, bool $force = false)
*/
protected function parseConnectionName($name)
{
+ $name = $name ?: $this->getDefaultConnection();
+
return Str::endsWith($name, ['::read', '::write'])
- ? explode('::', $name, 2)
- : [$name, null];
+ ? explode('::', $name, 2) : [$name, null];
}
/**
@@ -215,16 +126,18 @@ protected function makeConnection($name)
*/
protected function configuration($name)
{
- $connections = $this->app['config']['database.connections'];
+ $name = $name ?: $this->getDefaultConnection();
- $config = $this->dynamicConnectionConfigurations[$name] ?? Arr::get($connections, $name);
+ // To get the database connection configuration, we will just pull each of the
+ // connection configurations and get the configurations for the given name.
+ // If the configuration doesn't exist, we'll throw an exception and bail.
+ $connections = $this->app['config']['database.connections'];
- if (is_null($config)) {
- throw new InvalidArgumentException("Database connection [{$name}] not configured.");
+ if (is_null($config = Arr::get($connections, $name))) {
+ throw new InvalidArgumentException("Database [$name] not configured.");
}
- return (new ConfigurationUrlParser)
- ->parseConfiguration($config);
+ return $config;
}
/**
@@ -236,7 +149,7 @@ protected function configuration($name)
*/
protected function configure(Connection $connection, $type)
{
- $connection = $this->setPdoForType($connection, $type)->setReadWriteType($type);
+ $connection = $this->setPdoForType($connection, $type);
// First we'll set the fetch mode and a few other dependencies of the database
// connection. This method basically just configures and prepares it to get
@@ -245,47 +158,28 @@ protected function configure(Connection $connection, $type)
$connection->setEventDispatcher($this->app['events']);
}
- if ($this->app->bound('db.transactions')) {
- $connection->setTransactionManager($this->app['db.transactions']);
- }
-
// Here we'll set a reconnector callback. This reconnector can be any callable
// so we will set a Closure to reconnect from this manager with the name of
// the connection, which will allow us to reconnect from the connections.
- $connection->setReconnector($this->reconnector);
+ $connection->setReconnector(function ($connection) {
+ $this->reconnect($connection->getName());
+ });
return $connection;
}
- /**
- * Dispatch the ConnectionEstablished event if the event dispatcher is available.
- *
- * @param \Illuminate\Database\Connection $connection
- * @return void
- */
- protected function dispatchConnectionEstablishedEvent(Connection $connection)
- {
- if (! $this->app->bound('events')) {
- return;
- }
-
- $this->app['events']->dispatch(
- new ConnectionEstablished($connection)
- );
- }
-
/**
* Prepare the read / write mode for database connection instance.
*
* @param \Illuminate\Database\Connection $connection
- * @param string|null $type
+ * @param string $type
* @return \Illuminate\Database\Connection
*/
protected function setPdoForType(Connection $connection, $type = null)
{
- if ($type === 'read') {
+ if ($type == 'read') {
$connection->setPdo($connection->getReadPdo());
- } elseif ($type === 'write') {
+ } elseif ($type == 'write') {
$connection->setReadPdo($connection->getPdo());
}
@@ -295,12 +189,14 @@ protected function setPdoForType(Connection $connection, $type = null)
/**
* Disconnect from the given database and remove from local cache.
*
- * @param \UnitEnum|string|null $name
+ * @param string $name
* @return void
*/
public function purge($name = null)
{
- $this->disconnect($name = enum_value($name) ?: $this->getDefaultConnection());
+ $name = $name ?: $this->getDefaultConnection();
+
+ $this->disconnect($name);
unset($this->connections[$name]);
}
@@ -308,12 +204,12 @@ public function purge($name = null)
/**
* Disconnect from the given database.
*
- * @param \UnitEnum|string|null $name
+ * @param string $name
* @return void
*/
public function disconnect($name = null)
{
- if (isset($this->connections[$name = enum_value($name) ?: $this->getDefaultConnection()])) {
+ if (isset($this->connections[$name = $name ?: $this->getDefaultConnection()])) {
$this->connections[$name]->disconnect();
}
}
@@ -321,40 +217,18 @@ public function disconnect($name = null)
/**
* Reconnect to the given database.
*
- * @param \UnitEnum|string|null $name
+ * @param string $name
* @return \Illuminate\Database\Connection
*/
public function reconnect($name = null)
{
- $this->disconnect($name = enum_value($name) ?: $this->getDefaultConnection());
+ $this->disconnect($name = $name ?: $this->getDefaultConnection());
if (! isset($this->connections[$name])) {
return $this->connection($name);
}
- return tap($this->refreshPdoConnections($name), function ($connection) {
- $this->dispatchConnectionEstablishedEvent($connection);
- });
- }
-
- /**
- * Set the default database connection for the callback execution.
- *
- * @param \UnitEnum|string $name
- * @param callable $callback
- * @return mixed
- */
- public function usingConnection($name, callable $callback)
- {
- $previousName = $this->getDefaultConnection();
-
- $this->setDefaultConnection($name = enum_value($name));
-
- try {
- return $callback();
- } finally {
- $this->setDefaultConnection($previousName);
- }
+ return $this->refreshPdoConnections($name);
}
/**
@@ -365,15 +239,11 @@ public function usingConnection($name, callable $callback)
*/
protected function refreshPdoConnections($name)
{
- [$database, $type] = $this->parseConnectionName($name);
-
- $fresh = $this->configure(
- $this->makeConnection($database), $type
- );
+ $fresh = $this->makeConnection($name);
return $this->connections[$name]
- ->setPdo($fresh->getRawPdo())
- ->setReadPdo($fresh->getRawReadPdo());
+ ->setPdo($fresh->getPdo())
+ ->setReadPdo($fresh->getReadPdo());
}
/**
@@ -398,19 +268,19 @@ public function setDefaultConnection($name)
}
/**
- * Get all of the supported drivers.
+ * Get all of the support drivers.
*
- * @return string[]
+ * @return array
*/
public function supportedDrivers()
{
- return ['mysql', 'mariadb', 'pgsql', 'sqlite', 'sqlsrv'];
+ return ['mysql', 'pgsql', 'sqlite', 'sqlsrv'];
}
/**
* Get all of the drivers that are actually available.
*
- * @return string[]
+ * @return array
*/
public function availableDrivers()
{
@@ -423,7 +293,7 @@ public function availableDrivers()
/**
* Register an extension connection resolver.
*
- * @param string $name
+ * @param string $name
* @param callable $resolver
* @return void
*/
@@ -432,64 +302,25 @@ public function extend($name, callable $resolver)
$this->extensions[$name] = $resolver;
}
- /**
- * Remove an extension connection resolver.
- *
- * @param string $name
- * @return void
- */
- public function forgetExtension($name)
- {
- unset($this->extensions[$name]);
- }
-
/**
* Return all of the created connections.
*
- * @return array
+ * @return array
*/
public function getConnections()
{
return $this->connections;
}
- /**
- * Set the database reconnector callback.
- *
- * @param callable $reconnector
- * @return void
- */
- public function setReconnector(callable $reconnector)
- {
- $this->reconnector = $reconnector;
- }
-
- /**
- * Set the application instance used by the manager.
- *
- * @param \Illuminate\Contracts\Foundation\Application $app
- * @return $this
- */
- public function setApplication($app)
- {
- $this->app = $app;
-
- return $this;
- }
-
/**
* Dynamically pass methods to the default connection.
*
* @param string $method
- * @param array $parameters
+ * @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
- if (static::hasMacro($method)) {
- return $this->macroCall($method, $parameters);
- }
-
return $this->connection()->$method(...$parameters);
}
}
diff --git a/DatabaseServiceProvider.php b/DatabaseServiceProvider.php
index 8a6115ba1e..a8ee7b030b 100755
--- a/DatabaseServiceProvider.php
+++ b/DatabaseServiceProvider.php
@@ -4,23 +4,15 @@
use Faker\Factory as FakerFactory;
use Faker\Generator as FakerGenerator;
-use Illuminate\Contracts\Database\ConcurrencyErrorDetector as ConcurrencyErrorDetectorContract;
-use Illuminate\Contracts\Database\LostConnectionDetector as LostConnectionDetectorContract;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Queue\EntityResolver;
use Illuminate\Database\Connectors\ConnectionFactory;
-use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\QueueEntityResolver;
-use Illuminate\Support\ServiceProvider;
+use Illuminate\Database\Eloquent\Factory as EloquentFactory;
class DatabaseServiceProvider extends ServiceProvider
{
- /**
- * The array of resolved Faker instances.
- *
- * @var array
- */
- protected static $fakers = [];
-
/**
* Bootstrap the application events.
*
@@ -43,7 +35,9 @@ public function register()
Model::clearBootedModels();
$this->registerConnectionServices();
- $this->registerFakerGenerator();
+
+ $this->registerEloquentFactory();
+
$this->registerQueueableEntityResolver();
}
@@ -71,45 +65,23 @@ protected function registerConnectionServices()
$this->app->bind('db.connection', function ($app) {
return $app['db']->connection();
});
-
- $this->app->bind('db.schema', function ($app) {
- return $app['db']->connection()->getSchemaBuilder();
- });
-
- $this->app->singleton('db.transactions', function () {
- return new DatabaseTransactionsManager;
- });
-
- $this->app->singleton(ConcurrencyErrorDetectorContract::class, function () {
- return new ConcurrencyErrorDetector;
- });
-
- $this->app->singleton(LostConnectionDetectorContract::class, function () {
- return new LostConnectionDetector;
- });
}
/**
- * Register the Faker Generator instance in the container.
+ * Register the Eloquent factory instance in the container.
*
* @return void
*/
- protected function registerFakerGenerator()
+ protected function registerEloquentFactory()
{
- if (! class_exists(FakerGenerator::class)) {
- return;
- }
-
- $this->app->singleton(FakerGenerator::class, function ($app, $parameters) {
- $locale = $parameters['locale'] ?? $app['config']->get('app.faker_locale', 'en_US');
-
- if (! isset(static::$fakers[$locale])) {
- static::$fakers[$locale] = FakerFactory::create($locale);
- }
-
- static::$fakers[$locale]->unique(true);
+ $this->app->singleton(FakerGenerator::class, function ($app) {
+ return FakerFactory::create($app['config']->get('app.faker_locale', 'en_US'));
+ });
- return static::$fakers[$locale];
+ $this->app->singleton(EloquentFactory::class, function ($app) {
+ return EloquentFactory::construct(
+ $app->make(FakerGenerator::class), $this->app->databasePath('factories')
+ );
});
}
diff --git a/DatabaseTransactionRecord.php b/DatabaseTransactionRecord.php
deleted file mode 100755
index 08fd471323..0000000000
--- a/DatabaseTransactionRecord.php
+++ /dev/null
@@ -1,121 +0,0 @@
-connection = $connection;
- $this->level = $level;
- $this->parent = $parent;
- }
-
- /**
- * Register a callback to be executed after committing.
- *
- * @param callable $callback
- * @return void
- */
- public function addCallback($callback)
- {
- $this->callbacks[] = $callback;
- }
-
- /**
- * Register a callback to be executed after rollback.
- *
- * @param callable $callback
- * @return void
- */
- public function addCallbackForRollback($callback)
- {
- $this->callbacksForRollback[] = $callback;
- }
-
- /**
- * Execute all of the callbacks.
- *
- * @return void
- */
- public function executeCallbacks()
- {
- foreach ($this->callbacks as $callback) {
- $callback();
- }
- }
-
- /**
- * Execute all of the callbacks for rollback.
- *
- * @return void
- */
- public function executeCallbacksForRollback()
- {
- foreach ($this->callbacksForRollback as $callback) {
- $callback();
- }
- }
-
- /**
- * Get all of the callbacks.
- *
- * @return array
- */
- public function getCallbacks()
- {
- return $this->callbacks;
- }
-
- /**
- * Get all of the callbacks for rollback.
- *
- * @return array
- */
- public function getCallbacksForRollback()
- {
- return $this->callbacksForRollback;
- }
-}
diff --git a/DatabaseTransactionsManager.php b/DatabaseTransactionsManager.php
deleted file mode 100755
index 9713c66d82..0000000000
--- a/DatabaseTransactionsManager.php
+++ /dev/null
@@ -1,267 +0,0 @@
-
- */
- protected $committedTransactions;
-
- /**
- * All of the pending transactions.
- *
- * @var \Illuminate\Support\Collection
- */
- protected $pendingTransactions;
-
- /**
- * The current transaction.
- *
- * @var array
- */
- protected $currentTransaction = [];
-
- /**
- * Create a new database transactions manager instance.
- */
- public function __construct()
- {
- $this->committedTransactions = new Collection;
- $this->pendingTransactions = new Collection;
- }
-
- /**
- * Start a new database transaction.
- *
- * @param string $connection
- * @param int $level
- * @return void
- */
- public function begin($connection, $level)
- {
- $this->pendingTransactions->push(
- $newTransaction = new DatabaseTransactionRecord(
- $connection,
- $level,
- $this->currentTransaction[$connection] ?? null
- )
- );
-
- $this->currentTransaction[$connection] = $newTransaction;
- }
-
- /**
- * Commit the root database transaction and execute callbacks.
- *
- * @param string $connection
- * @param int $levelBeingCommitted
- * @param int $newTransactionLevel
- * @return array
- */
- public function commit($connection, $levelBeingCommitted, $newTransactionLevel)
- {
- $this->stageTransactions($connection, $levelBeingCommitted);
-
- if (isset($this->currentTransaction[$connection])) {
- $this->currentTransaction[$connection] = $this->currentTransaction[$connection]->parent;
- }
-
- if (! $this->afterCommitCallbacksShouldBeExecuted($newTransactionLevel) &&
- $newTransactionLevel !== 0) {
- return [];
- }
-
- // This method is only called when the root database transaction is committed so there
- // shouldn't be any pending transactions, but going to clear them here anyways just
- // in case. This method could be refactored to receive a level in the future too.
- $this->pendingTransactions = $this->pendingTransactions->reject(
- fn ($transaction) => $transaction->connection === $connection &&
- $transaction->level >= $levelBeingCommitted
- )->values();
-
- [$forThisConnection, $forOtherConnections] = $this->committedTransactions->partition(
- fn ($transaction) => $transaction->connection == $connection
- );
-
- $this->committedTransactions = $forOtherConnections->values();
-
- $forThisConnection->map->executeCallbacks();
-
- return $forThisConnection;
- }
-
- /**
- * Move relevant pending transactions to a committed state.
- *
- * @param string $connection
- * @param int $levelBeingCommitted
- * @return void
- */
- public function stageTransactions($connection, $levelBeingCommitted)
- {
- $this->committedTransactions = $this->committedTransactions->merge(
- $this->pendingTransactions->filter(
- fn ($transaction) => $transaction->connection === $connection &&
- $transaction->level >= $levelBeingCommitted
- )
- );
-
- $this->pendingTransactions = $this->pendingTransactions->reject(
- fn ($transaction) => $transaction->connection === $connection &&
- $transaction->level >= $levelBeingCommitted
- );
- }
-
- /**
- * Rollback the active database transaction.
- *
- * @param string $connection
- * @param int $newTransactionLevel
- * @return void
- */
- public function rollback($connection, $newTransactionLevel)
- {
- if ($newTransactionLevel === 0) {
- $this->removeAllTransactionsForConnection($connection);
- } else {
- $this->pendingTransactions = $this->pendingTransactions->reject(
- fn ($transaction) => $transaction->connection == $connection &&
- $transaction->level > $newTransactionLevel
- )->values();
-
- if ($this->currentTransaction) {
- do {
- $this->removeCommittedTransactionsThatAreChildrenOf($this->currentTransaction[$connection]);
-
- $this->currentTransaction[$connection]->executeCallbacksForRollback();
-
- $this->currentTransaction[$connection] = $this->currentTransaction[$connection]->parent;
- } while (
- isset($this->currentTransaction[$connection]) &&
- $this->currentTransaction[$connection]->level > $newTransactionLevel
- );
- }
- }
- }
-
- /**
- * Remove all pending, completed, and current transactions for the given connection name.
- *
- * @param string $connection
- * @return void
- */
- protected function removeAllTransactionsForConnection($connection)
- {
- if ($this->currentTransaction) {
- for ($currentTransaction = $this->currentTransaction[$connection]; isset($currentTransaction); $currentTransaction = $currentTransaction->parent) {
- $currentTransaction->executeCallbacksForRollback();
- }
- }
-
- $this->currentTransaction[$connection] = null;
-
- $this->pendingTransactions = $this->pendingTransactions->reject(
- fn ($transaction) => $transaction->connection == $connection
- )->values();
-
- $this->committedTransactions = $this->committedTransactions->reject(
- fn ($transaction) => $transaction->connection == $connection
- )->values();
- }
-
- /**
- * Remove all transactions that are children of the given transaction.
- *
- * @param \Illuminate\Database\DatabaseTransactionRecord $transaction
- * @return void
- */
- protected function removeCommittedTransactionsThatAreChildrenOf(DatabaseTransactionRecord $transaction)
- {
- [$removedTransactions, $this->committedTransactions] = $this->committedTransactions->partition(
- fn ($committed) => $committed->connection == $transaction->connection &&
- $committed->parent === $transaction
- );
-
- // There may be multiple deeply nested transactions that have already committed that we
- // also need to remove. We will recurse down the children of all removed transaction
- // instances until there are no more deeply nested child transactions for removal.
- $removedTransactions->each(
- fn ($transaction) => $this->removeCommittedTransactionsThatAreChildrenOf($transaction)
- );
- }
-
- /**
- * Register a transaction callback.
- *
- * @param callable $callback
- * @return void
- */
- public function addCallback($callback)
- {
- if ($current = $this->callbackApplicableTransactions()->last()) {
- return $current->addCallback($callback);
- }
-
- $callback();
- }
-
- /**
- * Register a callback for transaction rollback.
- *
- * @param callable $callback
- * @return void
- */
- public function addCallbackForRollback($callback)
- {
- if ($current = $this->callbackApplicableTransactions()->last()) {
- return $current->addCallbackForRollback($callback);
- }
- }
-
- /**
- * Get the transactions that are applicable to callbacks.
- *
- * @return \Illuminate\Support\Collection
- */
- public function callbackApplicableTransactions()
- {
- return $this->pendingTransactions;
- }
-
- /**
- * Determine if after commit callbacks should be executed for the given transaction level.
- *
- * @param int $level
- * @return bool
- */
- public function afterCommitCallbacksShouldBeExecuted($level)
- {
- return $level === 0;
- }
-
- /**
- * Get all of the pending transactions.
- *
- * @return \Illuminate\Support\Collection
- */
- public function getPendingTransactions()
- {
- return $this->pendingTransactions;
- }
-
- /**
- * Get all of the committed transactions.
- *
- * @return \Illuminate\Support\Collection
- */
- public function getCommittedTransactions()
- {
- return $this->committedTransactions;
- }
-}
diff --git a/DeadlockException.php b/DeadlockException.php
deleted file mode 100644
index 375a39bc96..0000000000
--- a/DeadlockException.php
+++ /dev/null
@@ -1,10 +0,0 @@
-bound(ConcurrencyErrorDetectorContract::class)
- ? $container[ConcurrencyErrorDetectorContract::class]
- : new ConcurrencyErrorDetector();
-
- return $detector->causedByConcurrencyError($e);
- }
-}
diff --git a/DetectsDeadlocks.php b/DetectsDeadlocks.php
new file mode 100644
index 0000000000..dcbbd00bbe
--- /dev/null
+++ b/DetectsDeadlocks.php
@@ -0,0 +1,30 @@
+getMessage();
+
+ return Str::contains($message, [
+ 'Deadlock found when trying to get lock',
+ 'deadlock detected',
+ 'The database file is locked',
+ 'database is locked',
+ 'database table is locked',
+ 'A table in the database is locked',
+ 'has been chosen as the deadlock victim',
+ ]);
+ }
+}
diff --git a/DetectsLostConnections.php b/DetectsLostConnections.php
index ba649afe2a..bee348203f 100644
--- a/DetectsLostConnections.php
+++ b/DetectsLostConnections.php
@@ -2,26 +2,32 @@
namespace Illuminate\Database;
-use Illuminate\Container\Container;
-use Illuminate\Contracts\Database\LostConnectionDetector as LostConnectionDetectorContract;
-use Throwable;
+use Exception;
+use Illuminate\Support\Str;
trait DetectsLostConnections
{
/**
* Determine if the given exception was caused by a lost connection.
*
- * @param \Throwable $e
+ * @param \Exception $e
* @return bool
*/
- protected function causedByLostConnection(Throwable $e)
+ protected function causedByLostConnection(Exception $e)
{
- $container = Container::getInstance();
+ $message = $e->getMessage();
- $detector = $container->bound(LostConnectionDetectorContract::class)
- ? $container[LostConnectionDetectorContract::class]
- : new LostConnectionDetector();
-
- return $detector->causedByLostConnection($e);
+ return Str::contains($message, [
+ 'server has gone away',
+ 'no connection to the server',
+ 'Lost connection',
+ 'is dead or not enabled',
+ 'Error while sending',
+ 'decryption failed or bad record mac',
+ 'server closed the connection unexpectedly',
+ 'SSL connection has been closed unexpectedly',
+ 'Error writing data to the connection',
+ 'Resource deadlock avoided',
+ ]);
}
}
diff --git a/Eloquent/Attributes/Appends.php b/Eloquent/Attributes/Appends.php
deleted file mode 100644
index 6b696e3123..0000000000
--- a/Eloquent/Attributes/Appends.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $columns
- */
- public function __construct(public array $columns)
- {
- }
-}
diff --git a/Eloquent/Attributes/Boot.php b/Eloquent/Attributes/Boot.php
deleted file mode 100644
index f57da7af94..0000000000
--- a/Eloquent/Attributes/Boot.php
+++ /dev/null
@@ -1,11 +0,0 @@
-> $collectionClass
- */
- public function __construct(public string $collectionClass)
- {
- }
-}
diff --git a/Eloquent/Attributes/Connection.php b/Eloquent/Attributes/Connection.php
deleted file mode 100644
index d02fcc502f..0000000000
--- a/Eloquent/Attributes/Connection.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $columns
- */
- public function __construct(public array $columns)
- {
- }
-}
diff --git a/Eloquent/Attributes/Guarded.php b/Eloquent/Attributes/Guarded.php
deleted file mode 100644
index d2f9c34e8d..0000000000
--- a/Eloquent/Attributes/Guarded.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $columns
- */
- public function __construct(public array $columns)
- {
- }
-}
diff --git a/Eloquent/Attributes/Hidden.php b/Eloquent/Attributes/Hidden.php
deleted file mode 100644
index a7dbfbc446..0000000000
--- a/Eloquent/Attributes/Hidden.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $columns
- */
- public function __construct(public array $columns)
- {
- }
-}
diff --git a/Eloquent/Attributes/Initialize.php b/Eloquent/Attributes/Initialize.php
deleted file mode 100644
index 58f48769eb..0000000000
--- a/Eloquent/Attributes/Initialize.php
+++ /dev/null
@@ -1,11 +0,0 @@
- $relations
- */
- public function __construct(public array $relations)
- {
- }
-}
diff --git a/Eloquent/Attributes/Unguarded.php b/Eloquent/Attributes/Unguarded.php
deleted file mode 100644
index 005105e47e..0000000000
--- a/Eloquent/Attributes/Unguarded.php
+++ /dev/null
@@ -1,11 +0,0 @@
- $builderClass
- */
- public function __construct(public string $builderClass)
- {
- }
-}
diff --git a/Eloquent/Attributes/UseFactory.php b/Eloquent/Attributes/UseFactory.php
deleted file mode 100644
index a013102fcd..0000000000
--- a/Eloquent/Attributes/UseFactory.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $factoryClass
- */
- public function __construct(public string $factoryClass)
- {
- }
-}
diff --git a/Eloquent/Attributes/UsePolicy.php b/Eloquent/Attributes/UsePolicy.php
deleted file mode 100644
index 9306598e07..0000000000
--- a/Eloquent/Attributes/UsePolicy.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $class
- */
- public function __construct(public string $class)
- {
- }
-}
diff --git a/Eloquent/Attributes/UseResource.php b/Eloquent/Attributes/UseResource.php
deleted file mode 100644
index a1cbc48f3a..0000000000
--- a/Eloquent/Attributes/UseResource.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $class
- */
- public function __construct(public string $class)
- {
- }
-}
diff --git a/Eloquent/Attributes/UseResourceCollection.php b/Eloquent/Attributes/UseResourceCollection.php
deleted file mode 100644
index c17e1f1768..0000000000
--- a/Eloquent/Attributes/UseResourceCollection.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $class
- */
- public function __construct(public string $class)
- {
- }
-}
diff --git a/Eloquent/Attributes/Visible.php b/Eloquent/Attributes/Visible.php
deleted file mode 100644
index b9c87575a4..0000000000
--- a/Eloquent/Attributes/Visible.php
+++ /dev/null
@@ -1,18 +0,0 @@
- $columns
- */
- public function __construct(public array $columns)
- {
- }
-}
diff --git a/Eloquent/BroadcastableModelEventOccurred.php b/Eloquent/BroadcastableModelEventOccurred.php
deleted file mode 100644
index 8bd028032e..0000000000
--- a/Eloquent/BroadcastableModelEventOccurred.php
+++ /dev/null
@@ -1,144 +0,0 @@
-model = $model;
- $this->event = $event;
- }
-
- /**
- * The channels the event should broadcast on.
- *
- * @return array
- */
- public function broadcastOn()
- {
- $channels = empty($this->channels)
- ? ($this->model->broadcastOn($this->event) ?: [])
- : $this->channels;
-
- return (new BaseCollection($channels))
- ->map(fn ($channel) => $channel instanceof Model ? new PrivateChannel($channel) : $channel)
- ->all();
- }
-
- /**
- * The name the event should broadcast as.
- *
- * @return string
- */
- public function broadcastAs()
- {
- $default = class_basename($this->model).ucfirst($this->event);
-
- return method_exists($this->model, 'broadcastAs')
- ? ($this->model->broadcastAs($this->event) ?: $default)
- : $default;
- }
-
- /**
- * Get the data that should be sent with the broadcasted event.
- *
- * @return array|null
- */
- public function broadcastWith()
- {
- return method_exists($this->model, 'broadcastWith')
- ? $this->model->broadcastWith($this->event)
- : null;
- }
-
- /**
- * Manually specify the channels the event should broadcast on.
- *
- * @param array $channels
- * @return $this
- */
- public function onChannels(array $channels)
- {
- $this->channels = $channels;
-
- return $this;
- }
-
- /**
- * Determine if the event should be broadcast synchronously.
- *
- * @return bool
- */
- public function shouldBroadcastNow()
- {
- return $this->event === 'deleted' &&
- ! method_exists($this->model, 'bootSoftDeletes');
- }
-
- /**
- * Get the event name.
- *
- * @return string
- */
- public function event()
- {
- return $this->event;
- }
-}
diff --git a/Eloquent/BroadcastsEvents.php b/Eloquent/BroadcastsEvents.php
deleted file mode 100644
index c0461ddb0a..0000000000
--- a/Eloquent/BroadcastsEvents.php
+++ /dev/null
@@ -1,197 +0,0 @@
-broadcastCreated();
- });
-
- static::updated(function ($model) {
- $model->broadcastUpdated();
- });
-
- if (method_exists(static::class, 'bootSoftDeletes')) {
- static::softDeleted(function ($model) {
- $model->broadcastTrashed();
- });
-
- static::restored(function ($model) {
- $model->broadcastRestored();
- });
- }
-
- static::deleted(function ($model) {
- $model->broadcastDeleted();
- });
- }
-
- /**
- * Broadcast that the model was created.
- *
- * @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
- * @return \Illuminate\Broadcasting\PendingBroadcast
- */
- public function broadcastCreated($channels = null)
- {
- return $this->broadcastIfBroadcastChannelsExistForEvent(
- $this->newBroadcastableModelEvent('created'), 'created', $channels
- );
- }
-
- /**
- * Broadcast that the model was updated.
- *
- * @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
- * @return \Illuminate\Broadcasting\PendingBroadcast
- */
- public function broadcastUpdated($channels = null)
- {
- return $this->broadcastIfBroadcastChannelsExistForEvent(
- $this->newBroadcastableModelEvent('updated'), 'updated', $channels
- );
- }
-
- /**
- * Broadcast that the model was trashed.
- *
- * @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
- * @return \Illuminate\Broadcasting\PendingBroadcast
- */
- public function broadcastTrashed($channels = null)
- {
- return $this->broadcastIfBroadcastChannelsExistForEvent(
- $this->newBroadcastableModelEvent('trashed'), 'trashed', $channels
- );
- }
-
- /**
- * Broadcast that the model was restored.
- *
- * @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
- * @return \Illuminate\Broadcasting\PendingBroadcast
- */
- public function broadcastRestored($channels = null)
- {
- return $this->broadcastIfBroadcastChannelsExistForEvent(
- $this->newBroadcastableModelEvent('restored'), 'restored', $channels
- );
- }
-
- /**
- * Broadcast that the model was deleted.
- *
- * @param \Illuminate\Broadcasting\Channel|\Illuminate\Contracts\Broadcasting\HasBroadcastChannel|array|null $channels
- * @return \Illuminate\Broadcasting\PendingBroadcast
- */
- public function broadcastDeleted($channels = null)
- {
- return $this->broadcastIfBroadcastChannelsExistForEvent(
- $this->newBroadcastableModelEvent('deleted'), 'deleted', $channels
- );
- }
-
- /**
- * Broadcast the given event instance if channels are configured for the model event.
- *
- * @param mixed $instance
- * @param string $event
- * @param mixed $channels
- * @return \Illuminate\Broadcasting\PendingBroadcast|null
- */
- protected function broadcastIfBroadcastChannelsExistForEvent($instance, $event, $channels = null)
- {
- if (! static::$isBroadcasting) {
- return;
- }
-
- if (! empty($this->broadcastOn($event)) || ! empty($channels)) {
- return broadcast($instance->onChannels(Arr::wrap($channels)));
- }
- }
-
- /**
- * Create a new broadcastable model event event.
- *
- * @param string $event
- * @return mixed
- */
- public function newBroadcastableModelEvent($event)
- {
- return tap($this->newBroadcastableEvent($event), function ($event) {
- $event->connection = property_exists($this, 'broadcastConnection')
- ? $this->broadcastConnection
- : $this->broadcastConnection();
-
- $event->queue = property_exists($this, 'broadcastQueue')
- ? $this->broadcastQueue
- : $this->broadcastQueue();
-
- $event->afterCommit = property_exists($this, 'broadcastAfterCommit')
- ? $this->broadcastAfterCommit
- : $this->broadcastAfterCommit();
- });
- }
-
- /**
- * Create a new broadcastable model event for the model.
- *
- * @param string $event
- * @return \Illuminate\Database\Eloquent\BroadcastableModelEventOccurred
- */
- protected function newBroadcastableEvent(string $event)
- {
- return new BroadcastableModelEventOccurred($this, $event);
- }
-
- /**
- * Get the channels that model events should broadcast on.
- *
- * @param string $event
- * @return \Illuminate\Broadcasting\Channel|array
- */
- public function broadcastOn($event)
- {
- return [$this];
- }
-
- /**
- * Get the queue connection that should be used to broadcast model events.
- *
- * @return string|null
- */
- public function broadcastConnection()
- {
- //
- }
-
- /**
- * Get the queue that should be used to broadcast model events.
- *
- * @return string|null
- */
- public function broadcastQueue()
- {
- //
- }
-
- /**
- * Determine if the model event broadcast queued job should be dispatched after all transactions are committed.
- *
- * @return bool
- */
- public function broadcastAfterCommit()
- {
- return false;
- }
-}
diff --git a/Eloquent/BroadcastsEventsAfterCommit.php b/Eloquent/BroadcastsEventsAfterCommit.php
deleted file mode 100644
index 806af70f87..0000000000
--- a/Eloquent/BroadcastsEventsAfterCommit.php
+++ /dev/null
@@ -1,18 +0,0 @@
- */
- use BuildsQueries, ForwardsCalls, QueriesRelationships {
- BuildsQueries::sole as baseSole;
- }
+ use BuildsQueries, Concerns\QueriesRelationships;
/**
* The base query builder instance.
@@ -49,17 +30,10 @@ class Builder implements BuilderContract
/**
* The model being queried.
*
- * @var TModel
+ * @var \Illuminate\Database\Eloquent\Model
*/
protected $model;
- /**
- * The attributes that should be added to new models created by this builder.
- *
- * @var array
- */
- public $pendingAttributes = [];
-
/**
* The relationships that should be eager loaded.
*
@@ -88,53 +62,14 @@ class Builder implements BuilderContract
*/
protected $onDelete;
- /**
- * The properties that should be returned from query builder.
- *
- * @var string[]
- */
- protected $propertyPassthru = [
- 'from',
- ];
-
/**
* The methods that should be returned from query builder.
*
- * @var string[]
+ * @var array
*/
protected $passthru = [
- 'aggregate',
- 'average',
- 'avg',
- 'count',
- 'dd',
- 'ddrawsql',
- 'doesntexist',
- 'doesntexistor',
- 'dump',
- 'dumprawsql',
- 'exists',
- 'existsor',
- 'explain',
- 'getbindings',
- 'getconnection',
- 'getcountforpagination',
- 'getgrammar',
- 'getrawbindings',
- 'implode',
- 'insert',
- 'insertgetid',
- 'insertorignore',
- 'insertusing',
- 'insertorignoreusing',
- 'max',
- 'min',
- 'numericaggregate',
- 'raw',
- 'rawvalue',
- 'sum',
- 'tosql',
- 'torawsql',
+ 'insert', 'insertGetId', 'getBindings', 'toSql',
+ 'exists', 'count', 'min', 'max', 'avg', 'sum', 'getConnection',
];
/**
@@ -151,41 +86,17 @@ class Builder implements BuilderContract
*/
protected $removedScopes = [];
- /**
- * The callbacks that should be invoked after retrieving data from the database.
- *
- * @var array
- */
- protected $afterQueryCallbacks = [];
-
- /**
- * The callbacks that should be invoked on clone.
- *
- * @var array
- */
- protected $onCloneCallbacks = [];
-
/**
* Create a new Eloquent query builder instance.
*
* @param \Illuminate\Database\Query\Builder $query
+ * @return void
*/
public function __construct(QueryBuilder $query)
{
$this->query = $query;
}
- /**
- * Create and return an un-saved model instance.
- *
- * @param array $attributes
- * @return TModel
- */
- public function make(array $attributes = [])
- {
- return $this->newModelInstance($attributes);
- }
-
/**
* Register a new global scope.
*
@@ -229,34 +140,19 @@ public function withoutGlobalScope($scope)
* @param array|null $scopes
* @return $this
*/
- public function withoutGlobalScopes(?array $scopes = null)
+ public function withoutGlobalScopes(array $scopes = null)
{
- if (! is_array($scopes)) {
- $scopes = array_keys($this->scopes);
- }
-
- foreach ($scopes as $scope) {
- $this->withoutGlobalScope($scope);
+ if (is_array($scopes)) {
+ foreach ($scopes as $scope) {
+ $this->withoutGlobalScope($scope);
+ }
+ } else {
+ $this->scopes = [];
}
return $this;
}
- /**
- * Remove all global scopes except the given scopes.
- *
- * @param array $scopes
- * @return $this
- */
- public function withoutGlobalScopesExcept(array $scopes = [])
- {
- $this->withoutGlobalScopes(
- array_diff(array_keys($this->scopes), $scopes)
- );
-
- return $this;
- }
-
/**
* Get an array of global scopes that were removed from the query.
*
@@ -275,90 +171,31 @@ public function removedScopes()
*/
public function whereKey($id)
{
- if ($id instanceof Model) {
- $id = $id->getKey();
- }
-
if (is_array($id) || $id instanceof Arrayable) {
- if (in_array($this->model->getKeyType(), ['int', 'integer'])) {
- $this->query->whereIntegerInRaw($this->model->getQualifiedKeyName(), $id);
- } else {
- $this->query->whereIn($this->model->getQualifiedKeyName(), $id);
- }
+ $this->query->whereIn($this->model->getQualifiedKeyName(), $id);
return $this;
}
- if ($id !== null && $this->model->getKeyType() === 'string') {
- $id = (string) $id;
- }
-
return $this->where($this->model->getQualifiedKeyName(), '=', $id);
}
- /**
- * Add a where clause on the primary key to the query.
- *
- * @param mixed $id
- * @return $this
- */
- public function whereKeyNot($id)
- {
- if ($id instanceof Model) {
- $id = $id->getKey();
- }
-
- if (is_array($id) || $id instanceof Arrayable) {
- if (in_array($this->model->getKeyType(), ['int', 'integer'])) {
- $this->query->whereIntegerNotInRaw($this->model->getQualifiedKeyName(), $id);
- } else {
- $this->query->whereNotIn($this->model->getQualifiedKeyName(), $id);
- }
-
- return $this;
- }
-
- if ($id !== null && $this->model->getKeyType() === 'string') {
- $id = (string) $id;
- }
-
- return $this->where($this->model->getQualifiedKeyName(), '!=', $id);
- }
-
- /**
- * Exclude the given models from the query results.
- *
- * @param iterable|mixed $models
- * @return static
- */
- public function except($models)
- {
- return $this->whereKeyNot(
- $models instanceof Model
- ? $models->getKey()
- : Collection::wrap($models)->modelKeys()
- );
- }
-
/**
* Add a basic where clause to the query.
*
- * @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column
- * @param mixed $operator
+ * @param string|\Closure $column
+ * @param string $operator
* @param mixed $value
* @param string $boolean
* @return $this
*/
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
- if ($column instanceof Closure && is_null($operator)) {
- $column($query = $this->model->newQueryWithoutRelationships());
+ if ($column instanceof Closure) {
+ $query = $this->model->newQueryWithoutScopes();
- $this->eagerLoad = array_merge($this->eagerLoad, $query->getEagerLoads());
+ $column($query);
- $this->withoutGlobalScopes(
- $query->removedScopes()
- );
$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
} else {
$this->query->where(...func_get_args());
@@ -367,186 +204,40 @@ public function where($column, $operator = null, $value = null, $boolean = 'and'
return $this;
}
- /**
- * Add a basic where clause to the query, and return the first result.
- *
- * @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column
- * @param mixed $operator
- * @param mixed $value
- * @param string $boolean
- * @return TModel|null
- */
- public function firstWhere($column, $operator = null, $value = null, $boolean = 'and')
- {
- return $this->where(...func_get_args())->first();
- }
-
/**
* Add an "or where" clause to the query.
*
- * @param (\Closure(static): mixed)|array|string|\Illuminate\Contracts\Database\Query\Expression $column
- * @param mixed $operator
+ * @param string|\Closure $column
+ * @param string $operator
* @param mixed $value
- * @return $this
+ * @return \Illuminate\Database\Eloquent\Builder|static
*/
public function orWhere($column, $operator = null, $value = null)
{
- [$value, $operator] = $this->query->prepareValueAndOperator(
- $value, $operator, func_num_args() === 2
- );
-
return $this->where($column, $operator, $value, 'or');
}
- /**
- * Add a basic "where not" clause to the query.
- *
- * @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column
- * @param mixed $operator
- * @param mixed $value
- * @param string $boolean
- * @return $this
- */
- public function whereNot($column, $operator = null, $value = null, $boolean = 'and')
- {
- return $this->where($column, $operator, $value, $boolean.' not');
- }
-
- /**
- * Add an "or where not" clause to the query.
- *
- * @param (\Closure(static): mixed)|array|string|\Illuminate\Contracts\Database\Query\Expression $column
- * @param mixed $operator
- * @param mixed $value
- * @return $this
- */
- public function orWhereNot($column, $operator = null, $value = null)
- {
- return $this->whereNot($column, $operator, $value, 'or');
- }
-
- /**
- * Add an "order by" clause for a timestamp to the query.
- *
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
- * @return $this
- */
- public function latest($column = null)
- {
- if (is_null($column)) {
- $column = $this->model->getCreatedAtColumn() ?? 'created_at';
- }
-
- $this->query->latest($column);
-
- return $this;
- }
-
- /**
- * Add an "order by" clause for a timestamp to the query.
- *
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
- * @return $this
- */
- public function oldest($column = null)
- {
- if (is_null($column)) {
- $column = $this->model->getCreatedAtColumn() ?? 'created_at';
- }
-
- $this->query->oldest($column);
-
- return $this;
- }
-
/**
* Create a collection of models from plain arrays.
*
* @param array $items
- * @return \Illuminate\Database\Eloquent\Collection
+ * @return \Illuminate\Database\Eloquent\Collection
*/
public function hydrate(array $items)
{
$instance = $this->newModelInstance();
- return $instance->newCollection(array_map(function ($item) use ($items, $instance) {
- $model = $instance->newFromBuilder($item);
-
- if (count($items) > 1) {
- $model->preventsLazyLoading = Model::preventsLazyLoading();
- }
-
- return $model;
+ return $instance->newCollection(array_map(function ($item) use ($instance) {
+ return $instance->newFromBuilder($item);
}, $items));
}
- /**
- * Insert into the database after merging the model's default attributes, setting timestamps, and casting values.
- *
- * @param array> $values
- * @return bool
- */
- public function fillAndInsert(array $values)
- {
- return $this->insert($this->fillForInsert($values));
- }
-
- /**
- * Insert (ignoring errors) into the database after merging the model's default attributes, setting timestamps, and casting values.
- *
- * @param array> $values
- * @return int
- */
- public function fillAndInsertOrIgnore(array $values)
- {
- return $this->insertOrIgnore($this->fillForInsert($values));
- }
-
- /**
- * Insert a record into the database and get its ID after merging the model's default attributes, setting timestamps, and casting values.
- *
- * @param array $values
- * @return int
- */
- public function fillAndInsertGetId(array $values)
- {
- return $this->insertGetId($this->fillForInsert([$values])[0]);
- }
-
- /**
- * Enrich the given values by merging in the model's default attributes, adding timestamps, and casting values.
- *
- * @param array> $values
- * @return array>
- */
- public function fillForInsert(array $values)
- {
- if (empty($values)) {
- return [];
- }
-
- if (! is_array(array_first($values))) {
- $values = [$values];
- }
-
- $this->model->unguarded(function () use (&$values) {
- foreach ($values as $key => $rowValues) {
- $values[$key] = tap(
- $this->newModelInstance($rowValues),
- fn ($model) => $model->setUniqueIds()
- )->getAttributes();
- }
- });
-
- return $this->addTimestampsToUpsertValues($values);
- }
-
/**
* Create a collection of models from a raw query.
*
* @param string $query
* @param array $bindings
- * @return \Illuminate\Database\Eloquent\Collection
+ * @return \Illuminate\Database\Eloquent\Collection
*/
public function fromQuery($query, $bindings = [])
{
@@ -559,44 +250,27 @@ public function fromQuery($query, $bindings = [])
* Find a model by its primary key.
*
* @param mixed $id
- * @param array|string $columns
- * @return ($id is (\Illuminate\Contracts\Support\Arrayable|array) ? \Illuminate\Database\Eloquent\Collection : TModel|null)
+ * @param array $columns
+ * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null
*/
public function find($id, $columns = ['*'])
{
- if (is_array($id) || $id instanceof Arrayable) {
+ if (is_array($id)) {
return $this->findMany($id, $columns);
}
return $this->whereKey($id)->first($columns);
}
- /**
- * Find a sole model by its primary key.
- *
- * @param mixed $id
- * @param array|string $columns
- * @return TModel
- *
- * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
- * @throws \Illuminate\Database\MultipleRecordsFoundException
- */
- public function findSole($id, $columns = ['*'])
- {
- return $this->whereKey($id)->sole($columns);
- }
-
/**
* Find multiple models by their primary keys.
*
- * @param \Illuminate\Contracts\Support\Arrayable|array $ids
- * @param array|string $columns
- * @return \Illuminate\Database\Eloquent\Collection
+ * @param array $ids
+ * @param array $columns
+ * @return \Illuminate\Database\Eloquent\Collection
*/
public function findMany($ids, $columns = ['*'])
{
- $ids = $ids instanceof Arrayable ? $ids->toArray() : $ids;
-
if (empty($ids)) {
return $this->model->newCollection();
}
@@ -608,42 +282,34 @@ public function findMany($ids, $columns = ['*'])
* Find a model by its primary key or throw an exception.
*
* @param mixed $id
- * @param array|string $columns
- * @return ($id is (\Illuminate\Contracts\Support\Arrayable|array) ? \Illuminate\Database\Eloquent\Collection : TModel)
+ * @param array $columns
+ * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection
*
- * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
+ * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function findOrFail($id, $columns = ['*'])
{
$result = $this->find($id, $columns);
- $id = $id instanceof Arrayable ? $id->toArray() : $id;
-
if (is_array($id)) {
- if (count($result) !== count(array_unique($id))) {
- throw (new ModelNotFoundException)->setModel(
- get_class($this->model), array_diff($id, $result->modelKeys())
- );
+ if (count($result) == count(array_unique($id))) {
+ return $result;
}
-
+ } elseif (! is_null($result)) {
return $result;
}
- if (is_null($result)) {
- throw (new ModelNotFoundException)->setModel(
- get_class($this->model), $id
- );
- }
-
- return $result;
+ throw (new ModelNotFoundException)->setModel(
+ get_class($this->model), $id
+ );
}
/**
* Find a model by its primary key or return fresh model instance.
*
* @param mixed $id
- * @param array|string $columns
- * @return ($id is (\Illuminate\Contracts\Support\Arrayable|array) ? \Illuminate\Database\Eloquent\Collection : TModel)
+ * @param array $columns
+ * @return \Illuminate\Database\Eloquent\Model
*/
public function findOrNew($id, $columns = ['*'])
{
@@ -654,83 +320,38 @@ public function findOrNew($id, $columns = ['*'])
return $this->newModelInstance();
}
- /**
- * Find a model by its primary key or call a callback.
- *
- * @template TValue
- *
- * @param mixed $id
- * @param (\Closure(): TValue)|list|string $columns
- * @param (\Closure(): TValue)|null $callback
- * @return (
- * $id is (\Illuminate\Contracts\Support\Arrayable|array)
- * ? \Illuminate\Database\Eloquent\Collection
- * : TModel|TValue
- * )
- */
- public function findOr($id, $columns = ['*'], ?Closure $callback = null)
- {
- if ($columns instanceof Closure) {
- $callback = $columns;
-
- $columns = ['*'];
- }
-
- if (! is_null($model = $this->find($id, $columns))) {
- return $model;
- }
-
- return $callback();
- }
-
/**
* Get the first record matching the attributes or instantiate it.
*
* @param array $attributes
* @param array $values
- * @return TModel
+ * @return \Illuminate\Database\Eloquent\Model
*/
- public function firstOrNew(array $attributes = [], array $values = [])
+ public function firstOrNew(array $attributes, array $values = [])
{
if (! is_null($instance = $this->where($attributes)->first())) {
return $instance;
}
- return $this->newModelInstance(array_merge($attributes, $values));
+ return $this->newModelInstance($attributes + $values);
}
/**
- * Get the first record matching the attributes. If the record is not found, create it.
+ * Get the first record matching the attributes or create it.
*
* @param array $attributes
- * @param (\Closure(): array)|array $values
- * @return TModel
+ * @param array $values
+ * @return \Illuminate\Database\Eloquent\Model
*/
- public function firstOrCreate(array $attributes = [], Closure|array $values = [])
+ public function firstOrCreate(array $attributes, array $values = [])
{
- if (! is_null($instance = (clone $this)->where($attributes)->first())) {
+ if (! is_null($instance = $this->where($attributes)->first())) {
return $instance;
}
- return $this->createOrFirst($attributes, $values);
- }
-
- /**
- * Attempt to create the record. If a unique constraint violation occurs, attempt to find the matching record.
- *
- * @param array $attributes
- * @param (\Closure(): array)|array $values
- * @return TModel
- *
- * @throws \Illuminate\Database\UniqueConstraintViolationException
- */
- public function createOrFirst(array $attributes = [], Closure|array $values = [])
- {
- try {
- return $this->withSavepointIfNeeded(fn () => $this->create(array_merge($attributes, value($values))));
- } catch (UniqueConstraintViolationException $e) {
- return $this->useWritePdo()->where($attributes)->first() ?? throw $e;
- }
+ return tap($this->newModelInstance($attributes + $values), function ($instance) {
+ $instance->save();
+ });
}
/**
@@ -738,43 +359,22 @@ public function createOrFirst(array $attributes = [], Closure|array $values = []
*
* @param array $attributes
* @param array $values
- * @return TModel
+ * @return \Illuminate\Database\Eloquent\Model
*/
public function updateOrCreate(array $attributes, array $values = [])
{
- return tap($this->firstOrCreate($attributes, $values), function ($instance) use ($values) {
- if (! $instance->wasRecentlyCreated) {
- $instance->fill($values)->save();
- }
- });
- }
-
- /**
- * Create a record matching the attributes, or increment the existing record.
- *
- * @param array $attributes
- * @param string $column
- * @param int|float $default
- * @param int|float $step
- * @param array $extra
- * @return TModel
- */
- public function incrementOrCreate(array $attributes, string $column = 'count', $default = 1, $step = 1, array $extra = [])
- {
- return tap($this->firstOrCreate($attributes, [$column => $default]), function ($instance) use ($column, $step, $extra) {
- if (! $instance->wasRecentlyCreated) {
- $instance->increment($column, $step, $extra);
- }
+ return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
+ $instance->fill($values)->save();
});
}
/**
* Execute the query and get the first result or throw an exception.
*
- * @param array|string $columns
- * @return TModel
+ * @param array $columns
+ * @return \Illuminate\Database\Eloquent\Model|static
*
- * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
+ * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function firstOrFail($columns = ['*'])
{
@@ -788,13 +388,11 @@ public function firstOrFail($columns = ['*'])
/**
* Execute the query and get the first result or call a callback.
*
- * @template TValue
- *
- * @param (\Closure(): TValue)|list $columns
- * @param (\Closure(): TValue)|null $callback
- * @return TModel|TValue
+ * @param \Closure|array $columns
+ * @param \Closure|null $callback
+ * @return \Illuminate\Database\Eloquent\Model|static|mixed
*/
- public function firstOr($columns = ['*'], ?Closure $callback = null)
+ public function firstOr($columns = ['*'], Closure $callback = null)
{
if ($columns instanceof Closure) {
$callback = $columns;
@@ -806,78 +404,27 @@ public function firstOr($columns = ['*'], ?Closure $callback = null)
return $model;
}
- return $callback();
- }
-
- /**
- * Execute the query and get the first result if it's the sole matching record.
- *
- * @param array|string $columns
- * @return TModel
- *
- * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
- * @throws \Illuminate\Database\MultipleRecordsFoundException
- */
- public function sole($columns = ['*'])
- {
- try {
- return $this->baseSole($columns);
- } catch (RecordsNotFoundException) {
- throw (new ModelNotFoundException)->setModel(get_class($this->model));
- }
+ return call_user_func($callback);
}
/**
* Get a single column's value from the first result of a query.
*
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
+ * @param string $column
* @return mixed
*/
public function value($column)
{
if ($result = $this->first([$column])) {
- $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
-
- return $result->{Str::afterLast($column, '.')};
+ return $result->{$column};
}
}
- /**
- * Get a single column's value from the first result of a query if it's the sole matching record.
- *
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
- * @return mixed
- *
- * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
- * @throws \Illuminate\Database\MultipleRecordsFoundException
- */
- public function soleValue($column)
- {
- $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
-
- return $this->sole([$column])->{Str::afterLast($column, '.')};
- }
-
- /**
- * Get a single column's value from the first result of the query or throw an exception.
- *
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
- * @return mixed
- *
- * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
- */
- public function valueOrFail($column)
- {
- $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
-
- return $this->firstOrFail([$column])->{Str::afterLast($column, '.')};
- }
-
/**
* Execute the query as a "select" statement.
*
- * @param array|string $columns
- * @return \Illuminate\Database\Eloquent\Collection
+ * @param array $columns
+ * @return \Illuminate\Database\Eloquent\Collection|static[]
*/
public function get($columns = ['*'])
{
@@ -890,16 +437,14 @@ public function get($columns = ['*'])
$models = $builder->eagerLoadRelations($models);
}
- return $this->applyAfterQueryCallbacks(
- $builder->getModel()->newCollection($models)
- );
+ return $builder->getModel()->newCollection($models);
}
/**
* Get the hydrated models without eager loading.
*
- * @param array|string $columns
- * @return array
+ * @param array $columns
+ * @return \Illuminate\Database\Eloquent\Model[]
*/
public function getModels($columns = ['*'])
{
@@ -911,8 +456,8 @@ public function getModels($columns = ['*'])
/**
* Eager load the relationships for the models.
*
- * @param array $models
- * @return array
+ * @param array $models
+ * @return array
*/
public function eagerLoadRelations(array $models)
{
@@ -920,7 +465,7 @@ public function eagerLoadRelations(array $models)
// For nested eager loads we'll skip loading them here and they will be set as an
// eager load on the query to retrieve the relation so that they will be eager
// loaded on that query, because that is where they get hydrated as models.
- if (! str_contains($name, '.')) {
+ if (strpos($name, '.') === false) {
$models = $this->eagerLoadRelation($models, $name, $constraints);
}
}
@@ -960,7 +505,7 @@ protected function eagerLoadRelation(array $models, $name, Closure $constraints)
* Get the relation instance for the given relation name.
*
* @param string $name
- * @return \Illuminate\Database\Eloquent\Relations\Relation<\Illuminate\Database\Eloquent\Model, TModel, *>
+ * @return \Illuminate\Database\Eloquent\Relations\Relation
*/
public function getRelation($name)
{
@@ -969,8 +514,8 @@ public function getRelation($name)
// and error prone. We don't want constraints because we add eager ones.
$relation = Relation::noConstraints(function () use ($name) {
try {
- return $this->getModel()->newInstance()->$name();
- } catch (BadMethodCallException) {
+ return $this->getModel()->{$name}();
+ } catch (BadMethodCallException $e) {
throw RelationNotFoundException::make($this->getModel(), $name);
}
});
@@ -1018,49 +563,63 @@ protected function relationsNestedUnder($relation)
*/
protected function isNestedUnder($relation, $name)
{
- return str_contains($name, '.') && str_starts_with($name, $relation.'.');
+ return Str::contains($name, '.') && Str::startsWith($name, $relation.'.');
}
/**
- * Register a closure to be invoked after the query is executed.
+ * Get a generator for the given query.
*
- * @param \Closure $callback
- * @return $this
+ * @return \Generator
*/
- public function afterQuery(Closure $callback)
+ public function cursor()
{
- $this->afterQueryCallbacks[] = $callback;
-
- return $this;
+ foreach ($this->applyScopes()->query->cursor() as $record) {
+ yield $this->model->newFromBuilder($record);
+ }
}
/**
- * Invoke the "after query" modification callbacks.
+ * Chunk the results of a query by comparing numeric IDs.
*
- * @param mixed $result
- * @return mixed
+ * @param int $count
+ * @param callable $callback
+ * @param string $column
+ * @param string|null $alias
+ * @return bool
*/
- public function applyAfterQueryCallbacks($result)
+ public function chunkById($count, callable $callback, $column = null, $alias = null)
{
- foreach ($this->afterQueryCallbacks as $afterQueryCallback) {
- $result = $afterQueryCallback($result) ?: $result;
- }
+ $column = is_null($column) ? $this->getModel()->getKeyName() : $column;
- return $result;
- }
+ $alias = is_null($alias) ? $column : $alias;
- /**
- * Get a lazy collection for the given query.
- *
- * @return \Illuminate\Support\LazyCollection
- */
- public function cursor()
- {
- return $this->applyScopes()->query->cursor()->map(function ($record) {
- $model = $this->newModelInstance()->newFromBuilder($record);
+ $lastId = 0;
+
+ do {
+ $clone = clone $this;
- return $this->applyAfterQueryCallbacks($this->newModelInstance()->newCollection([$model]))->first();
- })->reject(fn ($model) => is_null($model));
+ // 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};
+ } while ($countResults == $count);
+
+ return true;
}
/**
@@ -1076,61 +635,52 @@ protected function enforceOrderBy()
}
/**
- * Get a collection with the values of a given column.
+ * Get an array with the values of a given column.
*
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
+ * @param string $column
* @param string|null $key
- * @return \Illuminate\Support\Collection
+ * @return \Illuminate\Support\Collection
*/
public function pluck($column, $key = null)
{
$results = $this->toBase()->pluck($column, $key);
- $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
-
- $column = Str::after($column, "{$this->model->getTable()}.");
-
// If the model has a mutator for the requested column, we will spin through
// the results and mutate the values so that the mutated version of these
// columns are returned as you would expect from these Eloquent models.
- if (! $this->model->hasAnyGetMutator($column) &&
+ if (! $this->model->hasGetMutator($column) &&
! $this->model->hasCast($column) &&
! in_array($column, $this->model->getDates())) {
- return $this->applyAfterQueryCallbacks($results);
+ return $results;
}
- return $this->applyAfterQueryCallbacks(
- $results->map(function ($value) use ($column) {
- return $this->model->newFromBuilder([$column => $value])->{$column};
- })
- );
+ return $results->map(function ($value) use ($column) {
+ return $this->model->newFromBuilder([$column => $value])->{$column};
+ });
}
/**
* Paginate the given query.
*
- * @param int|null|\Closure $perPage
- * @param array|string $columns
+ * @param int $perPage
+ * @param array $columns
* @param string $pageName
* @param int|null $page
- * @param \Closure|int|null $total
- * @return \Illuminate\Pagination\LengthAwarePaginator
+ * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*
* @throws \InvalidArgumentException
*/
- public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null, $total = null)
+ public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
{
$page = $page ?: Paginator::resolveCurrentPage($pageName);
- $total = value($total) ?? $this->toBase()->getCountForPagination();
-
- $perPage = value($perPage, $total) ?: $this->model->getPerPage();
+ $perPage = $perPage ?: $this->model->getPerPage();
- $results = $total
- ? $this->forPage($page, $perPage)->get($columns)
- : $this->model->newCollection();
+ $results = ($total = $this->toBase()->getCountForPagination())
+ ? $this->forPage($page, $perPage)->get($columns)
+ : $this->model->newCollection();
- return $this->paginator($results, $total, $perPage, $page, [
+ return new LengthAwarePaginator($results, $total, $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => $pageName,
]);
@@ -1139,8 +689,8 @@ public function paginate($perPage = null, $columns = ['*'], $pageName = 'page',
/**
* Paginate the given query into a simple paginator.
*
- * @param int|null $perPage
- * @param array|string $columns
+ * @param int $perPage
+ * @param array $columns
* @param string $pageName
* @param int|null $page
* @return \Illuminate\Contracts\Pagination\Paginator
@@ -1154,69 +704,19 @@ public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'p
// Next we will set the limit and offset for this query so that when we get the
// results we get the proper section of results. Then, we'll create the full
// paginator instances for these results with the given page and per page.
- $this->offset(($page - 1) * $perPage)->limit($perPage + 1);
+ $this->skip(($page - 1) * $perPage)->take($perPage + 1);
- return $this->simplePaginator($this->get($columns), $perPage, $page, [
+ return new Paginator($this->get($columns), $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => $pageName,
]);
}
- /**
- * Paginate the given query into a cursor paginator.
- *
- * @param int|null $perPage
- * @param array|string $columns
- * @param string $cursorName
- * @param \Illuminate\Pagination\Cursor|string|null $cursor
- * @return \Illuminate\Contracts\Pagination\CursorPaginator
- */
- public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
- {
- $perPage = $perPage ?: $this->model->getPerPage();
-
- return $this->paginateUsingCursor($perPage, $columns, $cursorName, $cursor);
- }
-
- /**
- * Ensure the proper order by required for cursor pagination.
- *
- * @param bool $shouldReverse
- * @return \Illuminate\Support\Collection
- */
- protected function ensureOrderForCursorPagination($shouldReverse = false)
- {
- if (empty($this->query->orders) && empty($this->query->unionOrders)) {
- $this->enforceOrderBy();
- }
-
- $reverseDirection = function ($order) {
- if (! isset($order['direction'])) {
- return $order;
- }
-
- $order['direction'] = $order['direction'] === 'asc' ? 'desc' : 'asc';
-
- return $order;
- };
-
- if ($shouldReverse) {
- $this->query->orders = (new BaseCollection($this->query->orders))->map($reverseDirection)->toArray();
- $this->query->unionOrders = (new BaseCollection($this->query->unionOrders))->map($reverseDirection)->toArray();
- }
-
- $orders = ! empty($this->query->unionOrders) ? $this->query->unionOrders : $this->query->orders;
-
- return (new BaseCollection($orders))
- ->filter(fn ($order) => Arr::has($order, 'direction'))
- ->values();
- }
-
/**
* Save a new model and return the instance.
*
* @param array $attributes
- * @return TModel
+ * @return \Illuminate\Database\Eloquent\Model
*/
public function create(array $attributes = [])
{
@@ -1225,22 +725,11 @@ public function create(array $attributes = [])
});
}
- /**
- * Save a new model and return the instance without raising model events.
- *
- * @param array $attributes
- * @return TModel
- */
- public function createQuietly(array $attributes = [])
- {
- return Model::withoutEvents(fn () => $this->create($attributes));
- }
-
/**
* Save a new model and return the instance. Allow mass-assignment.
*
* @param array $attributes
- * @return TModel
+ * @return \Illuminate\Database\Eloquent\Model
*/
public function forceCreate(array $attributes)
{
@@ -1250,18 +739,7 @@ public function forceCreate(array $attributes)
}
/**
- * Save a new model instance with mass assignment without raising model events.
- *
- * @param array $attributes
- * @return TModel
- */
- public function forceCreateQuietly(array $attributes = [])
- {
- return Model::withoutEvents(fn () => $this->forceCreate($attributes));
- }
-
- /**
- * Update records in the database.
+ * Update a record in the database.
*
* @param array $values
* @return int
@@ -1271,63 +749,11 @@ public function update(array $values)
return $this->toBase()->update($this->addUpdatedAtColumn($values));
}
- /**
- * Insert new records or update the existing ones.
- *
- * @param array $values
- * @param array|string $uniqueBy
- * @param array|null $update
- * @return int
- */
- public function upsert(array $values, $uniqueBy, $update = null)
- {
- if (empty($values)) {
- return 0;
- }
-
- if (! is_array(array_first($values))) {
- $values = [$values];
- }
-
- if (is_null($update)) {
- $update = array_keys(array_first($values));
- }
-
- return $this->toBase()->upsert(
- $this->addTimestampsToUpsertValues($this->addUniqueIdsToUpsertValues($values)),
- $uniqueBy,
- $this->addUpdatedAtToUpsertColumns($update)
- );
- }
-
- /**
- * Update the column's update timestamp.
- *
- * @param string|null $column
- * @return int|false
- */
- public function touch($column = null)
- {
- $time = $this->model->freshTimestamp();
-
- if ($column) {
- return $this->toBase()->update([$column => $time]);
- }
-
- $column = $this->model->getUpdatedAtColumn();
-
- if (! $this->model->usesTimestamps() || is_null($column)) {
- return false;
- }
-
- return $this->toBase()->update([$column => $time]);
- }
-
/**
* Increment a column's value by a given amount.
*
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
- * @param float|int $amount
+ * @param string $column
+ * @param int $amount
* @param array $extra
* @return int
*/
@@ -1339,138 +765,40 @@ public function increment($column, $amount = 1, array $extra = [])
}
/**
- * Decrement a column's value by a given amount.
- *
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
- * @param float|int $amount
- * @param array $extra
- * @return int
- */
- public function decrement($column, $amount = 1, array $extra = [])
- {
- return $this->toBase()->decrement(
- $column, $amount, $this->addUpdatedAtColumn($extra)
- );
- }
-
- /**
- * Add the "updated at" column to an array of values.
- *
- * @param array $values
- * @return array
- */
- protected function addUpdatedAtColumn(array $values)
- {
- if (! $this->model->usesTimestamps() ||
- is_null($this->model->getUpdatedAtColumn())) {
- return $values;
- }
-
- $column = $this->model->getUpdatedAtColumn();
-
- if (! array_key_exists($column, $values)) {
- $timestamp = $this->model->freshTimestampString();
-
- if (
- $this->model->hasSetMutator($column)
- || $this->model->hasAttributeSetMutator($column)
- || $this->model->hasCast($column)
- ) {
- $timestamp = $this->model->newInstance()
- ->forceFill([$column => $timestamp])
- ->getAttributes()[$column] ?? $timestamp;
- }
-
- $values = array_merge([$column => $timestamp], $values);
- }
-
- $segments = preg_split('/\s+as\s+/i', $this->query->from);
-
- $qualifiedColumn = array_last($segments).'.'.$column;
-
- $values[$qualifiedColumn] = Arr::get($values, $qualifiedColumn, $values[$column]);
-
- unset($values[$column]);
-
- return $values;
- }
-
- /**
- * Add unique IDs to the inserted values.
- *
- * @param array $values
- * @return array
- */
- protected function addUniqueIdsToUpsertValues(array $values)
- {
- if (! $this->model->usesUniqueIds()) {
- return $values;
- }
-
- foreach ($this->model->uniqueIds() as $uniqueIdAttribute) {
- foreach ($values as &$row) {
- if (! array_key_exists($uniqueIdAttribute, $row)) {
- $row = array_merge([$uniqueIdAttribute => $this->model->newUniqueId()], $row);
- }
- }
- }
-
- return $values;
- }
-
- /**
- * Add timestamps to the inserted values.
- *
- * @param array $values
- * @return array
- */
- protected function addTimestampsToUpsertValues(array $values)
- {
- if (! $this->model->usesTimestamps()) {
- return $values;
- }
-
- $timestamp = $this->model->freshTimestampString();
-
- $columns = array_filter([
- $this->model->getCreatedAtColumn(),
- $this->model->getUpdatedAtColumn(),
- ]);
-
- foreach ($columns as $column) {
- foreach ($values as &$row) {
- $row = array_merge([$column => $timestamp], $row);
- }
- }
-
- return $values;
+ * Decrement a column's value by a given amount.
+ *
+ * @param string $column
+ * @param int $amount
+ * @param array $extra
+ * @return int
+ */
+ public function decrement($column, $amount = 1, array $extra = [])
+ {
+ return $this->toBase()->decrement(
+ $column, $amount, $this->addUpdatedAtColumn($extra)
+ );
}
/**
- * Add the "updated at" column to the updated columns.
+ * Add the "updated at" column to an array of values.
*
- * @param array $update
+ * @param array $values
* @return array
*/
- protected function addUpdatedAtToUpsertColumns(array $update)
+ protected function addUpdatedAtColumn(array $values)
{
if (! $this->model->usesTimestamps()) {
- return $update;
- }
-
- $column = $this->model->getUpdatedAtColumn();
-
- if (! is_null($column) &&
- ! array_key_exists($column, $update) &&
- ! in_array($column, $update)) {
- $update[] = $column;
+ return $values;
}
- return $update;
+ return Arr::add(
+ $values, $this->model->getUpdatedAtColumn(),
+ $this->model->freshTimestampString()
+ );
}
/**
- * Delete records from the database.
+ * Delete a record from the database.
*
* @return mixed
*/
@@ -1506,40 +834,30 @@ public function onDelete(Closure $callback)
$this->onDelete = $callback;
}
- /**
- * Determine if the given model has a scope.
- *
- * @param string $scope
- * @return bool
- */
- public function hasNamedScope($scope)
- {
- return $this->model && $this->model->hasNamedScope($scope);
- }
-
/**
* Call the given local model scopes.
*
- * @param array|string $scopes
- * @return static|mixed
+ * @param array $scopes
+ * @return mixed
*/
- public function scopes($scopes)
+ public function scopes(array $scopes)
{
$builder = $this;
- foreach (Arr::wrap($scopes) as $scope => $parameters) {
+ foreach ($scopes as $scope => $parameters) {
// If the scope key is an integer, then the scope was passed as the value and
// the parameter list is empty, so we will format the scope name and these
// parameters here. Then, we'll be ready to call the scope on the model.
if (is_int($scope)) {
- [$scope, $parameters] = [$parameters, []];
+ list($scope, $parameters) = [$parameters, []];
}
// Next we'll pass the scope callback to the callScope method which will take
- // care of grouping the "wheres" properly so the logical order doesn't get
+ // care of groping the "wheres" correctly so the logical order doesn't get
// messed up when adding scopes. Then we'll return back out the builder.
- $builder = $builder->callNamedScope(
- $scope, Arr::wrap($parameters)
+ $builder = $builder->callScope(
+ [$this->model, 'scope'.ucfirst($scope)],
+ (array) $parameters
);
}
@@ -1549,7 +867,7 @@ public function scopes($scopes)
/**
* Apply the scopes to the Eloquent builder instance and return it.
*
- * @return static
+ * @return \Illuminate\Database\Eloquent\Builder|static
*/
public function applyScopes()
{
@@ -1559,12 +877,8 @@ public function applyScopes()
$builder = clone $this;
- foreach ($this->scopes as $identifier => $scope) {
- if (! isset($builder->scopes[$identifier])) {
- continue;
- }
-
- $builder->callScope(function (self $builder) use ($scope) {
+ foreach ($this->scopes as $scope) {
+ $builder->callScope(function (Builder $builder) use ($scope) {
// If the scope is a Closure we will just go ahead and call the scope with the
// builder instance. The "callScope" method will properly group the clauses
// that are added to this query so "where" clauses maintain proper logic.
@@ -1591,7 +905,7 @@ public function applyScopes()
* @param array $parameters
* @return mixed
*/
- protected function callScope(callable $scope, array $parameters = [])
+ protected function callScope(callable $scope, $parameters = [])
{
array_unshift($parameters, $this);
@@ -1600,33 +914,17 @@ protected function callScope(callable $scope, array $parameters = [])
// We will keep track of how many wheres are on the query before running the
// scope so that we can properly group the added scope constraints in the
// query as their own isolated nested where statement and avoid issues.
- $originalWhereCount = is_null($query->wheres)
- ? 0
- : count($query->wheres);
+ $originalWhereCount = count($query->wheres);
- $result = $scope(...$parameters) ?? $this;
+ $result = $scope(...array_values($parameters)) ?? $this;
- if (count((array) $query->wheres) > $originalWhereCount) {
+ if (count($query->wheres) > $originalWhereCount) {
$this->addNewWheresWithinGroup($query, $originalWhereCount);
}
return $result;
}
- /**
- * Apply the given named scope on the current builder instance.
- *
- * @param string $scope
- * @param array $parameters
- * @return mixed
- */
- protected function callNamedScope($scope, array $parameters = [])
- {
- return $this->callScope(function (...$parameters) use ($scope) {
- return $this->model->callNamedScope($scope, $parameters);
- }, $parameters);
- }
-
/**
* Nest where conditions by slicing them at the given where count.
*
@@ -1661,14 +959,14 @@ protected function addNewWheresWithinGroup(QueryBuilder $query, $originalWhereCo
*/
protected function groupWhereSliceForScope(QueryBuilder $query, $whereSlice)
{
- $whereBooleans = (new BaseCollection($whereSlice))->pluck('boolean');
+ $whereBooleans = collect($whereSlice)->pluck('boolean');
// Here we'll check if the given subset of where clauses contains any "or"
// booleans and in this case create a nested where expression. That way
// we don't add any unnecessary nesting thus keeping the query clean.
- if ($whereBooleans->contains(fn ($logicalOperator) => str_contains($logicalOperator, 'or'))) {
+ if ($whereBooleans->contains('or')) {
$query->wheres[] = $this->createNestedWhere(
- $whereSlice, str_replace(' not', '', $whereBooleans->first())
+ $whereSlice, $whereBooleans->first()
);
} else {
$query->wheres = array_merge($query->wheres, $whereSlice);
@@ -1692,19 +990,14 @@ protected function createNestedWhere($whereSlice, $boolean = 'and')
}
/**
- * Specify relationships that should be eager loaded.
+ * Set the relationships that should be eager loaded.
*
- * @param array): mixed)|string>|string $relations
- * @param (\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string|null $callback
+ * @param mixed $relations
* @return $this
*/
- public function with($relations, $callback = null)
+ public function with($relations)
{
- if ($callback instanceof Closure) {
- $eagerLoad = $this->parseWithRelations([$relations => $callback]);
- } else {
- $eagerLoad = $this->parseWithRelations(is_string($relations) ? func_get_args() : $relations);
- }
+ $eagerLoad = $this->parseWithRelations(is_string($relations) ? func_get_args() : $relations);
$this->eagerLoad = array_merge($this->eagerLoad, $eagerLoad);
@@ -1726,29 +1019,14 @@ public function without($relations)
return $this;
}
- /**
- * Set the relationships that should be eager loaded while removing any previously added eager loading specifications.
- *
- * @param array): mixed)|string>|string $relations
- * @return $this
- */
- public function withOnly($relations)
- {
- $this->eagerLoad = [];
-
- return $this->with($relations);
- }
-
/**
* Create a new instance of the model being queried.
*
* @param array $attributes
- * @return TModel
+ * @return \Illuminate\Database\Eloquent\Model
*/
public function newModelInstance($attributes = [])
{
- $attributes = array_merge($this->pendingAttributes, $attributes);
-
return $this->model->newInstance($attributes)->setConnection(
$this->query->getConnection()->getName()
);
@@ -1762,16 +1040,25 @@ public function newModelInstance($attributes = [])
*/
protected function parseWithRelations(array $relations)
{
- if ($relations === []) {
- return [];
- }
-
$results = [];
- foreach ($this->prepareNestedWithRelationships($relations) as $name => $constraints) {
- // We need to separate out any nested includes, which allows the developers
+ foreach ($relations as $name => $constraints) {
+ // If the "relation" value is actually a numeric key, we can assume that no
+ // constraints have been specified for the eager load and we'll just put
+ // an empty Closure with the loader so that we can treat all the same.
+ if (is_numeric($name)) {
+ $name = $constraints;
+
+ list($name, $constraints) = Str::contains($name, ':')
+ ? $this->createSelectWithConstraint($name)
+ : [$name, function () {
+ //
+ }];
+ }
+
+ // We need to separate out any nested includes. Which allows the developers
// to load deep relationships using "dots" without stating each level of
- // the relationship with its own key in the array of eager-load names.
+ // the relationship with its own key in the array of eager load names.
$results = $this->addNestedWiths($name, $results);
$results[$name] = $constraints;
@@ -1780,91 +1067,6 @@ protected function parseWithRelations(array $relations)
return $results;
}
- /**
- * Prepare nested with relationships.
- *
- * @param array $relations
- * @param string $prefix
- * @return array
- */
- protected function prepareNestedWithRelationships($relations, $prefix = '')
- {
- $preparedRelationships = [];
-
- if ($prefix !== '') {
- $prefix .= '.';
- }
-
- // If any of the relationships are formatted with the [$attribute => array()]
- // syntax, we shall loop over the nested relations and prepend each key of
- // this array while flattening into the traditional dot notation format.
- foreach ($relations as $key => $value) {
- if (! is_string($key) || ! is_array($value)) {
- continue;
- }
-
- [$attribute, $attributeSelectConstraint] = $this->parseNameAndAttributeSelectionConstraint($key);
-
- $preparedRelationships = array_merge(
- $preparedRelationships,
- ["{$prefix}{$attribute}" => $attributeSelectConstraint],
- $this->prepareNestedWithRelationships($value, "{$prefix}{$attribute}"),
- );
-
- unset($relations[$key]);
- }
-
- // We now know that the remaining relationships are in a dot notation format
- // and may be a string or Closure. We'll loop over them and ensure all of
- // the present Closures are merged + strings are made into constraints.
- foreach ($relations as $key => $value) {
- if (is_numeric($key) && is_string($value)) {
- [$key, $value] = $this->parseNameAndAttributeSelectionConstraint($value);
- }
-
- $preparedRelationships[$prefix.$key] = $this->combineConstraints([
- $value,
- $preparedRelationships[$prefix.$key] ?? static function () {
- //
- },
- ]);
- }
-
- return $preparedRelationships;
- }
-
- /**
- * Combine an array of constraints into a single constraint.
- *
- * @param array $constraints
- * @return \Closure
- */
- protected function combineConstraints(array $constraints)
- {
- return function ($builder) use ($constraints) {
- foreach ($constraints as $constraint) {
- $builder = $constraint($builder) ?? $builder;
- }
-
- return $builder;
- };
- }
-
- /**
- * Parse the attribute select constraints from the name.
- *
- * @param string $name
- * @return array
- */
- protected function parseNameAndAttributeSelectionConstraint($name)
- {
- return str_contains($name, ':')
- ? $this->createSelectWithConstraint($name)
- : [$name, static function () {
- //
- }];
- }
-
/**
* Create a constraint to select the given columns for the relation.
*
@@ -1873,12 +1075,8 @@ protected function parseNameAndAttributeSelectionConstraint($name)
*/
protected function createSelectWithConstraint($name)
{
- return [explode(':', $name)[0], static function ($query) use ($name) {
- $query->select(array_map(static function ($column) use ($query) {
- return $query instanceof BelongsToMany
- ? $query->getRelated()->qualifyColumn($column)
- : $column;
- }, explode(',', explode(':', $name)[1])));
+ return [explode(':', $name)[0], function ($query) use ($name) {
+ $query->select(explode(',', explode(':', $name)[1]));
}];
}
@@ -1900,7 +1098,7 @@ protected function addNestedWiths($name, $results)
$progress[] = $segment;
if (! isset($results[$last = implode('.', $progress)])) {
- $results[$last] = static function () {
+ $results[$last] = function () {
//
};
}
@@ -1909,73 +1107,6 @@ protected function addNestedWiths($name, $results)
return $results;
}
- /**
- * Specify attributes that should be added to any new models created by this builder.
- *
- * The given key / value pairs will also be added as where conditions to the query.
- *
- * @param \Illuminate\Contracts\Database\Query\Expression|array|string $attributes
- * @param mixed $value
- * @param bool $asConditions
- * @return $this
- */
- public function withAttributes(Expression|array|string $attributes, $value = null, $asConditions = true)
- {
- if (! is_array($attributes)) {
- $attributes = [$attributes => $value];
- }
-
- if ($asConditions) {
- foreach ($attributes as $column => $value) {
- $this->where($this->qualifyColumn($column), $value);
- }
- }
-
- $this->pendingAttributes = array_merge($this->pendingAttributes, $attributes);
-
- return $this;
- }
-
- /**
- * Apply query-time casts to the model instance.
- *
- * @param array $casts
- * @return $this
- */
- public function withCasts($casts)
- {
- $this->model->mergeCasts($casts);
-
- return $this;
- }
-
- /**
- * Execute the given Closure within a transaction savepoint if needed.
- *
- * @template TModelValue
- *
- * @param \Closure(): TModelValue $scope
- * @return TModelValue
- */
- public function withSavepointIfNeeded(Closure $scope): mixed
- {
- return $this->getQuery()->getConnection()->transactionLevel() > 0
- ? $this->getQuery()->getConnection()->transaction($scope)
- : $scope();
- }
-
- /**
- * Get the Eloquent builder instances that are used in the union of the query.
- *
- * @return \Illuminate\Support\Collection
- */
- protected function getUnionBuilders()
- {
- return isset($this->query->unions)
- ? (new BaseCollection($this->query->unions))->pluck('query')
- : new BaseCollection;
- }
-
/**
* Get the underlying query builder instance.
*
@@ -2032,63 +1163,10 @@ public function setEagerLoads(array $eagerLoad)
return $this;
}
- /**
- * Indicate that the given relationships should not be eagerly loaded.
- *
- * @param array $relations
- * @return $this
- */
- public function withoutEagerLoad(array $relations)
- {
- $relations = array_diff(array_keys($this->model->getRelations()), $relations);
-
- return $this->with($relations);
- }
-
- /**
- * Flush the relationships being eagerly loaded.
- *
- * @return $this
- */
- public function withoutEagerLoads()
- {
- return $this->setEagerLoads([]);
- }
-
- /**
- * Get the "limit" value from the query or null if it's not set.
- *
- * @return mixed
- */
- public function getLimit()
- {
- return $this->query->getLimit();
- }
-
- /**
- * Get the "offset" value from the query or null if it's not set.
- *
- * @return mixed
- */
- public function getOffset()
- {
- return $this->query->getOffset();
- }
-
- /**
- * Get the default key name of the table.
- *
- * @return string
- */
- protected function defaultKeyName()
- {
- return $this->getModel()->getKeyName();
- }
-
/**
* Get the model instance being queried.
*
- * @return TModel
+ * @return \Illuminate\Database\Eloquent\Model
*/
public function getModel()
{
@@ -2098,10 +1176,8 @@ public function getModel()
/**
* Set a model instance for the model being queried.
*
- * @template TModelNew of \Illuminate\Database\Eloquent\Model
- *
- * @param TModelNew $model
- * @return static
+ * @param \Illuminate\Database\Eloquent\Model $model
+ * @return $this
*/
public function setModel(Model $model)
{
@@ -2112,30 +1188,6 @@ public function setModel(Model $model)
return $this;
}
- /**
- * Qualify the given column name by the model's table.
- *
- * @param string|\Illuminate\Contracts\Database\Query\Expression $column
- * @return string
- */
- public function qualifyColumn($column)
- {
- $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
-
- return $this->model->qualifyColumn($column);
- }
-
- /**
- * Qualify the given columns with the model's table.
- *
- * @param array|\Illuminate\Contracts\Database\Query\Expression $columns
- * @return array
- */
- public function qualifyColumns($columns)
- {
- return $this->model->qualifyColumns($columns);
- }
-
/**
* Get the given macro by name.
*
@@ -2147,60 +1199,6 @@ public function getMacro($name)
return Arr::get($this->localMacros, $name);
}
- /**
- * Checks if a macro is registered.
- *
- * @param string $name
- * @return bool
- */
- public function hasMacro($name)
- {
- return isset($this->localMacros[$name]);
- }
-
- /**
- * Get the given global macro by name.
- *
- * @param string $name
- * @return \Closure
- */
- public static function getGlobalMacro($name)
- {
- return Arr::get(static::$macros, $name);
- }
-
- /**
- * Checks if a global macro is registered.
- *
- * @param string $name
- * @return bool
- */
- public static function hasGlobalMacro($name)
- {
- return isset(static::$macros[$name]);
- }
-
- /**
- * Dynamically access builder proxies.
- *
- * @param string $key
- * @return mixed
- *
- * @throws \Exception
- */
- public function __get($key)
- {
- if (in_array($key, ['orWhere', 'whereNot', 'orWhereNot'])) {
- return new HigherOrderBuilderProxy($this, $key);
- }
-
- if (in_array($key, $this->propertyPassthru)) {
- return $this->toBase()->{$key};
- }
-
- throw new Exception("Property [{$key}] does not exist on the Eloquent builder instance.");
- }
-
/**
* Dynamically handle calls into the query instance.
*
@@ -2216,31 +1214,29 @@ public function __call($method, $parameters)
return;
}
- if ($this->hasMacro($method)) {
+ if (isset($this->localMacros[$method])) {
array_unshift($parameters, $this);
return $this->localMacros[$method](...$parameters);
}
- if (static::hasGlobalMacro($method)) {
- $callable = static::$macros[$method];
-
- if ($callable instanceof Closure) {
- $callable = $callable->bindTo($this, static::class);
- }
+ if (isset(static::$macros[$method]) and static::$macros[$method] instanceof Closure) {
+ return call_user_func_array(static::$macros[$method]->bindTo($this, static::class), $parameters);
+ }
- return $callable(...$parameters);
+ if (isset(static::$macros[$method])) {
+ return call_user_func_array(static::$macros[$method]->bindTo($this, static::class), $parameters);
}
- if ($this->hasNamedScope($method)) {
- return $this->callNamedScope($method, $parameters);
+ if (method_exists($this->model, $scope = 'scope'.ucfirst($method))) {
+ return $this->callScope([$this->model, $scope], $parameters);
}
- if (in_array(strtolower($method), $this->passthru)) {
+ if (in_array($method, $this->passthru)) {
return $this->toBase()->{$method}(...$parameters);
}
- $this->forwardCallTo($this->query, $method, $parameters);
+ $this->query->{$method}(...$parameters);
return $this;
}
@@ -2262,64 +1258,15 @@ public static function __callStatic($method, $parameters)
return;
}
- if ($method === 'mixin') {
- return static::registerMixin($parameters[0], $parameters[1] ?? true);
+ if (! isset(static::$macros[$method])) {
+ throw new BadMethodCallException("Method {$method} does not exist.");
}
- if (! static::hasGlobalMacro($method)) {
- static::throwBadMethodCallException($method);
+ if (static::$macros[$method] instanceof Closure) {
+ return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
}
- $callable = static::$macros[$method];
-
- if ($callable instanceof Closure) {
- $callable = $callable->bindTo(null, static::class);
- }
-
- return $callable(...$parameters);
- }
-
- /**
- * Register the given mixin with the builder.
- *
- * @param string $mixin
- * @param bool $replace
- * @return void
- */
- protected static function registerMixin($mixin, $replace)
- {
- $methods = (new ReflectionClass($mixin))->getMethods(
- ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
- );
-
- foreach ($methods as $method) {
- if ($replace || ! static::hasGlobalMacro($method->name)) {
- static::macro($method->name, $method->invoke($mixin));
- }
- }
- }
-
- /**
- * Clone the Eloquent query builder.
- *
- * @return static
- */
- public function clone()
- {
- return clone $this;
- }
-
- /**
- * Register a closure to be invoked on a clone.
- *
- * @param \Closure $callback
- * @return $this
- */
- public function onClone(Closure $callback)
- {
- $this->onCloneCallbacks[] = $callback;
-
- return $this;
+ return call_user_func_array(static::$macros[$method], $parameters);
}
/**
@@ -2330,9 +1277,5 @@ public function onClone(Closure $callback)
public function __clone()
{
$this->query = clone $this->query;
-
- foreach ($this->onCloneCallbacks as $onCloneCallback) {
- $onCloneCallback($this);
- }
}
}
diff --git a/Eloquent/Casts/ArrayObject.php b/Eloquent/Casts/ArrayObject.php
deleted file mode 100644
index 563545dacb..0000000000
--- a/Eloquent/Casts/ArrayObject.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- */
-class ArrayObject extends BaseArrayObject implements Arrayable, JsonSerializable
-{
- /**
- * Get a collection containing the underlying array.
- *
- * @return \Illuminate\Support\Collection
- */
- public function collect()
- {
- return new Collection($this->getArrayCopy());
- }
-
- /**
- * Get the instance as an array.
- *
- * @return array
- */
- public function toArray()
- {
- return $this->getArrayCopy();
- }
-
- /**
- * Get the array that should be JSON serialized.
- *
- * @return array
- */
- public function jsonSerialize(): array
- {
- return $this->getArrayCopy();
- }
-}
diff --git a/Eloquent/Casts/AsArrayObject.php b/Eloquent/Casts/AsArrayObject.php
deleted file mode 100644
index 5ee80d0bb4..0000000000
--- a/Eloquent/Casts/AsArrayObject.php
+++ /dev/null
@@ -1,42 +0,0 @@
-, iterable>
- */
- public static function castUsing(array $arguments)
- {
- return new class implements CastsAttributes
- {
- public function get($model, $key, $value, $attributes)
- {
- if (! isset($attributes[$key])) {
- return;
- }
-
- $data = Json::decode($attributes[$key]);
-
- return is_array($data) ? new ArrayObject($data, ArrayObject::ARRAY_AS_PROPS) : null;
- }
-
- public function set($model, $key, $value, $attributes)
- {
- return [$key => Json::encode($value)];
- }
-
- public function serialize($model, string $key, $value, array $attributes)
- {
- return $value->getArrayCopy();
- }
- };
- }
-}
diff --git a/Eloquent/Casts/AsBinary.php b/Eloquent/Casts/AsBinary.php
deleted file mode 100644
index 4f63154777..0000000000
--- a/Eloquent/Casts/AsBinary.php
+++ /dev/null
@@ -1,75 +0,0 @@
-format = $this->arguments[0]
- ?? throw new InvalidArgumentException('The binary codec format is required.');
-
- if (! in_array($this->format, BinaryCodec::formats(), true)) {
- throw new InvalidArgumentException(sprintf(
- 'Unsupported binary codec format [%s]. Allowed formats are: %s.',
- $this->format,
- implode(', ', BinaryCodec::formats()),
- ));
- }
- }
-
- public function get($model, $key, $value, $attributes)
- {
- return BinaryCodec::decode($attributes[$key] ?? null, $this->format);
- }
-
- public function set($model, $key, $value, $attributes)
- {
- return [$key => BinaryCodec::encode($value, $this->format)];
- }
- };
- }
-
- /**
- * Encode / decode values as binary UUIDs.
- */
- public static function uuid(): string
- {
- return self::class.':uuid';
- }
-
- /**
- * Encode / decode values as binary ULIDs.
- */
- public static function ulid(): string
- {
- return self::class.':ulid';
- }
-
- /**
- * Encode / decode values using the given format.
- */
- public static function of(string $format): string
- {
- return self::class.':'.$format;
- }
-}
diff --git a/Eloquent/Casts/AsCollection.php b/Eloquent/Casts/AsCollection.php
deleted file mode 100644
index c6b0ffe4b7..0000000000
--- a/Eloquent/Casts/AsCollection.php
+++ /dev/null
@@ -1,96 +0,0 @@
-, iterable>
- *
- * @throws \InvalidArgumentException
- */
- public static function castUsing(array $arguments)
- {
- return new class($arguments) implements CastsAttributes
- {
- public function __construct(protected array $arguments)
- {
- $this->arguments = array_pad(array_values($this->arguments), 2, '');
- }
-
- public function get($model, $key, $value, $attributes)
- {
- if (! isset($attributes[$key])) {
- return;
- }
-
- $data = Json::decode($attributes[$key]);
-
- $collectionClass = empty($this->arguments[0]) ? Collection::class : $this->arguments[0];
-
- if (! is_a($collectionClass, Collection::class, true)) {
- throw new InvalidArgumentException('The provided class must extend ['.Collection::class.'].');
- }
-
- if (! is_array($data)) {
- return null;
- }
-
- $instance = new $collectionClass($data);
-
- if (! isset($this->arguments[1]) || ! $this->arguments[1]) {
- return $instance;
- }
-
- if (is_string($this->arguments[1])) {
- $this->arguments[1] = Str::parseCallback($this->arguments[1]);
- }
-
- return is_callable($this->arguments[1])
- ? $instance->map($this->arguments[1])
- : $instance->mapInto($this->arguments[1][0]);
- }
-
- public function set($model, $key, $value, $attributes)
- {
- return [$key => Json::encode($value)];
- }
- };
- }
-
- /**
- * Specify the type of object each item in the collection should be mapped to.
- *
- * @param array{class-string, string}|class-string $map
- * @return string
- */
- public static function of($map)
- {
- return static::using('', $map);
- }
-
- /**
- * Specify the collection type for the cast.
- *
- * @param class-string $class
- * @param array{class-string, string}|class-string|null $map
- * @return string
- */
- public static function using($class, $map = null)
- {
- if (is_array($map) && is_callable($map)) {
- $map = $map[0].'@'.$map[1];
- }
-
- return static::class.':'.implode(',', [$class, $map]);
- }
-}
diff --git a/Eloquent/Casts/AsEncryptedArrayObject.php b/Eloquent/Casts/AsEncryptedArrayObject.php
deleted file mode 100644
index 2122a7b1f9..0000000000
--- a/Eloquent/Casts/AsEncryptedArrayObject.php
+++ /dev/null
@@ -1,45 +0,0 @@
-, iterable>
- */
- public static function castUsing(array $arguments)
- {
- return new class implements CastsAttributes
- {
- public function get($model, $key, $value, $attributes)
- {
- if (isset($attributes[$key])) {
- return new ArrayObject(Json::decode(Crypt::decryptString($attributes[$key])), ArrayObject::ARRAY_AS_PROPS);
- }
-
- return null;
- }
-
- public function set($model, $key, $value, $attributes)
- {
- if (! is_null($value)) {
- return [$key => Crypt::encryptString(Json::encode($value))];
- }
-
- return null;
- }
-
- public function serialize($model, string $key, $value, array $attributes)
- {
- return ! is_null($value) ? $value->getArrayCopy() : null;
- }
- };
- }
-}
diff --git a/Eloquent/Casts/AsEncryptedCollection.php b/Eloquent/Casts/AsEncryptedCollection.php
deleted file mode 100644
index a9078c2b38..0000000000
--- a/Eloquent/Casts/AsEncryptedCollection.php
+++ /dev/null
@@ -1,95 +0,0 @@
-, iterable>
- *
- * @throws \InvalidArgumentException
- */
- public static function castUsing(array $arguments)
- {
- return new class($arguments) implements CastsAttributes
- {
- public function __construct(protected array $arguments)
- {
- $this->arguments = array_pad(array_values($this->arguments), 2, '');
- }
-
- public function get($model, $key, $value, $attributes)
- {
- $collectionClass = empty($this->arguments[0]) ? Collection::class : $this->arguments[0];
-
- if (! is_a($collectionClass, Collection::class, true)) {
- throw new InvalidArgumentException('The provided class must extend ['.Collection::class.'].');
- }
-
- if (! isset($attributes[$key])) {
- return null;
- }
-
- $instance = new $collectionClass(Json::decode(Crypt::decryptString($attributes[$key])));
-
- if (! isset($this->arguments[1]) || ! $this->arguments[1]) {
- return $instance;
- }
-
- if (is_string($this->arguments[1])) {
- $this->arguments[1] = Str::parseCallback($this->arguments[1]);
- }
-
- return is_callable($this->arguments[1])
- ? $instance->map($this->arguments[1])
- : $instance->mapInto($this->arguments[1][0]);
- }
-
- public function set($model, $key, $value, $attributes)
- {
- if (! is_null($value)) {
- return [$key => Crypt::encryptString(Json::encode($value))];
- }
-
- return null;
- }
- };
- }
-
- /**
- * Specify the type of object each item in the collection should be mapped to.
- *
- * @param array{class-string, string}|class-string $map
- * @return string
- */
- public static function of($map)
- {
- return static::using('', $map);
- }
-
- /**
- * Specify the collection for the cast.
- *
- * @param class-string $class
- * @param array{class-string, string}|class-string|null $map
- * @return string
- */
- public static function using($class, $map = null)
- {
- if (is_array($map) && is_callable($map)) {
- $map = $map[0].'@'.$map[1];
- }
-
- return static::class.':'.implode(',', [$class, $map]);
- }
-}
diff --git a/Eloquent/Casts/AsEnumArrayObject.php b/Eloquent/Casts/AsEnumArrayObject.php
deleted file mode 100644
index 061dcbf57e..0000000000
--- a/Eloquent/Casts/AsEnumArrayObject.php
+++ /dev/null
@@ -1,97 +0,0 @@
-} $arguments
- * @return \Illuminate\Contracts\Database\Eloquent\CastsAttributes<\Illuminate\Database\Eloquent\Casts\ArrayObject, iterable>
- */
- public static function castUsing(array $arguments)
- {
- return new class($arguments) implements CastsAttributes
- {
- protected $arguments;
-
- public function __construct(array $arguments)
- {
- $this->arguments = $arguments;
- }
-
- public function get($model, $key, $value, $attributes)
- {
- if (! isset($attributes[$key])) {
- return;
- }
-
- $data = Json::decode($attributes[$key]);
-
- if (! is_array($data)) {
- return;
- }
-
- $enumClass = $this->arguments[0];
-
- return new ArrayObject((new Collection($data))->map(function ($value) use ($enumClass) {
- return is_subclass_of($enumClass, BackedEnum::class)
- ? $enumClass::from($value)
- : constant($enumClass.'::'.$value);
- })->toArray());
- }
-
- public function set($model, $key, $value, $attributes)
- {
- if ($value === null) {
- return [$key => null];
- }
-
- $storable = [];
-
- foreach ($value as $enum) {
- $storable[] = $this->getStorableEnumValue($enum);
- }
-
- return [$key => Json::encode($storable)];
- }
-
- public function serialize($model, string $key, $value, array $attributes)
- {
- return (new Collection($value->getArrayCopy()))
- ->map(fn ($enum) => $this->getStorableEnumValue($enum))
- ->toArray();
- }
-
- protected function getStorableEnumValue($enum)
- {
- if (is_string($enum) || is_int($enum)) {
- return $enum;
- }
-
- return enum_value($enum);
- }
- };
- }
-
- /**
- * Specify the Enum for the cast.
- *
- * @param class-string $class
- * @return string
- */
- public static function of($class)
- {
- return static::class.':'.$class;
- }
-}
diff --git a/Eloquent/Casts/AsEnumCollection.php b/Eloquent/Casts/AsEnumCollection.php
deleted file mode 100644
index fa7116a0d0..0000000000
--- a/Eloquent/Casts/AsEnumCollection.php
+++ /dev/null
@@ -1,93 +0,0 @@
-} $arguments
- * @return \Illuminate\Contracts\Database\Eloquent\CastsAttributes<\Illuminate\Support\Collection, iterable>
- */
- public static function castUsing(array $arguments)
- {
- return new class($arguments) implements CastsAttributes
- {
- protected $arguments;
-
- public function __construct(array $arguments)
- {
- $this->arguments = $arguments;
- }
-
- public function get($model, $key, $value, $attributes)
- {
- if (! isset($attributes[$key])) {
- return;
- }
-
- $data = Json::decode($attributes[$key]);
-
- if (! is_array($data)) {
- return;
- }
-
- $enumClass = $this->arguments[0];
-
- return (new Collection($data))->map(function ($value) use ($enumClass) {
- return is_subclass_of($enumClass, BackedEnum::class)
- ? $enumClass::from($value)
- : constant($enumClass.'::'.$value);
- });
- }
-
- public function set($model, $key, $value, $attributes)
- {
- $value = $value !== null
- ? Json::encode((new Collection($value))->map(function ($enum) {
- return $this->getStorableEnumValue($enum);
- })->jsonSerialize())
- : null;
-
- return [$key => $value];
- }
-
- public function serialize($model, string $key, $value, array $attributes)
- {
- return (new Collection($value))
- ->map(fn ($enum) => $this->getStorableEnumValue($enum))
- ->toArray();
- }
-
- protected function getStorableEnumValue($enum)
- {
- if (is_string($enum) || is_int($enum)) {
- return $enum;
- }
-
- return enum_value($enum);
- }
- };
- }
-
- /**
- * Specify the Enum for the cast.
- *
- * @param class-string $class
- * @return string
- */
- public static function of($class)
- {
- return static::class.':'.$class;
- }
-}
diff --git a/Eloquent/Casts/AsFluent.php b/Eloquent/Casts/AsFluent.php
deleted file mode 100644
index bba1b1dac9..0000000000
--- a/Eloquent/Casts/AsFluent.php
+++ /dev/null
@@ -1,32 +0,0 @@
-
- */
- public static function castUsing(array $arguments)
- {
- return new class implements CastsAttributes
- {
- public function get($model, $key, $value, $attributes)
- {
- return isset($value) ? new Fluent(Json::decode($value)) : null;
- }
-
- public function set($model, $key, $value, $attributes)
- {
- return isset($value) ? [$key => Json::encode($value)] : null;
- }
- };
- }
-}
diff --git a/Eloquent/Casts/AsHtmlString.php b/Eloquent/Casts/AsHtmlString.php
deleted file mode 100644
index d4182d258f..0000000000
--- a/Eloquent/Casts/AsHtmlString.php
+++ /dev/null
@@ -1,32 +0,0 @@
-
- */
- public static function castUsing(array $arguments)
- {
- return new class implements CastsAttributes
- {
- public function get($model, $key, $value, $attributes)
- {
- return isset($value) ? new HtmlString($value) : null;
- }
-
- public function set($model, $key, $value, $attributes)
- {
- return isset($value) ? (string) $value : null;
- }
- };
- }
-}
diff --git a/Eloquent/Casts/AsStringable.php b/Eloquent/Casts/AsStringable.php
deleted file mode 100644
index 4f6c787c85..0000000000
--- a/Eloquent/Casts/AsStringable.php
+++ /dev/null
@@ -1,32 +0,0 @@
-
- */
- public static function castUsing(array $arguments)
- {
- return new class implements CastsAttributes
- {
- public function get($model, $key, $value, $attributes)
- {
- return isset($value) ? new Stringable($value) : null;
- }
-
- public function set($model, $key, $value, $attributes)
- {
- return isset($value) ? (string) $value : null;
- }
- };
- }
-}
diff --git a/Eloquent/Casts/AsUri.php b/Eloquent/Casts/AsUri.php
deleted file mode 100644
index d55c6d7996..0000000000
--- a/Eloquent/Casts/AsUri.php
+++ /dev/null
@@ -1,32 +0,0 @@
-
- */
- public static function castUsing(array $arguments)
- {
- return new class implements CastsAttributes
- {
- public function get($model, $key, $value, $attributes)
- {
- return isset($value) ? new Uri($value) : null;
- }
-
- public function set($model, $key, $value, $attributes)
- {
- return isset($value) ? (string) $value : null;
- }
- };
- }
-}
diff --git a/Eloquent/Casts/Attribute.php b/Eloquent/Casts/Attribute.php
deleted file mode 100644
index 26d13ba3fb..0000000000
--- a/Eloquent/Casts/Attribute.php
+++ /dev/null
@@ -1,104 +0,0 @@
-get = $get;
- $this->set = $set;
- }
-
- /**
- * Create a new attribute accessor / mutator.
- *
- * @param callable|null $get
- * @param callable|null $set
- * @return static
- */
- public static function make(?callable $get = null, ?callable $set = null): static
- {
- return new static($get, $set);
- }
-
- /**
- * Create a new attribute accessor.
- *
- * @param callable $get
- * @return static
- */
- public static function get(callable $get)
- {
- return new static($get);
- }
-
- /**
- * Create a new attribute mutator.
- *
- * @param callable $set
- * @return static
- */
- public static function set(callable $set)
- {
- return new static(null, $set);
- }
-
- /**
- * Disable object caching for the attribute.
- *
- * @return static
- */
- public function withoutObjectCaching()
- {
- $this->withObjectCaching = false;
-
- return $this;
- }
-
- /**
- * Enable caching for the attribute.
- *
- * @return static
- */
- public function shouldCache()
- {
- $this->withCaching = true;
-
- return $this;
- }
-}
diff --git a/Eloquent/Casts/Json.php b/Eloquent/Casts/Json.php
deleted file mode 100644
index 783d5b9986..0000000000
--- a/Eloquent/Casts/Json.php
+++ /dev/null
@@ -1,56 +0,0 @@
-
- */
class Collection extends BaseCollection implements QueueableCollection
{
- use InteractsWithDictionary;
-
/**
* Find a model in the collection by key.
*
- * @template TFindDefault
- *
* @param mixed $key
- * @param TFindDefault $default
- * @return ($key is (\Illuminate\Contracts\Support\Arrayable|array) ? static : TModel|TFindDefault)
+ * @param mixed $default
+ * @return \Illuminate\Database\Eloquent\Model|static
*/
public function find($key, $default = null)
{
@@ -35,10 +22,6 @@ public function find($key, $default = null)
$key = $key->getKey();
}
- if ($key instanceof Arrayable) {
- $key = $key->toArray();
- }
-
if (is_array($key)) {
if ($this->isEmpty()) {
return new static;
@@ -47,54 +30,25 @@ public function find($key, $default = null)
return $this->whereIn($this->first()->getKeyName(), $key);
}
- return Arr::first($this->items, fn ($model) => $model->getKey() == $key, $default);
- }
-
- /**
- * Find a model in the collection by key or throw an exception.
- *
- * @param mixed $key
- * @return TModel
- *
- * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
- */
- public function findOrFail($key)
- {
- $result = $this->find($key);
-
- if (is_array($key) && count($result) === count(array_unique($key))) {
- return $result;
- } elseif (! is_array($key) && ! is_null($result)) {
- return $result;
- }
-
- $exception = new ModelNotFoundException;
-
- if (! $model = head($this->items)) {
- throw $exception;
- }
-
- $ids = is_array($key) ? array_diff($key, $result->modelKeys()) : $key;
-
- $exception->setModel(get_class($model), $ids);
-
- throw $exception;
+ return Arr::first($this->items, function ($model) use ($key) {
+ return $model->getKey() == $key;
+ }, $default);
}
/**
* Load a set of relationships onto the collection.
*
- * @param array): mixed)|string>|string $relations
+ * @param mixed $relations
* @return $this
*/
public function load($relations)
{
- if ($this->isNotEmpty()) {
+ if (count($this->items) > 0) {
if (is_string($relations)) {
$relations = func_get_args();
}
- $query = $this->first()->newQueryWithoutRelationships()->with($relations);
+ $query = $this->first()->newQuery()->with($relations);
$this->items = $query->eagerLoadRelations($this->items);
}
@@ -103,242 +57,14 @@ public function load($relations)
}
/**
- * Load a set of aggregations over relationship's column onto the collection.
- *
- * @param array): mixed)|string>|string $relations
- * @param string $column
- * @param string|null $function
- * @return $this
- */
- public function loadAggregate($relations, $column, $function = null)
- {
- if ($this->isEmpty()) {
- return $this;
- }
-
- $models = $this->first()->newModelQuery()
- ->whereKey($this->modelKeys())
- ->select($this->first()->getKeyName())
- ->withAggregate($relations, $column, $function)
- ->get()
- ->keyBy($this->first()->getKeyName());
-
- $attributes = Arr::except(
- array_keys($models->first()->getAttributes()),
- $models->first()->getKeyName()
- );
-
- $this->each(function ($model) use ($models, $attributes) {
- $extraAttributes = Arr::only($models->get($model->getKey())->getAttributes(), $attributes);
-
- $model->forceFill($extraAttributes)
- ->syncOriginalAttributes($attributes)
- ->mergeCasts($models->get($model->getKey())->getCasts());
- });
-
- return $this;
- }
-
- /**
- * Load a set of relationship counts onto the collection.
- *
- * @param array): mixed)|string>|string $relations
- * @return $this
- */
- public function loadCount($relations)
- {
- return $this->loadAggregate($relations, '*', 'count');
- }
-
- /**
- * Load a set of relationship's max column values onto the collection.
- *
- * @param array): mixed)|string>|string $relations
- * @param string $column
- * @return $this
- */
- public function loadMax($relations, $column)
- {
- return $this->loadAggregate($relations, $column, 'max');
- }
-
- /**
- * Load a set of relationship's min column values onto the collection.
- *
- * @param array): mixed)|string>|string $relations
- * @param string $column
- * @return $this
- */
- public function loadMin($relations, $column)
- {
- return $this->loadAggregate($relations, $column, 'min');
- }
-
- /**
- * Load a set of relationship's column summations onto the collection.
- *
- * @param array): mixed)|string>|string $relations
- * @param string $column
- * @return $this
- */
- public function loadSum($relations, $column)
- {
- return $this->loadAggregate($relations, $column, 'sum');
- }
-
- /**
- * Load a set of relationship's average column values onto the collection.
- *
- * @param array): mixed)|string>|string $relations
- * @param string $column
- * @return $this
- */
- public function loadAvg($relations, $column)
- {
- return $this->loadAggregate($relations, $column, 'avg');
- }
-
- /**
- * Load a set of related existences onto the collection.
- *
- * @param array): mixed)|string>|string $relations
- * @return $this
- */
- public function loadExists($relations)
- {
- return $this->loadAggregate($relations, '*', 'exists');
- }
-
- /**
- * Load a set of relationships onto the collection if they are not already eager loaded.
- *
- * @param array): mixed)|string>|string $relations
- * @return $this
- */
- public function loadMissing($relations)
- {
- if (is_string($relations)) {
- $relations = func_get_args();
- }
-
- if ($this->isNotEmpty()) {
- $query = $this->first()->newQueryWithoutRelationships()->with($relations);
-
- foreach ($query->getEagerLoads() as $key => $value) {
- $segments = explode('.', explode(':', $key)[0]);
-
- if (str_contains($key, ':')) {
- $segments[count($segments) - 1] .= ':'.explode(':', $key)[1];
- }
-
- $path = [];
-
- foreach ($segments as $segment) {
- $path[] = [$segment => $segment];
- }
-
- if (is_callable($value)) {
- $path[count($segments) - 1][array_last($segments)] = $value;
- }
-
- $this->loadMissingRelation($this, $path);
- }
- }
-
- return $this;
- }
-
- /**
- * Load a relationship path for models of the given type if it is not already eager loaded.
- *
- * @param array> $tuples
- * @return void
- */
- public function loadMissingRelationshipChain(array $tuples)
- {
- [$relation, $class] = array_shift($tuples);
-
- $this->filter(function ($model) use ($relation, $class) {
- return ! is_null($model) &&
- ! $model->relationLoaded($relation) &&
- $model::class === $class;
- })->load($relation);
-
- if (empty($tuples)) {
- return;
- }
-
- $models = $this->pluck($relation)->whereNotNull();
-
- if ($models->first() instanceof BaseCollection) {
- $models = $models->collapse();
- }
-
- (new static($models))->loadMissingRelationshipChain($tuples);
- }
-
- /**
- * Load a relationship path if it is not already eager loaded.
- *
- * @param \Illuminate\Database\Eloquent\Collection $models
- * @param array $path
- * @return void
- */
- protected function loadMissingRelation(self $models, array $path)
- {
- $relation = array_shift($path);
-
- $name = explode(':', key($relation))[0];
-
- if (is_string(reset($relation))) {
- $relation = reset($relation);
- }
-
- $models->filter(fn ($model) => ! is_null($model) && ! $model->relationLoaded($name))->load($relation);
-
- if (empty($path)) {
- return;
- }
-
- $models = $models->pluck($name)->filter();
-
- if ($models->first() instanceof BaseCollection) {
- $models = $models->collapse();
- }
-
- $this->loadMissingRelation(new static($models), $path);
- }
-
- /**
- * Load a set of relationships onto the mixed relationship collection.
- *
- * @param string $relation
- * @param array): mixed)|string> $relations
- * @return $this
- */
- public function loadMorph($relation, $relations)
- {
- $this->pluck($relation)
- ->filter()
- ->groupBy(fn ($model) => get_class($model))
- ->each(fn ($models, $className) => static::make($models)->load($relations[$className] ?? []));
-
- return $this;
- }
-
- /**
- * Load a set of relationship counts onto the mixed relationship collection.
+ * Add an item to the collection.
*
- * @param string $relation
- * @param array): mixed)|string> $relations
+ * @param mixed $item
* @return $this
*/
- public function loadMorphCount($relation, $relations)
+ public function add($item)
{
- $this->pluck($relation)
- ->filter()
- ->groupBy(fn ($model) => get_class($model))
- ->each(fn ($models, $className) => static::make($models)->loadCount($relations[$className] ?? []));
+ $this->items[] = $item;
return $this;
}
@@ -346,7 +72,7 @@ public function loadMorphCount($relation, $relations)
/**
* Determine if a key exists in the collection.
*
- * @param (callable(TModel, TKey): bool)|TModel|string|int $key
+ * @param mixed $key
* @param mixed $operator
* @param mixed $value
* @return bool
@@ -357,40 +83,29 @@ public function contains($key, $operator = null, $value = null)
return parent::contains(...func_get_args());
}
- if ($key instanceof Model) {
- return parent::contains(fn ($model) => $model->is($key));
- }
-
- return parent::contains(fn ($model) => $model->getKey() == $key);
- }
+ $key = $key instanceof Model ? $key->getKey() : $key;
- /**
- * Determine if a key does not exist in the collection.
- *
- * @param (callable(TModel, TKey): bool)|TModel|string|int $key
- * @param mixed $operator
- * @param mixed $value
- * @return bool
- */
- public function doesntContain($key, $operator = null, $value = null)
- {
- return ! $this->contains(...func_get_args());
+ return parent::contains(function ($model) use ($key) {
+ return $model->getKey() == $key;
+ });
}
/**
* Get the array of primary keys.
*
- * @return array
+ * @return array
*/
public function modelKeys()
{
- return array_map(fn ($model) => $model->getKey(), $this->items);
+ return array_map(function ($model) {
+ return $model->getKey();
+ }, $this->items);
}
/**
* Merge the collection with the given items.
*
- * @param iterable $items
+ * @param \ArrayAccess|array $items
* @return static
*/
public function merge($items)
@@ -398,7 +113,7 @@ public function merge($items)
$dictionary = $this->getDictionary();
foreach ($items as $item) {
- $dictionary[$this->getDictionaryKey($item->getKey())] = $item;
+ $dictionary[$item->getKey()] = $item;
}
return new static(array_values($dictionary));
@@ -407,64 +122,22 @@ public function merge($items)
/**
* Run a map over each of the items.
*
- * @template TMapValue
- *
- * @param callable(TModel, TKey): TMapValue $callback
- * @return \Illuminate\Support\Collection|static
+ * @param callable $callback
+ * @return \Illuminate\Support\Collection
*/
public function map(callable $callback)
{
$result = parent::map($callback);
- return $result->contains(fn ($item) => ! $item instanceof Model) ? $result->toBase() : $result;
- }
-
- /**
- * Run an associative map over each of the items.
- *
- * The callback should return an associative array with a single key / value pair.
- *
- * @template TMapWithKeysKey of array-key
- * @template TMapWithKeysValue
- *
- * @param callable(TModel, TKey): array $callback
- * @return \Illuminate\Support\Collection|static
- */
- public function mapWithKeys(callable $callback)
- {
- $result = parent::mapWithKeys($callback);
-
- return $result->contains(fn ($item) => ! $item instanceof Model) ? $result->toBase() : $result;
- }
-
- /**
- * Reload a fresh model instance from the database for all the entities.
- *
- * @param array|string $with
- * @return static
- */
- public function fresh($with = [])
- {
- if ($this->isEmpty()) {
- return new static;
- }
-
- $model = $this->first();
-
- $freshModels = $model->newQueryWithoutScopes()
- ->with(is_string($with) ? func_get_args() : $with)
- ->whereIn($model->getKeyName(), $this->modelKeys())
- ->get()
- ->getDictionary();
-
- return $this->filter(fn ($model) => $model->exists && isset($freshModels[$model->getKey()]))
- ->map(fn ($model) => $freshModels[$model->getKey()]);
+ return $result->contains(function ($item) {
+ return ! $item instanceof Model;
+ }) ? $result->toBase() : $result;
}
/**
* Diff the collection with the given items.
*
- * @param iterable $items
+ * @param \ArrayAccess|array $items
* @return static
*/
public function diff($items)
@@ -474,7 +147,7 @@ public function diff($items)
$dictionary = $this->getDictionary($items);
foreach ($this->items as $item) {
- if (! isset($dictionary[$this->getDictionaryKey($item->getKey())])) {
+ if (! isset($dictionary[$item->getKey()])) {
$diff->add($item);
}
}
@@ -485,21 +158,17 @@ public function diff($items)
/**
* Intersect the collection with the given items.
*
- * @param iterable $items
+ * @param \ArrayAccess|array $items
* @return static
*/
public function intersect($items)
{
$intersect = new static;
- if (empty($items)) {
- return $intersect;
- }
-
$dictionary = $this->getDictionary($items);
foreach ($this->items as $item) {
- if (isset($dictionary[$this->getDictionaryKey($item->getKey())])) {
+ if (isset($dictionary[$item->getKey()])) {
$intersect->add($item);
}
}
@@ -510,9 +179,9 @@ public function intersect($items)
/**
* Return only unique items from the collection.
*
- * @param (callable(TModel, TKey): mixed)|string|null $key
+ * @param string|callable|null $key
* @param bool $strict
- * @return static
+ * @return static|\Illuminate\Support\Collection
*/
public function unique($key = null, $strict = false)
{
@@ -526,7 +195,7 @@ public function unique($key = null, $strict = false)
/**
* Returns only the models from the collection with the specified keys.
*
- * @param array|null $keys
+ * @param mixed $keys
* @return static
*/
public function only($keys)
@@ -535,7 +204,7 @@ public function only($keys)
return new static($this->items);
}
- $dictionary = Arr::only($this->getDictionary(), array_map($this->getDictionaryKey(...), (array) $keys));
+ $dictionary = Arr::only($this->getDictionary(), $keys);
return new static(array_values($dictionary));
}
@@ -543,16 +212,12 @@ public function only($keys)
/**
* Returns all models in the collection except the models with specified keys.
*
- * @param array|null $keys
+ * @param mixed $keys
* @return static
*/
public function except($keys)
{
- if (is_null($keys)) {
- return new static($this->items);
- }
-
- $dictionary = Arr::except($this->getDictionary(), array_map($this->getDictionaryKey(...), (array) $keys));
+ $dictionary = Arr::except($this->getDictionary(), $keys);
return new static(array_values($dictionary));
}
@@ -560,106 +225,34 @@ public function except($keys)
/**
* Make the given, typically visible, attributes hidden across the entire collection.
*
- * @param array|string $attributes
+ * @param array|string $attributes
* @return $this
*/
public function makeHidden($attributes)
{
- return $this->each->makeHidden($attributes);
- }
-
- /**
- * Merge the given, typically visible, attributes hidden across the entire collection.
- *
- * @param array|string $attributes
- * @return $this
- */
- public function mergeHidden($attributes)
- {
- return $this->each->mergeHidden($attributes);
- }
-
- /**
- * Set the hidden attributes across the entire collection.
- *
- * @param array $hidden
- * @return $this
- */
- public function setHidden($hidden)
- {
- return $this->each->setHidden($hidden);
+ return $this->each(function ($model) use ($attributes) {
+ $model->addHidden($attributes);
+ });
}
/**
* Make the given, typically hidden, attributes visible across the entire collection.
*
- * @param array|string $attributes
+ * @param array|string $attributes
* @return $this
*/
public function makeVisible($attributes)
{
- return $this->each->makeVisible($attributes);
- }
-
- /**
- * Merge the given, typically hidden, attributes visible across the entire collection.
- *
- * @param array|string $attributes
- * @return $this
- */
- public function mergeVisible($attributes)
- {
- return $this->each->mergeVisible($attributes);
- }
-
- /**
- * Set the visible attributes across the entire collection.
- *
- * @param array $visible
- * @return $this
- */
- public function setVisible($visible)
- {
- return $this->each->setVisible($visible);
- }
-
- /**
- * Append an attribute across the entire collection.
- *
- * @param array|string $attributes
- * @return $this
- */
- public function append($attributes)
- {
- return $this->each->append($attributes);
- }
-
- /**
- * Sets the appends on every element of the collection, overwriting the existing appends for each.
- *
- * @param array $appends
- * @return $this
- */
- public function setAppends(array $appends)
- {
- return $this->each->setAppends($appends);
- }
-
- /**
- * Remove appended properties from every element in the collection.
- *
- * @return $this
- */
- public function withoutAppends()
- {
- return $this->setAppends([]);
+ return $this->each(function ($model) use ($attributes) {
+ $model->makeVisible($attributes);
+ });
}
/**
* Get a dictionary keyed by primary keys.
*
- * @param iterable|null $items
- * @return array
+ * @param \ArrayAccess|array|null $items
+ * @return array
*/
public function getDictionary($items = null)
{
@@ -668,7 +261,7 @@ public function getDictionary($items = null)
$dictionary = [];
foreach ($items as $value) {
- $dictionary[$this->getDictionaryKey($value->getKey())] = $value;
+ $dictionary[$value->getKey()] = $value;
}
return $dictionary;
@@ -679,153 +272,84 @@ public function getDictionary($items = null)
*/
/**
- * {@inheritDoc}
+ * Get an array with the values of a given key.
*
- * @return \Illuminate\Support\Collection
+ * @param string $value
+ * @param string|null $key
+ * @return \Illuminate\Support\Collection
*/
- #[\Override]
- public function countBy($countBy = null)
- {
- return $this->toBase()->countBy($countBy);
- }
-
- /**
- * {@inheritDoc}
- *
- * @return \Illuminate\Support\Collection
- */
- #[\Override]
- public function collapse()
- {
- return $this->toBase()->collapse();
- }
-
- /**
- * {@inheritDoc}
- *
- * @return \Illuminate\Support\Collection
- */
- #[\Override]
- public function flatten($depth = INF)
- {
- return $this->toBase()->flatten($depth);
- }
-
- /**
- * {@inheritDoc}
- *
- * @return \Illuminate\Support\Collection
- */
- #[\Override]
- public function flip()
+ public function pluck($value, $key = null)
{
- return $this->toBase()->flip();
+ return $this->toBase()->pluck($value, $key);
}
/**
- * {@inheritDoc}
+ * Get the keys of the collection items.
*
- * @return \Illuminate\Support\Collection
+ * @return \Illuminate\Support\Collection
*/
- #[\Override]
public function keys()
{
return $this->toBase()->keys();
}
/**
- * {@inheritDoc}
+ * Zip the collection together with one or more arrays.
*
- * @template TPadValue
- *
- * @return \Illuminate\Support\Collection
+ * @param mixed ...$items
+ * @return \Illuminate\Support\Collection
*/
- #[\Override]
- public function pad($size, $value)
- {
- return $this->toBase()->pad($size, $value);
- }
-
- /**
- * {@inheritDoc}
- *
- * @return \Illuminate\Support\Collection, static>
- */
- #[\Override]
- public function partition($key, $operator = null, $value = null)
- {
- return parent::partition(...func_get_args())->toBase();
- }
-
- /**
- * {@inheritDoc}
- *
- * @return \Illuminate\Support\Collection
- */
- #[\Override]
- public function pluck($value, $key = null)
+ public function zip($items)
{
- return $this->toBase()->pluck($value, $key);
+ return call_user_func_array([$this->toBase(), 'zip'], func_get_args());
}
/**
- * {@inheritDoc}
- *
- * @template TZipValue
+ * Collapse the collection of items into a single array.
*
- * @return \Illuminate\Support\Collection>
+ * @return \Illuminate\Support\Collection
*/
- #[\Override]
- public function zip($items)
+ public function collapse()
{
- return $this->toBase()->zip(...func_get_args());
+ return $this->toBase()->collapse();
}
/**
- * Get the comparison function to detect duplicates.
+ * Get a flattened array of the items in the collection.
*
- * @return callable(TModel, TModel): bool
+ * @param int $depth
+ * @return \Illuminate\Support\Collection
*/
- protected function duplicateComparator($strict)
+ public function flatten($depth = INF)
{
- return fn ($a, $b) => $a->is($b);
+ return $this->toBase()->flatten($depth);
}
/**
- * Enable relationship autoloading for all models in this collection.
+ * Flip the items in the collection.
*
- * @return $this
+ * @return \Illuminate\Support\Collection
*/
- public function withRelationshipAutoloading()
+ public function flip()
{
- $callback = fn ($tuples) => $this->loadMissingRelationshipChain($tuples);
-
- foreach ($this as $model) {
- if (! $model->hasRelationAutoloadCallback()) {
- $model->autoloadRelationsUsing($callback, $this);
- }
- }
-
- return $this;
+ return $this->toBase()->flip();
}
/**
* Get the type of the entities being queued.
*
* @return string|null
- *
- * @throws \LogicException
*/
public function getQueueableClass()
{
- if ($this->isEmpty()) {
+ if ($this->count() === 0) {
return;
}
- $class = $this->getQueueableModelClass($this->first());
+ $class = get_class($this->first());
$this->each(function ($model) use ($class) {
- if ($this->getQueueableModelClass($model) !== $class) {
+ if (get_class($model) !== $class) {
throw new LogicException('Queueing collections with multiple model types is not supported.');
}
});
@@ -833,102 +357,13 @@ public function getQueueableClass()
return $class;
}
- /**
- * Get the queueable class name for the given model.
- *
- * @param \Illuminate\Database\Eloquent\Model $model
- * @return string
- */
- protected function getQueueableModelClass($model)
- {
- return method_exists($model, 'getQueueableClassName')
- ? $model->getQueueableClassName()
- : get_class($model);
- }
-
/**
* Get the identifiers for all of the entities.
*
- * @return array
+ * @return array
*/
public function getQueueableIds()
{
- if ($this->isEmpty()) {
- return [];
- }
-
- return $this->first() instanceof QueueableEntity
- ? $this->map->getQueueableId()->all()
- : $this->modelKeys();
- }
-
- /**
- * Get the relationships of the entities being queued.
- *
- * @return array
- */
- public function getQueueableRelations()
- {
- if ($this->isEmpty()) {
- return [];
- }
-
- $relations = $this->map->getQueueableRelations()->all();
-
- if (count($relations) === 0 || $relations === [[]]) {
- return [];
- } elseif (count($relations) === 1) {
- return reset($relations);
- } else {
- return array_intersect(...array_values($relations));
- }
- }
-
- /**
- * Get the connection of the entities being queued.
- *
- * @return string|null
- *
- * @throws \LogicException
- */
- public function getQueueableConnection()
- {
- if ($this->isEmpty()) {
- return;
- }
-
- $connection = $this->first()->getConnectionName();
-
- $this->each(function ($model) use ($connection) {
- if ($model->getConnectionName() !== $connection) {
- throw new LogicException('Queueing collections with multiple model connections is not supported.');
- }
- });
-
- return $connection;
- }
-
- /**
- * Get the Eloquent query builder from the collection.
- *
- * @return \Illuminate\Database\Eloquent\Builder
- *
- * @throws \LogicException
- */
- public function toQuery()
- {
- $model = $this->first();
-
- if (! $model) {
- throw new LogicException('Unable to create query for empty collection.');
- }
-
- $class = get_class($model);
-
- if ($this->reject(fn ($model) => $model instanceof $class)->isNotEmpty()) {
- throw new LogicException('Unable to create query for collection with mixed types.');
- }
-
- return $model->newModelQuery()->whereKey($this->modelKeys());
+ return $this->modelKeys();
}
}
diff --git a/Eloquent/Concerns/GuardsAttributes.php b/Eloquent/Concerns/GuardsAttributes.php
index aa9b2c60c9..96317cf571 100644
--- a/Eloquent/Concerns/GuardsAttributes.php
+++ b/Eloquent/Concerns/GuardsAttributes.php
@@ -2,24 +2,21 @@
namespace Illuminate\Database\Eloquent\Concerns;
-use Illuminate\Database\Eloquent\Attributes\Fillable;
-use Illuminate\Database\Eloquent\Attributes\Guarded;
-use Illuminate\Database\Eloquent\Attributes\Initialize;
-use Illuminate\Database\Eloquent\Attributes\Unguarded;
+use Illuminate\Support\Str;
trait GuardsAttributes
{
/**
* The attributes that are mass assignable.
*
- * @var array
+ * @var array
*/
protected $fillable = [];
/**
* The attributes that aren't mass assignable.
*
- * @var array
+ * @var array
*/
protected $guarded = ['*'];
@@ -30,38 +27,10 @@ trait GuardsAttributes
*/
protected static $unguarded = false;
- /**
- * The actual columns that exist on the database and can be guarded.
- *
- * @var array>
- */
- protected static $guardableColumns = [];
-
- /**
- * Initialize the GuardsAttributes trait.
- *
- * @return void
- */
- #[Initialize]
- public function initializeGuardsAttributes()
- {
- if (empty($this->fillable)) {
- $this->fillable = static::resolveClassAttribute(Fillable::class, 'columns') ?? [];
- }
-
- if ($this->guarded === ['*']) {
- if (static::resolveClassAttribute(Unguarded::class) !== null) {
- $this->guarded = [];
- } else {
- $this->guarded = static::resolveClassAttribute(Guarded::class, 'columns') ?? ['*'];
- }
- }
- }
-
/**
* Get the fillable attributes for the model.
*
- * @return array
+ * @return array
*/
public function getFillable()
{
@@ -71,7 +40,7 @@ public function getFillable()
/**
* Set the fillable attributes for the model.
*
- * @param array $fillable
+ * @param array $fillable
* @return $this
*/
public function fillable(array $fillable)
@@ -81,35 +50,20 @@ public function fillable(array $fillable)
return $this;
}
- /**
- * Merge new fillable attributes with existing fillable attributes on the model.
- *
- * @param array $fillable
- * @return $this
- */
- public function mergeFillable(array $fillable)
- {
- $this->fillable = array_values(array_unique(array_merge($this->fillable, $fillable)));
-
- return $this;
- }
-
/**
* Get the guarded attributes for the model.
*
- * @return array
+ * @return array
*/
public function getGuarded()
{
- return self::$unguarded === true
- ? []
- : $this->guarded;
+ return $this->guarded;
}
/**
* Set the guarded attributes for the model.
*
- * @param array $guarded
+ * @param array $guarded
* @return $this
*/
public function guard(array $guarded)
@@ -119,19 +73,6 @@ public function guard(array $guarded)
return $this;
}
- /**
- * Merge new guarded attributes with existing guarded attributes on the model.
- *
- * @param array $guarded
- * @return $this
- */
- public function mergeGuarded(array $guarded)
- {
- $this->guarded = array_values(array_unique(array_merge($this->guarded, $guarded)));
-
- return $this;
- }
-
/**
* Disable all mass assignable restrictions.
*
@@ -154,7 +95,7 @@ public static function reguard()
}
/**
- * Determine if the current state is "unguarded".
+ * Determine if current state is "unguarded".
*
* @return bool
*/
@@ -166,10 +107,8 @@ public static function isUnguarded()
/**
* Run the given callable while being unguarded.
*
- * @template TReturn
- *
- * @param callable(): TReturn $callback
- * @return TReturn
+ * @param callable $callback
+ * @return mixed
*/
public static function unguarded(callable $callback)
{
@@ -213,8 +152,7 @@ public function isFillable($key)
}
return empty($this->getFillable()) &&
- ! str_contains($key, '.') &&
- ! str_starts_with($key, '_');
+ ! Str::startsWith($key, '_');
}
/**
@@ -225,40 +163,7 @@ public function isFillable($key)
*/
public function isGuarded($key)
{
- if (empty($this->getGuarded())) {
- return false;
- }
-
- return $this->getGuarded() == ['*'] ||
- ! empty(preg_grep('/^'.preg_quote($key, '/').'$/i', $this->getGuarded())) ||
- ! $this->isGuardableColumn($key);
- }
-
- /**
- * Determine if the given column is a valid, guardable column.
- *
- * @param string $key
- * @return bool
- */
- protected function isGuardableColumn($key)
- {
- if ($this->hasSetMutator($key) || $this->hasAttributeSetMutator($key) || $this->isClassCastable($key)) {
- return true;
- }
-
- if (! isset(static::$guardableColumns[get_class($this)])) {
- $columns = $this->getConnection()
- ->getSchemaBuilder()
- ->getColumnListing($this->getTable());
-
- if (empty($columns)) {
- return true;
- }
-
- static::$guardableColumns[get_class($this)] = $columns;
- }
-
- return in_array($key, static::$guardableColumns[get_class($this)]);
+ return in_array($key, $this->getGuarded()) || $this->getGuarded() == ['*'];
}
/**
@@ -268,14 +173,14 @@ protected function isGuardableColumn($key)
*/
public function totallyGuarded()
{
- return count($this->getFillable()) === 0 && $this->getGuarded() == ['*'];
+ return count($this->getFillable()) == 0 && $this->getGuarded() == ['*'];
}
/**
* Get the fillable attributes of a given array.
*
- * @param array $attributes
- * @return array
+ * @param array $attributes
+ * @return array
*/
protected function fillableFromArray(array $attributes)
{
diff --git a/Eloquent/Concerns/HasAttributes.php b/Eloquent/Concerns/HasAttributes.php
index a5717d2c30..36843e9dd2 100644
--- a/Eloquent/Concerns/HasAttributes.php
+++ b/Eloquent/Concerns/HasAttributes.php
@@ -2,143 +2,50 @@
namespace Illuminate\Database\Eloquent\Concerns;
-use BackedEnum;
-use Brick\Math\BigDecimal;
-use Brick\Math\Exception\MathException as BrickMathException;
-use Brick\Math\RoundingMode;
-use Carbon\CarbonImmutable;
-use Carbon\CarbonInterface;
-use DateTimeImmutable;
+use Carbon\Carbon;
+use LogicException;
use DateTimeInterface;
-use Illuminate\Contracts\Database\Eloquent\Castable;
-use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Str;
use Illuminate\Contracts\Support\Arrayable;
-use Illuminate\Database\Eloquent\Attributes\Appends;
-use Illuminate\Database\Eloquent\Attributes\Initialize;
-use Illuminate\Database\Eloquent\Attributes\Table;
-use Illuminate\Database\Eloquent\Casts\AsArrayObject;
-use Illuminate\Database\Eloquent\Casts\AsCollection;
-use Illuminate\Database\Eloquent\Casts\AsEncryptedArrayObject;
-use Illuminate\Database\Eloquent\Casts\AsEncryptedCollection;
-use Illuminate\Database\Eloquent\Casts\AsEnumArrayObject;
-use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
-use Illuminate\Database\Eloquent\Casts\Attribute;
-use Illuminate\Database\Eloquent\Casts\Json;
-use Illuminate\Database\Eloquent\InvalidCastException;
-use Illuminate\Database\Eloquent\JsonEncodingException;
-use Illuminate\Database\Eloquent\MissingAttributeException;
use Illuminate\Database\Eloquent\Relations\Relation;
-use Illuminate\Database\LazyLoadingViolationException;
-use Illuminate\Support\Arr;
-use Illuminate\Support\Carbon;
-use Illuminate\Support\Collection;
use Illuminate\Support\Collection as BaseCollection;
-use Illuminate\Support\Exceptions\MathException;
-use Illuminate\Support\Facades\Crypt;
-use Illuminate\Support\Facades\Date;
-use Illuminate\Support\Facades\Hash;
-use Illuminate\Support\Str;
-use InvalidArgumentException;
-use LogicException;
-use ReflectionClass;
-use ReflectionMethod;
-use ReflectionNamedType;
-use RuntimeException;
-use Stringable;
-use ValueError;
-
-use function Illuminate\Support\enum_value;
+use Illuminate\Database\Eloquent\JsonEncodingException;
trait HasAttributes
{
/**
* The model's attributes.
*
- * @var array
+ * @var array
*/
protected $attributes = [];
/**
* The model attribute's original state.
*
- * @var array
+ * @var array
*/
protected $original = [];
/**
- * The changed model attributes.
- *
- * @var array
- */
- protected $changes = [];
-
- /**
- * The previous state of the changed model attributes.
- *
- * @var array
- */
- protected $previous = [];
-
- /**
- * The attributes that should be cast.
+ * The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [];
/**
- * The attributes that have been cast using custom classes.
- *
- * @var array
- */
- protected $classCastCache = [];
-
- /**
- * The attributes that have been cast using "Attribute" return type mutators.
+ * The attributes that should be mutated to dates.
*
* @var array
*/
- protected $attributeCastCache = [];
-
- /**
- * The built-in, primitive cast types supported by Eloquent.
- *
- * @var string[]
- */
- protected static $primitiveCastTypes = [
- 'array',
- 'bool',
- 'boolean',
- 'collection',
- 'custom_datetime',
- 'date',
- 'datetime',
- 'decimal',
- 'double',
- 'encrypted',
- 'encrypted:array',
- 'encrypted:collection',
- 'encrypted:json',
- 'encrypted:object',
- 'float',
- 'hashed',
- 'immutable_date',
- 'immutable_datetime',
- 'immutable_custom_datetime',
- 'int',
- 'integer',
- 'json',
- 'json:unicode',
- 'object',
- 'real',
- 'string',
- 'timestamp',
- ];
+ protected $dates = [];
/**
* The storage format of the model's date columns.
*
- * @var string|null
+ * @var string
*/
protected $dateFormat;
@@ -163,63 +70,10 @@ trait HasAttributes
*/
protected static $mutatorCache = [];
- /**
- * The cache of the "Attribute" return type marked mutated attributes for each class.
- *
- * @var array
- */
- protected static $attributeMutatorCache = [];
-
- /**
- * The cache of the "Attribute" return type marked mutated, gettable attributes for each class.
- *
- * @var array
- */
- protected static $getAttributeMutatorCache = [];
-
- /**
- * The cache of the "Attribute" return type marked mutated, settable attributes for each class.
- *
- * @var array
- */
- protected static $setAttributeMutatorCache = [];
-
- /**
- * The cache of the converted cast types.
- *
- * @var array
- */
- protected static $castTypeCache = [];
-
- /**
- * The encrypter instance that is used to encrypt attributes.
- *
- * @var \Illuminate\Contracts\Encryption\Encrypter|null
- */
- public static $encrypter;
-
- /**
- * Initialize the trait.
- *
- * @return void
- */
- protected function initializeHasAttributes()
- {
- $this->casts = $this->ensureCastsAreStringValues(
- array_merge($this->casts, $this->casts()),
- );
-
- $this->dateFormat ??= static::resolveClassAttribute(Table::class)->dateFormat ?? null;
-
- if (empty($this->appends)) {
- $this->appends = static::resolveClassAttribute(Appends::class, 'columns') ?? [];
- }
- }
-
/**
* Convert the model's attributes to an array.
*
- * @return array
+ * @return array
*/
public function attributesToArray()
{
@@ -254,13 +108,13 @@ public function attributesToArray()
/**
* Add the date attributes to the attributes array.
*
- * @param array $attributes
- * @return array
+ * @param array $attributes
+ * @return array
*/
protected function addDateAttributesToArray(array $attributes)
{
foreach ($this->getDates() as $key) {
- if (is_null($key) || ! isset($attributes[$key])) {
+ if (! isset($attributes[$key])) {
continue;
}
@@ -275,9 +129,9 @@ protected function addDateAttributesToArray(array $attributes)
/**
* Add the mutated attributes to the attributes array.
*
- * @param array $attributes
- * @param array $mutatedAttributes
- * @return array
+ * @param array $attributes
+ * @param array $mutatedAttributes
+ * @return array
*/
protected function addMutatedAttributesToArray(array $attributes, array $mutatedAttributes)
{
@@ -303,15 +157,14 @@ protected function addMutatedAttributesToArray(array $attributes, array $mutated
/**
* Add the casted attributes to the attributes array.
*
- * @param array $attributes
- * @param array $mutatedAttributes
- * @return array
+ * @param array $attributes
+ * @param array $mutatedAttributes
+ * @return array
*/
protected function addCastAttributesToArray(array $attributes, array $mutatedAttributes)
{
foreach ($this->getCasts() as $key => $value) {
- if (! array_key_exists($key, $attributes) ||
- in_array($key, $mutatedAttributes)) {
+ if (! array_key_exists($key, $attributes) || in_array($key, $mutatedAttributes)) {
continue;
}
@@ -323,33 +176,12 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt
);
// If the attribute cast was a date or a datetime, we will serialize the date as
- // a string. This allows the developers to customize how dates are serialized
+ // a string. This allows the developers to customize hwo dates are serialized
// into an array without affecting how they are persisted into the storage.
- if (isset($attributes[$key]) && in_array($value, ['date', 'datetime', 'immutable_date', 'immutable_datetime'])) {
- $attributes[$key] = $this->serializeDate($attributes[$key]);
- }
-
- if (isset($attributes[$key]) && ($this->isCustomDateTimeCast($value) ||
- $this->isImmutableCustomDateTimeCast($value))) {
- $attributes[$key] = $attributes[$key]->format(explode(':', $value, 2)[1]);
- }
-
- if ($attributes[$key] instanceof DateTimeInterface &&
- $this->isClassCastable($key)) {
+ if ($attributes[$key] &&
+ ($value === 'date' || $value === 'datetime')) {
$attributes[$key] = $this->serializeDate($attributes[$key]);
}
-
- if (isset($attributes[$key]) && $this->isClassSerializable($key)) {
- $attributes[$key] = $this->serializeClassCastableAttribute($key, $attributes[$key]);
- }
-
- if ($this->isEnumCastable($key) && (! ($attributes[$key] ?? null) instanceof Arrayable)) {
- $attributes[$key] = isset($attributes[$key]) ? $this->getStorableEnumValue($this->getCasts()[$key], $attributes[$key]) : null;
- }
-
- if ($attributes[$key] instanceof Arrayable) {
- $attributes[$key] = $attributes[$key]->toArray();
- }
}
return $attributes;
@@ -358,11 +190,11 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt
/**
* Get an attribute array of all arrayable attributes.
*
- * @return array
+ * @return array
*/
protected function getArrayableAttributes()
{
- return $this->getArrayableItems($this->getAttributes());
+ return $this->getArrayableItems($this->attributes);
}
/**
@@ -372,14 +204,12 @@ protected function getArrayableAttributes()
*/
protected function getArrayableAppends()
{
- $appends = $this->getAppends();
-
- if (! count($appends)) {
+ if (! count($this->appends)) {
return [];
}
return $this->getArrayableItems(
- array_combine($appends, $appends)
+ array_combine($this->appends, $this->appends)
);
}
@@ -393,7 +223,7 @@ public function relationsToArray()
$attributes = [];
foreach ($this->getArrayableRelations() as $key => $value) {
- // If the values implement the Arrayable interface we can just call this
+ // If the values implements the Arrayable interface we can just call this
// toArray method on the instances which will convert both models and
// collections to their proper array form and we'll set the values.
if ($value instanceof Arrayable) {
@@ -401,8 +231,8 @@ public function relationsToArray()
}
// If the value is null, we'll still go ahead and set it in this list of
- // attributes, since null is used to represent empty relationships if
- // it has a has one or belongs to type relationships on the models.
+ // attributes since null is used to represent empty relationships if
+ // if it a has one or belongs to type relationships on the models.
elseif (is_null($value)) {
$relation = $value;
}
@@ -417,8 +247,8 @@ public function relationsToArray()
// If the relation value has been set, we will set it on this attributes
// list for returning. If it was not arrayable or null, we'll not set
// the value on the array because it is some type of invalid value.
- if (array_key_exists('relation', get_defined_vars())) { // check if $relation is in scope (could be null)
- $attributes[$key] = $relation ?? null;
+ if (isset($relation) || is_null($value)) {
+ $attributes[$key] = $relation;
}
unset($relation);
@@ -456,25 +286,6 @@ protected function getArrayableItems(array $values)
return $values;
}
- /**
- * Determine whether an attribute exists on the model.
- *
- * @param string $key
- * @return bool
- */
- public function hasAttribute($key)
- {
- if (! $key) {
- return false;
- }
-
- return array_key_exists($key, $this->attributes) ||
- array_key_exists($key, $this->casts) ||
- $this->hasGetMutator($key) ||
- $this->hasAttributeMutator($key) ||
- $this->isClassCastable($key);
- }
-
/**
* Get an attribute from the model.
*
@@ -490,54 +301,54 @@ public function getAttribute($key)
// If the attribute exists in the attribute array or has a "get" mutator we will
// get the attribute's value. Otherwise, we will proceed as if the developers
// are asking for a relationship's value. This covers both types of values.
- if ($this->hasAttribute($key)) {
+ if (array_key_exists($key, $this->attributes) ||
+ $this->hasGetMutator($key)) {
return $this->getAttributeValue($key);
}
// Here we will determine if the model base class itself contains this given key
- // since we don't want to treat any of those methods as relationships because
+ // since we do not want to treat any of those methods are relationships since
// they are all intended as helper methods and none of these are relations.
if (method_exists(self::class, $key)) {
- return $this->throwMissingAttributeExceptionIfApplicable($key);
+ return;
}
- return $this->isRelation($key) || $this->relationLoaded($key)
- ? $this->getRelationValue($key)
- : $this->throwMissingAttributeExceptionIfApplicable($key);
+ return $this->getRelationValue($key);
}
/**
- * Either throw a missing attribute exception or return null depending on Eloquent's configuration.
+ * Get a plain attribute (not a relationship).
*
* @param string $key
- * @return null
- *
- * @throws \Illuminate\Database\Eloquent\MissingAttributeException
+ * @return mixed
*/
- protected function throwMissingAttributeExceptionIfApplicable($key)
+ public function getAttributeValue($key)
{
- if ($this->exists &&
- ! $this->wasRecentlyCreated &&
- static::preventsAccessingMissingAttributes()) {
- if (isset(static::$missingAttributeViolationCallback)) {
- return call_user_func(static::$missingAttributeViolationCallback, $this, $key);
- }
+ $value = $this->getAttributeFromArray($key);
- throw new MissingAttributeException($this, $key);
+ // If the attribute has a get mutator, we will call that then return what
+ // it returns as the value, which is useful for transforming values on
+ // retrieval from the model to a form that is more useful for usage.
+ if ($this->hasGetMutator($key)) {
+ return $this->mutateAttribute($key, $value);
}
- return null;
- }
+ // If the attribute exists within the cast array, we will convert it to
+ // an appropriate native PHP type dependant upon the associated value
+ // given with the key in the pair. Dayle made this comment line up.
+ if ($this->hasCast($key)) {
+ return $this->castAttribute($key, $value);
+ }
- /**
- * Get a plain attribute (not a relationship).
- *
- * @param string $key
- * @return mixed
- */
- public function getAttributeValue($key)
- {
- return $this->transformModelValue($key, $this->getAttributeFromArray($key));
+ // If the attribute is listed as a date, we will convert it to a DateTime
+ // instance on retrieval, which makes it quite convenient to work with
+ // date fields without having to create a mutator for each property.
+ if (in_array($key, $this->getDates()) &&
+ ! is_null($value)) {
+ return $this->asDateTime($value);
+ }
+
+ return $value;
}
/**
@@ -548,9 +359,9 @@ public function getAttributeValue($key)
*/
protected function getAttributeFromArray($key)
{
- $this->mergeAttributeFromCachedCasts($key);
-
- return $this->attributes[$key] ?? null;
+ if (isset($this->attributes[$key])) {
+ return $this->attributes[$key];
+ }
}
/**
@@ -568,59 +379,12 @@ public function getRelationValue($key)
return $this->relations[$key];
}
- if (! $this->isRelation($key)) {
- return;
- }
-
- if ($this->attemptToAutoloadRelation($key)) {
- return $this->relations[$key];
- }
-
- if ($this->preventsLazyLoading) {
- $this->handleLazyLoadingViolation($key);
- }
-
// If the "attribute" exists as a method on the model, we will just assume
// it is a relationship and will load and return results from the query
// and hydrate the relationship's value on the "relationships" array.
- return $this->getRelationshipFromMethod($key);
- }
-
- /**
- * Determine if the given key is a relationship method on the model.
- *
- * @param string $key
- * @return bool
- */
- public function isRelation($key)
- {
- if ($this->hasAttributeMutator($key)) {
- return false;
- }
-
- return method_exists($this, $key) ||
- $this->relationResolver(static::class, $key);
- }
-
- /**
- * Handle a lazy loading violation.
- *
- * @param string $key
- * @return mixed
- *
- * @throws \Illuminate\Database\LazyLoadingViolationException
- */
- protected function handleLazyLoadingViolation($key)
- {
- if (isset(static::$lazyLoadingViolationCallback)) {
- return call_user_func(static::$lazyLoadingViolationCallback, $this, $key);
- }
-
- if (! $this->exists || $this->wasRecentlyCreated) {
- return;
+ if (method_exists($this, $key)) {
+ return $this->getRelationshipFromMethod($key);
}
-
- throw new LazyLoadingViolationException($this, $key);
}
/**
@@ -636,15 +400,7 @@ protected function getRelationshipFromMethod($method)
$relation = $this->$method();
if (! $relation instanceof Relation) {
- if (is_null($relation)) {
- throw new LogicException(sprintf(
- '%s::%s must return a relationship instance, but "null" was returned. Was the "return" keyword used?', static::class, $method
- ));
- }
-
- throw new LogicException(sprintf(
- '%s::%s must return a relationship instance.', static::class, $method
- ));
+ throw new LogicException(get_class($this).'::'.$method.' must return a relationship instance.');
}
return tap($relation->getResults(), function ($results) use ($method) {
@@ -663,59 +419,6 @@ public function hasGetMutator($key)
return method_exists($this, 'get'.Str::studly($key).'Attribute');
}
- /**
- * Determine if a "Attribute" return type marked mutator exists for an attribute.
- *
- * @param string $key
- * @return bool
- */
- public function hasAttributeMutator($key)
- {
- if (isset(static::$attributeMutatorCache[get_class($this)][$key])) {
- return static::$attributeMutatorCache[get_class($this)][$key];
- }
-
- if (! method_exists($this, $method = Str::camel($key))) {
- return static::$attributeMutatorCache[get_class($this)][$key] = false;
- }
-
- $returnType = (new ReflectionMethod($this, $method))->getReturnType();
-
- return static::$attributeMutatorCache[get_class($this)][$key] =
- $returnType instanceof ReflectionNamedType &&
- $returnType->getName() === Attribute::class;
- }
-
- /**
- * Determine if a "Attribute" return type marked get mutator exists for an attribute.
- *
- * @param string $key
- * @return bool
- */
- public function hasAttributeGetMutator($key)
- {
- if (isset(static::$getAttributeMutatorCache[get_class($this)][$key])) {
- return static::$getAttributeMutatorCache[get_class($this)][$key];
- }
-
- if (! $this->hasAttributeMutator($key)) {
- return static::$getAttributeMutatorCache[get_class($this)][$key] = false;
- }
-
- return static::$getAttributeMutatorCache[get_class($this)][$key] = is_callable($this->{Str::camel($key)}()->get);
- }
-
- /**
- * Determine if any get mutator exists for an attribute.
- *
- * @param string $key
- * @return bool
- */
- public function hasAnyGetMutator($key)
- {
- return $this->hasGetMutator($key) || $this->hasAttributeGetMutator($key);
- }
-
/**
* Get the value of an attribute using its mutator.
*
@@ -725,41 +428,9 @@ public function hasAnyGetMutator($key)
*/
protected function mutateAttribute($key, $value)
{
- $this->mergeAttributesFromCachedCasts();
-
return $this->{'get'.Str::studly($key).'Attribute'}($value);
}
- /**
- * Get the value of an "Attribute" return type marked attribute using its mutator.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function mutateAttributeMarkedAttribute($key, $value)
- {
- if (array_key_exists($key, $this->attributeCastCache)) {
- return $this->attributeCastCache[$key];
- }
-
- $this->mergeAttributesFromCachedCasts();
-
- $attribute = $this->{Str::camel($key)}();
-
- $value = call_user_func($attribute->get ?: function ($value) {
- return $value;
- }, $value, $this->attributes);
-
- if ($attribute->withCaching || (is_object($value) && $attribute->withObjectCaching)) {
- $this->attributeCastCache[$key] = $value;
- } else {
- unset($this->attributeCastCache[$key]);
- }
-
- return $value;
- }
-
/**
* Get the value of an attribute using its mutator for array conversion.
*
@@ -769,74 +440,11 @@ protected function mutateAttributeMarkedAttribute($key, $value)
*/
protected function mutateAttributeForArray($key, $value)
{
- if ($this->isClassCastable($key)) {
- $value = $this->getClassCastableAttributeValue($key, $value);
- } elseif (isset(static::$getAttributeMutatorCache[get_class($this)][$key]) &&
- static::$getAttributeMutatorCache[get_class($this)][$key] === true) {
- $value = $this->mutateAttributeMarkedAttribute($key, $value);
-
- $value = $value instanceof DateTimeInterface
- ? $this->serializeDate($value)
- : $value;
- } else {
- $value = $this->mutateAttribute($key, $value);
- }
+ $value = $this->mutateAttribute($key, $value);
return $value instanceof Arrayable ? $value->toArray() : $value;
}
- /**
- * Merge new casts with existing casts on the model.
- *
- * @param array $casts
- * @return $this
- */
- public function mergeCasts($casts)
- {
- $casts = $this->ensureCastsAreStringValues($casts);
-
- $this->casts = array_merge($this->casts, $casts);
-
- return $this;
- }
-
- /**
- * Ensure that the given casts are strings.
- *
- * @param array $casts
- * @return array
- *
- * @throws \InvalidArgumentException
- */
- protected function ensureCastsAreStringValues($casts)
- {
- foreach ($casts as $attribute => $cast) {
- $casts[$attribute] = match (true) {
- is_object($cast) => value(function () use ($cast, $attribute) {
- if ($cast instanceof Stringable) {
- return (string) $cast;
- }
-
- throw new InvalidArgumentException(
- "The cast object for the {$attribute} attribute must implement Stringable."
- );
- }),
- is_array($cast) => value(function () use ($cast) {
- if (count($cast) === 1) {
- return $cast[0];
- }
-
- [$cast, $arguments] = [array_shift($cast), $cast];
-
- return $cast.':'.implode(',', $arguments);
- }),
- default => $cast,
- };
- }
-
- return $casts;
- }
-
/**
* Cast an attribute to a native PHP type.
*
@@ -846,31 +454,18 @@ protected function ensureCastsAreStringValues($casts)
*/
protected function castAttribute($key, $value)
{
- $castType = $this->getCastType($key);
-
- if (is_null($value) && in_array($castType, static::$primitiveCastTypes)) {
+ if (is_null($value)) {
return $value;
}
- // If the key is one of the encrypted castable types, we'll first decrypt
- // the value and update the cast type so we may leverage the following
- // logic for casting this value to any additionally specified types.
- if ($this->isEncryptedCastable($key)) {
- $value = $this->fromEncryptedString($value);
-
- $castType = Str::after($castType, 'encrypted:');
- }
-
- switch ($castType) {
+ switch ($this->getCastType($key)) {
case 'int':
case 'integer':
return (int) $value;
case 'real':
case 'float':
case 'double':
- return $this->fromFloat($value);
- case 'decimal':
- return $this->asDecimal($value, explode(':', $this->getCasts()[$key], 2)[1]);
+ return (float) $value;
case 'string':
return (string) $value;
case 'bool':
@@ -880,195 +475,29 @@ protected function castAttribute($key, $value)
return $this->fromJson($value, true);
case 'array':
case 'json':
- case 'json:unicode':
return $this->fromJson($value);
case 'collection':
return new BaseCollection($this->fromJson($value));
case 'date':
return $this->asDate($value);
case 'datetime':
- case 'custom_datetime':
return $this->asDateTime($value);
- case 'immutable_date':
- return $this->asDate($value)->toImmutable();
- case 'immutable_custom_datetime':
- case 'immutable_datetime':
- return $this->asDateTime($value)->toImmutable();
case 'timestamp':
return $this->asTimestamp($value);
+ default:
+ return $value;
}
-
- if ($this->isEnumCastable($key)) {
- return $this->getEnumCastableAttributeValue($key, $value);
- }
-
- if ($this->isClassCastable($key)) {
- return $this->getClassCastableAttributeValue($key, $value);
- }
-
- return $value;
}
/**
- * Cast the given attribute using a custom cast class.
+ * Get the type of cast for a model attribute.
*
* @param string $key
- * @param mixed $value
- * @return mixed
+ * @return string
*/
- protected function getClassCastableAttributeValue($key, $value)
+ protected function getCastType($key)
{
- $caster = $this->resolveCasterClass($key);
-
- $objectCachingDisabled = $caster->withoutObjectCaching ?? false;
-
- if (isset($this->classCastCache[$key]) && ! $objectCachingDisabled) {
- return $this->classCastCache[$key];
- } else {
- $value = $caster instanceof CastsInboundAttributes
- ? $value
- : $caster->get($this, $key, $value, $this->attributes);
-
- if ($caster instanceof CastsInboundAttributes ||
- ! is_object($value) ||
- $objectCachingDisabled) {
- unset($this->classCastCache[$key]);
- } else {
- $this->classCastCache[$key] = $value;
- }
-
- return $value;
- }
- }
-
- /**
- * Cast the given attribute to an enum.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function getEnumCastableAttributeValue($key, $value)
- {
- if (is_null($value)) {
- return;
- }
-
- $castType = $this->getCasts()[$key];
-
- if ($value instanceof $castType) {
- return $value;
- }
-
- return $this->getEnumCaseFromValue($castType, $value);
- }
-
- /**
- * Get the type of cast for a model attribute.
- *
- * @param string $key
- * @return string
- */
- protected function getCastType($key)
- {
- $castType = $this->getCasts()[$key];
-
- if (isset(static::$castTypeCache[$castType])) {
- return static::$castTypeCache[$castType];
- }
-
- if ($this->isCustomDateTimeCast($castType)) {
- $convertedCastType = 'custom_datetime';
- } elseif ($this->isImmutableCustomDateTimeCast($castType)) {
- $convertedCastType = 'immutable_custom_datetime';
- } elseif ($this->isDecimalCast($castType)) {
- $convertedCastType = 'decimal';
- } elseif (class_exists($castType)) {
- $convertedCastType = $castType;
- } else {
- $convertedCastType = trim(strtolower($castType));
- }
-
- return static::$castTypeCache[$castType] = $convertedCastType;
- }
-
- /**
- * Increment or decrement the given attribute using the custom cast class.
- *
- * @param string $method
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function deviateClassCastableAttribute($method, $key, $value)
- {
- return $this->resolveCasterClass($key)->{$method}(
- $this, $key, $value, $this->attributes
- );
- }
-
- /**
- * Serialize the given attribute using the custom cast class.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function serializeClassCastableAttribute($key, $value)
- {
- return $this->resolveCasterClass($key)->serialize(
- $this, $key, $value, $this->attributes
- );
- }
-
- /**
- * Compare two values for the given attribute using the custom cast class.
- *
- * @param string $key
- * @param mixed $original
- * @param mixed $value
- * @return bool
- */
- protected function compareClassCastableAttribute($key, $original, $value)
- {
- return $this->resolveCasterClass($key)->compare(
- $this, $key, $original, $value
- );
- }
-
- /**
- * Determine if the cast type is a custom date time cast.
- *
- * @param string $cast
- * @return bool
- */
- protected function isCustomDateTimeCast($cast)
- {
- return str_starts_with($cast, 'date:') ||
- str_starts_with($cast, 'datetime:');
- }
-
- /**
- * Determine if the cast type is an immutable custom date time cast.
- *
- * @param string $cast
- * @return bool
- */
- protected function isImmutableCustomDateTimeCast($cast)
- {
- return str_starts_with($cast, 'immutable_date:') ||
- str_starts_with($cast, 'immutable_datetime:');
- }
-
- /**
- * Determine if the cast type is a decimal cast.
- *
- * @param string $cast
- * @return bool
- */
- protected function isDecimalCast($cast)
- {
- return str_starts_with($cast, 'decimal:');
+ return trim(strtolower($this->getCasts()[$key]));
}
/**
@@ -1076,57 +505,37 @@ protected function isDecimalCast($cast)
*
* @param string $key
* @param mixed $value
- * @return mixed
+ * @return $this
*/
public function setAttribute($key, $value)
{
// First we will check for the presence of a mutator for the set operation
// which simply lets the developers tweak the attribute as it is set on
- // this model, such as "json_encoding" a listing of data for storage.
+ // the model, such as "json_encoding" an listing of data for storage.
if ($this->hasSetMutator($key)) {
- return $this->setMutatedAttributeValue($key, $value);
- } elseif ($this->hasAttributeSetMutator($key)) {
- return $this->setAttributeMarkedMutatedAttributeValue($key, $value);
+ $method = 'set'.Str::studly($key).'Attribute';
+
+ return $this->{$method}($value);
}
// If an attribute is listed as a "date", we'll convert it from a DateTime
// instance into a form proper for storage on the database tables using
// the connection grammar's date format. We will auto set the values.
- elseif (! is_null($value) && $this->isDateAttribute($key)) {
+ elseif ($value && $this->isDateAttribute($key)) {
$value = $this->fromDateTime($value);
}
- if ($this->isEnumCastable($key)) {
- $this->setEnumCastableAttribute($key, $value);
-
- return $this;
- }
-
- if ($this->isClassCastable($key)) {
- $this->setClassCastableAttribute($key, $value);
-
- return $this;
- }
-
- if (! is_null($value) && $this->isJsonCastable($key)) {
+ if ($this->isJsonCastable($key) && ! is_null($value)) {
$value = $this->castAttributeAsJson($key, $value);
}
// If this attribute contains a JSON ->, we'll set the proper value in the
// attribute's underlying array. This takes care of properly nesting an
// attribute in the array's value in the case of deeply nested items.
- if (str_contains($key, '->')) {
+ if (Str::contains($key, '->')) {
return $this->fillJsonAttribute($key, $value);
}
- if (! is_null($value) && $this->isEncryptedCastable($key)) {
- $value = $this->castAttributeAsEncryptedString($key, $value);
- }
-
- if (! is_null($value) && $this->hasCast($key, 'hashed')) {
- $value = $this->castAttributeAsHashedString($key, $value);
- }
-
$this->attributes[$key] = $value;
return $this;
@@ -1143,79 +552,6 @@ public function hasSetMutator($key)
return method_exists($this, 'set'.Str::studly($key).'Attribute');
}
- /**
- * Determine if an "Attribute" return type marked set mutator exists for an attribute.
- *
- * @param string $key
- * @return bool
- */
- public function hasAttributeSetMutator($key)
- {
- $class = get_class($this);
-
- if (isset(static::$setAttributeMutatorCache[$class][$key])) {
- return static::$setAttributeMutatorCache[$class][$key];
- }
-
- if (! method_exists($this, $method = Str::camel($key))) {
- return static::$setAttributeMutatorCache[$class][$key] = false;
- }
-
- $returnType = (new ReflectionMethod($this, $method))->getReturnType();
-
- return static::$setAttributeMutatorCache[$class][$key] =
- $returnType instanceof ReflectionNamedType &&
- $returnType->getName() === Attribute::class &&
- is_callable($this->{$method}()->set);
- }
-
- /**
- * Set the value of an attribute using its mutator.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function setMutatedAttributeValue($key, $value)
- {
- $this->mergeAttributesFromCachedCasts();
-
- return $this->{'set'.Str::studly($key).'Attribute'}($value);
- }
-
- /**
- * Set the value of a "Attribute" return type marked attribute using its mutator.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function setAttributeMarkedMutatedAttributeValue($key, $value)
- {
- $this->mergeAttributesFromCachedCasts();
-
- $attribute = $this->{Str::camel($key)}();
-
- $callback = $attribute->set ?: function ($value) use ($key) {
- $this->attributes[$key] = $value;
- };
-
- $this->attributes = array_merge(
- $this->attributes,
- $this->normalizeCastClassResponse(
- $key, $callback($value, $this->attributes)
- )
- );
-
- if ($attribute->withCaching || (is_object($value) && $attribute->withObjectCaching)) {
- $this->attributeCastCache[$key] = $value;
- } else {
- unset($this->attributeCastCache[$key]);
- }
-
- return $this;
- }
-
/**
* Determine if the given attribute is a date or date castable.
*
@@ -1224,8 +560,8 @@ protected function setAttributeMarkedMutatedAttributeValue($key, $value)
*/
protected function isDateAttribute($key)
{
- return in_array($key, $this->getDates(), true) ||
- $this->isDateCastable($key);
+ return in_array($key, $this->getDates()) ||
+ $this->isDateCastable($key);
}
/**
@@ -1237,111 +573,22 @@ protected function isDateAttribute($key)
*/
public function fillJsonAttribute($key, $value)
{
- [$key, $path] = explode('->', $key, 2);
+ list($key, $path) = explode('->', $key, 2);
- $value = $this->asJson($this->getArrayAttributeWithValue(
+ $this->attributes[$key] = $this->asJson($this->getArrayAttributeWithValue(
$path, $key, $value
- ), $this->getJsonCastFlags($key));
-
- $this->attributes[$key] = $this->isEncryptedCastable($key)
- ? $this->castAttributeAsEncryptedString($key, $value)
- : $value;
-
- if ($this->isClassCastable($key)) {
- unset($this->classCastCache[$key]);
- }
+ ));
return $this;
}
- /**
- * Set the value of a class castable attribute.
- *
- * @param string $key
- * @param mixed $value
- * @return void
- */
- protected function setClassCastableAttribute($key, $value)
- {
- $caster = $this->resolveCasterClass($key);
-
- $this->attributes = array_replace(
- $this->attributes,
- $this->normalizeCastClassResponse($key, $caster->set(
- $this, $key, $value, $this->attributes
- ))
- );
-
- if ($caster instanceof CastsInboundAttributes ||
- ! is_object($value) ||
- ($caster->withoutObjectCaching ?? false)) {
- unset($this->classCastCache[$key]);
- } else {
- $this->classCastCache[$key] = $value;
- }
- }
-
- /**
- * Set the value of an enum castable attribute.
- *
- * @param string $key
- * @param \UnitEnum|string|int|null $value
- * @return void
- */
- protected function setEnumCastableAttribute($key, $value)
- {
- $enumClass = $this->getCasts()[$key];
-
- if (! isset($value)) {
- $this->attributes[$key] = null;
- } elseif (is_object($value)) {
- $this->attributes[$key] = $this->getStorableEnumValue($enumClass, $value);
- } else {
- $this->attributes[$key] = $this->getStorableEnumValue(
- $enumClass, $this->getEnumCaseFromValue($enumClass, $value)
- );
- }
- }
-
- /**
- * Get an enum case instance from a given class and value.
- *
- * @param string $enumClass
- * @param string|int $value
- * @return \UnitEnum
- */
- protected function getEnumCaseFromValue($enumClass, $value)
- {
- return is_subclass_of($enumClass, BackedEnum::class)
- ? $enumClass::from($value)
- : constant($enumClass.'::'.$value);
- }
-
- /**
- * Get the storable value from the given enum.
- *
- * @param string $expectedEnum
- * @param \UnitEnum $value
- * @return string|int
- *
- * @throws \ValueError
- */
- protected function getStorableEnumValue($expectedEnum, $value)
- {
- if (! $value instanceof $expectedEnum) {
- throw new ValueError(sprintf('Value [%s] is not of the expected enum type [%s].', var_export($value, true), $expectedEnum));
- }
-
- return enum_value($value);
- }
-
/**
* Get an array attribute with the given key and value set.
*
* @param string $path
* @param string $key
* @param mixed $value
- * @return array
+ * @return $this
*/
protected function getArrayAttributeWithValue($path, $key, $value)
{
@@ -1358,15 +605,8 @@ protected function getArrayAttributeWithValue($path, $key, $value)
*/
protected function getArrayAttributeByKey($key)
{
- if (! isset($this->attributes[$key])) {
- return [];
- }
-
- return $this->fromJson(
- $this->isEncryptedCastable($key)
- ? $this->fromEncryptedString($this->attributes[$key])
- : $this->attributes[$key]
- );
+ return isset($this->attributes[$key]) ?
+ $this->fromJson($this->attributes[$key]) : [];
}
/**
@@ -1375,12 +615,10 @@ protected function getArrayAttributeByKey($key)
* @param string $key
* @param mixed $value
* @return string
- *
- * @throws \Illuminate\Database\Eloquent\JsonEncodingException
*/
protected function castAttributeAsJson($key, $value)
{
- $value = $this->asJson($value, $this->getJsonCastFlags($key));
+ $value = $this->asJson($value);
if ($value === false) {
throw JsonEncodingException::forAttribute(
@@ -1391,900 +629,309 @@ protected function castAttributeAsJson($key, $value)
return $value;
}
- /**
- * Get the JSON casting flags for the given attribute.
- *
- * @param string $key
- * @return int
- */
- protected function getJsonCastFlags($key)
- {
- $flags = 0;
-
- if ($this->hasCast($key, ['json:unicode'])) {
- $flags |= JSON_UNESCAPED_UNICODE;
- }
-
- return $flags;
- }
-
/**
* Encode the given value as JSON.
*
* @param mixed $value
- * @param int $flags
* @return string
*/
- protected function asJson($value, $flags = 0)
+ protected function asJson($value)
{
- return Json::encode($value, $flags);
+ return json_encode($value);
}
/**
* Decode the given JSON back into an array or object.
*
- * @param string|null $value
+ * @param string $value
* @param bool $asObject
* @return mixed
*/
public function fromJson($value, $asObject = false)
{
- if ($value === null || $value === '') {
- return null;
- }
-
- return Json::decode($value, ! $asObject);
+ return json_decode($value, ! $asObject);
}
/**
- * Decrypt the given encrypted string.
+ * Return a timestamp as DateTime object with time set to 00:00:00.
*
- * @param string $value
- * @return mixed
+ * @param mixed $value
+ * @return \Carbon\Carbon
*/
- public function fromEncryptedString($value)
+ protected function asDate($value)
{
- return static::currentEncrypter()->decrypt($value, false);
+ return $this->asDateTime($value)->startOfDay();
}
/**
- * Cast the given attribute to an encrypted string.
+ * Return a timestamp as DateTime object.
*
- * @param string $key
* @param mixed $value
- * @return string
+ * @return \Carbon\Carbon
*/
- protected function castAttributeAsEncryptedString($key, #[\SensitiveParameter] $value)
+ protected function asDateTime($value)
{
- return static::currentEncrypter()->encrypt($value, false);
- }
+ // If this value is already a Carbon instance, we shall just return it as is.
+ // This prevents us having to re-instantiate a Carbon instance when we know
+ // it already is one, which wouldn't be fulfilled by the DateTime check.
+ if ($value instanceof Carbon) {
+ return $value;
+ }
- /**
- * Set the encrypter instance that will be used to encrypt attributes.
- *
- * @param \Illuminate\Contracts\Encryption\Encrypter|null $encrypter
- * @return void
- */
- public static function encryptUsing($encrypter)
- {
- static::$encrypter = $encrypter;
+ // If the value is already a DateTime instance, we will just skip the rest of
+ // these checks since they will be a waste of time, and hinder performance
+ // when checking the field. We will just return the DateTime right away.
+ if ($value instanceof DateTimeInterface) {
+ return new Carbon(
+ $value->format('Y-m-d H:i:s.u'), $value->getTimezone()
+ );
+ }
+
+ // If this value is an integer, we will assume it is a UNIX timestamp's value
+ // and format a Carbon object from this timestamp. This allows flexibility
+ // when defining your date fields as they might be UNIX timestamps here.
+ if (is_numeric($value)) {
+ return Carbon::createFromTimestamp($value);
+ }
+
+ // If the value is in simply year, month, day format, we will instantiate the
+ // Carbon instances from that format. Again, this provides for simple date
+ // fields on the database, while still supporting Carbonized conversion.
+ if ($this->isStandardDateFormat($value)) {
+ return Carbon::createFromFormat('Y-m-d', $value)->startOfDay();
+ }
+
+ // Finally, we will just assume this date is in the format used by default on
+ // the database connection and use that format to create the Carbon object
+ // that is returned back out to the developers after we convert it here.
+ return Carbon::createFromFormat(
+ $this->getDateFormat(), $value
+ );
}
/**
- * Get the current encrypter being used by the model.
+ * Determine if the given value is a standard date format.
*
- * @return \Illuminate\Contracts\Encryption\Encrypter
+ * @param string $value
+ * @return bool
*/
- public static function currentEncrypter()
+ protected function isStandardDateFormat($value)
{
- return static::$encrypter ?? Crypt::getFacadeRoot();
+ return preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value);
}
/**
- * Cast the given attribute to a hashed string.
+ * Convert a DateTime to a storable string.
*
- * @param string $key
- * @param mixed $value
+ * @param \DateTime|int $value
* @return string
- *
- * @throws \RuntimeException
*/
- protected function castAttributeAsHashedString($key, #[\SensitiveParameter] $value)
- {
- if ($value === null) {
- return null;
- }
-
- if (! Hash::isHashed($value)) {
- return Hash::make($value);
- }
-
- /** @phpstan-ignore staticMethod.notFound */
- if (! Hash::verifyConfiguration($value)) {
- throw new RuntimeException("Could not verify the hashed value's configuration.");
- }
-
- return $value;
- }
-
- /**
- * Decode the given float.
- *
- * @param mixed $value
- * @return mixed
- */
- public function fromFloat($value)
- {
- return match ((string) $value) {
- 'Infinity' => INF,
- '-Infinity' => -INF,
- 'NaN' => NAN,
- default => (float) $value,
- };
- }
-
- /**
- * Return a decimal as string.
- *
- * @param float|string $value
- * @param int $decimals
- * @return string
- *
- * @throws \Illuminate\Support\Exceptions\MathException
- */
- protected function asDecimal($value, $decimals)
- {
- try {
- return (string) BigDecimal::of((string) $value)->toScale($decimals, RoundingMode::HALF_UP);
- } catch (BrickMathException $e) {
- throw new MathException('Unable to cast value to a decimal.', previous: $e);
- }
- }
-
- /**
- * Return a timestamp as DateTime object with time set to 00:00:00.
- *
- * @param mixed $value
- * @return \Illuminate\Support\Carbon
- */
- protected function asDate($value)
- {
- return $this->asDateTime($value)->startOfDay();
- }
-
- /**
- * Return a timestamp as DateTime object.
- *
- * @param mixed $value
- * @return \Illuminate\Support\Carbon
- */
- protected function asDateTime($value)
- {
- // If this value is already a Carbon instance, we shall just return it as is.
- // This prevents us having to re-instantiate a Carbon instance when we know
- // it already is one, which wouldn't be fulfilled by the DateTime check.
- if ($value instanceof CarbonInterface) {
- return Date::instance($value);
- }
-
- // If the value is already a DateTime instance, we will just skip the rest of
- // these checks since they will be a waste of time, and hinder performance
- // when checking the field. We will just return the DateTime right away.
- if ($value instanceof DateTimeInterface) {
- return Date::parse(
- $value->format('Y-m-d H:i:s.u'), $value->getTimezone()
- );
- }
-
- // If this value is an integer, we will assume it is a UNIX timestamp's value
- // and format a Carbon object from this timestamp. This allows flexibility
- // when defining your date fields as they might be UNIX timestamps here.
- if (is_numeric($value)) {
- return Date::createFromTimestamp($value, date_default_timezone_get());
- }
-
- // If the value is in simply year, month, day format, we will instantiate the
- // Carbon instances from that format. Again, this provides for simple date
- // fields on the database, while still supporting Carbonized conversion.
- if ($this->isStandardDateFormat($value)) {
- return Date::instance(Carbon::createFromFormat('Y-m-d', $value)->startOfDay());
- }
-
- $format = $this->getDateFormat();
-
- // Finally, we will just assume this date is in the format used by default on
- // the database connection and use that format to create the Carbon object
- // that is returned back out to the developers after we convert it here.
- try {
- $date = Date::createFromFormat($format, $value);
- } catch (InvalidArgumentException) {
- $date = false;
- }
-
- return $date ?: Date::parse($value);
- }
-
- /**
- * Determine if the given value is a standard date format.
- *
- * @param string $value
- * @return bool
- */
- protected function isStandardDateFormat($value)
- {
- return preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value);
- }
-
- /**
- * Convert a DateTime to a storable string.
- *
- * @param mixed $value
- * @return string|null
- */
- public function fromDateTime($value)
- {
- return empty($value) ? $value : $this->asDateTime($value)->format(
- $this->getDateFormat()
- );
- }
-
- /**
- * Return a timestamp as unix timestamp.
- *
- * @param mixed $value
- * @return int
- */
- protected function asTimestamp($value)
- {
- return $this->asDateTime($value)->getTimestamp();
- }
-
- /**
- * Prepare a date for array / JSON serialization.
- *
- * @param \DateTimeInterface $date
- * @return string
- */
- protected function serializeDate(DateTimeInterface $date)
- {
- return $date instanceof DateTimeImmutable ?
- CarbonImmutable::instance($date)->toJSON() :
- Carbon::instance($date)->toJSON();
- }
-
- /**
- * Get the attributes that should be converted to dates.
- *
- * @return array
- */
- public function getDates()
- {
- return $this->usesTimestamps() ? [
- $this->getCreatedAtColumn(),
- $this->getUpdatedAtColumn(),
- ] : [];
- }
-
- /**
- * Get the format for database stored dates.
- *
- * @return string
- */
- public function getDateFormat()
- {
- return $this->dateFormat ?: $this->getConnection()->getQueryGrammar()->getDateFormat();
- }
-
- /**
- * Set the date format used by the model.
- *
- * @param string $format
- * @return $this
- */
- public function setDateFormat($format)
- {
- $this->dateFormat = $format;
-
- return $this;
- }
-
- /**
- * Determine whether an attribute should be cast to a native type.
- *
- * @param string $key
- * @param array|string|null $types
- * @return bool
- */
- public function hasCast($key, $types = null)
- {
- if (array_key_exists($key, $this->getCasts())) {
- return $types ? in_array($this->getCastType($key), (array) $types, true) : true;
- }
-
- return false;
- }
-
- /**
- * Get the attributes that should be cast.
- *
- * @return array
- */
- public function getCasts()
- {
- if ($this->getIncrementing()) {
- return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);
- }
-
- return $this->casts;
- }
-
- /**
- * Get the attributes that should be cast.
- *
- * @return array
- */
- protected function casts()
- {
- return [];
- }
-
- /**
- * Determine whether a value is Date / DateTime castable for inbound manipulation.
- *
- * @param string $key
- * @return bool
- */
- protected function isDateCastable($key)
- {
- return $this->hasCast($key, ['date', 'datetime', 'immutable_date', 'immutable_datetime']);
- }
-
- /**
- * Determine whether a value is Date / DateTime custom-castable for inbound manipulation.
- *
- * @param string $key
- * @return bool
- */
- protected function isDateCastableWithCustomFormat($key)
- {
- return $this->hasCast($key, ['custom_datetime', 'immutable_custom_datetime']);
- }
-
- /**
- * Determine whether a value is JSON castable for inbound manipulation.
- *
- * @param string $key
- * @return bool
- */
- protected function isJsonCastable($key)
- {
- return $this->hasCast($key, ['array', 'json', 'json:unicode', 'object', 'collection', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object']);
- }
-
- /**
- * Determine whether a value is an encrypted castable for inbound manipulation.
- *
- * @param string $key
- * @return bool
- */
- protected function isEncryptedCastable($key)
- {
- return $this->hasCast($key, ['encrypted', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object']);
- }
-
- /**
- * Determine if the given key is cast using a custom class.
- *
- * @param string $key
- * @return bool
- *
- * @throws \Illuminate\Database\Eloquent\InvalidCastException
- */
- protected function isClassCastable($key)
- {
- $casts = $this->getCasts();
-
- if (! array_key_exists($key, $casts)) {
- return false;
- }
-
- $castType = $this->parseCasterClass($casts[$key]);
-
- if (in_array($castType, static::$primitiveCastTypes)) {
- return false;
- }
-
- if (class_exists($castType)) {
- return true;
- }
-
- throw new InvalidCastException($this->getModel(), $key, $castType);
- }
-
- /**
- * Determine if the given key is cast using an enum.
- *
- * @param string $key
- * @return bool
- */
- protected function isEnumCastable($key)
- {
- $casts = $this->getCasts();
-
- if (! array_key_exists($key, $casts)) {
- return false;
- }
-
- $castType = $casts[$key];
-
- if (in_array($castType, static::$primitiveCastTypes)) {
- return false;
- }
-
- if (is_subclass_of($castType, Castable::class)) {
- return false;
- }
-
- return enum_exists($castType);
- }
-
- /**
- * Determine if the key is deviable using a custom class.
- *
- * @param string $key
- * @return bool
- *
- * @throws \Illuminate\Database\Eloquent\InvalidCastException
- */
- protected function isClassDeviable($key)
- {
- if (! $this->isClassCastable($key)) {
- return false;
- }
-
- $castType = $this->resolveCasterClass($key);
-
- return method_exists($castType::class, 'increment') && method_exists($castType::class, 'decrement');
- }
-
- /**
- * Determine if the key is serializable using a custom class.
- *
- * @param string $key
- * @return bool
- *
- * @throws \Illuminate\Database\Eloquent\InvalidCastException
- */
- protected function isClassSerializable($key)
- {
- return ! $this->isEnumCastable($key) &&
- $this->isClassCastable($key) &&
- method_exists($this->resolveCasterClass($key), 'serialize');
- }
-
- /**
- * Determine if the key is comparable using a custom class.
- *
- * @param string $key
- * @return bool
- */
- protected function isClassComparable($key)
- {
- return ! $this->isEnumCastable($key) &&
- $this->isClassCastable($key) &&
- method_exists($this->resolveCasterClass($key), 'compare');
- }
-
- /**
- * Resolve the custom caster class for a given key.
- *
- * @param string $key
- * @return mixed
- */
- protected function resolveCasterClass($key)
- {
- $castType = $this->getCasts()[$key];
-
- $arguments = [];
-
- if (is_string($castType) && str_contains($castType, ':')) {
- $segments = explode(':', $castType, 2);
-
- $castType = $segments[0];
- $arguments = explode(',', $segments[1]);
- }
-
- if (is_subclass_of($castType, Castable::class)) {
- $castType = $castType::castUsing($arguments);
- }
-
- if (is_object($castType)) {
- return $castType;
- }
-
- return new $castType(...$arguments);
- }
-
- /**
- * Parse the given caster class, removing any arguments.
- *
- * @param string $class
- * @return string
- */
- protected function parseCasterClass($class)
- {
- return ! str_contains($class, ':')
- ? $class
- : explode(':', $class, 2)[0];
- }
-
- /**
- * Merge the cast class and attribute cast attributes back into the model.
- *
- * @return void
- */
- protected function mergeAttributesFromCachedCasts()
- {
- $this->mergeAttributesFromClassCasts();
- $this->mergeAttributesFromAttributeCasts();
- }
-
- /**
- * Merge the a cast class and attribute cast attribute back into the model.
- *
- * @return void
- */
- protected function mergeAttributeFromCachedCasts(string $key)
- {
- $this->mergeAttributeFromClassCasts($key);
- $this->mergeAttributeFromAttributeCasts($key);
- }
-
- /**
- * Merge the cast class attributes back into the model.
- *
- * @return void
- */
- protected function mergeAttributesFromClassCasts()
- {
- foreach ($this->classCastCache as $key => $value) {
- $this->mergeAttributeFromClassCasts($key);
- }
- }
-
- /**
- * Merge the cast class attribute back into the model.
- *
- * @return void
- */
- protected function mergeAttributeFromClassCasts(string $key): void
- {
- if (! isset($this->classCastCache[$key])) {
- return;
- }
-
- $value = $this->classCastCache[$key];
-
- $caster = $this->resolveCasterClass($key);
-
- $this->attributes = array_merge(
- $this->attributes,
- $caster instanceof CastsInboundAttributes
- ? [$key => $value]
- : $this->normalizeCastClassResponse($key, $caster->set($this, $key, $value, $this->attributes))
- );
- }
-
- /**
- * Merge the cast class attributes back into the model.
- *
- * @return void
- */
- protected function mergeAttributesFromAttributeCasts()
- {
- foreach ($this->attributeCastCache as $key => $value) {
- $this->mergeAttributeFromAttributeCasts($key);
- }
- }
-
- /**
- * Merge the cast class attribute back into the model.
- *
- * @return void
- */
- protected function mergeAttributeFromAttributeCasts(string $key): void
- {
- if (! isset($this->attributeCastCache[$key])) {
- return;
- }
-
- $value = $this->attributeCastCache[$key];
-
- $attribute = $this->{Str::camel($key)}();
-
- if ($attribute->get && ! $attribute->set) {
- return;
- }
-
- $callback = $attribute->set ?: function ($value) use ($key) {
- $this->attributes[$key] = $value;
- };
-
- $this->attributes = array_merge(
- $this->attributes,
- $this->normalizeCastClassResponse(
- $key, $callback($value, $this->attributes)
- )
- );
- }
-
- /**
- * Normalize the response from a custom class caster.
- *
- * @param string $key
- * @param mixed $value
- * @return array
- */
- protected function normalizeCastClassResponse($key, $value)
- {
- return is_array($value) ? $value : [$key => $value];
- }
-
- /**
- * Get all of the current attributes on the model.
- *
- * @return array
- */
- public function getAttributes()
+ public function fromDateTime($value)
{
- $this->mergeAttributesFromCachedCasts();
-
- return $this->attributes;
+ return $this->asDateTime($value)->format(
+ $this->getDateFormat()
+ );
}
/**
- * Get all of the current attributes on the model for an insert operation.
+ * Return a timestamp as unix timestamp.
*
- * @return array
+ * @param mixed $value
+ * @return int
*/
- protected function getAttributesForInsert()
+ protected function asTimestamp($value)
{
- return $this->getAttributes();
+ return $this->asDateTime($value)->getTimestamp();
}
/**
- * Set the array of model attributes. No checking is done.
+ * Prepare a date for array / JSON serialization.
*
- * @param array $attributes
- * @param bool $sync
- * @return $this
+ * @param \DateTimeInterface $date
+ * @return string
*/
- public function setRawAttributes(array $attributes, $sync = false)
+ protected function serializeDate(DateTimeInterface $date)
{
- $this->attributes = $attributes;
-
- if ($sync) {
- $this->syncOriginal();
- }
-
- $this->classCastCache = [];
- $this->attributeCastCache = [];
-
- return $this;
+ return $date->format($this->getDateFormat());
}
/**
- * Get the model's original attribute values.
+ * Get the attributes that should be converted to dates.
*
- * @param string|null $key
- * @param mixed $default
- * @return ($key is null ? array : mixed)
+ * @return array
*/
- public function getOriginal($key = null, $default = null)
+ public function getDates()
{
- return (new static)->setRawAttributes(
- $this->original, $sync = true
- )->getOriginalWithoutRewindingModel($key, $default);
+ $defaults = [static::CREATED_AT, static::UPDATED_AT];
+
+ return $this->usesTimestamps() ? array_merge($this->dates, $defaults) : $this->dates;
}
/**
- * Get the model's original attribute values.
+ * Get the format for database stored dates.
*
- * @param string|null $key
- * @param mixed $default
- * @return ($key is null ? array : mixed)
+ * @return string
*/
- protected function getOriginalWithoutRewindingModel($key = null, $default = null)
+ protected function getDateFormat()
{
- if ($key) {
- return $this->transformModelValue(
- $key, Arr::get($this->original, $key, $default)
- );
- }
-
- return (new Collection($this->original))
- ->mapWithKeys(fn ($value, $key) => [$key => $this->transformModelValue($key, $value)])
- ->all();
+ return $this->dateFormat ?: $this->getConnection()->getQueryGrammar()->getDateFormat();
}
/**
- * Get the model's raw original attribute values.
+ * Set the date format used by the model.
*
- * @param string|null $key
- * @param mixed $default
- * @return ($key is null ? array : mixed)
+ * @param string $format
+ * @return $this
*/
- public function getRawOriginal($key = null, $default = null)
+ public function setDateFormat($format)
{
- return Arr::get($this->original, $key, $default);
+ $this->dateFormat = $format;
+
+ return $this;
}
/**
- * Get a subset of the model's attributes.
+ * Determine whether an attribute should be cast to a native type.
*
- * @param array|mixed $attributes
- * @return array
+ * @param string $key
+ * @param array|string|null $types
+ * @return bool
*/
- public function only($attributes)
+ public function hasCast($key, $types = null)
{
- $results = [];
-
- foreach (is_array($attributes) ? $attributes : func_get_args() as $attribute) {
- $results[$attribute] = $this->getAttribute($attribute);
+ if (array_key_exists($key, $this->getCasts())) {
+ return $types ? in_array($this->getCastType($key), (array) $types, true) : true;
}
- return $results;
+ return false;
}
/**
- * Get all attributes except the given ones.
+ * Get the casts array.
*
- * @param array|mixed $attributes
* @return array
*/
- public function except($attributes)
+ public function getCasts()
{
- $attributes = is_array($attributes) ? $attributes : func_get_args();
-
- $results = [];
-
- foreach ($this->getAttributes() as $key => $value) {
- if (! in_array($key, $attributes)) {
- $results[$key] = $this->getAttribute($key);
- }
+ if ($this->getIncrementing()) {
+ return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);
}
- return $results;
+ return $this->casts;
}
/**
- * Sync the original attributes with the current.
+ * Determine whether a value is Date / DateTime castable for inbound manipulation.
*
- * @return $this
+ * @param string $key
+ * @return bool
*/
- public function syncOriginal()
+ protected function isDateCastable($key)
{
- $this->original = $this->getAttributes();
-
- return $this;
+ return $this->hasCast($key, ['date', 'datetime']);
}
/**
- * Sync a single original attribute with its current value.
+ * Determine whether a value is JSON castable for inbound manipulation.
*
- * @param string $attribute
- * @return $this
+ * @param string $key
+ * @return bool
*/
- public function syncOriginalAttribute($attribute)
+ protected function isJsonCastable($key)
{
- return $this->syncOriginalAttributes($attribute);
+ return $this->hasCast($key, ['array', 'json', 'object', 'collection']);
}
/**
- * Sync multiple original attribute with their current values.
+ * Get all of the current attributes on the model.
*
- * @param array|string $attributes
- * @return $this
+ * @return array
*/
- public function syncOriginalAttributes($attributes)
+ public function getAttributes()
{
- $attributes = is_array($attributes) ? $attributes : func_get_args();
-
- $modelAttributes = $this->getAttributes();
-
- foreach ($attributes as $attribute) {
- $this->original[$attribute] = $modelAttributes[$attribute];
- }
-
- return $this;
+ return $this->attributes;
}
/**
- * Sync the changed attributes.
+ * Set the array of model attributes. No checking is done.
*
+ * @param array $attributes
+ * @param bool $sync
* @return $this
*/
- public function syncChanges()
+ public function setRawAttributes(array $attributes, $sync = false)
{
- $this->changes = $this->getDirty();
- $this->previous = array_intersect_key($this->getRawOriginal(), $this->changes);
+ $this->attributes = $attributes;
+
+ if ($sync) {
+ $this->syncOriginal();
+ }
return $this;
}
/**
- * Determine if the model or any of the given attribute(s) have been modified.
+ * Get the model's original attribute values.
*
- * @param array|string|null $attributes
- * @return bool
+ * @param string|null $key
+ * @param mixed $default
+ * @return mixed|array
*/
- public function isDirty($attributes = null)
+ public function getOriginal($key = null, $default = null)
{
- return $this->hasChanges(
- $this->getDirty(), is_array($attributes) ? $attributes : func_get_args()
- );
+ return Arr::get($this->original, $key, $default);
}
/**
- * Determine if the model or all the given attribute(s) have remained the same.
+ * Sync the original attributes with the current.
*
- * @param array|string|null $attributes
- * @return bool
+ * @return $this
*/
- public function isClean($attributes = null)
+ public function syncOriginal()
{
- return ! $this->isDirty(...func_get_args());
+ $this->original = $this->attributes;
+
+ return $this;
}
/**
- * Discard attribute changes and reset the attributes to their original state.
+ * Sync a single original attribute with its current value.
*
+ * @param string $attribute
* @return $this
*/
- public function discardChanges()
+ public function syncOriginalAttribute($attribute)
{
- [$this->attributes, $this->changes, $this->previous] = [$this->original, [], []];
-
- $this->classCastCache = [];
- $this->attributeCastCache = [];
+ $this->original[$attribute] = $this->attributes[$attribute];
return $this;
}
/**
- * Determine if the model or any of the given attribute(s) were changed when the model was last saved.
+ * Determine if the model or given attribute(s) have been modified.
*
- * @param array|string|null $attributes
+ * @param array|string|null $attributes
* @return bool
*/
- public function wasChanged($attributes = null)
+ public function isDirty($attributes = null)
{
- return $this->hasChanges(
- $this->getChanges(), is_array($attributes) ? $attributes : func_get_args()
- );
- }
+ $dirty = $this->getDirty();
- /**
- * Determine if any of the given attributes were changed when the model was last saved.
- *
- * @param array $changes
- * @param array|string|null $attributes
- * @return bool
- */
- protected function hasChanges($changes, $attributes = null)
- {
// If no specific attributes were provided, we will just see if the dirty array
// already contains any attributes. If it does we will just return that this
// count is greater than zero. Else, we need to check specific attributes.
- if (empty($attributes)) {
- return count($changes) > 0;
+ if (is_null($attributes)) {
+ return count($dirty) > 0;
}
+ $attributes = is_array($attributes)
+ ? $attributes : func_get_args();
+
// Here we will spin through every attribute and see if this is in the array of
// dirty attributes. If it is, we will return true and if we make it through
// all of the attributes for the entire array we will return false at end.
- foreach (Arr::wrap($attributes) as $attribute) {
- if (array_key_exists($attribute, $changes)) {
+ foreach ($attributes as $attribute) {
+ if (array_key_exists($attribute, $dirty)) {
return true;
}
}
@@ -2293,16 +940,27 @@ protected function hasChanges($changes, $attributes = null)
}
/**
- * Get the attributes that have been changed since the last sync.
+ * Determine if the model or given attribute(s) have remained the same.
+ *
+ * @param array|string|null $attributes
+ * @return bool
+ */
+ public function isClean($attributes = null)
+ {
+ return ! $this->isDirty(...func_get_args());
+ }
+
+ /**
+ * Get the attributes that have been changed since last sync.
*
- * @return array
+ * @return array
*/
public function getDirty()
{
$dirty = [];
foreach ($this->getAttributes() as $key => $value) {
- if (! $this->originalIsEquivalent($key)) {
+ if (! $this->originalIsEquivalent($key, $value)) {
$dirty[$key] = $value;
}
}
@@ -2310,158 +968,52 @@ public function getDirty()
return $dirty;
}
- /**
- * Get the attributes that have been changed since the last sync for an update operation.
- *
- * @return array
- */
- protected function getDirtyForUpdate()
- {
- return $this->getDirty();
- }
-
- /**
- * Get the attributes that were changed when the model was last saved.
- *
- * @return array
- */
- public function getChanges()
- {
- return $this->changes;
- }
-
- /**
- * Get the attributes that were previously original before the model was last saved.
- *
- * @return array
- */
- public function getPrevious()
- {
- return $this->previous;
- }
-
/**
* Determine if the new and old values for a given key are equivalent.
*
- * @param string $key
+ * @param string $key
+ * @param mixed $current
* @return bool
*/
- public function originalIsEquivalent($key)
+ protected function originalIsEquivalent($key, $current)
{
if (! array_key_exists($key, $this->original)) {
return false;
}
- $attribute = Arr::get($this->attributes, $key);
- $original = Arr::get($this->original, $key);
+ $original = $this->getOriginal($key);
- if ($attribute === $original) {
+ if ($current === $original) {
return true;
- } elseif (is_null($attribute)) {
+ } elseif (is_null($current)) {
return false;
- } elseif ($this->isDateAttribute($key) || $this->isDateCastableWithCustomFormat($key)) {
- return $this->fromDateTime($attribute) ===
- $this->fromDateTime($original);
- } elseif ($this->hasCast($key, ['object', 'collection'])) {
- return $this->fromJson($attribute) ===
- $this->fromJson($original);
- } elseif ($this->hasCast($key, ['real', 'float', 'double'])) {
- if ($original === null) {
- return false;
- }
-
- return abs($this->castAttribute($key, $attribute) - $this->castAttribute($key, $original)) < PHP_FLOAT_EPSILON * 4;
- } elseif ($this->isEncryptedCastable($key) && ! empty(static::currentEncrypter()->getPreviousKeys())) {
- return false;
- } elseif ($this->hasCast($key, static::$primitiveCastTypes)) {
- return $this->castAttribute($key, $attribute) ===
- $this->castAttribute($key, $original);
- } elseif ($this->isClassCastable($key) && Str::startsWith($this->getCasts()[$key], [AsArrayObject::class, AsCollection::class])) {
- return $this->fromJson($attribute) === $this->fromJson($original);
- } elseif ($this->isClassCastable($key) && Str::startsWith($this->getCasts()[$key], [AsEnumArrayObject::class, AsEnumCollection::class])) {
- return $this->fromJson($attribute) === $this->fromJson($original);
- } elseif ($this->isClassCastable($key) && $original !== null && Str::startsWith($this->getCasts()[$key], [AsEncryptedArrayObject::class, AsEncryptedCollection::class])) {
- if (empty(static::currentEncrypter()->getPreviousKeys())) {
- return $this->fromEncryptedString($attribute) === $this->fromEncryptedString($original);
- }
-
- return false;
- } elseif ($this->isClassComparable($key)) {
- return $this->compareClassCastableAttribute($key, $original, $attribute);
- }
-
- return is_numeric($attribute) && is_numeric($original)
- && strcmp((string) $attribute, (string) $original) === 0;
- }
-
- /**
- * Transform a raw model value using mutators, casts, etc.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- protected function transformModelValue($key, $value)
- {
- // If the attribute has a get mutator, we will call that then return what
- // it returns as the value, which is useful for transforming values on
- // retrieval from the model to a form that is more useful for usage.
- if ($this->hasGetMutator($key)) {
- return $this->mutateAttribute($key, $value);
- } elseif ($this->hasAttributeGetMutator($key)) {
- return $this->mutateAttributeMarkedAttribute($key, $value);
- }
-
- // If the attribute exists within the cast array, we will convert it to
- // an appropriate native PHP type dependent upon the associated value
- // given with the key in the pair. Dayle made this comment line up.
- if ($this->hasCast($key)) {
- if (static::preventsAccessingMissingAttributes() &&
- ! array_key_exists($key, $this->attributes) &&
- ($this->isEnumCastable($key) ||
- in_array($this->getCastType($key), static::$primitiveCastTypes))) {
- $this->throwMissingAttributeExceptionIfApplicable($key);
- }
-
- return $this->castAttribute($key, $value);
- }
-
- // If the attribute is listed as a date, we will convert it to a DateTime
- // instance on retrieval, which makes it quite convenient to work with
- // date fields without having to create a mutator for each property.
- if ($value !== null
- && \in_array($key, $this->getDates(), false)) {
- return $this->asDateTime($value);
+ } elseif ($this->isDateAttribute($key)) {
+ return $this->fromDateTime($current) ===
+ $this->fromDateTime($original);
+ } elseif ($this->hasCast($key)) {
+ return $this->castAttribute($key, $current) ===
+ $this->castAttribute($key, $original);
}
- return $value;
+ return is_numeric($current) && is_numeric($original)
+ && strcmp((string) $current, (string) $original) === 0;
}
/**
* Append attributes to query when building a query.
*
- * @param array|string $attributes
+ * @param array|string $attributes
* @return $this
*/
public function append($attributes)
{
- $this->appends = array_values(array_unique(
+ $this->appends = array_unique(
array_merge($this->appends, is_string($attributes) ? func_get_args() : $attributes)
- ));
+ );
return $this;
}
- /**
- * Get the accessors that are being appended to model arrays.
- *
- * @return array
- */
- public function getAppends()
- {
- return $this->appends;
- }
-
/**
* Set the accessors to append to model arrays.
*
@@ -2475,40 +1027,6 @@ public function setAppends(array $appends)
return $this;
}
- /**
- * Merge new appended attributes with existing appended attributes on the model.
- *
- * @param array $appends
- * @return $this
- */
- public function mergeAppends(array $appends)
- {
- $this->appends = array_values(array_unique(array_merge($this->appends, $appends)));
-
- return $this;
- }
-
- /**
- * Return whether the accessor attribute has been appended.
- *
- * @param string $attribute
- * @return bool
- */
- public function hasAppended($attribute)
- {
- return in_array($attribute, $this->getAppends());
- }
-
- /**
- * Remove all appended properties from the model.
- *
- * @return $this
- */
- public function withoutAppends()
- {
- return $this->setAppends([]);
- }
-
/**
* Get the mutated attributes for a given instance.
*
@@ -2516,33 +1034,26 @@ public function withoutAppends()
*/
public function getMutatedAttributes()
{
- if (! isset(static::$mutatorCache[static::class])) {
- static::cacheMutatedAttributes($this);
+ $class = static::class;
+
+ if (! isset(static::$mutatorCache[$class])) {
+ static::cacheMutatedAttributes($class);
}
- return static::$mutatorCache[static::class];
+ return static::$mutatorCache[$class];
}
/**
* Extract and cache all the mutated attributes of a class.
*
- * @param object|string $classOrInstance
+ * @param string $class
* @return void
*/
- public static function cacheMutatedAttributes($classOrInstance)
+ public static function cacheMutatedAttributes($class)
{
- $reflection = new ReflectionClass($classOrInstance);
-
- $class = $reflection->getName();
-
- static::$getAttributeMutatorCache[$class] = (new Collection($attributeMutatorMethods = static::getAttributeMarkedMutatorMethods($classOrInstance)))
- ->mapWithKeys(fn ($match) => [lcfirst(static::$snakeAttributes ? Str::snake($match) : $match) => true])
- ->all();
-
- static::$mutatorCache[$class] = (new Collection(static::getMutatorMethods($class)))
- ->merge($attributeMutatorMethods)
- ->map(fn ($match) => lcfirst(static::$snakeAttributes ? Str::snake($match) : $match))
- ->all();
+ static::$mutatorCache[$class] = collect(static::getMutatorMethods($class))->map(function ($match) {
+ return lcfirst(static::$snakeAttributes ? Str::snake($match) : $match);
+ })->all();
}
/**
@@ -2557,28 +1068,4 @@ protected static function getMutatorMethods($class)
return $matches[1];
}
-
- /**
- * Get all of the "Attribute" return typed attribute mutator methods.
- *
- * @param mixed $class
- * @return array
- */
- protected static function getAttributeMarkedMutatorMethods($class)
- {
- $instance = is_object($class) ? $class : new $class;
-
- return (new Collection((new ReflectionClass($instance))->getMethods()))->filter(function ($method) use ($instance) {
- $returnType = $method->getReturnType();
-
- if ($returnType instanceof ReflectionNamedType &&
- $returnType->getName() === Attribute::class) {
- if (is_callable($method->invoke($instance)->get)) {
- return true;
- }
- }
-
- return false;
- })->map->name->values()->all();
- }
}
diff --git a/Eloquent/Concerns/HasEvents.php b/Eloquent/Concerns/HasEvents.php
index cc0368e67d..a769221734 100644
--- a/Eloquent/Concerns/HasEvents.php
+++ b/Eloquent/Concerns/HasEvents.php
@@ -3,13 +3,6 @@
namespace Illuminate\Database\Eloquent\Concerns;
use Illuminate\Contracts\Events\Dispatcher;
-use Illuminate\Database\Eloquent\Attributes\ObservedBy;
-use Illuminate\Database\Eloquent\Model;
-use Illuminate\Events\NullDispatcher;
-use Illuminate\Support\Arr;
-use Illuminate\Support\Collection;
-use InvalidArgumentException;
-use ReflectionClass;
trait HasEvents
{
@@ -18,7 +11,7 @@ trait HasEvents
*
* Allows for object-based events for native Eloquent events.
*
- * @var array
+ * @var array
*/
protected $dispatchesEvents = [];
@@ -27,114 +20,44 @@ trait HasEvents
*
* These are extra user-defined events observers may subscribe to.
*
- * @var string[]
+ * @var array
*/
protected $observables = [];
/**
- * Boot the has event trait for a model.
+ * Register an observer with the Model.
*
+ * @param object|string $class
* @return void
*/
- public static function bootHasEvents()
- {
- static::whenBooted(fn () => static::observe(static::resolveObserveAttributes()));
- }
-
- /**
- * Resolve the observe class names from the attributes.
- *
- * @return array
- */
- public static function resolveObserveAttributes()
- {
- $reflectionClass = new ReflectionClass(static::class);
-
- $isEloquentGrandchild = is_subclass_of(static::class, Model::class)
- && get_parent_class(static::class) !== Model::class;
-
- return (new Collection($reflectionClass->getAttributes(ObservedBy::class)))
- ->map(fn ($attribute) => $attribute->getArguments())
- ->flatten()
- ->when($isEloquentGrandchild, function (Collection $attributes) {
- return (new Collection(get_parent_class(static::class)::resolveObserveAttributes()))
- ->merge($attributes);
- })
- ->all();
- }
-
- /**
- * Register observers with the model.
- *
- * @param object|string[]|string $classes
- * @return void
- *
- * @throws \RuntimeException
- */
- public static function observe($classes)
+ public static function observe($class)
{
$instance = new static;
- foreach (Arr::wrap($classes) as $class) {
- $instance->registerObserver($class);
- }
- }
-
- /**
- * Register a single observer with the model.
- *
- * @param object|string $class
- * @return void
- *
- * @throws \RuntimeException
- */
- protected function registerObserver($class)
- {
- $className = $this->resolveObserverClassName($class);
+ $className = is_string($class) ? $class : get_class($class);
// When registering a model observer, we will spin through the possible events
// and determine if this observer has that method. If it does, we will hook
// it into the model's event system, making it convenient to watch these.
- foreach ($this->getObservableEvents() as $event) {
+ foreach ($instance->getObservableEvents() as $event) {
if (method_exists($class, $event)) {
static::registerModelEvent($event, $className.'@'.$event);
}
}
}
- /**
- * Resolve the observer's class name from an object or string.
- *
- * @param object|string $class
- * @return class-string
- *
- * @throws \InvalidArgumentException
- */
- private function resolveObserverClassName($class)
- {
- if (is_object($class)) {
- return get_class($class);
- }
-
- if (class_exists($class)) {
- return $class;
- }
-
- throw new InvalidArgumentException('Unable to find observer: '.$class);
- }
-
/**
* Get the observable event names.
*
- * @return string[]
+ * @return array
*/
public function getObservableEvents()
{
return array_merge(
[
- 'retrieved', 'creating', 'created', 'updating', 'updated',
- 'saving', 'saved', 'restoring', 'restored', 'replicating',
- 'trashed', 'deleting', 'deleted', 'forceDeleting', 'forceDeleted',
+ 'creating', 'created', 'updating', 'updated',
+ 'deleting', 'deleted', 'saving', 'saved',
+ 'restoring', 'restored',
],
$this->observables
);
@@ -143,7 +66,7 @@ public function getObservableEvents()
/**
* Set the observable event names.
*
- * @param string[] $observables
+ * @param array $observables
* @return $this
*/
public function setObservableEvents(array $observables)
@@ -156,7 +79,7 @@ public function setObservableEvents(array $observables)
/**
* Add an observable event name.
*
- * @param string|string[] $observables
+ * @param array|mixed $observables
* @return void
*/
public function addObservableEvents($observables)
@@ -169,7 +92,7 @@ public function addObservableEvents($observables)
/**
* Remove an observable event name.
*
- * @param string|string[] $observables
+ * @param array|mixed $observables
* @return void
*/
public function removeObservableEvents($observables)
@@ -183,7 +106,7 @@ public function removeObservableEvents($observables)
* Register a model event with the dispatcher.
*
* @param string $event
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
protected static function registerModelEvent($event, $callback)
@@ -211,7 +134,7 @@ protected function fireModelEvent($event, $halt = true)
// First, we will get the proper method to call on the event dispatcher, and then we
// will attempt to fire a custom, object based event for the given event. If that
// returns a result we can return that result, or we'll call the string events.
- $method = $halt ? 'until' : 'dispatch';
+ $method = $halt ? 'until' : 'fire';
$result = $this->filterModelEventResults(
$this->fireCustomModelEvent($event, $method)
@@ -230,8 +153,8 @@ protected function fireModelEvent($event, $halt = true)
* Fire a custom model event for the given event.
*
* @param string $event
- * @param 'until'|'dispatch' $method
- * @return array|null|void
+ * @param string $method
+ * @return mixed|null
*/
protected function fireCustomModelEvent($event, $method)
{
@@ -263,21 +186,10 @@ protected function filterModelEventResults($result)
return $result;
}
- /**
- * Register a retrieved model event with the dispatcher.
- *
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
- * @return void
- */
- public static function retrieved($callback)
- {
- static::registerModelEvent('retrieved', $callback);
- }
-
/**
* Register a saving model event with the dispatcher.
*
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function saving($callback)
@@ -288,7 +200,7 @@ public static function saving($callback)
/**
* Register a saved model event with the dispatcher.
*
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function saved($callback)
@@ -299,7 +211,7 @@ public static function saved($callback)
/**
* Register an updating model event with the dispatcher.
*
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function updating($callback)
@@ -310,7 +222,7 @@ public static function updating($callback)
/**
* Register an updated model event with the dispatcher.
*
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function updated($callback)
@@ -321,7 +233,7 @@ public static function updated($callback)
/**
* Register a creating model event with the dispatcher.
*
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function creating($callback)
@@ -332,7 +244,7 @@ public static function creating($callback)
/**
* Register a created model event with the dispatcher.
*
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function created($callback)
@@ -340,21 +252,10 @@ public static function created($callback)
static::registerModelEvent('created', $callback);
}
- /**
- * Register a replicating model event with the dispatcher.
- *
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
- * @return void
- */
- public static function replicating($callback)
- {
- static::registerModelEvent('replicating', $callback);
- }
-
/**
* Register a deleting model event with the dispatcher.
*
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function deleting($callback)
@@ -365,7 +266,7 @@ public static function deleting($callback)
/**
* Register a deleted model event with the dispatcher.
*
- * @param \Illuminate\Events\QueuedClosure|callable|array|class-string $callback
+ * @param \Closure|string $callback
* @return void
*/
public static function deleted($callback)
@@ -374,7 +275,7 @@ public static function deleted($callback)
}
/**
- * Remove all the event listeners for the model.
+ * Remove all of the event listeners for the model.
*
* @return void
*/
@@ -389,26 +290,12 @@ public static function flushEventListeners()
foreach ($instance->getObservableEvents() as $event) {
static::$dispatcher->forget("eloquent.{$event}: ".static::class);
}
-
- foreach ($instance->dispatchesEvents as $event) {
- static::$dispatcher->forget($event);
- }
- }
-
- /**
- * Get the event map for the model.
- *
- * @return array
- */
- public function dispatchesEvents()
- {
- return $this->dispatchesEvents;
}
/**
* Get the event dispatcher instance.
*
- * @return \Illuminate\Contracts\Events\Dispatcher|null
+ * @return \Illuminate\Contracts\Events\Dispatcher
*/
public static function getEventDispatcher()
{
@@ -435,27 +322,4 @@ public static function unsetEventDispatcher()
{
static::$dispatcher = null;
}
-
- /**
- * Execute a callback without firing any model events for any model type.
- *
- * @param callable $callback
- * @return mixed
- */
- public static function withoutEvents(callable $callback)
- {
- $dispatcher = static::getEventDispatcher();
-
- if ($dispatcher) {
- static::setEventDispatcher(new NullDispatcher($dispatcher));
- }
-
- try {
- return $callback();
- } finally {
- if ($dispatcher) {
- static::setEventDispatcher($dispatcher);
- }
- }
- }
}
diff --git a/Eloquent/Concerns/HasGlobalScopes.php b/Eloquent/Concerns/HasGlobalScopes.php
index 635ac8d1fe..97a549f6c7 100644
--- a/Eloquent/Concerns/HasGlobalScopes.php
+++ b/Eloquent/Concerns/HasGlobalScopes.php
@@ -3,85 +3,32 @@
namespace Illuminate\Database\Eloquent\Concerns;
use Closure;
-use Illuminate\Database\Eloquent\Attributes\ScopedBy;
-use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Arr;
-use Illuminate\Support\Collection;
use InvalidArgumentException;
-use ReflectionAttribute;
-use ReflectionClass;
+use Illuminate\Database\Eloquent\Scope;
trait HasGlobalScopes
{
- /**
- * Boot the has global scopes trait for a model.
- *
- * @return void
- */
- public static function bootHasGlobalScopes()
- {
- static::addGlobalScopes(static::resolveGlobalScopeAttributes());
- }
-
- /**
- * Resolve the global scope class names from the attributes.
- *
- * @return array
- */
- public static function resolveGlobalScopeAttributes()
- {
- $reflectionClass = new ReflectionClass(static::class);
-
- $attributes = (new Collection($reflectionClass->getAttributes(ScopedBy::class, ReflectionAttribute::IS_INSTANCEOF)));
-
- foreach ($reflectionClass->getTraits() as $trait) {
- $attributes->push(...$trait->getAttributes(ScopedBy::class, ReflectionAttribute::IS_INSTANCEOF));
- }
-
- return $attributes->map(fn ($attribute) => $attribute->getArguments())
- ->flatten()
- ->all();
- }
-
/**
* Register a new global scope on the model.
*
- * @param \Illuminate\Database\Eloquent\Scope|(\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string $scope
- * @param \Illuminate\Database\Eloquent\Scope|(\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|null $implementation
+ * @param \Illuminate\Database\Eloquent\Scope|\Closure|string $scope
+ * @param \Closure|null $implementation
* @return mixed
*
* @throws \InvalidArgumentException
*/
- public static function addGlobalScope($scope, $implementation = null)
+ public static function addGlobalScope($scope, Closure $implementation = null)
{
- if (is_string($scope) && ($implementation instanceof Closure || $implementation instanceof Scope)) {
+ if (is_string($scope) && ! is_null($implementation)) {
return static::$globalScopes[static::class][$scope] = $implementation;
} elseif ($scope instanceof Closure) {
return static::$globalScopes[static::class][spl_object_hash($scope)] = $scope;
} elseif ($scope instanceof Scope) {
return static::$globalScopes[static::class][get_class($scope)] = $scope;
- } elseif (is_string($scope) && class_exists($scope) && is_subclass_of($scope, Scope::class)) {
- return static::$globalScopes[static::class][$scope] = new $scope;
}
- throw new InvalidArgumentException('Global scope must be an instance of Closure or Scope or be a class name of a class extending '.Scope::class);
- }
-
- /**
- * Register multiple global scopes on the model.
- *
- * @param array $scopes
- * @return void
- */
- public static function addGlobalScopes(array $scopes)
- {
- foreach ($scopes as $key => $scope) {
- if (is_string($key)) {
- static::addGlobalScope($key, $scope);
- } else {
- static::addGlobalScope($scope);
- }
- }
+ throw new InvalidArgumentException('Global scope must be an instance of Closure or Scope.');
}
/**
@@ -99,7 +46,7 @@ public static function hasGlobalScope($scope)
* Get a global scope registered with the model.
*
* @param \Illuminate\Database\Eloquent\Scope|string $scope
- * @return \Illuminate\Database\Eloquent\Scope|(\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|null
+ * @return \Illuminate\Database\Eloquent\Scope|\Closure|null
*/
public static function getGlobalScope($scope)
{
@@ -112,27 +59,6 @@ public static function getGlobalScope($scope)
);
}
- /**
- * Get all of the global scopes that are currently registered.
- *
- * @return array
- */
- public static function getAllGlobalScopes()
- {
- return static::$globalScopes;
- }
-
- /**
- * Set the current global scopes.
- *
- * @param array $scopes
- * @return void
- */
- public static function setAllGlobalScopes($scopes)
- {
- static::$globalScopes = $scopes;
- }
-
/**
* Get the global scopes for this class instance.
*
diff --git a/Eloquent/Concerns/HasRelationships.php b/Eloquent/Concerns/HasRelationships.php
index 9380eb10e0..6f054bd4a5 100644
--- a/Eloquent/Concerns/HasRelationships.php
+++ b/Eloquent/Concerns/HasRelationships.php
@@ -2,28 +2,20 @@
namespace Illuminate\Database\Eloquent\Concerns;
-use Closure;
-use Illuminate\Database\ClassMorphViolationException;
-use Illuminate\Database\Eloquent\Attributes\Initialize;
-use Illuminate\Database\Eloquent\Attributes\Touches;
-use Illuminate\Database\Eloquent\Builder;
-use Illuminate\Database\Eloquent\Collection as EloquentCollection;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Model;
-use Illuminate\Database\Eloquent\PendingHasThroughRelationship;
-use Illuminate\Database\Eloquent\Relations\BelongsTo;
-use Illuminate\Database\Eloquent\Relations\BelongsToMany;
-use Illuminate\Database\Eloquent\Relations\HasMany;
-use Illuminate\Database\Eloquent\Relations\HasManyThrough;
+use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasOne;
-use Illuminate\Database\Eloquent\Relations\HasOneThrough;
-use Illuminate\Database\Eloquent\Relations\MorphMany;
-use Illuminate\Database\Eloquent\Relations\MorphOne;
+use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
-use Illuminate\Database\Eloquent\Relations\MorphToMany;
-use Illuminate\Database\Eloquent\Relations\Pivot;
+use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\Relation;
-use Illuminate\Support\Arr;
-use Illuminate\Support\Str;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\MorphMany;
+use Illuminate\Database\Eloquent\Relations\MorphToMany;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasManyThrough;
trait HasRelationships
{
@@ -41,188 +33,23 @@ trait HasRelationships
*/
protected $touches = [];
- /**
- * The relationship autoloader callback.
- *
- * @var \Closure|null
- */
- protected $relationAutoloadCallback = null;
-
- /**
- * The relationship autoloader callback context.
- *
- * @var mixed
- */
- protected $relationAutoloadContext = null;
-
/**
* The many to many relationship methods.
*
- * @var string[]
+ * @var array
*/
public static $manyMethods = [
'belongsToMany', 'morphToMany', 'morphedByMany',
+ 'guessBelongsToManyRelation', 'findFirstMethodThatIsntRelation',
];
- /**
- * The relation resolver callbacks.
- *
- * @var array
- */
- protected static $relationResolvers = [];
-
- /**
- * Initialize the HasRelationships trait.
- *
- * @return void
- */
- #[Initialize]
- public function initializeHasRelationships()
- {
- if (empty($this->touches)) {
- $this->touches = static::resolveClassAttribute(Touches::class, 'relations') ?? [];
- }
- }
-
- /**
- * Get the dynamic relation resolver if defined or inherited, or return null.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $class
- * @param string $key
- * @return Closure|null
- */
- public function relationResolver($class, $key)
- {
- if ($resolver = static::$relationResolvers[$class][$key] ?? null) {
- return $resolver;
- }
-
- if ($parent = get_parent_class($class)) {
- return $this->relationResolver($parent, $key);
- }
-
- return null;
- }
-
- /**
- * Define a dynamic relation resolver.
- *
- * @param string $name
- * @param \Closure $callback
- * @return void
- */
- public static function resolveRelationUsing($name, Closure $callback)
- {
- static::$relationResolvers = array_replace_recursive(
- static::$relationResolvers,
- [static::class => [$name => $callback]]
- );
- }
-
- /**
- * Determine if a relationship autoloader callback has been defined.
- *
- * @return bool
- */
- public function hasRelationAutoloadCallback()
- {
- return ! is_null($this->relationAutoloadCallback);
- }
-
- /**
- * Define an automatic relationship autoloader callback for this model and its relations.
- *
- * @param \Closure $callback
- * @param mixed $context
- * @return $this
- */
- public function autoloadRelationsUsing(Closure $callback, $context = null)
- {
- // Prevent circular relation autoloading...
- if ($context && $this->relationAutoloadContext === $context) {
- return $this;
- }
-
- $this->relationAutoloadCallback = $callback;
- $this->relationAutoloadContext = $context;
-
- foreach ($this->relations as $key => $value) {
- $this->propagateRelationAutoloadCallbackToRelation($key, $value);
- }
-
- return $this;
- }
-
- /**
- * Attempt to autoload the given relationship using the autoload callback.
- *
- * @param string $key
- * @return bool
- */
- protected function attemptToAutoloadRelation($key)
- {
- if (! $this->hasRelationAutoloadCallback()) {
- return false;
- }
-
- $this->invokeRelationAutoloadCallbackFor($key, []);
-
- return $this->relationLoaded($key);
- }
-
- /**
- * Invoke the relationship autoloader callback for the given relationships.
- *
- * @param string $key
- * @param array $tuples
- * @return void
- */
- protected function invokeRelationAutoloadCallbackFor($key, $tuples)
- {
- $tuples = array_merge([[$key, get_class($this)]], $tuples);
-
- call_user_func($this->relationAutoloadCallback, $tuples);
- }
-
- /**
- * Propagate the relationship autoloader callback to the given related models.
- *
- * @param string $key
- * @param mixed $models
- * @return void
- */
- protected function propagateRelationAutoloadCallbackToRelation($key, $models)
- {
- if (! $this->hasRelationAutoloadCallback() || ! $models) {
- return;
- }
-
- if ($models instanceof Model) {
- $models = [$models];
- }
-
- if (! is_iterable($models)) {
- return;
- }
-
- $callback = fn (array $tuples) => $this->invokeRelationAutoloadCallbackFor($key, $tuples);
-
- foreach ($models as $model) {
- $model->autoloadRelationsUsing($callback, $this->relationAutoloadContext);
- }
- }
-
/**
* Define a one-to-one relationship.
*
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $related
- * @param string|null $foreignKey
- * @param string|null $localKey
- * @return \Illuminate\Database\Eloquent\Relations\HasOne
+ * @param string $related
+ * @param string $foreignKey
+ * @param string $localKey
+ * @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function hasOne($related, $foreignKey = null, $localKey = null)
{
@@ -232,131 +59,40 @@ public function hasOne($related, $foreignKey = null, $localKey = null)
$localKey = $localKey ?: $this->getKeyName();
- return $this->newHasOne($instance->newQuery(), $this, $instance->qualifyColumn($foreignKey), $localKey);
- }
-
- /**
- * Instantiate a new HasOne relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param TDeclaringModel $parent
- * @param string $foreignKey
- * @param string $localKey
- * @return \Illuminate\Database\Eloquent\Relations\HasOne
- */
- protected function newHasOne(Builder $query, Model $parent, $foreignKey, $localKey)
- {
- return new HasOne($query, $parent, $foreignKey, $localKey);
- }
-
- /**
- * Define a has-one-through relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TIntermediateModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $related
- * @param class-string $through
- * @param string|null $firstKey
- * @param string|null $secondKey
- * @param string|null $localKey
- * @param string|null $secondLocalKey
- * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
- */
- public function hasOneThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
- {
- $through = $this->newRelatedThroughInstance($through);
-
- $firstKey = $firstKey ?: $this->getForeignKey();
-
- $secondKey = $secondKey ?: $through->getForeignKey();
-
- return $this->newHasOneThrough(
- $this->newRelatedInstance($related)->newQuery(),
- $this,
- $through,
- $firstKey,
- $secondKey,
- $localKey ?: $this->getKeyName(),
- $secondLocalKey ?: $through->getKeyName(),
- );
- }
-
- /**
- * Instantiate a new HasOneThrough relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TIntermediateModel of \Illuminate\Database\Eloquent\Model
- * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param TDeclaringModel $farParent
- * @param TIntermediateModel $throughParent
- * @param string $firstKey
- * @param string $secondKey
- * @param string $localKey
- * @param string $secondLocalKey
- * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
- */
- protected function newHasOneThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
- {
- return new HasOneThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
+ return new HasOne($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
}
/**
* Define a polymorphic one-to-one relationship.
*
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $related
+ * @param string $related
* @param string $name
- * @param string|null $type
- * @param string|null $id
- * @param string|null $localKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphOne
+ * @param string $type
+ * @param string $id
+ * @param string $localKey
+ * @return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
{
$instance = $this->newRelatedInstance($related);
- [$type, $id] = $this->getMorphs($name, $type, $id);
+ list($type, $id) = $this->getMorphs($name, $type, $id);
- $localKey = $localKey ?: $this->getKeyName();
+ $table = $instance->getTable();
- return $this->newMorphOne($instance->newQuery(), $this, $instance->qualifyColumn($type), $instance->qualifyColumn($id), $localKey);
- }
+ $localKey = $localKey ?: $this->getKeyName();
- /**
- * Instantiate a new MorphOne relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param TDeclaringModel $parent
- * @param string $type
- * @param string $id
- * @param string $localKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphOne
- */
- protected function newMorphOne(Builder $query, Model $parent, $type, $id, $localKey)
- {
- return new MorphOne($query, $parent, $type, $id, $localKey);
+ return new MorphOne($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
}
/**
* Define an inverse one-to-one or many relationship.
*
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $related
- * @param string|null $foreignKey
- * @param string|null $ownerKey
- * @param string|null $relation
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ * @param string $related
+ * @param string $foreignKey
+ * @param string $ownerKey
+ * @param string $relation
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relation = null)
{
@@ -376,60 +112,41 @@ public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relat
$foreignKey = Str::snake($relation).'_'.$instance->getKeyName();
}
- // Once we have the foreign key names we'll just create a new Eloquent query
- // for the related models and return the relationship instance which will
- // actually be responsible for retrieving and hydrating every relation.
+ // Once we have the foreign key names, we'll just create a new Eloquent query
+ // for the related models and returns the relationship instance which will
+ // actually be responsible for retrieving and hydrating every relations.
$ownerKey = $ownerKey ?: $instance->getKeyName();
- return $this->newBelongsTo(
+ return new BelongsTo(
$instance->newQuery(), $this, $foreignKey, $ownerKey, $relation
);
}
- /**
- * Instantiate a new BelongsTo relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param TDeclaringModel $child
- * @param string $foreignKey
- * @param string $ownerKey
- * @param string $relation
- * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
- */
- protected function newBelongsTo(Builder $query, Model $child, $foreignKey, $ownerKey, $relation)
- {
- return new BelongsTo($query, $child, $foreignKey, $ownerKey, $relation);
- }
-
/**
* Define a polymorphic, inverse one-to-one or many relationship.
*
- * @param string|null $name
- * @param string|null $type
- * @param string|null $id
- * @param string|null $ownerKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphTo<\Illuminate\Database\Eloquent\Model, $this>
+ * @param string $name
+ * @param string $type
+ * @param string $id
+ * @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
- public function morphTo($name = null, $type = null, $id = null, $ownerKey = null)
+ public function morphTo($name = null, $type = null, $id = null)
{
// If no name is provided, we will use the backtrace to get the function name
// since that is most likely the name of the polymorphic interface. We can
// use that to get both the class and foreign key that will be utilized.
$name = $name ?: $this->guessBelongsToRelation();
- [$type, $id] = $this->getMorphs(
+ list($type, $id) = $this->getMorphs(
Str::snake($name), $type, $id
);
// If the type value is null it is probably safe to assume we're eager loading
// the relationship. In this case we'll just pass in a dummy query where we
// need to remove any eager loads that may already be defined on a model.
- return is_null($class = $this->getAttributeFromArray($type)) || $class === ''
- ? $this->morphEagerTo($name, $type, $id, $ownerKey)
- : $this->morphInstanceTo($class, $name, $type, $id, $ownerKey);
+ return empty($class = $this->{$type})
+ ? $this->morphEagerTo($name, $type, $id)
+ : $this->morphInstanceTo($class, $name, $type, $id);
}
/**
@@ -438,13 +155,12 @@ public function morphTo($name = null, $type = null, $id = null, $ownerKey = null
* @param string $name
* @param string $type
* @param string $id
- * @param string|null $ownerKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphTo<\Illuminate\Database\Eloquent\Model, $this>
+ * @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
- protected function morphEagerTo($name, $type, $id, $ownerKey)
+ protected function morphEagerTo($name, $type, $id)
{
- return $this->newMorphTo(
- $this->newQuery()->setEagerLoads([]), $this, $id, $ownerKey, $type, $name
+ return new MorphTo(
+ $this->newQuery()->setEagerLoads([]), $this, $id, null, $type, $name
);
}
@@ -455,39 +171,19 @@ protected function morphEagerTo($name, $type, $id, $ownerKey)
* @param string $name
* @param string $type
* @param string $id
- * @param string|null $ownerKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphTo<\Illuminate\Database\Eloquent\Model, $this>
+ * @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
- protected function morphInstanceTo($target, $name, $type, $id, $ownerKey)
+ protected function morphInstanceTo($target, $name, $type, $id)
{
$instance = $this->newRelatedInstance(
static::getActualClassNameForMorph($target)
);
- return $this->newMorphTo(
- $instance->newQuery(), $this, $id, $ownerKey ?? $instance->getKeyName(), $type, $name
+ return new MorphTo(
+ $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
);
}
- /**
- * Instantiate a new MorphTo relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param TDeclaringModel $parent
- * @param string $foreignKey
- * @param string|null $ownerKey
- * @param string $type
- * @param string $relation
- * @return \Illuminate\Database\Eloquent\Relations\MorphTo
- */
- protected function newMorphTo(Builder $query, Model $parent, $foreignKey, $ownerKey, $type, $relation)
- {
- return new MorphTo($query, $parent, $foreignKey, $ownerKey, $type, $relation);
- }
-
/**
* Retrieve the actual class name for a given morph class.
*
@@ -496,7 +192,7 @@ protected function newMorphTo(Builder $query, Model $parent, $foreignKey, $owner
*/
public static function getActualClassNameForMorph($class)
{
- return Arr::get(Relation::morphMap() ?: [], $class, $class);
+ return Arr::get(Relation::morphMap(), $class, $class);
}
/**
@@ -506,45 +202,18 @@ public static function getActualClassNameForMorph($class)
*/
protected function guessBelongsToRelation()
{
- [, , $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
+ list($one, $two, $caller) = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
return $caller['function'];
}
- /**
- * Create a pending has-many-through or has-one-through relationship.
- *
- * @template TIntermediateModel of \Illuminate\Database\Eloquent\Model
- *
- * @param string|\Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Eloquent\Relations\HasOne $relationship
- * @return (
- * $relationship is string
- * ? \Illuminate\Database\Eloquent\PendingHasThroughRelationship<\Illuminate\Database\Eloquent\Model, $this>
- * : (
- * $relationship is \Illuminate\Database\Eloquent\Relations\HasMany
- * ? \Illuminate\Database\Eloquent\PendingHasThroughRelationship>
- * : \Illuminate\Database\Eloquent\PendingHasThroughRelationship>
- * )
- * )
- */
- public function through($relationship)
- {
- if (is_string($relationship)) {
- $relationship = $this->{$relationship}();
- }
-
- return new PendingHasThroughRelationship($this, $relationship);
- }
-
/**
* Define a one-to-many relationship.
*
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $related
- * @param string|null $foreignKey
- * @param string|null $localKey
- * @return \Illuminate\Database\Eloquent\Relations\HasMany
+ * @param string $related
+ * @param string $foreignKey
+ * @param string $localKey
+ * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function hasMany($related, $foreignKey = null, $localKey = null)
{
@@ -554,93 +223,45 @@ public function hasMany($related, $foreignKey = null, $localKey = null)
$localKey = $localKey ?: $this->getKeyName();
- return $this->newHasMany(
- $instance->newQuery(), $this, $instance->qualifyColumn($foreignKey), $localKey
+ return new HasMany(
+ $instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey
);
}
- /**
- * Instantiate a new HasMany relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param TDeclaringModel $parent
- * @param string $foreignKey
- * @param string $localKey
- * @return \Illuminate\Database\Eloquent\Relations\HasMany
- */
- protected function newHasMany(Builder $query, Model $parent, $foreignKey, $localKey)
- {
- return new HasMany($query, $parent, $foreignKey, $localKey);
- }
-
/**
* Define a has-many-through relationship.
*
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TIntermediateModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $related
- * @param class-string $through
+ * @param string $related
+ * @param string $through
* @param string|null $firstKey
* @param string|null $secondKey
* @param string|null $localKey
- * @param string|null $secondLocalKey
- * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
+ * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
- public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
+ public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null)
{
- $through = $this->newRelatedThroughInstance($through);
+ $through = new $through;
$firstKey = $firstKey ?: $this->getForeignKey();
$secondKey = $secondKey ?: $through->getForeignKey();
- return $this->newHasManyThrough(
- $this->newRelatedInstance($related)->newQuery(),
- $this,
- $through,
- $firstKey,
- $secondKey,
- $localKey ?: $this->getKeyName(),
- $secondLocalKey ?: $through->getKeyName()
- );
- }
+ $localKey = $localKey ?: $this->getKeyName();
- /**
- * Instantiate a new HasManyThrough relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TIntermediateModel of \Illuminate\Database\Eloquent\Model
- * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param TDeclaringModel $farParent
- * @param TIntermediateModel $throughParent
- * @param string $firstKey
- * @param string $secondKey
- * @param string $localKey
- * @param string $secondLocalKey
- * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
- */
- protected function newHasManyThrough(Builder $query, Model $farParent, Model $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey)
- {
- return new HasManyThrough($query, $farParent, $throughParent, $firstKey, $secondKey, $localKey, $secondLocalKey);
+ $instance = $this->newRelatedInstance($related);
+
+ return new HasManyThrough($instance->newQuery(), $this, $through, $firstKey, $secondKey, $localKey);
}
/**
* Define a polymorphic one-to-many relationship.
*
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $related
+ * @param string $related
* @param string $name
- * @param string|null $type
- * @param string|null $id
- * @param string|null $localKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphMany
+ * @param string $type
+ * @param string $id
+ * @param string $localKey
+ * @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
{
@@ -649,54 +270,30 @@ public function morphMany($related, $name, $type = null, $id = null, $localKey =
// Here we will gather up the morph type and ID for the relationship so that we
// can properly query the intermediate table of a relation. Finally, we will
// get the table and create the relationship instances for the developers.
- [$type, $id] = $this->getMorphs($name, $type, $id);
+ list($type, $id) = $this->getMorphs($name, $type, $id);
- $localKey = $localKey ?: $this->getKeyName();
+ $table = $instance->getTable();
- return $this->newMorphMany($instance->newQuery(), $this, $instance->qualifyColumn($type), $instance->qualifyColumn($id), $localKey);
- }
+ $localKey = $localKey ?: $this->getKeyName();
- /**
- * Instantiate a new MorphMany relationship.
- *
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- * @template TDeclaringModel of \Illuminate\Database\Eloquent\Model
- *
- * @param \Illuminate\Database\Eloquent\Builder $query
- * @param TDeclaringModel $parent
- * @param string $type
- * @param string $id
- * @param string $localKey
- * @return \Illuminate\Database\Eloquent\Relations\MorphMany
- */
- protected function newMorphMany(Builder $query, Model $parent, $type, $id, $localKey)
- {
- return new MorphMany($query, $parent, $type, $id, $localKey);
+ return new MorphMany($instance->newQuery(), $this, $table.'.'.$type, $table.'.'.$id, $localKey);
}
/**
* Define a many-to-many relationship.
*
- * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
- *
- * @param class-string $related
- * @param string|class-string<\Illuminate\Database\Eloquent\Model>|null $table
- * @param string|null $foreignPivotKey
- * @param string|null $relatedPivotKey
- * @param string|null $parentKey
- * @param string|null $relatedKey
- * @param string|null $relation
- * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany