upgraded dependencies

This commit is contained in:
RafficMohammed
2023-01-08 01:59:16 +05:30
parent 51056e3aad
commit f9ae387337
6895 changed files with 133617 additions and 178680 deletions

View File

@@ -3,6 +3,7 @@
namespace Illuminate\Auth\Access;
use Exception;
use Throwable;
class AuthorizationException extends Exception
{
@@ -18,10 +19,10 @@ class AuthorizationException extends Exception
*
* @param string|null $message
* @param mixed $code
* @param \Exception|null $previous
* @param \Throwable|null $previous
* @return void
*/
public function __construct($message = null, $code = null, Exception $previous = null)
public function __construct($message = null, $code = null, Throwable $previous = null)
{
parent::__construct($message ?? 'This action is unauthorized.', 0, $previous);

View File

@@ -126,6 +126,10 @@ class Gate implements GateContract
*/
public function define($ability, $callback)
{
if (is_array($callback) && isset($callback[0]) && is_string($callback[0])) {
$callback = $callback[0].'@'.$callback[1];
}
if (is_callable($callback)) {
$this->abilities[$ability] = $callback;
} elseif (is_string($callback)) {

View File

@@ -73,7 +73,7 @@ trait GuardHelpers
/**
* Get the ID for the currently authenticated user.
*
* @return int|null
* @return int|string|null
*/
public function id()
{

View File

@@ -5,8 +5,9 @@ namespace Illuminate\Auth\Middleware;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Auth\Factory as Auth;
use Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests;
class Authenticate
class Authenticate implements AuthenticatesRequests
{
/**
* The authentication factory instance.

View File

@@ -15,6 +15,13 @@ class ResetPassword extends Notification
*/
public $token;
/**
* The callback that should be used to create the reset password URL.
*
* @var \Closure|null
*/
public static $createUrlCallback;
/**
* The callback that should be used to build the mail message.
*
@@ -56,14 +63,34 @@ class ResetPassword extends Notification
return call_user_func(static::$toMailCallback, $notifiable, $this->token);
}
if (static::$createUrlCallback) {
$url = call_user_func(static::$createUrlCallback, $notifiable, $this->token);
} else {
$url = url(route('password.reset', [
'token' => $this->token,
'email' => $notifiable->getEmailForPasswordReset(),
], false));
}
return (new MailMessage)
->subject(Lang::get('Reset Password Notification'))
->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
->action(Lang::get('Reset Password'), url(route('password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->action(Lang::get('Reset Password'), $url)
->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
->line(Lang::get('If you did not request a password reset, no further action is required.'));
}
/**
* Set a callback that should be used when creating the reset password button URL.
*
* @param \Closure $callback
* @return void
*/
public static function createUrlUsing($callback)
{
static::$createUrlCallback = $callback;
}
/**
* Set a callback that should be used when building the notification mail message.
*

View File

@@ -55,8 +55,7 @@ class PasswordBroker implements PasswordBrokerContract
return static::INVALID_USER;
}
if (method_exists($this->tokens, 'recentlyCreatedToken') &&
$this->tokens->recentlyCreatedToken($user)) {
if ($this->tokens->recentlyCreatedToken($user)) {
return static::RESET_THROTTLED;
}

View File

@@ -23,6 +23,14 @@ interface TokenRepositoryInterface
*/
public function exists(CanResetPasswordContract $user, $token);
/**
* Determine if the given user recently created a password reset token.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return bool
*/
public function recentlyCreatedToken(CanResetPasswordContract $user);
/**
* Delete a token record.
*

View File

@@ -199,7 +199,7 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
/**
* Get the ID for the currently authenticated user.
*
* @return int|null
* @return int|string|null
*/
public function id()
{
@@ -632,7 +632,8 @@ class SessionGuard implements StatefulGuard, SupportsBasicAuth
/**
* Fires the validated event if the dispatcher is set.
*
* @param $user
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
protected function fireValidatedEvent($user)
{

View File

@@ -15,10 +15,10 @@
],
"require": {
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/http": "^6.0",
"illuminate/queue": "^6.0",
"illuminate/support": "^6.0"
"illuminate/contracts": "^7.0",
"illuminate/http": "^7.0",
"illuminate/queue": "^7.0",
"illuminate/support": "^7.0"
},
"autoload": {
"psr-4": {
@@ -27,13 +27,13 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"suggest": {
"illuminate/console": "Required to use the auth:clear-resets command (^6.0).",
"illuminate/queue": "Required to fire login / logout events (^6.0).",
"illuminate/session": "Required to use the session based guard (^6.0)."
"illuminate/console": "Required to use the auth:clear-resets command (^7.0).",
"illuminate/queue": "Required to fire login / logout events (^7.0).",
"illuminate/session": "Required to use the session based guard (^7.0)."
},
"config": {
"sort-packages": true

View File

@@ -10,6 +10,7 @@ use Illuminate\Broadcasting\Broadcasters\RedisBroadcaster;
use Illuminate\Contracts\Broadcasting\Factory as FactoryContract;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcherContract;
use Illuminate\Contracts\Foundation\CachesRoutes;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Pusher\Pusher;
@@ -22,7 +23,7 @@ class BroadcastManager implements FactoryContract
/**
* The application instance.
*
* @var \Illuminate\Contracts\Foundation\Application
* @var \Illuminate\Contracts\Container\Container
*/
protected $app;
@@ -43,7 +44,7 @@ class BroadcastManager implements FactoryContract
/**
* Create a new manager instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @param \Illuminate\Contracts\Container\Container $app
* @return void
*/
public function __construct($app)
@@ -59,7 +60,7 @@ class BroadcastManager implements FactoryContract
*/
public function routes(array $attributes = null)
{
if ($this->app->routesAreCached()) {
if ($this->app instanceof CachesRoutes && $this->app->routesAreCached()) {
return;
}

View File

@@ -262,7 +262,7 @@ abstract class Broadcaster implements BroadcasterContract
* Normalize the given callback into a callable.
*
* @param mixed $callback
* @return \Closure|callable
* @return callable
*/
protected function normalizeChannelHandlerToCallable($callback)
{

View File

@@ -17,10 +17,10 @@
"php": "^7.2.5|^8.0",
"ext-json": "*",
"psr/log": "^1.0",
"illuminate/bus": "^6.0",
"illuminate/contracts": "^6.0",
"illuminate/queue": "^6.0",
"illuminate/support": "^6.0"
"illuminate/bus": "^7.0",
"illuminate/contracts": "^7.0",
"illuminate/queue": "^7.0",
"illuminate/support": "^7.0"
},
"autoload": {
"psr-4": {
@@ -29,7 +29,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"suggest": {

View File

@@ -2,7 +2,10 @@
namespace Illuminate\Bus;
use Closure;
use Illuminate\Queue\CallQueuedClosure;
use Illuminate\Support\Arr;
use RuntimeException;
trait Queueable
{
@@ -43,6 +46,8 @@ trait Queueable
/**
* The middleware the job should be dispatched through.
*
* @var array
*/
public $middleware = [];
@@ -120,16 +125,6 @@ trait Queueable
return $this;
}
/**
* Get the middleware the job should be dispatched through.
*
* @return array
*/
public function middleware()
{
return [];
}
/**
* Specify the middleware the job should be dispatched through.
*
@@ -152,12 +147,33 @@ trait Queueable
public function chain($chain)
{
$this->chained = collect($chain)->map(function ($job) {
return serialize($job);
return $this->serializeJob($job);
})->all();
return $this;
}
/**
* Serialize a job for queuing.
*
* @param mixed $job
* @return string
*/
protected function serializeJob($job)
{
if ($job instanceof Closure) {
if (! class_exists(CallQueuedClosure::class)) {
throw new RuntimeException(
'To enable support for closure jobs, please install the illuminate/queue package.'
);
}
$job = CallQueuedClosure::create($job);
}
return serialize($job);
}
/**
* Dispatch the next job on the chain.
*

View File

@@ -15,9 +15,9 @@
],
"require": {
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/pipeline": "^6.0",
"illuminate/support": "^6.0"
"illuminate/contracts": "^7.0",
"illuminate/pipeline": "^7.0",
"illuminate/support": "^7.0"
},
"autoload": {
"psr-4": {
@@ -26,9 +26,12 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"suggest": {
"illuminate/queue": "Required to use closures when chaining jobs (^7.0)."
},
"config": {
"sort-packages": true
},

View File

@@ -2,7 +2,7 @@
namespace Illuminate\Cache;
use Carbon\Carbon;
use Illuminate\Support\Carbon;
class ArrayLock extends Lock
{

View File

@@ -23,6 +23,24 @@ class ArrayStore extends TaggableStore implements LockProvider
*/
public $locks = [];
/**
* Indicates if values are serialized within the store.
*
* @var bool
*/
protected $serializesValues;
/**
* Create a new Array store.
*
* @param bool $serializesValues
* @return void
*/
public function __construct($serializesValues = false)
{
$this->serializesValues = $serializesValues;
}
/**
* Retrieve an item from the cache by key.
*
@@ -45,7 +63,7 @@ class ArrayStore extends TaggableStore implements LockProvider
return;
}
return $item['value'];
return $this->serializesValues ? unserialize($item['value']) : $item['value'];
}
/**
@@ -59,7 +77,7 @@ class ArrayStore extends TaggableStore implements LockProvider
public function put($key, $value, $seconds)
{
$this->storage[$key] = [
'value' => $value,
'value' => $this->serializesValues ? serialize($value) : $value,
'expiresAt' => $this->calculateExpiration($seconds),
];
@@ -75,15 +93,17 @@ class ArrayStore extends TaggableStore implements LockProvider
*/
public function increment($key, $value = 1)
{
if (! isset($this->storage[$key])) {
$this->forever($key, $value);
if (! is_null($existing = $this->get($key))) {
return tap(((int) $existing) + $value, function ($incremented) use ($key) {
$value = $this->serializesValues ? serialize($incremented) : $incremented;
return $this->storage[$key]['value'];
$this->storage[$key]['value'] = $value;
});
}
$this->storage[$key]['value'] = ((int) $this->storage[$key]['value']) + $value;
$this->forever($key, $value);
return $this->storage[$key]['value'];
return $value;
}
/**

View File

@@ -138,11 +138,12 @@ class CacheManager implements FactoryContract
/**
* Create an instance of the array cache driver.
*
* @param array $config
* @return \Illuminate\Cache\Repository
*/
protected function createArrayDriver()
protected function createArrayDriver(array $config)
{
return $this->repository(new ArrayStore);
return $this->repository(new ArrayStore($config['serialize'] ?? false));
}
/**
@@ -213,7 +214,11 @@ class CacheManager implements FactoryContract
return $this->repository(
new DatabaseStore(
$connection, $config['table'], $this->getPrefix($config)
$connection,
$config['table'],
$this->getPrefix($config),
$config['lock_table'] ?? 'cache_locks',
$config['lock_lottery'] ?? [2, 100]
)
);
}
@@ -226,11 +231,21 @@ class CacheManager implements FactoryContract
*/
protected function createDynamodbDriver(array $config)
{
$client = $this->newDynamodbClient($config);
$dynamoConfig = [
'region' => $config['region'],
'version' => 'latest',
'endpoint' => $config['endpoint'] ?? null,
];
if ($config['key'] && $config['secret']) {
$dynamoConfig['credentials'] = Arr::only(
$config, ['key', 'secret', 'token']
);
}
return $this->repository(
new DynamoDbStore(
$client,
new DynamoDbClient($dynamoConfig),
$config['table'],
$config['attributes']['key'] ?? 'key',
$config['attributes']['value'] ?? 'value',
@@ -240,28 +255,6 @@ class CacheManager implements FactoryContract
);
}
/**
* Create new DynamoDb Client instance.
*
* @return DynamoDbClient
*/
protected function newDynamodbClient(array $config)
{
$dynamoConfig = [
'region' => $config['region'],
'version' => 'latest',
'endpoint' => $config['endpoint'] ?? null,
];
if (isset($config['key']) && isset($config['secret'])) {
$dynamoConfig['credentials'] = Arr::only(
$config, ['key', 'secret', 'token']
);
}
return new DynamoDbClient($dynamoConfig);
}
/**
* Create a new cache repository with the given implementation.
*

View File

@@ -0,0 +1,139 @@
<?php
namespace Illuminate\Cache;
use Illuminate\Database\Connection;
use Illuminate\Database\QueryException;
use Illuminate\Support\Carbon;
class DatabaseLock extends Lock
{
/**
* The database connection instance.
*
* @var \Illuminate\Database\Connection
*/
protected $connection;
/**
* The database table name.
*
* @var string
*/
protected $table;
/**
* The prune probability odds.
*
* @var array
*/
protected $lottery;
/**
* Create a new lock instance.
*
* @param \Illuminate\Database\Connection $connection
* @param string $table
* @param string $name
* @param int $seconds
* @param string|null $owner
* @param array $lottery
* @return void
*/
public function __construct(Connection $connection, $table, $name, $seconds, $owner = null, $lottery = [2, 100])
{
parent::__construct($name, $seconds, $owner);
$this->connection = $connection;
$this->table = $table;
$this->lottery = $lottery;
}
/**
* Attempt to acquire the lock.
*
* @return bool
*/
public function acquire()
{
$acquired = false;
try {
$this->connection->table($this->table)->insert([
'key' => $this->name,
'owner' => $this->owner,
'expiration' => $this->expiresAt(),
]);
$acquired = true;
} catch (QueryException $e) {
$updated = $this->connection->table($this->table)
->where('key', $this->name)
->where(function ($query) {
return $query->where('owner', $this->owner)->orWhere('expiration', '<=', time());
})->update([
'owner' => $this->owner,
'expiration' => $this->expiresAt(),
]);
$acquired = $updated >= 1;
}
if (random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
$this->connection->table($this->table)->where('expiration', '<=', time())->delete();
}
return $acquired;
}
/**
* Get the UNIX timestamp indicating when the lock should expire.
*
* @return int
*/
protected function expiresAt()
{
return $this->seconds > 0 ? time() + $this->seconds : Carbon::now()->addDays(1)->getTimestamp();
}
/**
* Release the lock.
*
* @return bool
*/
public function release()
{
if ($this->isOwnedByCurrentProcess()) {
$this->connection->table($this->table)
->where('key', $this->name)
->where('owner', $this->owner)
->delete();
return true;
}
return false;
}
/**
* Releases this lock in disregard of ownership.
*
* @return void
*/
public function forceRelease()
{
$this->connection->table($this->table)
->where('key', $this->name)
->delete();
}
/**
* Returns the owner value written into the driver for this lock.
*
* @return string
*/
protected function getCurrentOwner()
{
return optional($this->connection->table($this->table)->where('key', $this->name)->first())->owner;
}
}

View File

@@ -4,13 +4,15 @@ namespace Illuminate\Cache;
use Closure;
use Exception;
use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\PostgresConnection;
use Illuminate\Database\QueryException;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Support\Str;
class DatabaseStore implements Store
class DatabaseStore implements LockProvider, Store
{
use InteractsWithTime, RetrievesMultipleKeys;
@@ -35,19 +37,41 @@ class DatabaseStore implements Store
*/
protected $prefix;
/**
* The name of the cache locks table.
*
* @var string
*/
protected $lockTable;
/**
* An array representation of the lock lottery odds.
*
* @var array
*/
protected $lockLottery;
/**
* Create a new database store.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param string $table
* @param string $prefix
* @param string $lockTable
* @param array $lockLottery
* @return void
*/
public function __construct(ConnectionInterface $connection, $table, $prefix = '')
public function __construct(ConnectionInterface $connection,
$table,
$prefix = '',
$lockTable = 'cache_locks',
$lockLottery = [2, 100])
{
$this->table = $table;
$this->prefix = $prefix;
$this->connection = $connection;
$this->lockTable = $lockTable;
$this->lockLottery = $lockLottery;
}
/**
@@ -94,9 +118,7 @@ class DatabaseStore implements Store
public function put($key, $value, $seconds)
{
$key = $this->prefix.$key;
$value = $this->serialize($value);
$expiration = $this->getTime() + $seconds;
try {
@@ -108,6 +130,35 @@ class DatabaseStore implements Store
}
}
/**
* Store an item in the cache if the key doesn't exist.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function add($key, $value, $seconds)
{
$key = $this->prefix.$key;
$value = $this->serialize($value);
$expiration = $this->getTime() + $seconds;
try {
return $this->table()->insert(compact('key', 'value', 'expiration'));
} catch (QueryException $e) {
return $this->table()
->where('key', $key)
->where('expiration', '<=', $this->getTime())
->update([
'value' => $value,
'expiration' => $expiration,
]) >= 1;
}
return false;
}
/**
* Increment the value of an item in the cache.
*
@@ -205,6 +256,38 @@ class DatabaseStore implements Store
return $this->put($key, $value, 315360000);
}
/**
* Get a lock instance.
*
* @param string $name
* @param int $seconds
* @param string|null $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function lock($name, $seconds = 0, $owner = null)
{
return new DatabaseLock(
$this->connection,
$this->lockTable,
$this->prefix.$name,
$seconds,
$owner,
$this->lockLottery
);
}
/**
* Restore a lock instance using the owner identifier.
*
* @param string $name
* @param string $owner
* @return \Illuminate\Contracts\Cache\Lock
*/
public function restoreLock($name, $owner)
{
return $this->lock($name, 0, $owner);
}
/**
* Remove an item from the cache.
*

View File

@@ -525,14 +525,4 @@ class DynamoDbStore implements LockProvider, Store
{
$this->prefix = ! empty($prefix) ? $prefix.':' : '';
}
/**
* Get the DynamoDb Client instance.
*
* @return DynamoDbClient
*/
public function getClient()
{
return $this->dynamo;
}
}

View File

@@ -32,6 +32,13 @@ abstract class Lock implements LockContract
*/
protected $owner;
/**
* The number of milliseconds to wait before re-attempting to acquire a lock while blocking.
*
* @var int
*/
protected $sleepMilliseconds = 250;
/**
* Create a new lock instance.
*
@@ -98,7 +105,7 @@ abstract class Lock implements LockContract
*
* @param int $seconds
* @param callable|null $callback
* @return mixed
* @return bool
*
* @throws \Illuminate\Contracts\Cache\LockTimeoutException
*/
@@ -107,7 +114,7 @@ abstract class Lock implements LockContract
$starting = $this->currentTime();
while (! $this->acquire()) {
usleep(250 * 1000);
usleep($this->sleepMilliseconds * 1000);
if ($this->currentTime() - $seconds >= $starting) {
throw new LockTimeoutException;
@@ -144,4 +151,17 @@ abstract class Lock implements LockContract
{
return $this->getCurrentOwner() === $this->owner;
}
/**
* Specify the number of milliseconds to sleep in between blocked lock aquisition attempts.
*
* @param int $milliseconds
* @return $this
*/
public function betweenBlockedAttemptsSleepFor($milliseconds)
{
$this->sleepMilliseconds = $milliseconds;
return $this;
}
}

View File

@@ -36,8 +36,6 @@ class RateLimiter
*/
public function tooManyAttempts($key, $maxAttempts)
{
$key = $this->cleanRateLimiterKey($key);
if ($this->attempts($key) >= $maxAttempts) {
if ($this->cache->has($key.':timer')) {
return true;
@@ -58,8 +56,6 @@ class RateLimiter
*/
public function hit($key, $decaySeconds = 60)
{
$key = $this->cleanRateLimiterKey($key);
$this->cache->add(
$key.':timer', $this->availableAt($decaySeconds), $decaySeconds
);
@@ -83,8 +79,6 @@ class RateLimiter
*/
public function attempts($key)
{
$key = $this->cleanRateLimiterKey($key);
return $this->cache->get($key, 0);
}
@@ -96,8 +90,6 @@ class RateLimiter
*/
public function resetAttempts($key)
{
$key = $this->cleanRateLimiterKey($key);
return $this->cache->forget($key);
}
@@ -110,8 +102,6 @@ class RateLimiter
*/
public function retriesLeft($key, $maxAttempts)
{
$key = $this->cleanRateLimiterKey($key);
$attempts = $this->attempts($key);
return $maxAttempts - $attempts;
@@ -125,8 +115,6 @@ class RateLimiter
*/
public function clear($key)
{
$key = $this->cleanRateLimiterKey($key);
$this->resetAttempts($key);
$this->cache->forget($key.':timer');
@@ -140,19 +128,6 @@ class RateLimiter
*/
public function availableIn($key)
{
$key = $this->cleanRateLimiterKey($key);
return $this->cache->get($key.':timer') - $this->currentTime();
}
/**
* Clean the rate limiter key from unicode characters.
*
* @param string $key
* @return string
*/
public function cleanRateLimiterKey($key)
{
return preg_replace('/&([a-z])[a-z]+;/i', '$1', htmlentities($key));
}
}

View File

@@ -15,8 +15,8 @@
],
"require": {
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/support": "^6.0"
"illuminate/contracts": "^7.0",
"illuminate/support": "^7.0"
},
"autoload": {
"psr-4": {
@@ -25,15 +25,15 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"suggest": {
"ext-memcached": "Required to use the memcache cache driver.",
"illuminate/database": "Required to use the database cache driver (^6.0).",
"illuminate/filesystem": "Required to use the file cache driver (^6.0).",
"illuminate/redis": "Required to use the redis cache driver (^6.0).",
"symfony/cache": "Required to PSR-6 cache bridge (^4.3.4)."
"illuminate/database": "Required to use the database cache driver (^7.0).",
"illuminate/filesystem": "Required to use the file cache driver (^7.0).",
"illuminate/redis": "Required to use the redis cache driver (^7.0).",
"symfony/cache": "Required to PSR-6 cache bridge (^5.0)."
},
"config": {
"sort-packages": true

View File

@@ -15,8 +15,8 @@
],
"require": {
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/support": "^6.0"
"illuminate/contracts": "^7.0",
"illuminate/support": "^7.0"
},
"autoload": {
"psr-4": {
@@ -25,7 +25,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"config": {

View File

@@ -116,7 +116,7 @@ class Application extends SymfonyApplication implements ApplicationContract
*/
public static function artisanBinary()
{
return ProcessUtils::escapeArgument(defined('ARTISAN_BINARY') ? ARTISAN_BINARY : 'artisan');
return defined('ARTISAN_BINARY') ? ProcessUtils::escapeArgument(ARTISAN_BINARY) : 'artisan';
}
/**

View File

@@ -127,11 +127,11 @@ class Command extends SymfonyCommand
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return mixed
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
return $this->laravel->call([$this, 'handle']);
return (int) $this->laravel->call([$this, 'handle']);
}
/**
@@ -170,7 +170,7 @@ class Command extends SymfonyCommand
/**
* {@inheritdoc}
*/
public function setHidden($hidden)
public function setHidden(bool $hidden)
{
parent::setHidden($this->hidden = $hidden);

View File

@@ -199,10 +199,10 @@ trait InteractsWithIO
* @param array $choices
* @param string|null $default
* @param mixed|null $attempts
* @param bool|null $multiple
* @return string
* @param bool $multiple
* @return string|array
*/
public function choice($question, array $choices, $default = null, $attempts = null, $multiple = null)
public function choice($question, array $choices, $default = null, $attempts = null, $multiple = false)
{
$question = new ChoiceQuestion($question, $choices, $default);

View File

@@ -1,21 +0,0 @@
<?php
namespace Illuminate\Console;
use Illuminate\Container\Container;
/**
* @deprecated Usage of this trait is deprecated and it will be removed in Laravel 7.0.
*/
trait DetectsApplicationNamespace
{
/**
* Get the application namespace.
*
* @return string
*/
protected function getAppNamespace()
{
return Container::getInstance()->getNamespace();
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Illuminate\Console\Events;
use Illuminate\Console\Scheduling\Event;
use Throwable;
class ScheduledTaskFailed
{
/**
* The scheduled event that failed.
*
* @var \Illuminate\Console\Scheduling\Event
*/
public $task;
/**
* The exception that was thrown.
*
* @var \Throwable
*/
public $exception;
/**
* Create a new event instance.
*
* @param \Illuminate\Console\Scheduling\Event $task
* @param \Throwable $exception
*/
public function __construct(Event $task, Throwable $exception)
{
$this->task = $task;
$this->exception = $exception;
}
}

View File

@@ -22,6 +22,82 @@ abstract class GeneratorCommand extends Command
*/
protected $type;
/**
* Reserved names that cannot be used for generation.
*
* @var array
*/
protected $reservedNames = [
'__halt_compiler',
'abstract',
'and',
'array',
'as',
'break',
'callable',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'die',
'do',
'echo',
'else',
'elseif',
'empty',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'eval',
'exit',
'extends',
'final',
'finally',
'fn',
'for',
'foreach',
'function',
'global',
'goto',
'if',
'implements',
'include',
'include_once',
'instanceof',
'insteadof',
'interface',
'isset',
'list',
'namespace',
'new',
'or',
'print',
'private',
'protected',
'public',
'require',
'require_once',
'return',
'static',
'switch',
'throw',
'trait',
'try',
'unset',
'use',
'var',
'while',
'xor',
'yield',
];
/**
* Create a new controller creator command instance.
*
@@ -51,11 +127,20 @@ abstract class GeneratorCommand extends Command
*/
public function handle()
{
// First we need to ensure that the given name is not a reserved word within the PHP
// language and that the class name will actually be valid. If it is not valid we
// can error now and prevent from polluting the filesystem using invalid files.
if ($this->isReservedName($this->getNameInput())) {
$this->error('The name "'.$this->getNameInput().'" is reserved by PHP.');
return false;
}
$name = $this->qualifyClass($this->getNameInput());
$path = $this->getPath($name);
// First we will check to see if the class already exists. If it does, we don't want
// Next, We will check to see if the class already exists. If it does, we don't want
// to create the class and overwrite the user's code. So, we will bail out so the
// code is untouched. Otherwise, we will continue generating this class' files.
if ((! $this->hasOption('force') ||
@@ -173,11 +258,19 @@ abstract class GeneratorCommand extends Command
*/
protected function replaceNamespace(&$stub, $name)
{
$stub = str_replace(
$searches = [
['DummyNamespace', 'DummyRootNamespace', 'NamespacedDummyUserModel'],
[$this->getNamespace($name), $this->rootNamespace(), $this->userProviderModel()],
$stub
);
['{{ namespace }}', '{{ rootNamespace }}', '{{ namespacedUserModel }}'],
['{{namespace}}', '{{rootNamespace}}', '{{namespacedUserModel}}'],
];
foreach ($searches as $search) {
$stub = str_replace(
$search,
[$this->getNamespace($name), $this->rootNamespace(), $this->userProviderModel()],
$stub
);
}
return $this;
}
@@ -204,7 +297,7 @@ abstract class GeneratorCommand extends Command
{
$class = str_replace($this->getNamespace($name).'\\', '', $name);
return str_replace('DummyClass', $class, $stub);
return str_replace(['DummyClass', '{{ class }}', '{{class}}'], $class, $stub);
}
/**
@@ -260,6 +353,32 @@ abstract class GeneratorCommand extends Command
return $config->get("auth.providers.{$provider}.model");
}
/**
* Checks whether the given name is reserved.
*
* @param string $name
* @return bool
*/
protected function isReservedName($name)
{
$name = strtolower($name);
return in_array($name, $this->reservedNames);
}
/**
* Get the first view directory path from the application configuration.
*
* @param string $path
* @return string
*/
protected function viewPath($path = '')
{
$views = $this->laravel['config']['view.paths'][0] ?? resource_path('views');
return $views.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
/**
* Get the console command arguments.
*

View File

@@ -0,0 +1,14 @@
<?php
namespace Illuminate\Console\Scheduling;
interface CacheAware
{
/**
* Specify the cache store that should be used.
*
* @param string $store
* @return $this
*/
public function useStore($store);
}

View File

@@ -4,7 +4,7 @@ namespace Illuminate\Console\Scheduling;
use Illuminate\Contracts\Cache\Factory as Cache;
class CacheEventMutex implements EventMutex
class CacheEventMutex implements EventMutex, CacheAware
{
/**
* The cache repository implementation.

View File

@@ -5,7 +5,7 @@ namespace Illuminate\Console\Scheduling;
use DateTimeInterface;
use Illuminate\Contracts\Cache\Factory as Cache;
class CacheSchedulingMutex implements SchedulingMutex
class CacheSchedulingMutex implements SchedulingMutex, CacheAware
{
/**
* The cache factory implementation.

View File

@@ -10,15 +10,17 @@ use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Reflector;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Support\Traits\ReflectsClosures;
use Psr\Http\Client\ClientExceptionInterface;
use Symfony\Component\Process\Process;
class Event
{
use Macroable, ManagesFrequencies;
use Macroable, ManagesFrequencies, ReflectsClosures;
/**
* The command string.
@@ -319,10 +321,10 @@ class Event
*/
protected function expressionPasses()
{
$date = Date::now();
$date = Carbon::now();
if ($this->timezone) {
$date = $date->setTimezone($this->timezone);
$date->setTimezone($this->timezone);
}
return CronExpression::factory($this->expression)->isDue($date->toDateTimeString());
@@ -576,7 +578,7 @@ class Event
{
return function (Container $container, HttpClient $http) use ($url) {
try {
$http->get($url);
$http->request('GET', $url);
} catch (ClientExceptionInterface|TransferException $e) {
$container->make(ExceptionHandler::class)->report($e);
}
@@ -731,6 +733,20 @@ class Event
return $this;
}
/**
* Register a callback that uses the output after the job runs.
*
* @param \Closure $callback
* @param bool $onlyIfOutputExists
* @return $this
*/
public function thenWithOutput(Closure $callback, $onlyIfOutputExists = false)
{
$this->ensureOutputIsBeingCaptured();
return $this->then($this->withOutputCallback($callback, $onlyIfOutputExists));
}
/**
* Register a callback to be called if the operation succeeds.
*
@@ -746,6 +762,20 @@ class Event
});
}
/**
* Register a callback that uses the output if the operation succeeds.
*
* @param \Closure $callback
* @param bool $onlyIfOutputExists
* @return $this
*/
public function onSuccessWithOutput(Closure $callback, $onlyIfOutputExists = false)
{
$this->ensureOutputIsBeingCaptured();
return $this->onSuccess($this->withOutputCallback($callback, $onlyIfOutputExists));
}
/**
* Register a callback to be called if the operation fails.
*
@@ -761,6 +791,38 @@ class Event
});
}
/**
* Register a callback that uses the output if the operation fails.
*
* @param \Closure $callback
* @param bool $onlyIfOutputExists
* @return $this
*/
public function onFailureWithOutput(Closure $callback, $onlyIfOutputExists = false)
{
$this->ensureOutputIsBeingCaptured();
return $this->onFailure($this->withOutputCallback($callback, $onlyIfOutputExists));
}
/**
* Get a callback that provides output.
*
* @param \Closure $callback
* @param bool $onlyIfOutputExists
* @return \Closure
*/
protected function withOutputCallback(Closure $callback, $onlyIfOutputExists = false)
{
return function (Container $container) use ($callback, $onlyIfOutputExists) {
$output = $this->output && file_exists($this->output) ? file_get_contents($this->output) : '';
return $onlyIfOutputExists && empty($output)
? null
: $container->call($callback, ['output' => $output]);
};
}
/**
* Set the human-friendly description of the event.
*

View File

@@ -81,6 +81,36 @@ trait ManagesFrequencies
return $this->spliceIntoPosition(1, '*');
}
/**
* Schedule the event to run every two minutes.
*
* @return $this
*/
public function everyTwoMinutes()
{
return $this->spliceIntoPosition(1, '*/2');
}
/**
* Schedule the event to run every three minutes.
*
* @return $this
*/
public function everyThreeMinutes()
{
return $this->spliceIntoPosition(1, '*/3');
}
/**
* Schedule the event to run every four minutes.
*
* @return $this
*/
public function everyFourMinutes()
{
return $this->spliceIntoPosition(1, '*/4');
}
/**
* Schedule the event to run every five minutes.
*
@@ -144,6 +174,50 @@ trait ManagesFrequencies
return $this->spliceIntoPosition(1, $offset);
}
/**
* Schedule the event to run every two hours.
*
* @return $this
*/
public function everyTwoHours()
{
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, '*/2');
}
/**
* Schedule the event to run every three hours.
*
* @return $this
*/
public function everyThreeHours()
{
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, '*/3');
}
/**
* Schedule the event to run every four hours.
*
* @return $this
*/
public function everyFourHours()
{
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, '*/4');
}
/**
* Schedule the event to run every six hours.
*
* @return $this
*/
public function everySixHours()
{
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, '*/6');
}
/**
* Schedule the event to run daily.
*
@@ -338,21 +412,37 @@ trait ManagesFrequencies
}
/**
* Schedule the event to run twice monthly.
* Schedule the event to run twice monthly at a given time.
*
* @param int $first
* @param int $second
* @param string $time
* @return $this
*/
public function twiceMonthly($first = 1, $second = 16)
public function twiceMonthly($first = 1, $second = 16, $time = '0:0')
{
$days = $first.','.$second;
$this->dailyAt($time);
return $this->spliceIntoPosition(1, 0)
->spliceIntoPosition(2, 0)
->spliceIntoPosition(3, $days);
}
/**
* Schedule the event to run on the last day of the month.
*
* @param string $time
* @return $this
*/
public function lastDayOfMonth($time = '0:0')
{
$this->dailyAt($time);
return $this->spliceIntoPosition(3, Carbon::now()->endOfMonth()->day);
}
/**
* Schedule the event to run quarterly.
*

View File

@@ -278,11 +278,11 @@ class Schedule
*/
public function useCache($store)
{
if ($this->eventMutex instanceof CacheEventMutex) {
if ($this->eventMutex instanceof CacheAware) {
$this->eventMutex->useStore($store);
}
if ($this->schedulingMutex instanceof CacheSchedulingMutex) {
if ($this->schedulingMutex instanceof CacheAware) {
$this->schedulingMutex->useStore($store);
}

View File

@@ -3,11 +3,14 @@
namespace Illuminate\Console\Scheduling;
use Illuminate\Console\Command;
use Illuminate\Console\Events\ScheduledTaskFailed;
use Illuminate\Console\Events\ScheduledTaskFinished;
use Illuminate\Console\Events\ScheduledTaskSkipped;
use Illuminate\Console\Events\ScheduledTaskStarting;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\Date;
use Throwable;
class ScheduleRunCommand extends Command
{
@@ -53,6 +56,13 @@ class ScheduleRunCommand extends Command
*/
protected $dispatcher;
/**
* The exception handler.
*
* @var \Illuminate\Contracts\Debug\ExceptionHandler
*/
protected $handler;
/**
* Create a new command instance.
*
@@ -70,12 +80,14 @@ class ScheduleRunCommand extends Command
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
* @param \Illuminate\Contracts\Debug\ExceptionHandler $handler
* @return void
*/
public function handle(Schedule $schedule, Dispatcher $dispatcher)
public function handle(Schedule $schedule, Dispatcher $dispatcher, ExceptionHandler $handler)
{
$this->schedule = $schedule;
$this->dispatcher = $dispatcher;
$this->handler = $handler;
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
@@ -127,13 +139,19 @@ class ScheduleRunCommand extends Command
$start = microtime(true);
$event->run($this->laravel);
try {
$event->run($this->laravel);
$this->dispatcher->dispatch(new ScheduledTaskFinished(
$event,
round(microtime(true) - $start, 2)
));
$this->dispatcher->dispatch(new ScheduledTaskFinished(
$event,
round(microtime(true) - $start, 2)
));
$this->eventsRan = true;
$this->eventsRan = true;
} catch (Throwable $e) {
$this->dispatcher->dispatch(new ScheduledTaskFailed($event, $e));
$this->handler->report($e);
}
}
}

View File

@@ -15,10 +15,10 @@
],
"require": {
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/support": "^6.0",
"symfony/console": "^4.3.4",
"symfony/process": "^4.3.4"
"illuminate/contracts": "^7.0",
"illuminate/support": "^7.0",
"symfony/console": "^5.0",
"symfony/process": "^5.0"
},
"autoload": {
"psr-4": {
@@ -27,16 +27,16 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"suggest": {
"dragonmantank/cron-expression": "Required to use scheduler (^2.3.1).",
"guzzlehttp/guzzle": "Required to use the ping methods on schedules (^6.3.1|^7.0.1).",
"illuminate/bus": "Required to use the scheduled job dispatcher (^6.0)",
"illuminate/container": "Required to use the scheduler (^6.0)",
"illuminate/filesystem": "Required to use the generator command (^6.0)",
"illuminate/queue": "Required to use closures for scheduled jobs (^6.0)"
"illuminate/bus": "Required to use the scheduled job dispatcher (^7.0).",
"illuminate/container": "Required to use the scheduler (^7.0).",
"illuminate/filesystem": "Required to use the generator command (^7.0).",
"illuminate/queue": "Required to use closures for scheduled jobs (^7.0)."
},
"config": {
"sort-packages": true

View File

@@ -234,6 +234,10 @@ class Container implements ArrayAccess, ContainerContract
// bound into this container to the abstract type and we will just wrap it
// up inside its own Closure to give us more convenience when extending.
if (! $concrete instanceof Closure) {
if (! is_string($concrete)) {
throw new \TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
}
$concrete = $this->getClosure($abstract, $concrete);
}
@@ -581,9 +585,11 @@ class Container implements ArrayAccess, ContainerContract
* Call the given Closure / class@method and inject its dependencies.
*
* @param callable|string $callback
* @param array $parameters
* @param array<string, mixed> $parameters
* @param string|null $defaultMethod
* @return mixed
*
* @throws \InvalidArgumentException
*/
public function call($callback, array $parameters = [], $defaultMethod = null)
{
@@ -609,6 +615,8 @@ class Container implements ArrayAccess, ContainerContract
* @param string $abstract
* @param array $parameters
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function makeWith($abstract, array $parameters = [])
{
@@ -659,9 +667,9 @@ class Container implements ArrayAccess, ContainerContract
{
$abstract = $this->getAlias($abstract);
$needsContextualBuild = ! empty($parameters) || ! is_null(
$this->getContextualConcrete($abstract)
);
$concrete = $this->getContextualConcrete($abstract);
$needsContextualBuild = ! empty($parameters) || ! is_null($concrete);
// If an instance of the type is currently being managed as a singleton we'll
// just return an existing instance instead of instantiating new instances
@@ -672,7 +680,9 @@ class Container implements ArrayAccess, ContainerContract
$this->with[] = $parameters;
$concrete = $this->getConcrete($abstract);
if (is_null($concrete)) {
$concrete = $this->getConcrete($abstract);
}
// We're ready to instantiate an instance of the concrete type registered for
// the binding. This will instantiate the types, as well as resolve any of
@@ -719,10 +729,6 @@ class Container implements ArrayAccess, ContainerContract
*/
protected function getConcrete($abstract)
{
if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
return $concrete;
}
// If we don't have a registered resolver or concrete for the type, we'll just
// assume each type is a concrete name and will attempt to resolve it as is
// since the container should be able to resolve concretes automatically.
@@ -737,7 +743,7 @@ class Container implements ArrayAccess, ContainerContract
* Get the contextual concrete binding for the given abstract.
*
* @param string $abstract
* @return \Closure|string|null
* @return \Closure|string|array|null
*/
protected function getContextualConcrete($abstract)
{
@@ -785,7 +791,7 @@ class Container implements ArrayAccess, ContainerContract
/**
* Instantiate a concrete instance of the given type.
*
* @param string $concrete
* @param \Closure|string $concrete
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
@@ -856,7 +862,7 @@ class Container implements ArrayAccess, ContainerContract
$results = [];
foreach ($dependencies as $dependency) {
// If this dependency has a override for this particular build we will use
// If the dependency has an override for this particular build we will use
// that instead as the value. Otherwise, we will continue with this run
// of resolutions and let reflection attempt to determine the result.
if ($this->hasParameterOverride($dependency)) {
@@ -868,9 +874,15 @@ class Container implements ArrayAccess, ContainerContract
// If the class is null, it means the dependency is a string or some other
// primitive type which we can not resolve since it is not a class and
// we will just bomb out with an error since we have no-where to go.
$results[] = is_null(Util::getParameterClassName($dependency))
$result = is_null(Util::getParameterClassName($dependency))
? $this->resolvePrimitive($dependency)
: $this->resolveClass($dependency);
if ($dependency->isVariadic()) {
$results = array_merge($results, $result);
} else {
$results[] = $result;
}
}
return $results;
@@ -942,21 +954,48 @@ class Container implements ArrayAccess, ContainerContract
protected function resolveClass(ReflectionParameter $parameter)
{
try {
return $this->make(Util::getParameterClassName($parameter));
return $parameter->isVariadic()
? $this->resolveVariadicClass($parameter)
: $this->make(Util::getParameterClassName($parameter));
}
// If we can not resolve the class instance, we will check to see if the value
// is optional, and if it is we will return the optional parameter value as
// the value of the dependency, similarly to how we do this with scalars.
catch (BindingResolutionException $e) {
if ($parameter->isOptional()) {
if ($parameter->isDefaultValueAvailable()) {
return $parameter->getDefaultValue();
}
if ($parameter->isVariadic()) {
return [];
}
throw $e;
}
}
/**
* Resolve a class based variadic dependency from the container.
*
* @param \ReflectionParameter $parameter
* @return mixed
*/
protected function resolveVariadicClass(ReflectionParameter $parameter)
{
$className = Util::getParameterClassName($parameter);
$abstract = $this->getAlias($className);
if (! is_array($concrete = $this->getContextualConcrete($abstract))) {
return $this->make($className);
}
return array_map(function ($abstract) {
return $this->resolve($abstract);
}, $concrete);
}
/**
* Throw an exception that the concrete is not instantiable.
*

View File

@@ -57,7 +57,7 @@ class ContextualBindingBuilder implements ContextualBindingBuilderContract
/**
* Define the implementation for the contextual binding.
*
* @param \Closure|string $implementation
* @param \Closure|string|array $implementation
* @return void
*/
public function give($implementation)
@@ -66,4 +66,19 @@ class ContextualBindingBuilder implements ContextualBindingBuilderContract
$this->container->addContextualBinding($concrete, $this->needs, $implementation);
}
}
/**
* Define tagged services to be used as the implementation for the contextual binding.
*
* @param string $tag
* @return void
*/
public function giveTagged($tag)
{
$this->give(function ($container) use ($tag) {
$taggedServices = $container->tagged($tag);
return is_array($taggedServices) ? $taggedServices : iterator_to_array($taggedServices);
});
}
}

View File

@@ -15,9 +15,12 @@
],
"require": {
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/contracts": "^7.0",
"psr/container": "^1.0"
},
"provide": {
"psr/container-implementation": "1.0"
},
"autoload": {
"psr-4": {
"Illuminate\\Container\\": ""
@@ -25,7 +28,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"config": {

View File

@@ -7,9 +7,9 @@ interface Authorizable
/**
* Determine if the entity has a given ability.
*
* @param string $ability
* @param iterable|string $abilities
* @param array|mixed $arguments
* @return bool
*/
public function can($ability, $arguments = []);
public function can($abilities, $arguments = []);
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Illuminate\Contracts\Auth\Middleware;
interface AuthenticatesRequests
{
//
}

View File

@@ -43,7 +43,7 @@ interface StatefulGuard extends Guard
* Log the given user ID into the application without sessions or cookies.
*
* @param mixed $id
* @return bool
* @return \Illuminate\Contracts\Auth\Authenticatable|bool
*/
public function onceUsingId($id);

View File

@@ -8,7 +8,7 @@ interface Factory
* Get a broadcaster implementation by name.
*
* @param string|null $name
* @return void
* @return \Illuminate\Contracts\Broadcasting\Broadcaster
*/
public function connection($name = null);
}

View File

@@ -17,7 +17,7 @@ interface Lock
*
* @param int $seconds
* @param callable|null $callback
* @return mixed
* @return bool
*/
public function block($seconds, $callback = null);

View File

@@ -0,0 +1,13 @@
<?php
namespace Illuminate\Contracts\Database\Eloquent;
interface Castable
{
/**
* Get the name of the caster class to use when casting from / to this cast target.
*
* @return string|\Illuminate\Contracts\Database\Eloquent\CastsAttributes|\Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes
*/
public static function castUsing();
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Illuminate\Contracts\Database\Eloquent;
interface CastsAttributes
{
/**
* Transform the attribute from the underlying model values.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function get($model, string $key, $value, array $attributes);
/**
* Transform the attribute to its underlying model values.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function set($model, string $key, $value, array $attributes);
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Illuminate\Contracts\Database\Eloquent;
interface CastsInboundAttributes
{
/**
* Transform the attribute to its underlying model values.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return mixed
*/
public function set($model, string $key, $value, array $attributes);
}

View File

@@ -2,45 +2,45 @@
namespace Illuminate\Contracts\Debug;
use Exception;
use Throwable;
interface ExceptionHandler
{
/**
* Report or log an exception.
*
* @param \Exception $e
* @param \Throwable $e
* @return void
*
* @throws \Exception
* @throws \Throwable
*/
public function report(Exception $e);
public function report(Throwable $e);
/**
* Determine if the exception should be reported.
*
* @param \Exception $e
* @param \Throwable $e
* @return bool
*/
public function shouldReport(Exception $e);
public function shouldReport(Throwable $e);
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @param \Throwable $e
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Exception
* @throws \Throwable
*/
public function render($request, Exception $e);
public function render($request, Throwable $e);
/**
* Render an exception to the console.
*
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @param \Exception $e
* @param \Throwable $e
* @return void
*/
public function renderForConsole($output, Exception $e);
public function renderForConsole($output, Throwable $e);
}

View File

@@ -2,7 +2,6 @@
namespace Illuminate\Contracts\Foundation;
use Closure;
use Illuminate\Contracts\Container\Container;
interface Application extends Container
@@ -25,7 +24,7 @@ interface Application extends Container
/**
* Get the path to the bootstrap directory.
*
* @param string $path
* @param string $path Optionally, a path to append to the bootstrap path
* @return string
*/
public function bootstrapPath($path = '');
@@ -33,7 +32,7 @@ interface Application extends Container
/**
* Get the path to the application configuration files.
*
* @param string $path
* @param string $path Optionally, a path to append to the config path
* @return string
*/
public function configPath($path = '');
@@ -41,18 +40,11 @@ interface Application extends Container
/**
* Get the path to the database directory.
*
* @param string $path
* @param string $path Optionally, a path to append to the database path
* @return string
*/
public function databasePath($path = '');
/**
* Get the path to the environment file directory.
*
* @return string
*/
public function environmentPath();
/**
* Get the path to the resources directory.
*
@@ -161,63 +153,6 @@ interface Application extends Container
*/
public function bootstrapWith(array $bootstrappers);
/**
* Determine if the application configuration is cached.
*
* @return bool
*/
public function configurationIsCached();
/**
* Detect the application's current environment.
*
* @param \Closure $callback
* @return string
*/
public function detectEnvironment(Closure $callback);
/**
* Get the environment file the application is using.
*
* @return string
*/
public function environmentFile();
/**
* Get the fully qualified path to the environment file.
*
* @return string
*/
public function environmentFilePath();
/**
* Get the path to the configuration cache file.
*
* @return string
*/
public function getCachedConfigPath();
/**
* Get the path to the cached services.php file.
*
* @return string
*/
public function getCachedServicesPath();
/**
* Get the path to the cached packages.php file.
*
* @return string
*/
public function getCachedPackagesPath();
/**
* Get the path to the routes cache file.
*
* @return string
*/
public function getCachedRoutesPath();
/**
* Get the current application locale.
*
@@ -256,21 +191,6 @@ interface Application extends Container
*/
public function loadDeferredProviders();
/**
* Set the environment file to be loaded during bootstrapping.
*
* @param string $file
* @return $this
*/
public function loadEnvironmentFrom($file);
/**
* Determine if the application routes are cached.
*
* @return bool
*/
public function routesAreCached();
/**
* Set the current application locale.
*

View File

@@ -0,0 +1,27 @@
<?php
namespace Illuminate\Contracts\Foundation;
interface CachesConfiguration
{
/**
* Determine if the application configuration is cached.
*
* @return bool
*/
public function configurationIsCached();
/**
* Get the path to the configuration cache file.
*
* @return string
*/
public function getCachedConfigPath();
/**
* Get the path to the cached services.php file.
*
* @return string
*/
public function getCachedServicesPath();
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Illuminate\Contracts\Foundation;
interface CachesRoutes
{
/**
* Determine if the application routes are cached.
*
* @return bool
*/
public function routesAreCached();
/**
* Get the path to the routes cache file.
*
* @return string
*/
public function getCachedRoutesPath();
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Illuminate\Contracts\Mail;
interface Factory
{
/**
* Get a mailer instance by name.
*
* @param string|null $name
* @return \Illuminate\Mail\Mailer
*/
public function mailer($name = null);
}

View File

@@ -9,10 +9,10 @@ interface Mailable
/**
* Send the message using the given mailer.
*
* @param \Illuminate\Contracts\Mail\Mailer $mailer
* @param \Illuminate\Contracts\Mail\Factory|\Illuminate\Contracts\Mail\Mailer $mailer
* @return void
*/
public function send(Mailer $mailer);
public function send($mailer);
/**
* Queue the given message.
@@ -30,4 +30,47 @@ interface Mailable
* @return mixed
*/
public function later($delay, Queue $queue);
/**
* Set the recipients of the message.
*
* @param object|array|string $address
* @param string|null $name
* @return self
*/
public function cc($address, $name = null);
/**
* Set the recipients of the message.
*
* @param object|array|string $address
* @param string|null $name
* @return $this
*/
public function bcc($address, $name = null);
/**
* Set the recipients of the message.
*
* @param object|array|string $address
* @param string|null $name
* @return $this
*/
public function to($address, $name = null);
/**
* Set the locale of the message.
*
* @param string $locale
* @return $this
*/
public function locale($locale);
/**
* Set the name of the mailer that should be used to send the message.
*
* @param string $mailer
* @return $this
*/
public function mailer($mailer);
}

View File

@@ -4,6 +4,13 @@ namespace Illuminate\Contracts\Queue;
interface Job
{
/**
* Get the UUID of the job.
*
* @return string|null
*/
public function uuid();
/**
* Get the job identifier.
*
@@ -99,6 +106,13 @@ interface Job
*/
public function maxTries();
/**
* Get the maximum number of exceptions allowed, regardless of attempts.
*
* @return int|null
*/
public function maxExceptions();
/**
* Get the number of seconds the job can run.
*

View File

@@ -77,7 +77,7 @@ interface Queue
/**
* Pop the next job off of the queue.
*
* @param string $queue
* @param string|null $queue
* @return \Illuminate\Contracts\Queue\Job|null
*/
public function pop($queue = null);

View File

@@ -8,7 +8,7 @@ interface Registrar
* Register a new GET route with the router.
*
* @param string $uri
* @param \Closure|array|string|callable $action
* @param array|string|callable $action
* @return \Illuminate\Routing\Route
*/
public function get($uri, $action);
@@ -17,7 +17,7 @@ interface Registrar
* Register a new POST route with the router.
*
* @param string $uri
* @param \Closure|array|string|callable $action
* @param array|string|callable $action
* @return \Illuminate\Routing\Route
*/
public function post($uri, $action);
@@ -26,7 +26,7 @@ interface Registrar
* Register a new PUT route with the router.
*
* @param string $uri
* @param \Closure|array|string|callable $action
* @param array|string|callable $action
* @return \Illuminate\Routing\Route
*/
public function put($uri, $action);
@@ -35,7 +35,7 @@ interface Registrar
* Register a new DELETE route with the router.
*
* @param string $uri
* @param \Closure|array|string|callable $action
* @param array|string|callable $action
* @return \Illuminate\Routing\Route
*/
public function delete($uri, $action);
@@ -44,7 +44,7 @@ interface Registrar
* Register a new PATCH route with the router.
*
* @param string $uri
* @param \Closure|array|string|callable $action
* @param array|string|callable $action
* @return \Illuminate\Routing\Route
*/
public function patch($uri, $action);
@@ -53,7 +53,7 @@ interface Registrar
* Register a new OPTIONS route with the router.
*
* @param string $uri
* @param \Closure|array|string|callable $action
* @param array|string|callable $action
* @return \Illuminate\Routing\Route
*/
public function options($uri, $action);
@@ -63,7 +63,7 @@ interface Registrar
*
* @param array|string $methods
* @param string $uri
* @param \Closure|array|string|callable $action
* @param array|string|callable $action
* @return \Illuminate\Routing\Route
*/
public function match($methods, $uri, $action);

View File

@@ -37,7 +37,7 @@ interface ResponseFactory
/**
* Create a new JSON response instance.
*
* @param string|array|object $data
* @param mixed $data
* @param int $status
* @param array $headers
* @param int $options
@@ -49,7 +49,7 @@ interface ResponseFactory
* Create a new JSONP response instance.
*
* @param string $callback
* @param string|array|object $data
* @param mixed $data
* @param int $status
* @param array $headers
* @param int $options
@@ -113,7 +113,7 @@ interface ResponseFactory
* Create a new redirect response to a named route.
*
* @param string $route
* @param array $parameters
* @param mixed $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
@@ -124,7 +124,7 @@ interface ResponseFactory
* Create a new redirect response to a controller action.
*
* @param string $action
* @param array $parameters
* @param mixed $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse

View File

@@ -22,7 +22,18 @@ interface UrlRoutable
* Retrieve the model for a bound value.
*
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value);
public function resolveRouteBinding($value, $field = null);
/**
* Retrieve the child model for a bound value.
*
* @param string $childType
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveChildRouteBinding($childType, $value, $field);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Illuminate\Contracts\Support;
interface DeferringDisplayableValue
{
/**
* Resolve the displayable value that the class is deferring.
*
* @return \Illuminate\Contracts\Support\Htmlable|string
*/
public function resolveDisplayableValue();
}

View File

@@ -10,6 +10,8 @@ interface Validator extends MessageProvider
* Run the validator's rules against its data.
*
* @return array
*
* @throws \Illuminate\Validation\ValidationException
*/
public function validate();
@@ -17,6 +19,8 @@ interface Validator extends MessageProvider
* Get the attributes and values that were validated.
*
* @return array
*
* @throws \Illuminate\Validation\ValidationException
*/
public function validated();

View File

@@ -25,7 +25,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"config": {

View File

@@ -27,18 +27,18 @@ class CookieJar implements JarContract
protected $domain;
/**
* The default secure setting (defaults to false).
* The default secure setting (defaults to null).
*
* @var bool
* @var bool|null
*/
protected $secure = false;
protected $secure;
/**
* The default SameSite option (if specified).
* The default SameSite option (defaults to lax).
*
* @var string
*/
protected $sameSite;
protected $sameSite = 'lax';
/**
* All of the cookies queued for sending.
@@ -119,7 +119,7 @@ class CookieJar implements JarContract
* @param string $key
* @param mixed $default
* @param string|null $path
* @return \Symfony\Component\HttpFoundation\Cookie
* @return \Symfony\Component\HttpFoundation\Cookie|null
*/
public function queued($key, $default = null, $path = null)
{
@@ -140,8 +140,8 @@ class CookieJar implements JarContract
*/
public function queue(...$parameters)
{
if (head($parameters) instanceof Cookie) {
$cookie = head($parameters);
if (isset($parameters[0]) && $parameters[0] instanceof Cookie) {
$cookie = $parameters[0];
} else {
$cookie = $this->make(...array_values($parameters));
}

View File

@@ -15,10 +15,10 @@
],
"require": {
"php": "^7.2.5|^8.0",
"illuminate/contracts": "^6.0",
"illuminate/support": "^6.0",
"symfony/http-foundation": "^4.3.4",
"symfony/http-kernel": "^4.3.4"
"illuminate/contracts": "^7.0",
"illuminate/support": "^7.0",
"symfony/http-foundation": "^5.0",
"symfony/http-kernel": "^5.0"
},
"autoload": {
"psr-4": {
@@ -27,7 +27,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
"dev-master": "7.x-dev"
}
},
"config": {

View File

@@ -113,7 +113,7 @@ trait BuildsQueries
}
/**
* Execute a callback over each item while chunking by id.
* Execute a callback over each item while chunking by ID.
*
* @param callable $callback
* @param int $count

View File

@@ -3,7 +3,6 @@
namespace Illuminate\Database\Concerns;
use Closure;
use Exception;
use Throwable;
trait ManagesTransactions
@@ -15,7 +14,7 @@ trait ManagesTransactions
* @param int $attempts
* @return mixed
*
* @throws \Exception|\Throwable
* @throws \Throwable
*/
public function transaction(Closure $callback, $attempts = 1)
{
@@ -32,16 +31,12 @@ trait ManagesTransactions
// If we catch an exception we'll rollback this transaction and try again if we
// are not out of attempts. If we are out of attempts we will just throw the
// exception back out and let the developer handle an uncaught exceptions.
catch (Exception $e) {
catch (Throwable $e) {
$this->handleTransactionException(
$e, $currentAttempt, $attempts
);
continue;
} catch (Throwable $e) {
$this->rollBack();
throw $e;
}
try {
@@ -50,9 +45,7 @@ trait ManagesTransactions
}
$this->transactions = max(0, $this->transactions - 1);
} catch (Exception $e) {
$commitFailed = true;
} catch (Throwable $e) {
$this->handleCommitTransactionException(
$e, $currentAttempt, $attempts
);
@@ -60,9 +53,7 @@ trait ManagesTransactions
continue;
}
if (! isset($commitFailed)) {
$this->fireConnectionEvent('committed');
}
$this->fireConnectionEvent('committed');
return $callbackResult;
}
@@ -71,14 +62,14 @@ trait ManagesTransactions
/**
* Handle an exception encountered when running a transacted statement.
*
* @param \Exception $e
* @param \Throwable $e
* @param int $currentAttempt
* @param int $maxAttempts
* @return void
*
* @throws \Exception
* @throws \Throwable
*/
protected function handleTransactionException($e, $currentAttempt, $maxAttempts)
protected function handleTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
{
// On a deadlock, MySQL rolls back the entire transaction so we can't just
// retry the query. We have to throw this exception all the way out and
@@ -108,7 +99,7 @@ trait ManagesTransactions
*
* @return void
*
* @throws \Exception
* @throws \Throwable
*/
public function beginTransaction()
{
@@ -123,6 +114,8 @@ trait ManagesTransactions
* Create a transaction within the database.
*
* @return void
*
* @throws \Throwable
*/
protected function createTransaction()
{
@@ -131,7 +124,7 @@ trait ManagesTransactions
try {
$this->getPdo()->beginTransaction();
} catch (Exception $e) {
} catch (Throwable $e) {
$this->handleBeginTransactionException($e);
}
} elseif ($this->transactions >= 1 && $this->queryGrammar->supportsSavepoints()) {
@@ -143,6 +136,8 @@ trait ManagesTransactions
* Create a save point within the database.
*
* @return void
*
* @throws \Throwable
*/
protected function createSavepoint()
{
@@ -157,9 +152,9 @@ trait ManagesTransactions
* @param \Throwable $e
* @return void
*
* @throws \Exception
* @throws \Throwable
*/
protected function handleBeginTransactionException($e)
protected function handleBeginTransactionException(Throwable $e)
{
if ($this->causedByLostConnection($e)) {
$this->reconnect();
@@ -174,6 +169,8 @@ trait ManagesTransactions
* Commit the active database transaction.
*
* @return void
*
* @throws \Throwable
*/
public function commit()
{
@@ -189,12 +186,14 @@ trait ManagesTransactions
/**
* Handle an exception encountered when committing a transaction.
*
* @param \Exception $e
* @param \Throwable $e
* @param int $currentAttempt
* @param int $maxAttempts
* @return void
*
* @throws \Throwable
*/
protected function handleCommitTransactionException($e, $currentAttempt, $maxAttempts)
protected function handleCommitTransactionException(Throwable $e, $currentAttempt, $maxAttempts)
{
$this->transactions = max(0, $this->transactions - 1);
@@ -216,7 +215,7 @@ trait ManagesTransactions
* @param int|null $toLevel
* @return void
*
* @throws \Exception
* @throws \Throwable
*/
public function rollBack($toLevel = null)
{
@@ -236,7 +235,7 @@ trait ManagesTransactions
// level that was passed into this method so it will be right from here out.
try {
$this->performRollBack($toLevel);
} catch (Exception $e) {
} catch (Throwable $e) {
$this->handleRollBackException($e);
}
@@ -250,6 +249,8 @@ trait ManagesTransactions
*
* @param int $toLevel
* @return void
*
* @throws \Throwable
*/
protected function performRollBack($toLevel)
{
@@ -265,12 +266,12 @@ trait ManagesTransactions
/**
* Handle an exception from a rollback.
*
* @param \Exception $e
* @param \Throwable $e
* @return void
*
* @throws \Exception
* @throws \Throwable
*/
protected function handleRollBackException($e)
protected function handleRollBackException(Throwable $e)
{
if ($this->causedByLostConnection($e)) {
$this->transactions = 0;

View File

@@ -327,8 +327,9 @@ class Connection implements ConnectionInterface
// 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));
$statement = $this->prepared(
$this->getPdoForSelect($useReadPdo)->prepare($query)
);
$this->bindValues($statement, $this->prepareBindings($bindings));
@@ -576,7 +577,8 @@ class Connection implements ConnectionInterface
{
foreach ($bindings as $key => $value) {
$statement->bindValue(
is_string($key) ? $key : $key + 1, $value,
is_string($key) ? $key : $key + 1,
$value,
is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
);
}

View File

@@ -78,7 +78,7 @@ class ConnectionFactory
}
/**
* Create a single database connection instance.
* Create a read / write database connection instance.
*
* @param array $config
* @return \Illuminate\Database\Connection
@@ -115,7 +115,7 @@ class ConnectionFactory
}
/**
* Get the read configuration for a read / write connection.
* Get the write configuration for a read / write connection.
*
* @param array $config
* @return array
@@ -171,6 +171,8 @@ class ConnectionFactory
*
* @param array $config
* @return \Closure
*
* @throws \PDOException
*/
protected function createPdoResolverWithHosts(array $config)
{
@@ -250,7 +252,7 @@ class ConnectionFactory
return new SqlServerConnector;
}
throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]");
throw new InvalidArgumentException("Unsupported driver [{$config['driver']}].");
}
/**
@@ -282,6 +284,6 @@ class ConnectionFactory
return new SqlServerConnection($connection, $database, $prefix, $config);
}
throw new InvalidArgumentException("Unsupported driver [{$driver}]");
throw new InvalidArgumentException("Unsupported driver [{$driver}].");
}
}

View File

@@ -27,6 +27,8 @@ class MySqlConnector extends Connector implements ConnectorInterface
$connection->exec("use `{$config['database']}`;");
}
$this->configureIsolationLevel($connection, $config);
$this->configureEncoding($connection, $config);
// Next, we will check to see if a timezone has been specified in this config
@@ -40,12 +42,30 @@ class MySqlConnector extends Connector implements ConnectorInterface
}
/**
* Set the connection character set and collation.
* Set the connection transaction isolation level.
*
* @param \PDO $connection
* @param array $config
* @return void
*/
protected function configureIsolationLevel($connection, array $config)
{
if (! isset($config['isolation_level'])) {
return;
}
$connection->prepare(
"SET SESSION TRANSACTION ISOLATION LEVEL {$config['isolation_level']}"
)->execute();
}
/**
* Set the connection character set and collation.
*
* @param \PDO $connection
* @param array $config
* @return void|\PDO
*/
protected function configureEncoding($connection, array $config)
{
if (! isset($config['charset'])) {
@@ -147,7 +167,7 @@ class MySqlConnector extends Connector implements ConnectorInterface
$this->setCustomModes($connection, $config);
} elseif (isset($config['strict'])) {
if ($config['strict']) {
$connection->prepare($this->strictMode($connection))->execute();
$connection->prepare($this->strictMode($connection, $config))->execute();
} else {
$connection->prepare("set session sql_mode='NO_ENGINE_SUBSTITUTION'")->execute();
}
@@ -172,11 +192,14 @@ class MySqlConnector extends Connector implements ConnectorInterface
* Get the query to enable strict mode.
*
* @param \PDO $connection
* @param array $config
* @return string
*/
protected function strictMode(PDO $connection)
protected function strictMode(PDO $connection, $config)
{
if (version_compare($connection->getAttribute(PDO::ATTR_SERVER_VERSION), '8.0.11') >= 0) {
$version = $config['version'] ?? $connection->getAttribute(PDO::ATTR_SERVER_VERSION);
if (version_compare($version, '8.0.11') >= 0) {
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_ENGINE_SUBSTITUTION'";
}

View File

@@ -47,6 +47,8 @@ class PostgresConnector extends Connector implements ConnectorInterface
// determine if the option has been specified and run a statement if so.
$this->configureApplicationName($connection, $config);
$this->configureSynchronousCommit($connection, $config);
return $connection;
}
@@ -173,4 +175,20 @@ class PostgresConnector extends Connector implements ConnectorInterface
return $dsn;
}
/**
* Configure the synchronous_commit setting.
*
* @param \PDO $connection
* @param array $config
* @return void
*/
protected function configureSynchronousCommit($connection, array $config)
{
if (! isset($config['synchronous_commit'])) {
return;
}
$connection->prepare("set synchronous_commit to '{$config['synchronous_commit']}'")->execute();
}
}

View File

@@ -156,6 +156,10 @@ class SqlServerConnector extends Connector implements ConnectorInterface
$arguments['KeyStoreSecret'] = $config['key_store_secret'];
}
if (isset($config['login_timeout'])) {
$arguments['LoginTimeout'] = $config['login_timeout'];
}
return $this->buildConnectString('sqlsrv', $arguments);
}

View File

@@ -35,7 +35,20 @@ class FactoryMakeCommand extends GeneratorCommand
*/
protected function getStub()
{
return __DIR__.'/stubs/factory.stub';
return $this->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;
}
/**
@@ -52,16 +65,17 @@ class FactoryMakeCommand extends GeneratorCommand
$model = class_basename($namespaceModel);
$replace = [
'NamespacedDummyModel' => $namespaceModel,
'{{ namespacedModel }}' => $namespaceModel,
'{{namespacedModel}}' => $namespaceModel,
'DummyModel' => $model,
'{{ model }}' => $model,
'{{model}}' => $model,
];
return str_replace(
[
'NamespacedDummyModel',
'DummyModel',
],
[
$namespaceModel,
$model,
],
parent::buildClass($name)
array_keys($replace), array_values($replace), parent::buildClass($name)
);
}

View File

@@ -3,9 +3,9 @@
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use NamespacedDummyModel;
use {{ namespacedModel }};
$factory->define(DummyModel::class, function (Faker $faker) {
$factory->define({{ model }}::class, function (Faker $faker) {
return [
//
];

View File

@@ -27,12 +27,12 @@ class FreshCommand extends Command
/**
* Execute the console command.
*
* @return void
* @return int
*/
public function handle()
{
if (! $this->confirmToProceed()) {
return;
return 1;
}
$database = $this->input->getOption('database');
@@ -55,6 +55,8 @@ class FreshCommand extends Command
if ($this->needsSeeding()) {
$this->runSeeder($database);
}
return 0;
}
/**

View File

@@ -52,31 +52,35 @@ class MigrateCommand extends BaseCommand
/**
* Execute the console command.
*
* @return void
* @return int
*/
public function handle()
{
if (! $this->confirmToProceed()) {
return;
return 1;
}
$this->prepareDatabase();
$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'),
]);
// 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', ['--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->option('pretend')) {
$this->call('db:seed', ['--force' => true]);
}
});
return 0;
}
/**
@@ -86,8 +90,6 @@ class MigrateCommand extends BaseCommand
*/
protected function prepareDatabase()
{
$this->migrator->setConnection($this->option('database'));
if (! $this->migrator->repositoryExists()) {
$this->call('migrate:install', array_filter([
'--database' => $this->option('database'),

View File

@@ -27,12 +27,12 @@ class RefreshCommand extends Command
/**
* Execute the console command.
*
* @return void
* @return int
*/
public function handle()
{
if (! $this->confirmToProceed()) {
return;
return 1;
}
// Next we'll gather some of the options so that we can have the right options
@@ -66,6 +66,8 @@ class RefreshCommand extends Command
if ($this->needsSeeding()) {
$this->runSeeder($database);
}
return 0;
}
/**

View File

@@ -47,26 +47,28 @@ class ResetCommand extends BaseCommand
/**
* Execute the console command.
*
* @return void
* @return int
*/
public function handle()
{
if (! $this->confirmToProceed()) {
return;
return 1;
}
$this->migrator->setConnection($this->option('database'));
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->comment('Migration table not found.');
}
// 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->setOutput($this->output)->reset(
$this->getMigrationPaths(), $this->option('pretend')
);
});
$this->migrator->setOutput($this->output)->reset(
$this->getMigrationPaths(), $this->option('pretend')
);
return 0;
}
/**

View File

@@ -47,22 +47,24 @@ class RollbackCommand extends BaseCommand
/**
* Execute the console command.
*
* @return void
* @return int
*/
public function handle()
{
if (! $this->confirmToProceed()) {
return;
return 1;
}
$this->migrator->setConnection($this->option('database'));
$this->migrator->usingConnection($this->option('database'), function () {
$this->migrator->setOutput($this->output)->rollback(
$this->getMigrationPaths(), [
'pretend' => $this->option('pretend'),
'step' => (int) $this->option('step'),
]
);
});
$this->migrator->setOutput($this->output)->rollback(
$this->getMigrationPaths(), [
'pretend' => $this->option('pretend'),
'step' => (int) $this->option('step'),
]
);
return 0;
}
/**

View File

@@ -45,27 +45,27 @@ class StatusCommand extends BaseCommand
/**
* Execute the console command.
*
* @return void
* @return int|null
*/
public function handle()
{
$this->migrator->setConnection($this->option('database'));
return $this->migrator->usingConnection($this->option('database'), function () {
if (! $this->migrator->repositoryExists()) {
$this->error('Migration table not found.');
if (! $this->migrator->repositoryExists()) {
$this->error('Migration table not found.');
return 1;
}
return 1;
}
$ran = $this->migrator->getRepository()->getRan();
$ran = $this->migrator->getRepository()->getRan();
$batches = $this->migrator->getRepository()->getMigrationBatches();
$batches = $this->migrator->getRepository()->getMigrationBatches();
if (count($migrations = $this->getStatusFor($ran, $batches)) > 0) {
$this->table(['Ran?', 'Migration', 'Batch'], $migrations);
} else {
$this->error('No migrations found');
}
if (count($migrations = $this->getStatusFor($ran, $batches)) > 0) {
$this->table(['Ran?', 'Migration', 'Batch'], $migrations);
} else {
$this->error('No migrations found');
}
});
}
/**

View File

@@ -49,21 +49,29 @@ class SeedCommand extends Command
/**
* Execute the console command.
*
* @return void
* @return int
*/
public function handle()
{
if (! $this->confirmToProceed()) {
return;
return 1;
}
$previousConnection = $this->resolver->getDefaultConnection();
$this->resolver->setDefaultConnection($this->getDatabase());
Model::unguarded(function () {
$this->getSeeder()->__invoke();
});
if ($previousConnection) {
$this->resolver->setDefaultConnection($previousConnection);
}
$this->info('Database seeding completed successfully.');
return 0;
}
/**

View File

@@ -69,7 +69,20 @@ class SeederMakeCommand extends GeneratorCommand
*/
protected function getStub()
{
return __DIR__.'/stubs/seeder.stub';
return $this->resolveStubPath('/stubs/seeder.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;
}
/**

View File

@@ -2,7 +2,7 @@
use Illuminate\Database\Seeder;
class DummyClass extends Seeder
class {{ class }} extends Seeder
{
/**
* Run the database seeds.

View File

@@ -27,12 +27,12 @@ class WipeCommand extends Command
/**
* Execute the console command.
*
* @return void
* @return int
*/
public function handle()
{
if (! $this->confirmToProceed()) {
return;
return 1;
}
$database = $this->input->getOption('database');
@@ -52,6 +52,8 @@ class WipeCommand extends Command
$this->info('Dropped all types successfully.');
}
return 0;
}
/**

View File

@@ -245,6 +245,24 @@ class DatabaseManager implements ConnectionResolverInterface
return $this->refreshPdoConnections($name);
}
/**
* Set the default database connection for the callback execution.
*
* @param string $name
* @param callable $callback
* @return mixed
*/
public function usingConnection($name, callable $callback)
{
$previousName = $this->getDefaultConnection();
$this->setDefaultConnection($name);
return tap($callback(), function () use ($previousName) {
$this->setDefaultConnection($previousName);
});
}
/**
* Refresh the PDO connections on a given connection.
*

View File

@@ -2,19 +2,19 @@
namespace Illuminate\Database;
use Exception;
use Illuminate\Support\Str;
use PDOException;
use Throwable;
trait DetectsConcurrencyErrors
{
/**
* Determine if the given exception was caused by a concurrency error such as a deadlock or serialization failure.
*
* @param \Exception $e
* @param \Throwable $e
* @return bool
*/
protected function causedByConcurrencyError(Exception $e)
protected function causedByConcurrencyError(Throwable $e)
{
if ($e instanceof PDOException && $e->getCode() === '40001') {
return true;

View File

@@ -50,13 +50,6 @@ trait DetectsLostConnections
'SSL: Connection timed out',
'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.',
'Temporary failure in name resolution',
'SSL: Broken pipe',
'SQLSTATE[08S01]: Communication link failure',
'SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host',
'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: No route to host',
'The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.',
'SQLSTATE[08006] [7] could not translate host name',
'TCP Provider: Error code 0x274C',
]);
}
}

View File

@@ -75,7 +75,7 @@ class Builder
*/
protected $passthru = [
'insert', 'insertOrIgnore', 'insertGetId', 'insertUsing', 'getBindings', 'toSql', 'dump', 'dd',
'exists', 'doesntExist', 'count', 'min', 'max', 'avg', 'average', 'sum', 'getConnection',
'exists', 'doesntExist', 'count', 'min', 'max', 'avg', 'average', 'sum', 'getConnection', 'raw', 'getGrammar',
];
/**
@@ -233,7 +233,7 @@ class Builder
*/
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column instanceof Closure) {
if ($column instanceof Closure && is_null($operator)) {
$column($query = $this->model->newQueryWithoutRelationships());
$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
@@ -264,7 +264,7 @@ class Builder
* @param \Closure|array|string|\Illuminate\Database\Query\Expression $column
* @param mixed $operator
* @param mixed $value
* @return \Illuminate\Database\Eloquent\Builder|static
* @return $this
*/
public function orWhere($column, $operator = null, $value = null)
{
@@ -385,6 +385,8 @@ class Builder
{
$result = $this->find($id, $columns);
$id = $id instanceof Arrayable ? $id->toArray() : $id;
if (is_array($id)) {
if (count($result) === count(array_unique($id))) {
return $result;
@@ -421,7 +423,7 @@ class Builder
* @param array $values
* @return \Illuminate\Database\Eloquent\Model|static
*/
public function firstOrNew(array $attributes, array $values = [])
public function firstOrNew(array $attributes = [], array $values = [])
{
if (! is_null($instance = $this->where($attributes)->first())) {
return $instance;
@@ -897,6 +899,17 @@ class Builder
$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.
*
@@ -918,10 +931,7 @@ class Builder
// 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
// messed up when adding scopes. Then we'll return back out the builder.
$builder = $builder->callScope(
[$this->model, 'scope'.ucfirst($scope)],
Arr::wrap($parameters)
);
$builder = $builder->callNamedScope($scope, (array) $parameters);
}
return $builder;
@@ -972,7 +982,7 @@ class Builder
* @param array $parameters
* @return mixed
*/
protected function callScope(callable $scope, $parameters = [])
protected function callScope(callable $scope, array $parameters = [])
{
array_unshift($parameters, $this);
@@ -993,6 +1003,20 @@ class Builder
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.
*
@@ -1111,9 +1135,9 @@ class Builder
$results = [];
foreach ($relations as $name => $constraints) {
// If the "name" value is a numeric key, we can assume that no
// constraints have been specified. We'll just put an empty
// Closure there, so that we can treat them all the same.
// If the "name" value is a numeric key, we can assume that no constraints
// have been specified. We will just put an empty Closure there so that
// we can treat these all the same while we are looping through them.
if (is_numeric($name)) {
$name = $constraints;
@@ -1183,6 +1207,19 @@ class Builder
return $results;
}
/**
* Apply query-time casts to the model instance.
*
* @param array $casts
* @return $this
*/
public function withCasts($casts)
{
$this->model->mergeCasts($casts);
return $this;
}
/**
* Get the underlying query builder instance.
*
@@ -1377,8 +1414,8 @@ class Builder
return $callable(...$parameters);
}
if ($this->model !== null && method_exists($this->model, $scope = 'scope'.ucfirst($method))) {
return $this->callScope([$this->model, $scope], $parameters);
if ($this->hasNamedScope($method)) {
return $this->callNamedScope($method, $parameters);
}
if (in_array($method, $this->passthru)) {

View File

@@ -79,17 +79,18 @@ class Collection extends BaseCollection implements QueueableCollection
->whereKey($this->modelKeys())
->select($this->first()->getKeyName())
->withCount(...func_get_args())
->get();
->get()
->keyBy($this->first()->getKeyName());
$attributes = Arr::except(
array_keys($models->first()->getAttributes()),
$models->first()->getKeyName()
);
$models->each(function ($model) use ($attributes) {
$this->find($model->getKey())->forceFill(
Arr::only($model->getAttributes(), $attributes)
)->syncOriginalAttributes($attributes);
$this->each(function ($model) use ($models, $attributes) {
$extraAttributes = Arr::only($models->get($model->getKey())->getAttributes(), $attributes);
$model->forceFill($extraAttributes)->syncOriginalAttributes($attributes);
});
return $this;
@@ -159,7 +160,7 @@ class Collection extends BaseCollection implements QueueableCollection
return;
}
$models = $models->pluck($name)->whereNotNull();
$models = $models->pluck($name);
if ($models->first() instanceof BaseCollection) {
$models = $models->collapse();
@@ -189,6 +190,27 @@ class Collection extends BaseCollection implements QueueableCollection
return $this;
}
/**
* Load a set of relationship counts onto the mixed relationship collection.
*
* @param string $relation
* @param array $relations
* @return $this
*/
public function loadMorphCount($relation, $relations)
{
$this->pluck($relation)
->filter()
->groupBy(function ($model) {
return get_class($model);
})
->each(function ($models, $className) use ($relations) {
static::make($models)->loadCount($relations[$className] ?? []);
});
return $this;
}
/**
* Determine if a key exists in the collection.
*
@@ -401,7 +423,7 @@ class Collection extends BaseCollection implements QueueableCollection
*/
public function makeHidden($attributes)
{
return $this->each->addHidden($attributes);
return $this->each->makeHidden($attributes);
}
/**
@@ -415,6 +437,17 @@ class Collection extends BaseCollection implements QueueableCollection
return $this->each->makeVisible($attributes);
}
/**
* Append an attribute across the entire collection.
*
* @param array|string $attributes
* @return $this
*/
public function append($attributes)
{
return $this->each->append($attributes);
}
/**
* Get a dictionary keyed by primary keys.
*
@@ -583,7 +616,7 @@ class Collection extends BaseCollection implements QueueableCollection
if (count($relations) === 0 || $relations === [[]]) {
return [];
} elseif (count($relations) === 1) {
return array_values($relations)[0];
return reset($relations);
} else {
return array_intersect(...$relations);
}
@@ -612,4 +645,30 @@ class Collection extends BaseCollection implements QueueableCollection
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->filter(function ($model) use ($class) {
return ! $model instanceof $class;
})->isNotEmpty()) {
throw new LogicException('Unable to create query for collection with mixed types.');
}
return $model->newModelQuery()->whereKey($this->modelKeys());
}
}

View File

@@ -57,6 +57,19 @@ trait GuardsAttributes
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_merge($this->fillable, $fillable);
return $this;
}
/**
* Get the guarded attributes for the model.
*
@@ -80,6 +93,19 @@ trait GuardsAttributes
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_merge($this->guarded, $guarded);
return $this;
}
/**
* Disable all mass assignable restrictions.
*

View File

@@ -4,6 +4,8 @@ namespace Illuminate\Database\Eloquent\Concerns;
use Carbon\CarbonInterface;
use DateTimeInterface;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\JsonEncodingException;
use Illuminate\Database\Eloquent\Relations\Relation;
@@ -38,12 +40,44 @@ trait HasAttributes
protected $changes = [];
/**
* The attributes that should be cast to native types.
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [];
/**
* The attributes that have been cast using custom classes.
*
* @var array
*/
protected $classCastCache = [];
/**
* The built-in, primitive cast types supported by Eloquent.
*
* @var array
*/
protected static $primitiveCastTypes = [
'array',
'bool',
'boolean',
'collection',
'custom_datetime',
'date',
'datetime',
'decimal',
'double',
'float',
'int',
'integer',
'json',
'object',
'real',
'string',
'timestamp',
];
/**
* The attributes that should be mutated to dates.
*
@@ -173,7 +207,8 @@ trait HasAttributes
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;
}
@@ -196,6 +231,11 @@ trait HasAttributes
$attributes[$key] = $attributes[$key]->format(explode(':', $value, 2)[1]);
}
if ($attributes[$key] && $attributes[$key] instanceof DateTimeInterface &&
$this->isClassCastable($key)) {
$attributes[$key] = $this->serializeDate($attributes[$key]);
}
if ($attributes[$key] instanceof Arrayable) {
$attributes[$key] = $attributes[$key]->toArray();
}
@@ -211,7 +251,7 @@ trait HasAttributes
*/
protected function getArrayableAttributes()
{
return $this->getArrayableItems($this->attributes);
return $this->getArrayableItems($this->getAttributes());
}
/**
@@ -319,7 +359,9 @@ trait HasAttributes
// 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 (array_key_exists($key, $this->attributes) ||
$this->hasGetMutator($key)) {
array_key_exists($key, $this->casts) ||
$this->hasGetMutator($key) ||
$this->isClassCastable($key)) {
return $this->getAttributeValue($key);
}
@@ -341,31 +383,7 @@ trait HasAttributes
*/
public function getAttributeValue($key)
{
$value = $this->getAttributeFromArray($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);
}
// 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)) {
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 (in_array($key, $this->getDates()) &&
! is_null($value)) {
return $this->asDateTime($value);
}
return $value;
return $this->transformModelValue($key, $this->getAttributeFromArray($key));
}
/**
@@ -376,7 +394,7 @@ trait HasAttributes
*/
protected function getAttributeFromArray($key)
{
return $this->attributes[$key] ?? null;
return $this->getAttributes()[$key] ?? null;
}
/**
@@ -397,7 +415,8 @@ trait HasAttributes
// 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.
if (method_exists($this, $key)) {
if (method_exists($this, $key) ||
(static::$relationResolvers[get_class($this)][$key] ?? null)) {
return $this->getRelationshipFromMethod($key);
}
}
@@ -463,11 +482,24 @@ trait HasAttributes
*/
protected function mutateAttributeForArray($key, $value)
{
$value = $this->mutateAttribute($key, $value);
$value = $this->isClassCastable($key)
? $this->getClassCastableAttributeValue($key, $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 void
*/
public function mergeCasts($casts)
{
$this->casts = array_merge($this->casts, $casts);
}
/**
* Cast an attribute to a native PHP type.
*
@@ -477,11 +509,13 @@ trait HasAttributes
*/
protected function castAttribute($key, $value)
{
if (is_null($value)) {
$castType = $this->getCastType($key);
if (is_null($value) && in_array($castType, static::$primitiveCastTypes)) {
return $value;
}
switch ($this->getCastType($key)) {
switch ($castType) {
case 'int':
case 'integer':
return (int) $value;
@@ -510,8 +544,40 @@ trait HasAttributes
return $this->asDateTime($value);
case 'timestamp':
return $this->asTimestamp($value);
default:
return $value;
}
if ($this->isClassCastable($key)) {
return $this->getClassCastableAttributeValue($key, $value);
}
return $value;
}
/**
* Cast the given attribute using a custom cast class.
*
* @param string $key
* @param mixed $value
* @return mixed
*/
protected function getClassCastableAttributeValue($key, $value)
{
if (isset($this->classCastCache[$key])) {
return $this->classCastCache[$key];
} else {
$caster = $this->resolveCasterClass($key);
$value = $caster instanceof CastsInboundAttributes
? $value
: $caster->get($this, $key, $value, $this->attributes);
if ($caster instanceof CastsInboundAttributes || ! is_object($value)) {
unset($this->classCastCache[$key]);
} else {
$this->classCastCache[$key] = $value;
}
return $value;
}
}
@@ -580,6 +646,12 @@ trait HasAttributes
$value = $this->fromDateTime($value);
}
if ($this->isClassCastable($key)) {
$this->setClassCastableAttribute($key, $value);
return $this;
}
if ($this->isJsonCastable($key) && ! is_null($value)) {
$value = $this->castAttributeAsJson($key, $value);
}
@@ -649,6 +721,41 @@ trait HasAttributes
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);
if (is_null($value)) {
$this->attributes = array_merge($this->attributes, array_map(
function () {
},
$this->normalizeCastClassResponse($key, $caster->set(
$this, $key, $this->{$key}, $this->attributes
))
));
} else {
$this->attributes = array_merge(
$this->attributes,
$this->normalizeCastClassResponse($key, $caster->set(
$this, $key, $value, $this->attributes
))
);
}
if ($caster instanceof CastsInboundAttributes || ! is_object($value)) {
unset($this->classCastCache[$key]);
} else {
$this->classCastCache[$key] = $value;
}
}
/**
* Get an array attribute with the given key and value set.
*
@@ -802,15 +909,14 @@ trait HasAttributes
$format = $this->getDateFormat();
// https://bugs.php.net/bug.php?id=75577
if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) {
$format = str_replace('.v', '.u', $format);
}
// 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 Date::createFromFormat($format, $value);
if (Date::hasFormat($value, $format)) {
return Date::createFromFormat($format, $value);
}
return Date::parse($value);
}
/**
@@ -856,7 +962,7 @@ trait HasAttributes
*/
protected function serializeDate(DateTimeInterface $date)
{
return $date->format($this->getDateFormat());
return Carbon::instance($date)->toJSON();
}
/**
@@ -866,14 +972,16 @@ trait HasAttributes
*/
public function getDates()
{
if (! $this->usesTimestamps()) {
return $this->dates;
}
$defaults = [
$this->getCreatedAtColumn(),
$this->getUpdatedAtColumn(),
];
return $this->usesTimestamps()
? array_unique(array_merge($this->dates, $defaults))
: $this->dates;
return array_unique(array_merge($this->dates, $defaults));
}
/**
@@ -951,6 +1059,93 @@ trait HasAttributes
return $this->hasCast($key, ['array', 'json', 'object', 'collection']);
}
/**
* Determine if the given key is cast using a custom class.
*
* @param string $key
* @return bool
*/
protected function isClassCastable($key)
{
return array_key_exists($key, $this->getCasts()) &&
class_exists($class = $this->parseCasterClass($this->getCasts()[$key])) &&
! in_array($class, static::$primitiveCastTypes);
}
/**
* 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) && strpos($castType, ':') !== false) {
$segments = explode(':', $castType, 2);
$castType = $segments[0];
$arguments = explode(',', $segments[1]);
}
if (is_subclass_of($castType, Castable::class)) {
$castType = $castType::castUsing();
}
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 strpos($class, ':') === false
? $class
: explode(':', $class, 2)[0];
}
/**
* Merge the cast class attributes back into the model.
*
* @return void
*/
protected function mergeAttributesFromClassCasts()
{
foreach ($this->classCastCache as $key => $value) {
$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))
);
}
}
/**
* 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.
*
@@ -958,6 +1153,8 @@ trait HasAttributes
*/
public function getAttributes()
{
$this->mergeAttributesFromClassCasts();
return $this->attributes;
}
@@ -976,6 +1173,8 @@ trait HasAttributes
$this->syncOriginal();
}
$this->classCastCache = [];
return $this;
}
@@ -987,6 +1186,40 @@ trait HasAttributes
* @return mixed|array
*/
public function getOriginal($key = null, $default = null)
{
return (new static)->setRawAttributes(
$this->original, $sync = true
)->getOriginalWithoutRewindingModel($key, $default);
}
/**
* Get the model's original attribute values.
*
* @param string|null $key
* @param mixed $default
* @return mixed|array
*/
protected function getOriginalWithoutRewindingModel($key = null, $default = null)
{
if ($key) {
return $this->transformModelValue(
$key, Arr::get($this->original, $key, $default)
);
}
return collect($this->original)->mapWithKeys(function ($value, $key) {
return [$key => $this->transformModelValue($key, $value)];
})->all();
}
/**
* Get the model's raw original attribute values.
*
* @param string|null $key
* @param mixed $default
* @return mixed|array
*/
public function getRawOriginal($key = null, $default = null)
{
return Arr::get($this->original, $key, $default);
}
@@ -1015,7 +1248,7 @@ trait HasAttributes
*/
public function syncOriginal()
{
$this->original = $this->attributes;
$this->original = $this->getAttributes();
return $this;
}
@@ -1041,8 +1274,10 @@ trait HasAttributes
{
$attributes = is_array($attributes) ? $attributes : func_get_args();
$modelAttributes = $this->getAttributes();
foreach ($attributes as $attribute) {
$this->original[$attribute] = $this->attributes[$attribute];
$this->original[$attribute] = $modelAttributes[$attribute];
}
return $this;
@@ -1135,7 +1370,7 @@ trait HasAttributes
$dirty = [];
foreach ($this->getAttributes() as $key => $value) {
if (! $this->originalIsEquivalent($key, $value)) {
if (! $this->originalIsEquivalent($key)) {
$dirty[$key] = $value;
}
}
@@ -1157,40 +1392,74 @@ trait HasAttributes
* Determine if the new and old values for a given key are equivalent.
*
* @param string $key
* @param mixed $current
* @return bool
*/
public function originalIsEquivalent($key, $current)
public function originalIsEquivalent($key)
{
if (! array_key_exists($key, $this->original)) {
return false;
}
$original = $this->getOriginal($key);
$attribute = Arr::get($this->attributes, $key);
$original = Arr::get($this->original, $key);
if ($current === $original) {
if ($attribute === $original) {
return true;
} elseif (is_null($current)) {
} elseif (is_null($attribute)) {
return false;
} elseif ($this->isDateAttribute($key)) {
return $this->fromDateTime($current) ===
return $this->fromDateTime($attribute) ===
$this->fromDateTime($original);
} elseif ($this->hasCast($key, ['object', 'collection'])) {
return $this->castAttribute($key, $current) ==
return $this->castAttribute($key, $attribute) ==
$this->castAttribute($key, $original);
} elseif ($this->hasCast($key, ['real', 'float', 'double'])) {
if (($current === null && $original !== null) || ($current !== null && $original === null)) {
if (($attribute === null && $original !== null) || ($attribute !== null && $original === null)) {
return false;
}
return abs($this->castAttribute($key, $current) - $this->castAttribute($key, $original)) < PHP_FLOAT_EPSILON * 4;
} elseif ($this->hasCast($key)) {
return $this->castAttribute($key, $current) ===
return abs($this->castAttribute($key, $attribute) - $this->castAttribute($key, $original)) < PHP_FLOAT_EPSILON * 4;
} elseif ($this->hasCast($key, static::$primitiveCastTypes)) {
return $this->castAttribute($key, $attribute) ===
$this->castAttribute($key, $original);
}
return is_numeric($current) && is_numeric($original)
&& strcmp((string) $current, (string) $original) === 0;
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);
}
// 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)) {
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);
}
return $value;
}
/**
@@ -1221,6 +1490,17 @@ trait HasAttributes
return $this;
}
/**
* Return whether the accessor attribute has been appended.
*
* @param string $attribute
* @return bool
*/
public function hasAppended($attribute)
{
return in_array($attribute, $this->appends);
}
/**
* Get the mutated attributes for a given instance.
*

View File

@@ -2,6 +2,7 @@
namespace Illuminate\Database\Eloquent\Concerns;
use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
@@ -44,6 +45,28 @@ trait HasRelationships
'belongsToMany', 'morphToMany', 'morphedByMany',
];
/**
* The relation resolver callbacks.
*
* @var array
*/
protected static $relationResolvers = [];
/**
* 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]]
);
}
/**
* Define a one-to-one relationship.
*
@@ -369,8 +392,12 @@ trait HasRelationships
$secondKey = $secondKey ?: $through->getForeignKey();
return $this->newHasManyThrough(
$this->newRelatedInstance($related)->newQuery(), $this, $through,
$firstKey, $secondKey, $localKey ?: $this->getKeyName(),
$this->newRelatedInstance($related)->newQuery(),
$this,
$through,
$firstKey,
$secondKey,
$localKey ?: $this->getKeyName(),
$secondLocalKey ?: $through->getKeyName()
);
}
@@ -488,7 +515,7 @@ trait HasRelationships
* @param string $relatedPivotKey
* @param string $parentKey
* @param string $relatedKey
* @param string $relationName
* @param string|null $relationName
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
protected function newBelongsToMany(Builder $query, Model $parent, $table, $foreignPivotKey, $relatedPivotKey,
@@ -655,7 +682,7 @@ trait HasRelationships
*/
public function touches($relation)
{
return in_array($relation, $this->touches);
return in_array($relation, $this->getTouchedRelations());
}
/**
@@ -665,7 +692,7 @@ trait HasRelationships
*/
public function touchOwners()
{
foreach ($this->touches as $relation) {
foreach ($this->getTouchedRelations() as $relation) {
$this->$relation()->touch();
if ($this->$relation instanceof self) {

View File

@@ -2,6 +2,8 @@
namespace Illuminate\Database\Eloquent\Concerns;
use Closure;
trait HidesAttributes
{
/**
@@ -41,19 +43,6 @@ trait HidesAttributes
return $this;
}
/**
* Add hidden attributes for the model.
*
* @param array|string|null $attributes
* @return void
*/
public function addHidden($attributes = null)
{
$this->hidden = array_merge(
$this->hidden, is_array($attributes) ? $attributes : func_get_args()
);
}
/**
* Get the visible attributes for the model.
*
@@ -77,50 +66,65 @@ trait HidesAttributes
return $this;
}
/**
* Add visible attributes for the model.
*
* @param array|string|null $attributes
* @return void
*/
public function addVisible($attributes = null)
{
$this->visible = array_merge(
$this->visible, is_array($attributes) ? $attributes : func_get_args()
);
}
/**
* Make the given, typically hidden, attributes visible.
*
* @param array|string $attributes
* @param array|string|null $attributes
* @return $this
*/
public function makeVisible($attributes)
{
$this->hidden = array_diff($this->hidden, (array) $attributes);
$attributes = is_array($attributes) ? $attributes : func_get_args();
$this->hidden = array_diff($this->hidden, $attributes);
if (! empty($this->visible)) {
$this->addVisible($attributes);
$this->visible = array_merge($this->visible, $attributes);
}
return $this;
}
/**
* Make the given, typically hidden, attributes visible if the given truth test passes.
*
* @param bool|Closure $condition
* @param array|string|null $attributes
* @return $this
*/
public function makeVisibleIf($condition, $attributes)
{
$condition = $condition instanceof Closure ? $condition($this) : $condition;
return $condition ? $this->makeVisible($attributes) : $this;
}
/**
* Make the given, typically visible, attributes hidden.
*
* @param array|string $attributes
* @param array|string|null $attributes
* @return $this
*/
public function makeHidden($attributes)
{
$attributes = (array) $attributes;
$this->visible = array_diff($this->visible, $attributes);
$this->hidden = array_unique(array_merge($this->hidden, $attributes));
$this->hidden = array_merge(
$this->hidden, is_array($attributes) ? $attributes : func_get_args()
);
return $this;
}
/**
* Make the given, typically visible, attributes hidden if the given truth test passes.
*
* @param bool|Closure $condition
* @param array|string|null $attributes
* @return $this
*/
public function makeHiddenIf($condition, $attributes)
{
$condition = $condition instanceof Closure ? $condition($this) : $condition;
return value($condition) ? $this->makeHidden($attributes) : $this;
}
}

View File

@@ -152,7 +152,7 @@ trait QueriesRelationships
* Add a relationship count / exists condition to the query with where clauses and an "or".
*
* @param string $relation
* @param \Closure $callback
* @param \Closure|null $callback
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
@@ -178,7 +178,7 @@ trait QueriesRelationships
* Add a relationship count / exists condition to the query with where clauses and an "or".
*
* @param string $relation
* @param \Closure $callback
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function orWhereDoesntHave($relation, Closure $callback = null)
@@ -205,10 +205,10 @@ trait QueriesRelationships
if ($types === ['*']) {
$types = $this->model->newModelQuery()->distinct()->pluck($relation->getMorphType())->filter()->all();
}
foreach ($types as &$type) {
$type = Relation::getMorphedModel($type) ?? $type;
}
foreach ($types as &$type) {
$type = Relation::getMorphedModel($type) ?? $type;
}
return $this->where(function ($query) use ($relation, $callback, $operator, $count, $types) {
@@ -311,7 +311,7 @@ trait QueriesRelationships
*
* @param string $relation
* @param string|array $types
* @param \Closure $callback
* @param \Closure|null $callback
* @param string $operator
* @param int $count
* @return \Illuminate\Database\Eloquent\Builder|static
@@ -339,7 +339,7 @@ trait QueriesRelationships
*
* @param string $relation
* @param string|array $types
* @param \Closure $callback
* @param \Closure|null $callback
* @return \Illuminate\Database\Eloquent\Builder|static
*/
public function orWhereDoesntHaveMorph($relation, $types, Closure $callback = null)
@@ -390,6 +390,10 @@ trait QueriesRelationships
$query = $query->mergeConstraintsFrom($relation->getQuery())->toBase();
$query->orders = null;
$query->setBindings([], 'order');
if (count($query->columns) > 1) {
$query->columns = [$query->columns[0]];

View File

@@ -68,30 +68,16 @@ class Factory implements ArrayAccess
return (new static($faker))->load($pathToFactories);
}
/**
* Define a class with a given short-name.
*
* @param string $class
* @param string $name
* @param callable $attributes
* @return $this
*/
public function defineAs($class, $name, callable $attributes)
{
return $this->define($class, $attributes, $name);
}
/**
* Define a class with a given set of attributes.
*
* @param string $class
* @param callable $attributes
* @param string $name
* @return $this
*/
public function define($class, callable $attributes, $name = 'default')
public function define($class, callable $attributes)
{
$this->definitions[$class][$name] = $attributes;
$this->definitions[$class] = $attributes;
return $this;
}
@@ -179,19 +165,6 @@ class Factory implements ArrayAccess
return $this->of($class)->create($attributes);
}
/**
* Create an instance of the given model and type and persist it to the database.
*
* @param string $class
* @param string $name
* @param array $attributes
* @return mixed
*/
public function createAs($class, $name, array $attributes = [])
{
return $this->of($class, $name)->create($attributes);
}
/**
* Create an instance of the given model.
*
@@ -204,44 +177,17 @@ class Factory implements ArrayAccess
return $this->of($class)->make($attributes);
}
/**
* Create an instance of the given model and type.
*
* @param string $class
* @param string $name
* @param array $attributes
* @return mixed
*/
public function makeAs($class, $name, array $attributes = [])
{
return $this->of($class, $name)->make($attributes);
}
/**
* Get the raw attribute array for a given named model.
*
* @param string $class
* @param string $name
* @param array $attributes
* @return array
*/
public function rawOf($class, $name, array $attributes = [])
{
return $this->raw($class, $attributes, $name);
}
/**
* Get the raw attribute array for a given model.
*
* @param string $class
* @param array $attributes
* @param string $name
* @return array
*/
public function raw($class, array $attributes = [], $name = 'default')
public function raw($class, array $attributes = [])
{
return array_merge(
call_user_func($this->definitions[$class][$name], $this->faker), $attributes
call_user_func($this->definitions[$class], $this->faker), $attributes
);
}
@@ -249,13 +195,12 @@ class Factory implements ArrayAccess
* Create a builder for the given model.
*
* @param string $class
* @param string $name
* @return \Illuminate\Database\Eloquent\FactoryBuilder
*/
public function of($class, $name = 'default')
public function of($class)
{
return new FactoryBuilder(
$class, $name, $this->definitions, $this->states,
$class, $this->definitions, $this->states,
$this->afterMaking, $this->afterCreating, $this->faker
);
}

View File

@@ -24,13 +24,6 @@ class FactoryBuilder
*/
protected $class;
/**
* The name of the model being built.
*
* @var string
*/
protected $name = 'default';
/**
* The database connection on which the model instance should be persisted.
*
@@ -84,7 +77,6 @@ class FactoryBuilder
* Create an new builder instance.
*
* @param string $class
* @param string $name
* @param array $definitions
* @param array $states
* @param array $afterMaking
@@ -92,10 +84,9 @@ class FactoryBuilder
* @param \Faker\Generator $faker
* @return void
*/
public function __construct($class, $name, array $definitions, array $states,
public function __construct($class, array $definitions, array $states,
array $afterMaking, array $afterCreating, Faker $faker)
{
$this->name = $name;
$this->class = $class;
$this->faker = $faker;
$this->states = $states;
@@ -278,12 +269,12 @@ class FactoryBuilder
*/
protected function getRawAttributes(array $attributes = [])
{
if (! isset($this->definitions[$this->class][$this->name])) {
throw new InvalidArgumentException("Unable to locate factory with name [{$this->name}] [{$this->class}].");
if (! isset($this->definitions[$this->class])) {
throw new InvalidArgumentException("Unable to locate factory for [{$this->class}].");
}
$definition = call_user_func(
$this->definitions[$this->class][$this->name],
$this->definitions[$this->class],
$this->faker, $attributes
);
@@ -416,7 +407,7 @@ class FactoryBuilder
*/
protected function callAfter(array $afterCallbacks, $models)
{
$states = array_merge([$this->name], $this->activeStates);
$states = array_merge(['default'], $this->activeStates);
$models->each(function ($model) use ($states, $afterCallbacks) {
foreach ($states as $state) {

View File

@@ -26,6 +26,7 @@ class HigherOrderBuilderProxy
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param string $method
* @return void
*/
public function __construct(Builder $builder, $method)
{

View File

@@ -18,6 +18,20 @@ class JsonEncodingException extends RuntimeException
return new static('Error encoding model ['.get_class($model).'] with ID ['.$model->getKey().'] to JSON: '.$message);
}
/**
* Create a new JSON encoding exception for the resource.
*
* @param \Illuminate\Http\Resources\Json\JsonResource $resource
* @param string $message
* @return static
*/
public static function forResource($resource, $message)
{
$model = $resource->resource;
return new static('Error encoding resource ['.get_class($resource).'] with model ['.get_class($model).'] with ID ['.$model->getKey().'] to JSON: '.$message);
}
/**
* Create a new JSON encoding exception for an attribute.
*

View File

@@ -10,7 +10,9 @@ use Illuminate\Contracts\Routing\UrlRoutable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Database\ConnectionResolverInterface as Resolver;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\Concerns\AsPivot;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection as BaseCollection;
@@ -184,14 +186,26 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
$this->fireModelEvent('booting', false);
static::booting();
static::boot();
static::booted();
$this->fireModelEvent('booted', false);
}
}
/**
* The "booting" method of the model.
* Perform any actions required before the model boots.
*
* @return void
*/
protected static function booting()
{
//
}
/**
* Bootstrap the model and its traits.
*
* @return void
*/
@@ -244,6 +258,16 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
}
}
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted()
{
//
}
/**
* Clear the list of booted models so they will be re-booted.
*
@@ -372,6 +396,8 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
*
* @param string $key
* @return string
*
* @deprecated This method is deprecated and will be removed in a future Laravel version.
*/
protected function removeTableFromKey($key)
{
@@ -400,6 +426,8 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
$model->setTable($this->getTable());
$model->mergeCasts($this->casts);
return $model;
}
@@ -494,6 +522,22 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
return $this;
}
/**
* Eager load relationships on the polymorphic relation of a model.
*
* @param string $relation
* @param array $relations
* @return $this
*/
public function loadMorph($relation, $relations)
{
$className = get_class($this->{$relation});
$this->{$relation}->load($relations[$className] ?? []);
return $this;
}
/**
* Eager load relations on the model if they are not already eager loaded.
*
@@ -524,6 +568,22 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
return $this;
}
/**
* Eager load relationship counts on the polymorphic relation of a model.
*
* @param string $relation
* @param array $relations
* @return $this
*/
public function loadMorphCount($relation, $relations)
{
$className = get_class($this->{$relation});
$this->{$relation}->loadCount($relations[$className] ?? []);
return $this;
}
/**
* Increment a column's value by a given amount.
*
@@ -644,6 +704,8 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
*/
public function save(array $options = [])
{
$this->mergeAttributesFromClassCasts();
$query = $this->newModelQuery();
// If the "saving" event returns false we'll bail out of the save and return
@@ -845,7 +907,7 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
/**
* Destroy the models for the given IDs.
*
* @param \Illuminate\Support\Collection|array|int $ids
* @param \Illuminate\Support\Collection|array|int|string $ids
* @return int
*/
public static function destroy($ids)
@@ -884,6 +946,8 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
*/
public function delete()
{
$this->mergeAttributesFromClassCasts();
if (is_null($this->getKeyName())) {
throw new Exception('No primary key defined on model.');
}
@@ -1079,6 +1143,29 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
: Pivot::fromAttributes($parent, $attributes, $table, $exists);
}
/**
* Determine if the model has a given scope.
*
* @param string $scope
* @return bool
*/
public function hasNamedScope($scope)
{
return method_exists($this, 'scope'.ucfirst($scope));
}
/**
* Apply the given named scope if possible.
*
* @param string $scope
* @param array $parameters
* @return mixed
*/
public function callNamedScope($scope, array $parameters = [])
{
return $this->{'scope'.ucfirst($scope)}(...$parameters);
}
/**
* Convert the model instance to an array.
*
@@ -1176,7 +1263,7 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
];
$attributes = Arr::except(
$this->attributes, $except ? array_unique(array_merge($except, $defaults)) : $defaults
$this->getAttributes(), $except ? array_unique(array_merge($except, $defaults)) : $defaults
);
return tap(new static, function ($instance) use ($attributes) {
@@ -1476,11 +1563,34 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
* Retrieve the model for a bound value.
*
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value)
public function resolveRouteBinding($value, $field = null)
{
return $this->where($this->getRouteKeyName(), $value)->first();
return $this->where($field ?? $this->getRouteKeyName(), $value)->first();
}
/**
* Retrieve the child model for a bound value.
*
* @param string $childType
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveChildRouteBinding($childType, $value, $field)
{
$relationship = $this->{Str::plural(Str::camel($childType))}();
$field = $field ?: $relationship->getRelated()->getRouteKeyName();
if ($relationship instanceof HasManyThrough ||
$relationship instanceof BelongsToMany) {
return $relationship->where($relationship->getRelated()->getTable().'.'.$field, $value)->first();
} else {
return $relationship->where($field, $value)->first();
}
}
/**
@@ -1619,11 +1729,15 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
return $this->$method(...$parameters);
}
if ($resolver = (static::$relationResolvers[get_class($this)][$method] ?? null)) {
return $resolver($this);
}
return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}
/**
* Handle dynamic static method calls into the method.
* Handle dynamic static method calls into the model.
*
* @param string $method
* @param array $parameters
@@ -1644,6 +1758,20 @@ abstract class Model implements Arrayable, ArrayAccess, Jsonable, JsonSerializab
return $this->toJson();
}
/**
* Prepare the object for serialization.
*
* @return array
*/
public function __sleep()
{
$this->mergeAttributesFromClassCasts();
$this->classCastCache = [];
return array_keys(get_object_vars($this));
}
/**
* When a model is being unserialized, check if it needs to be booted.
*

View File

@@ -13,6 +13,8 @@ class BelongsTo extends Relation
/**
* The child model instance of the relation.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $child;
@@ -206,7 +208,7 @@ class BelongsTo extends Relation
if ($model instanceof Model) {
$this->child->setRelation($this->relationName, $model);
} elseif ($this->child->isDirty($this->foreignKey)) {
} else {
$this->child->unsetRelation($this->relationName);
}
@@ -283,7 +285,7 @@ class BelongsTo extends Relation
protected function relationHasIncrementingId()
{
return $this->related->getIncrementing() &&
$this->related->getKeyType() === 'int';
in_array($this->related->getKeyType(), ['int', 'integer']);
}
/**

Some files were not shown because too many files have changed in this diff Show More