├── Capsule └── Manager.php ├── ClassMorphViolationException.php ├── Concerns ├── BuildsQueries.php ├── BuildsWhereDateClauses.php ├── CompilesJsonPaths.php ├── ExplainsQueries.php ├── ManagesTransactions.php └── ParsesSearchPath.php ├── ConfigurationUrlParser.php ├── Connection.php ├── ConnectionInterface.php ├── ConnectionResolver.php ├── ConnectionResolverInterface.php ├── Connectors ├── ConnectionFactory.php ├── Connector.php ├── ConnectorInterface.php ├── MariaDbConnector.php ├── MySqlConnector.php ├── PostgresConnector.php ├── SQLiteConnector.php └── SqlServerConnector.php ├── Console ├── DatabaseInspectionCommand.php ├── DbCommand.php ├── DumpCommand.php ├── Factories │ ├── FactoryMakeCommand.php │ └── stubs │ │ └── factory.stub ├── Migrations │ ├── BaseCommand.php │ ├── FreshCommand.php │ ├── InstallCommand.php │ ├── MigrateCommand.php │ ├── MigrateMakeCommand.php │ ├── RefreshCommand.php │ ├── ResetCommand.php │ ├── RollbackCommand.php │ ├── StatusCommand.php │ └── TableGuesser.php ├── MonitorCommand.php ├── PruneCommand.php ├── Seeds │ ├── SeedCommand.php │ ├── SeederMakeCommand.php │ ├── WithoutModelEvents.php │ └── stubs │ │ └── seeder.stub ├── ShowCommand.php ├── ShowModelCommand.php ├── TableCommand.php └── WipeCommand.php ├── DatabaseManager.php ├── DatabaseServiceProvider.php ├── DatabaseTransactionRecord.php ├── DatabaseTransactionsManager.php ├── DeadlockException.php ├── DetectsConcurrencyErrors.php ├── DetectsLostConnections.php ├── Eloquent ├── Attributes │ ├── CollectedBy.php │ ├── ObservedBy.php │ ├── Scope.php │ ├── ScopedBy.php │ └── UseFactory.php ├── BroadcastableModelEventOccurred.php ├── BroadcastsEvents.php ├── BroadcastsEventsAfterCommit.php ├── Builder.php ├── Casts │ ├── ArrayObject.php │ ├── AsArrayObject.php │ ├── AsCollection.php │ ├── AsEncryptedArrayObject.php │ ├── AsEncryptedCollection.php │ ├── AsEnumArrayObject.php │ ├── AsEnumCollection.php │ ├── AsHtmlString.php │ ├── AsStringable.php │ ├── Attribute.php │ └── Json.php ├── Collection.php ├── Concerns │ ├── GuardsAttributes.php │ ├── HasAttributes.php │ ├── HasEvents.php │ ├── HasGlobalScopes.php │ ├── HasRelationships.php │ ├── HasTimestamps.php │ ├── HasUlids.php │ ├── HasUniqueIds.php │ ├── HasUniqueStringIds.php │ ├── HasUuids.php │ ├── HasVersion4Uuids.php │ ├── HidesAttributes.php │ ├── PreventsCircularRecursion.php │ ├── QueriesRelationships.php │ └── TransformsToResource.php ├── Factories │ ├── BelongsToManyRelationship.php │ ├── BelongsToRelationship.php │ ├── CrossJoinSequence.php │ ├── Factory.php │ ├── HasFactory.php │ ├── Relationship.php │ └── Sequence.php ├── HasBuilder.php ├── HasCollection.php ├── HigherOrderBuilderProxy.php ├── InvalidCastException.php ├── JsonEncodingException.php ├── MassAssignmentException.php ├── MassPrunable.php ├── MissingAttributeException.php ├── Model.php ├── ModelInspector.php ├── ModelNotFoundException.php ├── PendingHasThroughRelationship.php ├── Prunable.php ├── QueueEntityResolver.php ├── RelationNotFoundException.php ├── Relations │ ├── BelongsTo.php │ ├── BelongsToMany.php │ ├── Concerns │ │ ├── AsPivot.php │ │ ├── CanBeOneOfMany.php │ │ ├── ComparesRelatedModels.php │ │ ├── InteractsWithDictionary.php │ │ ├── InteractsWithPivotTable.php │ │ ├── SupportsDefaultModels.php │ │ └── SupportsInverseRelations.php │ ├── HasMany.php │ ├── HasManyThrough.php │ ├── HasOne.php │ ├── HasOneOrMany.php │ ├── HasOneOrManyThrough.php │ ├── HasOneThrough.php │ ├── MorphMany.php │ ├── MorphOne.php │ ├── MorphOneOrMany.php │ ├── MorphPivot.php │ ├── MorphTo.php │ ├── MorphToMany.php │ ├── Pivot.php │ └── Relation.php ├── Scope.php ├── SoftDeletes.php └── SoftDeletingScope.php ├── Events ├── ConnectionEstablished.php ├── ConnectionEvent.php ├── DatabaseBusy.php ├── DatabaseRefreshed.php ├── MigrationEnded.php ├── MigrationEvent.php ├── MigrationStarted.php ├── MigrationsEnded.php ├── MigrationsEvent.php ├── MigrationsPruned.php ├── MigrationsStarted.php ├── ModelPruningFinished.php ├── ModelPruningStarting.php ├── ModelsPruned.php ├── NoPendingMigrations.php ├── QueryExecuted.php ├── SchemaDumped.php ├── SchemaLoaded.php ├── StatementPrepared.php ├── TransactionBeginning.php ├── TransactionCommitted.php ├── TransactionCommitting.php └── TransactionRolledBack.php ├── Grammar.php ├── LICENSE.md ├── LazyLoadingViolationException.php ├── LostConnectionException.php ├── MariaDbConnection.php ├── MigrationServiceProvider.php ├── Migrations ├── DatabaseMigrationRepository.php ├── Migration.php ├── MigrationCreator.php ├── MigrationRepositoryInterface.php ├── MigrationResult.php ├── Migrator.php └── stubs │ ├── migration.create.stub │ ├── migration.stub │ └── migration.update.stub ├── MultipleColumnsSelectedException.php ├── MultipleRecordsFoundException.php ├── MySqlConnection.php ├── PostgresConnection.php ├── Query ├── Builder.php ├── Expression.php ├── Grammars │ ├── Grammar.php │ ├── MariaDbGrammar.php │ ├── MySqlGrammar.php │ ├── PostgresGrammar.php │ ├── SQLiteGrammar.php │ └── SqlServerGrammar.php ├── IndexHint.php ├── JoinClause.php ├── JoinLateralClause.php └── Processors │ ├── MariaDbProcessor.php │ ├── MySqlProcessor.php │ ├── PostgresProcessor.php │ ├── Processor.php │ ├── SQLiteProcessor.php │ └── SqlServerProcessor.php ├── QueryException.php ├── README.md ├── RecordNotFoundException.php ├── RecordsNotFoundException.php ├── SQLiteConnection.php ├── SQLiteDatabaseDoesNotExistException.php ├── Schema ├── Blueprint.php ├── BlueprintState.php ├── Builder.php ├── ColumnDefinition.php ├── ForeignIdColumnDefinition.php ├── ForeignKeyDefinition.php ├── Grammars │ ├── Grammar.php │ ├── MariaDbGrammar.php │ ├── MySqlGrammar.php │ ├── PostgresGrammar.php │ ├── SQLiteGrammar.php │ └── SqlServerGrammar.php ├── IndexDefinition.php ├── MariaDbBuilder.php ├── MariaDbSchemaState.php ├── MySqlBuilder.php ├── MySqlSchemaState.php ├── PostgresBuilder.php ├── PostgresSchemaState.php ├── SQLiteBuilder.php ├── SchemaState.php ├── SqlServerBuilder.php └── SqliteSchemaState.php ├── Seeder.php ├── SqlServerConnection.php ├── UniqueConstraintViolationException.php └── composer.json /ClassMorphViolationException.php: -------------------------------------------------------------------------------- 1 | model = $class; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Concerns/CompilesJsonPaths.php: -------------------------------------------------------------------------------- 1 | ', $column, 2); 19 | 20 | $field = $this->wrap($parts[0]); 21 | 22 | $path = count($parts) > 1 ? ', '.$this->wrapJsonPath($parts[1], '->') : ''; 23 | 24 | return [$field, $path]; 25 | } 26 | 27 | /** 28 | * Wrap the given JSON path. 29 | * 30 | * @param string $value 31 | * @param string $delimiter 32 | * @return string 33 | */ 34 | protected function wrapJsonPath($value, $delimiter = '->') 35 | { 36 | $value = preg_replace("/([\\\\]+)?\\'/", "''", $value); 37 | 38 | $jsonPath = (new Collection(explode($delimiter, $value))) 39 | ->map(fn ($segment) => $this->wrapJsonPathSegment($segment)) 40 | ->join('.'); 41 | 42 | return "'$".(str_starts_with($jsonPath, '[') ? '' : '.').$jsonPath."'"; 43 | } 44 | 45 | /** 46 | * Wrap the given JSON path segment. 47 | * 48 | * @param string $segment 49 | * @return string 50 | */ 51 | protected function wrapJsonPathSegment($segment) 52 | { 53 | if (preg_match('/(\[[^\]]+\])+$/', $segment, $parts)) { 54 | $key = Str::beforeLast($segment, $parts[0]); 55 | 56 | if (! empty($key)) { 57 | return '"'.$key.'"'.$parts[0]; 58 | } 59 | 60 | return $parts[0]; 61 | } 62 | 63 | return '"'.$segment.'"'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Concerns/ExplainsQueries.php: -------------------------------------------------------------------------------- 1 | toSql(); 17 | 18 | $bindings = $this->getBindings(); 19 | 20 | $explanation = $this->getConnection()->select('EXPLAIN '.$sql, $bindings); 21 | 22 | return new Collection($explanation); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Concerns/ParsesSearchPath.php: -------------------------------------------------------------------------------- 1 | $connections 25 | */ 26 | public function __construct(array $connections = []) 27 | { 28 | foreach ($connections as $name => $connection) { 29 | $this->addConnection($name, $connection); 30 | } 31 | } 32 | 33 | /** 34 | * Get a database connection instance. 35 | * 36 | * @param string|null $name 37 | * @return \Illuminate\Database\ConnectionInterface 38 | */ 39 | public function connection($name = null) 40 | { 41 | if (is_null($name)) { 42 | $name = $this->getDefaultConnection(); 43 | } 44 | 45 | return $this->connections[$name]; 46 | } 47 | 48 | /** 49 | * Add a connection to the resolver. 50 | * 51 | * @param string $name 52 | * @param \Illuminate\Database\ConnectionInterface $connection 53 | * @return void 54 | */ 55 | public function addConnection($name, ConnectionInterface $connection) 56 | { 57 | $this->connections[$name] = $connection; 58 | } 59 | 60 | /** 61 | * Check if a connection has been registered. 62 | * 63 | * @param string $name 64 | * @return bool 65 | */ 66 | public function hasConnection($name) 67 | { 68 | return isset($this->connections[$name]); 69 | } 70 | 71 | /** 72 | * Get the default connection name. 73 | * 74 | * @return string 75 | */ 76 | public function getDefaultConnection() 77 | { 78 | return $this->default; 79 | } 80 | 81 | /** 82 | * Set the default connection name. 83 | * 84 | * @param string $name 85 | * @return void 86 | */ 87 | public function setDefaultConnection($name) 88 | { 89 | $this->default = $name; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ConnectionResolverInterface.php: -------------------------------------------------------------------------------- 1 | PDO::CASE_NATURAL, 21 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 22 | PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, 23 | PDO::ATTR_STRINGIFY_FETCHES => false, 24 | PDO::ATTR_EMULATE_PREPARES => false, 25 | ]; 26 | 27 | /** 28 | * Create a new PDO connection. 29 | * 30 | * @param string $dsn 31 | * @param array $config 32 | * @param array $options 33 | * @return \PDO 34 | * 35 | * @throws \Exception 36 | */ 37 | public function createConnection($dsn, array $config, array $options) 38 | { 39 | [$username, $password] = [ 40 | $config['username'] ?? null, $config['password'] ?? null, 41 | ]; 42 | 43 | try { 44 | return $this->createPdoConnection( 45 | $dsn, $username, $password, $options 46 | ); 47 | } catch (Exception $e) { 48 | return $this->tryAgainIfCausedByLostConnection( 49 | $e, $dsn, $username, $password, $options 50 | ); 51 | } 52 | } 53 | 54 | /** 55 | * Create a new PDO connection instance. 56 | * 57 | * @param string $dsn 58 | * @param string $username 59 | * @param string $password 60 | * @param array $options 61 | * @return \PDO 62 | */ 63 | protected function createPdoConnection($dsn, $username, #[\SensitiveParameter] $password, $options) 64 | { 65 | return version_compare(phpversion(), '8.4.0', '<') 66 | ? new PDO($dsn, $username, $password, $options) 67 | : PDO::connect($dsn, $username, $password, $options); /** @phpstan-ignore staticMethod.notFound (PHP 8.4) */ 68 | } 69 | 70 | /** 71 | * Handle an exception that occurred during connect execution. 72 | * 73 | * @param \Throwable $e 74 | * @param string $dsn 75 | * @param string $username 76 | * @param string $password 77 | * @param array $options 78 | * @return \PDO 79 | * 80 | * @throws \Throwable 81 | */ 82 | protected function tryAgainIfCausedByLostConnection(Throwable $e, $dsn, $username, #[\SensitiveParameter] $password, $options) 83 | { 84 | if ($this->causedByLostConnection($e)) { 85 | return $this->createPdoConnection($dsn, $username, $password, $options); 86 | } 87 | 88 | throw $e; 89 | } 90 | 91 | /** 92 | * Get the PDO options based on the configuration. 93 | * 94 | * @param array $config 95 | * @return array 96 | */ 97 | public function getOptions(array $config) 98 | { 99 | $options = $config['options'] ?? []; 100 | 101 | return array_diff_key($this->options, $options) + $options; 102 | } 103 | 104 | /** 105 | * Get the default PDO connection options. 106 | * 107 | * @return array 108 | */ 109 | public function getDefaultOptions() 110 | { 111 | return $this->options; 112 | } 113 | 114 | /** 115 | * Set the default PDO connection options. 116 | * 117 | * @param array $options 118 | * @return void 119 | */ 120 | public function setDefaultOptions(array $options) 121 | { 122 | $this->options = $options; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Connectors/ConnectorInterface.php: -------------------------------------------------------------------------------- 1 | getDriverTitle(); 23 | } 24 | 25 | /** 26 | * Get the number of open connections for a database. 27 | * 28 | * @param \Illuminate\Database\ConnectionInterface $connection 29 | * @return int|null 30 | * 31 | * @deprecated 32 | */ 33 | protected function getConnectionCount(ConnectionInterface $connection) 34 | { 35 | return $connection->threadCount(); 36 | } 37 | 38 | /** 39 | * Get the connection configuration details for the given connection. 40 | * 41 | * @param string|null $database 42 | * @return array 43 | */ 44 | protected function getConfigFromDatabase($database) 45 | { 46 | $database ??= config('database.default'); 47 | 48 | return Arr::except(config('database.connections.'.$database), ['password']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Console/DumpCommand.php: -------------------------------------------------------------------------------- 1 | connection($database = $this->input->getOption('database')); 45 | 46 | $this->schemaState($connection)->dump( 47 | $connection, $path = $this->path($connection) 48 | ); 49 | 50 | $dispatcher->dispatch(new SchemaDumped($connection, $path)); 51 | 52 | $info = 'Database schema dumped'; 53 | 54 | if ($this->option('prune')) { 55 | (new Filesystem)->deleteDirectory( 56 | $path = database_path('migrations'), preserve: false 57 | ); 58 | 59 | $info .= ' and pruned'; 60 | 61 | $dispatcher->dispatch(new MigrationsPruned($connection, $path)); 62 | } 63 | 64 | $this->components->info($info.' successfully.'); 65 | } 66 | 67 | /** 68 | * Create a schema state instance for the given connection. 69 | * 70 | * @param \Illuminate\Database\Connection $connection 71 | * @return mixed 72 | */ 73 | protected function schemaState(Connection $connection) 74 | { 75 | $migrations = Config::get('database.migrations', 'migrations'); 76 | 77 | $migrationTable = is_array($migrations) ? ($migrations['table'] ?? 'migrations') : $migrations; 78 | 79 | return $connection->getSchemaState() 80 | ->withMigrationTable($migrationTable) 81 | ->handleOutputUsing(function ($type, $buffer) { 82 | $this->output->write($buffer); 83 | }); 84 | } 85 | 86 | /** 87 | * Get the path that the dump should be written to. 88 | * 89 | * @param \Illuminate\Database\Connection $connection 90 | */ 91 | protected function path(Connection $connection) 92 | { 93 | return tap($this->option('path') ?: database_path('schema/'.$connection->getName().'-schema.sql'), function ($path) { 94 | (new Filesystem)->ensureDirectoryExists(dirname($path)); 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Console/Factories/stubs/factory.stub: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class {{ factory }}Factory extends Factory 11 | { 12 | /** 13 | * Define the model's default state. 14 | * 15 | * @return array 16 | */ 17 | public function definition(): array 18 | { 19 | return [ 20 | // 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Console/Migrations/BaseCommand.php: -------------------------------------------------------------------------------- 1 | input->hasOption('path') && $this->option('path')) { 21 | return (new Collection($this->option('path')))->map(function ($path) { 22 | return ! $this->usingRealPath() 23 | ? $this->laravel->basePath().'/'.$path 24 | : $path; 25 | })->all(); 26 | } 27 | 28 | return array_merge( 29 | $this->migrator->paths(), [$this->getMigrationPath()] 30 | ); 31 | } 32 | 33 | /** 34 | * Determine if the given path(s) are pre-resolved "real" paths. 35 | * 36 | * @return bool 37 | */ 38 | protected function usingRealPath() 39 | { 40 | return $this->input->hasOption('realpath') && $this->option('realpath'); 41 | } 42 | 43 | /** 44 | * Get the path to the migration directory. 45 | * 46 | * @return string 47 | */ 48 | protected function getMigrationPath() 49 | { 50 | return $this->laravel->databasePath().DIRECTORY_SEPARATOR.'migrations'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Console/Migrations/InstallCommand.php: -------------------------------------------------------------------------------- 1 | repository = $repository; 44 | } 45 | 46 | /** 47 | * Execute the console command. 48 | * 49 | * @return void 50 | */ 51 | public function handle() 52 | { 53 | $this->repository->setSource($this->input->getOption('database')); 54 | 55 | if (! $this->repository->repositoryExists()) { 56 | $this->repository->createRepository(); 57 | } 58 | 59 | $this->components->info('Migration table created successfully.'); 60 | } 61 | 62 | /** 63 | * Get the console command options. 64 | * 65 | * @return array 66 | */ 67 | protected function getOptions() 68 | { 69 | return [ 70 | ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'], 71 | ]; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Console/Migrations/ResetCommand.php: -------------------------------------------------------------------------------- 1 | migrator = $migrator; 48 | } 49 | 50 | /** 51 | * Execute the console command. 52 | * 53 | * @return int 54 | */ 55 | public function handle() 56 | { 57 | if ($this->isProhibited() || 58 | ! $this->confirmToProceed()) { 59 | return Command::FAILURE; 60 | } 61 | 62 | return $this->migrator->usingConnection($this->option('database'), function () { 63 | // First, we'll make sure that the migration table actually exists before we 64 | // start trying to rollback and re-run all of the migrations. If it's not 65 | // present we'll just bail out with an info message for the developers. 66 | if (! $this->migrator->repositoryExists()) { 67 | return $this->components->warn('Migration table not found.'); 68 | } 69 | 70 | $this->migrator->setOutput($this->output)->reset( 71 | $this->getMigrationPaths(), $this->option('pretend') 72 | ); 73 | }); 74 | } 75 | 76 | /** 77 | * Get the console command options. 78 | * 79 | * @return array 80 | */ 81 | protected function getOptions() 82 | { 83 | return [ 84 | ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'], 85 | 86 | ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'], 87 | 88 | ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'], 89 | 90 | ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'], 91 | 92 | ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run'], 93 | ]; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Console/Migrations/RollbackCommand.php: -------------------------------------------------------------------------------- 1 | migrator = $migrator; 48 | } 49 | 50 | /** 51 | * Execute the console command. 52 | * 53 | * @return int 54 | */ 55 | public function handle() 56 | { 57 | if ($this->isProhibited() || 58 | ! $this->confirmToProceed()) { 59 | return Command::FAILURE; 60 | } 61 | 62 | $this->migrator->usingConnection($this->option('database'), function () { 63 | $this->migrator->setOutput($this->output)->rollback( 64 | $this->getMigrationPaths(), [ 65 | 'pretend' => $this->option('pretend'), 66 | 'step' => (int) $this->option('step'), 67 | 'batch' => (int) $this->option('batch'), 68 | ] 69 | ); 70 | }); 71 | 72 | return 0; 73 | } 74 | 75 | /** 76 | * Get the console command options. 77 | * 78 | * @return array 79 | */ 80 | protected function getOptions() 81 | { 82 | return [ 83 | ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'], 84 | ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'], 85 | ['path', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'The path(s) to the migrations files to be executed'], 86 | ['realpath', null, InputOption::VALUE_NONE, 'Indicate any provided migration file paths are pre-resolved absolute paths'], 87 | ['pretend', null, InputOption::VALUE_NONE, 'Dump the SQL queries that would be run'], 88 | ['step', null, InputOption::VALUE_OPTIONAL, 'The number of migrations to be reverted'], 89 | ['batch', null, InputOption::VALUE_REQUIRED, 'The batch of migrations (identified by their batch number) to be reverted'], 90 | ]; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Console/Migrations/TableGuesser.php: -------------------------------------------------------------------------------- 1 | resolver = $resolver; 50 | } 51 | 52 | /** 53 | * Execute the console command. 54 | * 55 | * @return int 56 | */ 57 | public function handle() 58 | { 59 | if ($this->isProhibited() || 60 | ! $this->confirmToProceed()) { 61 | return Command::FAILURE; 62 | } 63 | 64 | $this->components->info('Seeding database.'); 65 | 66 | $previousConnection = $this->resolver->getDefaultConnection(); 67 | 68 | $this->resolver->setDefaultConnection($this->getDatabase()); 69 | 70 | Model::unguarded(function () { 71 | $this->getSeeder()->__invoke(); 72 | }); 73 | 74 | if ($previousConnection) { 75 | $this->resolver->setDefaultConnection($previousConnection); 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | /** 82 | * Get a seeder instance from the container. 83 | * 84 | * @return \Illuminate\Database\Seeder 85 | */ 86 | protected function getSeeder() 87 | { 88 | $class = $this->input->getArgument('class') ?? $this->input->getOption('class'); 89 | 90 | if (! str_contains($class, '\\')) { 91 | $class = 'Database\\Seeders\\'.$class; 92 | } 93 | 94 | if ($class === 'Database\\Seeders\\DatabaseSeeder' && 95 | ! class_exists($class)) { 96 | $class = 'DatabaseSeeder'; 97 | } 98 | 99 | return $this->laravel->make($class) 100 | ->setContainer($this->laravel) 101 | ->setCommand($this); 102 | } 103 | 104 | /** 105 | * Get the name of the database connection to use. 106 | * 107 | * @return string 108 | */ 109 | protected function getDatabase() 110 | { 111 | $database = $this->input->getOption('database'); 112 | 113 | return $database ?: $this->laravel['config']['database.default']; 114 | } 115 | 116 | /** 117 | * Get the console command arguments. 118 | * 119 | * @return array 120 | */ 121 | protected function getArguments() 122 | { 123 | return [ 124 | ['class', InputArgument::OPTIONAL, 'The class name of the root seeder', null], 125 | ]; 126 | } 127 | 128 | /** 129 | * Get the console command options. 130 | * 131 | * @return array 132 | */ 133 | protected function getOptions() 134 | { 135 | return [ 136 | ['class', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder', 'Database\\Seeders\\DatabaseSeeder'], 137 | ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to seed'], 138 | ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'], 139 | ]; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Console/Seeds/SeederMakeCommand.php: -------------------------------------------------------------------------------- 1 | resolveStubPath('/stubs/seeder.stub'); 51 | } 52 | 53 | /** 54 | * Resolve the fully-qualified path to the stub. 55 | * 56 | * @param string $stub 57 | * @return string 58 | */ 59 | protected function resolveStubPath($stub) 60 | { 61 | return is_file($customPath = $this->laravel->basePath(trim($stub, '/'))) 62 | ? $customPath 63 | : __DIR__.$stub; 64 | } 65 | 66 | /** 67 | * Get the destination class path. 68 | * 69 | * @param string $name 70 | * @return string 71 | */ 72 | protected function getPath($name) 73 | { 74 | $name = str_replace('\\', '/', Str::replaceFirst($this->rootNamespace(), '', $name)); 75 | 76 | if (is_dir($this->laravel->databasePath().'/seeds')) { 77 | return $this->laravel->databasePath().'/seeds/'.$name.'.php'; 78 | } 79 | 80 | return $this->laravel->databasePath().'/seeders/'.$name.'.php'; 81 | } 82 | 83 | /** 84 | * Get the root namespace for the class. 85 | * 86 | * @return string 87 | */ 88 | protected function rootNamespace() 89 | { 90 | return 'Database\Seeders\\'; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Console/Seeds/WithoutModelEvents.php: -------------------------------------------------------------------------------- 1 | Model::withoutEvents($callback); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Console/Seeds/stubs/seeder.stub: -------------------------------------------------------------------------------- 1 | isProhibited() || 38 | ! $this->confirmToProceed()) { 39 | return Command::FAILURE; 40 | } 41 | 42 | $database = $this->input->getOption('database'); 43 | 44 | if ($this->option('drop-views')) { 45 | $this->dropAllViews($database); 46 | 47 | $this->components->info('Dropped all views successfully.'); 48 | } 49 | 50 | $this->dropAllTables($database); 51 | 52 | $this->components->info('Dropped all tables successfully.'); 53 | 54 | if ($this->option('drop-types')) { 55 | $this->dropAllTypes($database); 56 | 57 | $this->components->info('Dropped all types successfully.'); 58 | } 59 | 60 | return 0; 61 | } 62 | 63 | /** 64 | * Drop all of the database tables. 65 | * 66 | * @param string $database 67 | * @return void 68 | */ 69 | protected function dropAllTables($database) 70 | { 71 | $this->laravel['db']->connection($database) 72 | ->getSchemaBuilder() 73 | ->dropAllTables(); 74 | } 75 | 76 | /** 77 | * Drop all of the database views. 78 | * 79 | * @param string $database 80 | * @return void 81 | */ 82 | protected function dropAllViews($database) 83 | { 84 | $this->laravel['db']->connection($database) 85 | ->getSchemaBuilder() 86 | ->dropAllViews(); 87 | } 88 | 89 | /** 90 | * Drop all of the database types. 91 | * 92 | * @param string $database 93 | * @return void 94 | */ 95 | protected function dropAllTypes($database) 96 | { 97 | $this->laravel['db']->connection($database) 98 | ->getSchemaBuilder() 99 | ->dropAllTypes(); 100 | } 101 | 102 | /** 103 | * Get the console command options. 104 | * 105 | * @return array 106 | */ 107 | protected function getOptions() 108 | { 109 | return [ 110 | ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to use'], 111 | ['drop-views', null, InputOption::VALUE_NONE, 'Drop all tables and views'], 112 | ['drop-types', null, InputOption::VALUE_NONE, 'Drop all tables and types (Postgres only)'], 113 | ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'], 114 | ]; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /DatabaseServiceProvider.php: -------------------------------------------------------------------------------- 1 | app['db']); 30 | 31 | Model::setEventDispatcher($this->app['events']); 32 | } 33 | 34 | /** 35 | * Register the service provider. 36 | * 37 | * @return void 38 | */ 39 | public function register() 40 | { 41 | Model::clearBootedModels(); 42 | 43 | $this->registerConnectionServices(); 44 | $this->registerFakerGenerator(); 45 | $this->registerQueueableEntityResolver(); 46 | } 47 | 48 | /** 49 | * Register the primary database bindings. 50 | * 51 | * @return void 52 | */ 53 | protected function registerConnectionServices() 54 | { 55 | // The connection factory is used to create the actual connection instances on 56 | // the database. We will inject the factory into the manager so that it may 57 | // make the connections while they are actually needed and not of before. 58 | $this->app->singleton('db.factory', function ($app) { 59 | return new ConnectionFactory($app); 60 | }); 61 | 62 | // The database manager is used to resolve various connections, since multiple 63 | // connections might be managed. It also implements the connection resolver 64 | // interface which may be used by other components requiring connections. 65 | $this->app->singleton('db', function ($app) { 66 | return new DatabaseManager($app, $app['db.factory']); 67 | }); 68 | 69 | $this->app->bind('db.connection', function ($app) { 70 | return $app['db']->connection(); 71 | }); 72 | 73 | $this->app->bind('db.schema', function ($app) { 74 | return $app['db']->connection()->getSchemaBuilder(); 75 | }); 76 | 77 | $this->app->singleton('db.transactions', function ($app) { 78 | return new DatabaseTransactionsManager; 79 | }); 80 | } 81 | 82 | /** 83 | * Register the Faker Generator instance in the container. 84 | * 85 | * @return void 86 | */ 87 | protected function registerFakerGenerator() 88 | { 89 | $this->app->singleton(FakerGenerator::class, function ($app, $parameters) { 90 | $locale = $parameters['locale'] ?? $app['config']->get('app.faker_locale', 'en_US'); 91 | 92 | if (! isset(static::$fakers[$locale])) { 93 | static::$fakers[$locale] = FakerFactory::create($locale); 94 | } 95 | 96 | static::$fakers[$locale]->unique(true); 97 | 98 | return static::$fakers[$locale]; 99 | }); 100 | } 101 | 102 | /** 103 | * Register the queueable entity resolver implementation. 104 | * 105 | * @return void 106 | */ 107 | protected function registerQueueableEntityResolver() 108 | { 109 | $this->app->singleton(EntityResolver::class, function () { 110 | return new QueueEntityResolver; 111 | }); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /DatabaseTransactionRecord.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 52 | $this->level = $level; 53 | $this->parent = $parent; 54 | } 55 | 56 | /** 57 | * Register a callback to be executed after committing. 58 | * 59 | * @param callable $callback 60 | * @return void 61 | */ 62 | public function addCallback($callback) 63 | { 64 | $this->callbacks[] = $callback; 65 | } 66 | 67 | /** 68 | * Register a callback to be executed after rollback. 69 | * 70 | * @param callable $callback 71 | * @return void 72 | */ 73 | public function addCallbackForRollback($callback) 74 | { 75 | $this->callbacksForRollback[] = $callback; 76 | } 77 | 78 | /** 79 | * Execute all of the callbacks. 80 | * 81 | * @return void 82 | */ 83 | public function executeCallbacks() 84 | { 85 | foreach ($this->callbacks as $callback) { 86 | $callback(); 87 | } 88 | } 89 | 90 | /** 91 | * Execute all of the callbacks for rollback. 92 | * 93 | * @return void 94 | */ 95 | public function executeCallbacksForRollback() 96 | { 97 | foreach ($this->callbacksForRollback as $callback) { 98 | $callback(); 99 | } 100 | } 101 | 102 | /** 103 | * Get all of the callbacks. 104 | * 105 | * @return array 106 | */ 107 | public function getCallbacks() 108 | { 109 | return $this->callbacks; 110 | } 111 | 112 | /** 113 | * Get all of the callbacks for rollback. 114 | * 115 | * @return array 116 | */ 117 | public function getCallbacksForRollback() 118 | { 119 | return $this->callbacksForRollback; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /DeadlockException.php: -------------------------------------------------------------------------------- 1 | getCode() === 40001 || $e->getCode() === '40001')) { 20 | return true; 21 | } 22 | 23 | $message = $e->getMessage(); 24 | 25 | return Str::contains($message, [ 26 | 'Deadlock found when trying to get lock', 27 | 'deadlock detected', 28 | 'The database file is locked', 29 | 'database is locked', 30 | 'database table is locked', 31 | 'A table in the database is locked', 32 | 'has been chosen as the deadlock victim', 33 | 'Lock wait timeout exceeded; try restarting transaction', 34 | 'WSREP detected deadlock/conflict and aborted the transaction. Try restarting the transaction', 35 | ]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Eloquent/Attributes/CollectedBy.php: -------------------------------------------------------------------------------- 1 | > $collectionClass 14 | */ 15 | public function __construct(public string $collectionClass) 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Eloquent/Attributes/ObservedBy.php: -------------------------------------------------------------------------------- 1 | $factoryClass 14 | */ 15 | public function __construct(public string $factoryClass) 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Eloquent/BroadcastableModelEventOccurred.php: -------------------------------------------------------------------------------- 1 | model = $model; 66 | $this->event = $event; 67 | } 68 | 69 | /** 70 | * The channels the event should broadcast on. 71 | * 72 | * @return array 73 | */ 74 | public function broadcastOn() 75 | { 76 | $channels = empty($this->channels) 77 | ? ($this->model->broadcastOn($this->event) ?: []) 78 | : $this->channels; 79 | 80 | return (new BaseCollection($channels)) 81 | ->map(fn ($channel) => $channel instanceof Model ? new PrivateChannel($channel) : $channel) 82 | ->all(); 83 | } 84 | 85 | /** 86 | * The name the event should broadcast as. 87 | * 88 | * @return string 89 | */ 90 | public function broadcastAs() 91 | { 92 | $default = class_basename($this->model).ucfirst($this->event); 93 | 94 | return method_exists($this->model, 'broadcastAs') 95 | ? ($this->model->broadcastAs($this->event) ?: $default) 96 | : $default; 97 | } 98 | 99 | /** 100 | * Get the data that should be sent with the broadcasted event. 101 | * 102 | * @return array|null 103 | */ 104 | public function broadcastWith() 105 | { 106 | return method_exists($this->model, 'broadcastWith') 107 | ? $this->model->broadcastWith($this->event) 108 | : null; 109 | } 110 | 111 | /** 112 | * Manually specify the channels the event should broadcast on. 113 | * 114 | * @param array $channels 115 | * @return $this 116 | */ 117 | public function onChannels(array $channels) 118 | { 119 | $this->channels = $channels; 120 | 121 | return $this; 122 | } 123 | 124 | /** 125 | * Determine if the event should be broadcast synchronously. 126 | * 127 | * @return bool 128 | */ 129 | public function shouldBroadcastNow() 130 | { 131 | return $this->event === 'deleted' && 132 | ! method_exists($this->model, 'bootSoftDeletes'); 133 | } 134 | 135 | /** 136 | * Get the event name. 137 | * 138 | * @return string 139 | */ 140 | public function event() 141 | { 142 | return $this->event; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Eloquent/BroadcastsEventsAfterCommit.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class ArrayObject extends BaseArrayObject implements Arrayable, JsonSerializable 17 | { 18 | /** 19 | * Get a collection containing the underlying array. 20 | * 21 | * @return \Illuminate\Support\Collection 22 | */ 23 | public function collect() 24 | { 25 | return new Collection($this->getArrayCopy()); 26 | } 27 | 28 | /** 29 | * Get the instance as an array. 30 | * 31 | * @return array 32 | */ 33 | public function toArray() 34 | { 35 | return $this->getArrayCopy(); 36 | } 37 | 38 | /** 39 | * Get the array that should be JSON serialized. 40 | * 41 | * @return array 42 | */ 43 | public function jsonSerialize(): array 44 | { 45 | return $this->getArrayCopy(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Eloquent/Casts/AsArrayObject.php: -------------------------------------------------------------------------------- 1 | , iterable> 15 | */ 16 | public static function castUsing(array $arguments) 17 | { 18 | return new class implements CastsAttributes 19 | { 20 | public function get($model, $key, $value, $attributes) 21 | { 22 | if (! isset($attributes[$key])) { 23 | return; 24 | } 25 | 26 | $data = Json::decode($attributes[$key]); 27 | 28 | return is_array($data) ? new ArrayObject($data, ArrayObject::ARRAY_AS_PROPS) : null; 29 | } 30 | 31 | public function set($model, $key, $value, $attributes) 32 | { 33 | return [$key => Json::encode($value)]; 34 | } 35 | 36 | public function serialize($model, string $key, $value, array $attributes) 37 | { 38 | return $value->getArrayCopy(); 39 | } 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Eloquent/Casts/AsCollection.php: -------------------------------------------------------------------------------- 1 | , iterable> 18 | */ 19 | public static function castUsing(array $arguments) 20 | { 21 | return new class($arguments) implements CastsAttributes 22 | { 23 | public function __construct(protected array $arguments) 24 | { 25 | $this->arguments = array_pad(array_values($this->arguments), 2, ''); 26 | } 27 | 28 | public function get($model, $key, $value, $attributes) 29 | { 30 | if (! isset($attributes[$key])) { 31 | return; 32 | } 33 | 34 | $data = Json::decode($attributes[$key]); 35 | 36 | $collectionClass = empty($this->arguments[0]) ? Collection::class : $this->arguments[0]; 37 | 38 | if (! is_a($collectionClass, Collection::class, true)) { 39 | throw new InvalidArgumentException('The provided class must extend ['.Collection::class.'].'); 40 | } 41 | 42 | if (! is_array($data)) { 43 | return null; 44 | } 45 | 46 | $instance = new $collectionClass($data); 47 | 48 | if (! isset($this->arguments[1]) || ! $this->arguments[1]) { 49 | return $instance; 50 | } 51 | 52 | if (is_string($this->arguments[1])) { 53 | $this->arguments[1] = Str::parseCallback($this->arguments[1]); 54 | } 55 | 56 | return is_callable($this->arguments[1]) 57 | ? $instance->map($this->arguments[1]) 58 | : $instance->mapInto($this->arguments[1][0]); 59 | } 60 | 61 | public function set($model, $key, $value, $attributes) 62 | { 63 | return [$key => Json::encode($value)]; 64 | } 65 | }; 66 | } 67 | 68 | /** 69 | * Specify the type of object each item in the collection should be mapped to. 70 | * 71 | * @param array{class-string, string}|class-string $map 72 | * @return string 73 | */ 74 | public static function of($map) 75 | { 76 | return static::using('', $map); 77 | } 78 | 79 | /** 80 | * Specify the collection type for the cast. 81 | * 82 | * @param class-string $class 83 | * @param array{class-string, string}|class-string $map 84 | * @return string 85 | */ 86 | public static function using($class, $map = null) 87 | { 88 | if (is_array($map) && is_callable($map)) { 89 | $map = $map[0].'@'.$map[1]; 90 | } 91 | 92 | return static::class.':'.implode(',', [$class, $map]); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Eloquent/Casts/AsEncryptedArrayObject.php: -------------------------------------------------------------------------------- 1 | , iterable> 16 | */ 17 | public static function castUsing(array $arguments) 18 | { 19 | return new class implements CastsAttributes 20 | { 21 | public function get($model, $key, $value, $attributes) 22 | { 23 | if (isset($attributes[$key])) { 24 | return new ArrayObject(Json::decode(Crypt::decryptString($attributes[$key]))); 25 | } 26 | 27 | return null; 28 | } 29 | 30 | public function set($model, $key, $value, $attributes) 31 | { 32 | if (! is_null($value)) { 33 | return [$key => Crypt::encryptString(Json::encode($value))]; 34 | } 35 | 36 | return null; 37 | } 38 | 39 | public function serialize($model, string $key, $value, array $attributes) 40 | { 41 | return ! is_null($value) ? $value->getArrayCopy() : null; 42 | } 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Eloquent/Casts/AsEncryptedCollection.php: -------------------------------------------------------------------------------- 1 | , iterable> 19 | */ 20 | public static function castUsing(array $arguments) 21 | { 22 | return new class($arguments) implements CastsAttributes 23 | { 24 | public function __construct(protected array $arguments) 25 | { 26 | $this->arguments = array_pad(array_values($this->arguments), 2, ''); 27 | } 28 | 29 | public function get($model, $key, $value, $attributes) 30 | { 31 | $collectionClass = empty($this->arguments[0]) ? Collection::class : $this->arguments[0]; 32 | 33 | if (! is_a($collectionClass, Collection::class, true)) { 34 | throw new InvalidArgumentException('The provided class must extend ['.Collection::class.'].'); 35 | } 36 | 37 | if (! isset($attributes[$key])) { 38 | return null; 39 | } 40 | 41 | $instance = new $collectionClass(Json::decode(Crypt::decryptString($attributes[$key]))); 42 | 43 | if (! isset($this->arguments[1]) || ! $this->arguments[1]) { 44 | return $instance; 45 | } 46 | 47 | if (is_string($this->arguments[1])) { 48 | $this->arguments[1] = Str::parseCallback($this->arguments[1]); 49 | } 50 | 51 | return is_callable($this->arguments[1]) 52 | ? $instance->map($this->arguments[1]) 53 | : $instance->mapInto($this->arguments[1][0]); 54 | } 55 | 56 | public function set($model, $key, $value, $attributes) 57 | { 58 | if (! is_null($value)) { 59 | return [$key => Crypt::encryptString(Json::encode($value))]; 60 | } 61 | 62 | return null; 63 | } 64 | }; 65 | } 66 | 67 | /** 68 | * Specify the type of object each item in the collection should be mapped to. 69 | * 70 | * @param array{class-string, string}|class-string $map 71 | * @return string 72 | */ 73 | public static function of($map) 74 | { 75 | return static::using('', $map); 76 | } 77 | 78 | /** 79 | * Specify the collection for the cast. 80 | * 81 | * @param class-string $class 82 | * @param array{class-string, string}|class-string $map 83 | * @return string 84 | */ 85 | public static function using($class, $map = null) 86 | { 87 | if (is_array($map) && is_callable($map)) { 88 | $map = $map[0].'@'.$map[1]; 89 | } 90 | 91 | return static::class.':'.implode(',', [$class, $map]); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Eloquent/Casts/AsEnumArrayObject.php: -------------------------------------------------------------------------------- 1 | } $arguments 20 | * @return \Illuminate\Contracts\Database\Eloquent\CastsAttributes<\Illuminate\Database\Eloquent\Casts\ArrayObject, iterable> 21 | */ 22 | public static function castUsing(array $arguments) 23 | { 24 | return new class($arguments) implements CastsAttributes 25 | { 26 | protected $arguments; 27 | 28 | public function __construct(array $arguments) 29 | { 30 | $this->arguments = $arguments; 31 | } 32 | 33 | public function get($model, $key, $value, $attributes) 34 | { 35 | if (! isset($attributes[$key])) { 36 | return; 37 | } 38 | 39 | $data = Json::decode($attributes[$key]); 40 | 41 | if (! is_array($data)) { 42 | return; 43 | } 44 | 45 | $enumClass = $this->arguments[0]; 46 | 47 | return new ArrayObject((new Collection($data))->map(function ($value) use ($enumClass) { 48 | return is_subclass_of($enumClass, BackedEnum::class) 49 | ? $enumClass::from($value) 50 | : constant($enumClass.'::'.$value); 51 | })->toArray()); 52 | } 53 | 54 | public function set($model, $key, $value, $attributes) 55 | { 56 | if ($value === null) { 57 | return [$key => null]; 58 | } 59 | 60 | $storable = []; 61 | 62 | foreach ($value as $enum) { 63 | $storable[] = $this->getStorableEnumValue($enum); 64 | } 65 | 66 | return [$key => Json::encode($storable)]; 67 | } 68 | 69 | public function serialize($model, string $key, $value, array $attributes) 70 | { 71 | return (new Collection($value->getArrayCopy()))->map(function ($enum) { 72 | return $this->getStorableEnumValue($enum); 73 | })->toArray(); 74 | } 75 | 76 | protected function getStorableEnumValue($enum) 77 | { 78 | if (is_string($enum) || is_int($enum)) { 79 | return $enum; 80 | } 81 | 82 | return enum_value($enum); 83 | } 84 | }; 85 | } 86 | 87 | /** 88 | * Specify the Enum for the cast. 89 | * 90 | * @param class-string $class 91 | * @return string 92 | */ 93 | public static function of($class) 94 | { 95 | return static::class.':'.$class; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Eloquent/Casts/AsEnumCollection.php: -------------------------------------------------------------------------------- 1 | } $arguments 20 | * @return \Illuminate\Contracts\Database\Eloquent\CastsAttributes<\Illuminate\Support\Collection, iterable> 21 | */ 22 | public static function castUsing(array $arguments) 23 | { 24 | return new class($arguments) implements CastsAttributes 25 | { 26 | protected $arguments; 27 | 28 | public function __construct(array $arguments) 29 | { 30 | $this->arguments = $arguments; 31 | } 32 | 33 | public function get($model, $key, $value, $attributes) 34 | { 35 | if (! isset($attributes[$key])) { 36 | return; 37 | } 38 | 39 | $data = Json::decode($attributes[$key]); 40 | 41 | if (! is_array($data)) { 42 | return; 43 | } 44 | 45 | $enumClass = $this->arguments[0]; 46 | 47 | return (new Collection($data))->map(function ($value) use ($enumClass) { 48 | return is_subclass_of($enumClass, BackedEnum::class) 49 | ? $enumClass::from($value) 50 | : constant($enumClass.'::'.$value); 51 | }); 52 | } 53 | 54 | public function set($model, $key, $value, $attributes) 55 | { 56 | $value = $value !== null 57 | ? Json::encode((new Collection($value))->map(function ($enum) { 58 | return $this->getStorableEnumValue($enum); 59 | })->jsonSerialize()) 60 | : null; 61 | 62 | return [$key => $value]; 63 | } 64 | 65 | public function serialize($model, string $key, $value, array $attributes) 66 | { 67 | return (new Collection($value))->map(function ($enum) { 68 | return $this->getStorableEnumValue($enum); 69 | })->toArray(); 70 | } 71 | 72 | protected function getStorableEnumValue($enum) 73 | { 74 | if (is_string($enum) || is_int($enum)) { 75 | return $enum; 76 | } 77 | 78 | return enum_value($enum); 79 | } 80 | }; 81 | } 82 | 83 | /** 84 | * Specify the Enum for the cast. 85 | * 86 | * @param class-string $class 87 | * @return string 88 | */ 89 | public static function of($class) 90 | { 91 | return static::class.':'.$class; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Eloquent/Casts/AsHtmlString.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public static function castUsing(array $arguments) 18 | { 19 | return new class implements CastsAttributes 20 | { 21 | public function get($model, $key, $value, $attributes) 22 | { 23 | return isset($value) ? new HtmlString($value) : null; 24 | } 25 | 26 | public function set($model, $key, $value, $attributes) 27 | { 28 | return isset($value) ? (string) $value : null; 29 | } 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Eloquent/Casts/AsStringable.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public static function castUsing(array $arguments) 18 | { 19 | return new class implements CastsAttributes 20 | { 21 | public function get($model, $key, $value, $attributes) 22 | { 23 | return isset($value) ? new Stringable($value) : null; 24 | } 25 | 26 | public function set($model, $key, $value, $attributes) 27 | { 28 | return isset($value) ? (string) $value : null; 29 | } 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Eloquent/Casts/Attribute.php: -------------------------------------------------------------------------------- 1 | get = $get; 44 | $this->set = $set; 45 | } 46 | 47 | /** 48 | * Create a new attribute accessor / mutator. 49 | * 50 | * @param callable|null $get 51 | * @param callable|null $set 52 | * @return static 53 | */ 54 | public static function make(?callable $get = null, ?callable $set = null): static 55 | { 56 | return new static($get, $set); 57 | } 58 | 59 | /** 60 | * Create a new attribute accessor. 61 | * 62 | * @param callable $get 63 | * @return static 64 | */ 65 | public static function get(callable $get) 66 | { 67 | return new static($get); 68 | } 69 | 70 | /** 71 | * Create a new attribute mutator. 72 | * 73 | * @param callable $set 74 | * @return static 75 | */ 76 | public static function set(callable $set) 77 | { 78 | return new static(null, $set); 79 | } 80 | 81 | /** 82 | * Disable object caching for the attribute. 83 | * 84 | * @return static 85 | */ 86 | public function withoutObjectCaching() 87 | { 88 | $this->withObjectCaching = false; 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * Enable caching for the attribute. 95 | * 96 | * @return static 97 | */ 98 | public function shouldCache() 99 | { 100 | $this->withCaching = true; 101 | 102 | return $this; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Eloquent/Casts/Json.php: -------------------------------------------------------------------------------- 1 | usesUniqueIds; 22 | } 23 | 24 | /** 25 | * Generate unique keys for the model. 26 | * 27 | * @return void 28 | */ 29 | public function setUniqueIds() 30 | { 31 | foreach ($this->uniqueIds() as $column) { 32 | if (empty($this->{$column})) { 33 | $this->{$column} = $this->newUniqueId(); 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * Generate a new key for the model. 40 | * 41 | * @return string 42 | */ 43 | public function newUniqueId() 44 | { 45 | return null; 46 | } 47 | 48 | /** 49 | * Get the columns that should receive a unique identifier. 50 | * 51 | * @return array 52 | */ 53 | public function uniqueIds() 54 | { 55 | return []; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Eloquent/Concerns/HasUniqueStringIds.php: -------------------------------------------------------------------------------- 1 | usesUniqueIds = true; 32 | } 33 | 34 | /** 35 | * Get the columns that should receive a unique identifier. 36 | * 37 | * @return array 38 | */ 39 | public function uniqueIds() 40 | { 41 | return $this->usesUniqueIds() ? [$this->getKeyName()] : parent::uniqueIds(); 42 | } 43 | 44 | /** 45 | * Retrieve the model for a bound value. 46 | * 47 | * @param \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation<*, *, *> $query 48 | * @param mixed $value 49 | * @param string|null $field 50 | * @return \Illuminate\Contracts\Database\Eloquent\Builder 51 | * 52 | * @throws \Illuminate\Database\Eloquent\ModelNotFoundException 53 | */ 54 | public function resolveRouteBindingQuery($query, $value, $field = null) 55 | { 56 | if ($field && in_array($field, $this->uniqueIds()) && ! $this->isValidUniqueId($value)) { 57 | $this->handleInvalidUniqueId($value, $field); 58 | } 59 | 60 | if (! $field && in_array($this->getRouteKeyName(), $this->uniqueIds()) && ! $this->isValidUniqueId($value)) { 61 | $this->handleInvalidUniqueId($value, $field); 62 | } 63 | 64 | return parent::resolveRouteBindingQuery($query, $value, $field); 65 | } 66 | 67 | /** 68 | * Get the auto-incrementing key type. 69 | * 70 | * @return string 71 | */ 72 | public function getKeyType() 73 | { 74 | if (in_array($this->getKeyName(), $this->uniqueIds())) { 75 | return 'string'; 76 | } 77 | 78 | return parent::getKeyType(); 79 | } 80 | 81 | /** 82 | * Get the value indicating whether the IDs are incrementing. 83 | * 84 | * @return bool 85 | */ 86 | public function getIncrementing() 87 | { 88 | if (in_array($this->getKeyName(), $this->uniqueIds())) { 89 | return false; 90 | } 91 | 92 | return parent::getIncrementing(); 93 | } 94 | 95 | /** 96 | * Throw an exception for the given invalid unique ID. 97 | * 98 | * @param mixed $value 99 | * @param string|null $field 100 | * @return never 101 | * 102 | * @throws \Illuminate\Database\Eloquent\ModelNotFoundException 103 | */ 104 | protected function handleInvalidUniqueId($value, $field) 105 | { 106 | throw (new ModelNotFoundException)->setModel(get_class($this), $value); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Eloquent/Concerns/HasUuids.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | protected $hidden = []; 13 | 14 | /** 15 | * The attributes that should be visible in serialization. 16 | * 17 | * @var array 18 | */ 19 | protected $visible = []; 20 | 21 | /** 22 | * Get the hidden attributes for the model. 23 | * 24 | * @return array 25 | */ 26 | public function getHidden() 27 | { 28 | return $this->hidden; 29 | } 30 | 31 | /** 32 | * Set the hidden attributes for the model. 33 | * 34 | * @param array $hidden 35 | * @return $this 36 | */ 37 | public function setHidden(array $hidden) 38 | { 39 | $this->hidden = $hidden; 40 | 41 | return $this; 42 | } 43 | 44 | /** 45 | * Get the visible attributes for the model. 46 | * 47 | * @return array 48 | */ 49 | public function getVisible() 50 | { 51 | return $this->visible; 52 | } 53 | 54 | /** 55 | * Set the visible attributes for the model. 56 | * 57 | * @param array $visible 58 | * @return $this 59 | */ 60 | public function setVisible(array $visible) 61 | { 62 | $this->visible = $visible; 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * Make the given, typically hidden, attributes visible. 69 | * 70 | * @param array|string|null $attributes 71 | * @return $this 72 | */ 73 | public function makeVisible($attributes) 74 | { 75 | $attributes = is_array($attributes) ? $attributes : func_get_args(); 76 | 77 | $this->hidden = array_diff($this->hidden, $attributes); 78 | 79 | if (! empty($this->visible)) { 80 | $this->visible = array_values(array_unique(array_merge($this->visible, $attributes))); 81 | } 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Make the given, typically hidden, attributes visible if the given truth test passes. 88 | * 89 | * @param bool|\Closure $condition 90 | * @param array|string|null $attributes 91 | * @return $this 92 | */ 93 | public function makeVisibleIf($condition, $attributes) 94 | { 95 | return value($condition, $this) ? $this->makeVisible($attributes) : $this; 96 | } 97 | 98 | /** 99 | * Make the given, typically visible, attributes hidden. 100 | * 101 | * @param array|string|null $attributes 102 | * @return $this 103 | */ 104 | public function makeHidden($attributes) 105 | { 106 | $this->hidden = array_values(array_unique(array_merge( 107 | $this->hidden, is_array($attributes) ? $attributes : func_get_args() 108 | ))); 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * Make the given, typically visible, attributes hidden if the given truth test passes. 115 | * 116 | * @param bool|\Closure $condition 117 | * @param array|string|null $attributes 118 | * @return $this 119 | */ 120 | public function makeHiddenIf($condition, $attributes) 121 | { 122 | return value($condition, $this) ? $this->makeHidden($attributes) : $this; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Eloquent/Concerns/PreventsCircularRecursion.php: -------------------------------------------------------------------------------- 1 | > 15 | */ 16 | protected static $recursionCache; 17 | 18 | /** 19 | * Prevent a method from being called multiple times on the same object within the same call stack. 20 | * 21 | * @param callable $callback 22 | * @param mixed $default 23 | * @return mixed 24 | */ 25 | protected function withoutRecursion($callback, $default = null) 26 | { 27 | $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); 28 | 29 | $onceable = Onceable::tryFromTrace($trace, $callback); 30 | 31 | if (is_null($onceable)) { 32 | return call_user_func($callback); 33 | } 34 | 35 | $stack = static::getRecursiveCallStack($this); 36 | 37 | if (array_key_exists($onceable->hash, $stack)) { 38 | return is_callable($stack[$onceable->hash]) 39 | ? static::setRecursiveCallValue($this, $onceable->hash, call_user_func($stack[$onceable->hash])) 40 | : $stack[$onceable->hash]; 41 | } 42 | 43 | try { 44 | static::setRecursiveCallValue($this, $onceable->hash, $default); 45 | 46 | return call_user_func($onceable->callable); 47 | } finally { 48 | static::clearRecursiveCallValue($this, $onceable->hash); 49 | } 50 | } 51 | 52 | /** 53 | * Remove an entry from the recursion cache for an object. 54 | * 55 | * @param object $object 56 | * @param string $hash 57 | */ 58 | protected static function clearRecursiveCallValue($object, string $hash) 59 | { 60 | if ($stack = Arr::except(static::getRecursiveCallStack($object), $hash)) { 61 | static::getRecursionCache()->offsetSet($object, $stack); 62 | } elseif (static::getRecursionCache()->offsetExists($object)) { 63 | static::getRecursionCache()->offsetUnset($object); 64 | } 65 | } 66 | 67 | /** 68 | * Get the stack of methods being called recursively for the current object. 69 | * 70 | * @param object $object 71 | * @return array 72 | */ 73 | protected static function getRecursiveCallStack($object): array 74 | { 75 | return static::getRecursionCache()->offsetExists($object) 76 | ? static::getRecursionCache()->offsetGet($object) 77 | : []; 78 | } 79 | 80 | /** 81 | * Get the current recursion cache being used by the model. 82 | * 83 | * @return WeakMap 84 | */ 85 | protected static function getRecursionCache() 86 | { 87 | return static::$recursionCache ??= new WeakMap(); 88 | } 89 | 90 | /** 91 | * Set a value in the recursion cache for the given object and method. 92 | * 93 | * @param object $object 94 | * @param string $hash 95 | * @param mixed $value 96 | * @return mixed 97 | */ 98 | protected static function setRecursiveCallValue($object, string $hash, $value) 99 | { 100 | static::getRecursionCache()->offsetSet( 101 | $object, 102 | tap(static::getRecursiveCallStack($object), fn (&$stack) => $stack[$hash] = $value), 103 | ); 104 | 105 | return static::getRecursiveCallStack($object)[$hash]; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Eloquent/Concerns/TransformsToResource.php: -------------------------------------------------------------------------------- 1 | |null $resourceClass 15 | * @return \Illuminate\Http\Resources\Json\JsonResource 16 | * 17 | * @throws \Throwable 18 | */ 19 | public function toResource(?string $resourceClass = null): JsonResource 20 | { 21 | if ($resourceClass === null) { 22 | return $this->guessResource(); 23 | } 24 | 25 | return $resourceClass::make($this); 26 | } 27 | 28 | /** 29 | * Guess the resource class for the model. 30 | * 31 | * @return \Illuminate\Http\Resources\Json\JsonResource 32 | * 33 | * @throws \Throwable 34 | */ 35 | protected function guessResource(): JsonResource 36 | { 37 | foreach (static::guessResourceName() as $resourceClass) { 38 | if (is_string($resourceClass) && class_exists($resourceClass)) { 39 | return $resourceClass::make($this); 40 | } 41 | } 42 | 43 | throw new LogicException(sprintf('Failed to find resource class for model [%s].', get_class($this))); 44 | } 45 | 46 | /** 47 | * Guess the resource class name for the model. 48 | * 49 | * @return array> 50 | */ 51 | public static function guessResourceName(): array 52 | { 53 | $modelClass = static::class; 54 | 55 | if (! Str::contains($modelClass, '\\Models\\')) { 56 | return []; 57 | } 58 | 59 | $relativeNamespace = Str::after($modelClass, '\\Models\\'); 60 | 61 | $relativeNamespace = Str::contains($relativeNamespace, '\\') 62 | ? Str::before($relativeNamespace, '\\'.class_basename($modelClass)) 63 | : ''; 64 | 65 | $potentialResource = sprintf( 66 | '%s\\Http\\Resources\\%s%s', 67 | Str::before($modelClass, '\\Models'), 68 | strlen($relativeNamespace) > 0 ? $relativeNamespace.'\\' : '', 69 | class_basename($modelClass) 70 | ); 71 | 72 | return [$potentialResource.'Resource', $potentialResource]; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Eloquent/Factories/BelongsToManyRelationship.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 41 | $this->pivot = $pivot; 42 | $this->relationship = $relationship; 43 | } 44 | 45 | /** 46 | * Create the attached relationship for the given model. 47 | * 48 | * @param \Illuminate\Database\Eloquent\Model $model 49 | * @return void 50 | */ 51 | public function createFor(Model $model) 52 | { 53 | $factoryInstance = $this->factory instanceof Factory; 54 | 55 | if ($factoryInstance) { 56 | $relationship = $model->{$this->relationship}(); 57 | } 58 | 59 | Collection::wrap($factoryInstance ? $this->factory->prependState($relationship->getQuery()->pendingAttributes)->create([], $model) : $this->factory)->each(function ($attachable) use ($model) { 60 | $model->{$this->relationship}()->attach( 61 | $attachable, 62 | is_callable($this->pivot) ? call_user_func($this->pivot, $model) : $this->pivot 63 | ); 64 | }); 65 | } 66 | 67 | /** 68 | * Specify the model instances to always use when creating relationships. 69 | * 70 | * @param \Illuminate\Support\Collection $recycle 71 | * @return $this 72 | */ 73 | public function recycle($recycle) 74 | { 75 | if ($this->factory instanceof Factory) { 76 | $this->factory = $this->factory->recycle($recycle); 77 | } 78 | 79 | return $this; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Eloquent/Factories/BelongsToRelationship.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 40 | $this->relationship = $relationship; 41 | } 42 | 43 | /** 44 | * Get the parent model attributes and resolvers for the given child model. 45 | * 46 | * @param \Illuminate\Database\Eloquent\Model $model 47 | * @return array 48 | */ 49 | public function attributesFor(Model $model) 50 | { 51 | $relationship = $model->{$this->relationship}(); 52 | 53 | return $relationship instanceof MorphTo ? [ 54 | $relationship->getMorphType() => $this->factory instanceof Factory ? $this->factory->newModel()->getMorphClass() : $this->factory->getMorphClass(), 55 | $relationship->getForeignKeyName() => $this->resolver($relationship->getOwnerKeyName()), 56 | ] : [ 57 | $relationship->getForeignKeyName() => $this->resolver($relationship->getOwnerKeyName()), 58 | ]; 59 | } 60 | 61 | /** 62 | * Get the deferred resolver for this relationship's parent ID. 63 | * 64 | * @param string|null $key 65 | * @return \Closure 66 | */ 67 | protected function resolver($key) 68 | { 69 | return function () use ($key) { 70 | if (! $this->resolved) { 71 | $instance = $this->factory instanceof Factory 72 | ? ($this->factory->getRandomRecycledModel($this->factory->modelName()) ?? $this->factory->create()) 73 | : $this->factory; 74 | 75 | return $this->resolved = $key ? $instance->{$key} : $instance->getKey(); 76 | } 77 | 78 | return $this->resolved; 79 | }; 80 | } 81 | 82 | /** 83 | * Specify the model instances to always use when creating relationships. 84 | * 85 | * @param \Illuminate\Support\Collection $recycle 86 | * @return $this 87 | */ 88 | public function recycle($recycle) 89 | { 90 | if ($this->factory instanceof Factory) { 91 | $this->factory = $this->factory->recycle($recycle); 92 | } 93 | 94 | return $this; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Eloquent/Factories/CrossJoinSequence.php: -------------------------------------------------------------------------------- 1 | , static|null): array)|array|int|null $count 16 | * @param (callable(array, static|null): array)|array $state 17 | * @return TFactory 18 | */ 19 | public static function factory($count = null, $state = []) 20 | { 21 | $factory = static::newFactory() ?? Factory::factoryForModel(static::class); 22 | 23 | return $factory 24 | ->count(is_numeric($count) ? $count : null) 25 | ->state(is_callable($count) || is_array($count) ? $count : $state); 26 | } 27 | 28 | /** 29 | * Create a new factory instance for the model. 30 | * 31 | * @return TFactory|null 32 | */ 33 | protected static function newFactory() 34 | { 35 | if (isset(static::$factory)) { 36 | return static::$factory::new(); 37 | } 38 | 39 | return static::getUseFactoryAttribute() ?? null; 40 | } 41 | 42 | /** 43 | * Get the factory from the UseFactory class attribute. 44 | * 45 | * @return TFactory|null 46 | */ 47 | protected static function getUseFactoryAttribute() 48 | { 49 | $attributes = (new \ReflectionClass(static::class)) 50 | ->getAttributes(UseFactory::class); 51 | 52 | if ($attributes !== []) { 53 | $useFactory = $attributes[0]->newInstance(); 54 | 55 | $factory = $useFactory->factoryClass::new(); 56 | 57 | $factory->guessModelNamesUsing(fn () => static::class); 58 | 59 | return $factory; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Eloquent/Factories/Relationship.php: -------------------------------------------------------------------------------- 1 | factory = $factory; 35 | $this->relationship = $relationship; 36 | } 37 | 38 | /** 39 | * Create the child relationship for the given parent model. 40 | * 41 | * @param \Illuminate\Database\Eloquent\Model $parent 42 | * @return void 43 | */ 44 | public function createFor(Model $parent) 45 | { 46 | $relationship = $parent->{$this->relationship}(); 47 | 48 | if ($relationship instanceof MorphOneOrMany) { 49 | $this->factory->state([ 50 | $relationship->getMorphType() => $relationship->getMorphClass(), 51 | $relationship->getForeignKeyName() => $relationship->getParentKey(), 52 | ])->prependState($relationship->getQuery()->pendingAttributes)->create([], $parent); 53 | } elseif ($relationship instanceof HasOneOrMany) { 54 | $this->factory->state([ 55 | $relationship->getForeignKeyName() => $relationship->getParentKey(), 56 | ])->prependState($relationship->getQuery()->pendingAttributes)->create([], $parent); 57 | } elseif ($relationship instanceof BelongsToMany) { 58 | $relationship->attach( 59 | $this->factory->prependState($relationship->getQuery()->pendingAttributes)->create([], $parent) 60 | ); 61 | } 62 | } 63 | 64 | /** 65 | * Specify the model instances to always use when creating relationships. 66 | * 67 | * @param \Illuminate\Support\Collection $recycle 68 | * @return $this 69 | */ 70 | public function recycle($recycle) 71 | { 72 | $this->factory = $this->factory->recycle($recycle); 73 | 74 | return $this; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Eloquent/Factories/Sequence.php: -------------------------------------------------------------------------------- 1 | sequence = $sequence; 38 | $this->count = count($sequence); 39 | } 40 | 41 | /** 42 | * Get the current count of the sequence items. 43 | * 44 | * @return int 45 | */ 46 | public function count(): int 47 | { 48 | return $this->count; 49 | } 50 | 51 | /** 52 | * Get the next value in the sequence. 53 | * 54 | * @return mixed 55 | */ 56 | public function __invoke() 57 | { 58 | return tap(value($this->sequence[$this->index % $this->count], $this), function () { 59 | $this->index = $this->index + 1; 60 | }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Eloquent/HasBuilder.php: -------------------------------------------------------------------------------- 1 | , class-string> 17 | */ 18 | protected static array $resolvedCollectionClasses = []; 19 | 20 | /** 21 | * Create a new Eloquent Collection instance. 22 | * 23 | * @param array $models 24 | * @return TCollection 25 | */ 26 | public function newCollection(array $models = []) 27 | { 28 | static::$resolvedCollectionClasses[static::class] ??= ($this->resolveCollectionFromAttribute() ?? static::$collectionClass); 29 | 30 | $collection = new static::$resolvedCollectionClasses[static::class]($models); 31 | 32 | if (Model::isAutomaticallyEagerLoadingRelationships()) { 33 | $collection->withRelationshipAutoloading(); 34 | } 35 | 36 | return $collection; 37 | } 38 | 39 | /** 40 | * Resolve the collection class name from the CollectedBy attribute. 41 | * 42 | * @return class-string|null 43 | */ 44 | public function resolveCollectionFromAttribute() 45 | { 46 | $reflectionClass = new ReflectionClass(static::class); 47 | 48 | $attributes = $reflectionClass->getAttributes(CollectedBy::class); 49 | 50 | if (! isset($attributes[0]) || ! isset($attributes[0]->getArguments()[0])) { 51 | return; 52 | } 53 | 54 | return $attributes[0]->getArguments()[0]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Eloquent/HigherOrderBuilderProxy.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $builder; 16 | 17 | /** 18 | * The method being proxied. 19 | * 20 | * @var string 21 | */ 22 | protected $method; 23 | 24 | /** 25 | * Create a new proxy instance. 26 | * 27 | * @param \Illuminate\Database\Eloquent\Builder<*> $builder 28 | * @param string $method 29 | */ 30 | public function __construct(Builder $builder, $method) 31 | { 32 | $this->method = $method; 33 | $this->builder = $builder; 34 | } 35 | 36 | /** 37 | * Proxy a scope call onto the query builder. 38 | * 39 | * @param string $method 40 | * @param array $parameters 41 | * @return mixed 42 | */ 43 | public function __call($method, $parameters) 44 | { 45 | return $this->builder->{$this->method}(function ($value) use ($method, $parameters) { 46 | return $value->{$method}(...$parameters); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Eloquent/InvalidCastException.php: -------------------------------------------------------------------------------- 1 | model = $class; 44 | $this->column = $column; 45 | $this->castType = $castType; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Eloquent/JsonEncodingException.php: -------------------------------------------------------------------------------- 1 | getKey().'] to JSON: '.$message); 19 | } 20 | 21 | /** 22 | * Create a new JSON encoding exception for the resource. 23 | * 24 | * @param \Illuminate\Http\Resources\Json\JsonResource $resource 25 | * @param string $message 26 | * @return static 27 | */ 28 | public static function forResource($resource, $message) 29 | { 30 | $model = $resource->resource; 31 | 32 | return new static('Error encoding resource ['.get_class($resource).'] with model ['.get_class($model).'] with ID ['.$model->getKey().'] to JSON: '.$message); 33 | } 34 | 35 | /** 36 | * Create a new JSON encoding exception for an attribute. 37 | * 38 | * @param mixed $model 39 | * @param mixed $key 40 | * @param string $message 41 | * @return static 42 | */ 43 | public static function forAttribute($model, $key, $message) 44 | { 45 | $class = get_class($model); 46 | 47 | return new static("Unable to encode attribute [{$key}] for model [{$class}] to JSON: {$message}."); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Eloquent/MassAssignmentException.php: -------------------------------------------------------------------------------- 1 | prunable(), function ($query) use ($chunkSize) { 19 | $query->when(! $query->getQuery()->limit, function ($query) use ($chunkSize) { 20 | $query->limit($chunkSize); 21 | }); 22 | }); 23 | 24 | $total = 0; 25 | 26 | $softDeletable = in_array(SoftDeletes::class, class_uses_recursive(get_class($this))); 27 | 28 | do { 29 | $total += $count = $softDeletable 30 | ? $query->forceDelete() 31 | : $query->delete(); 32 | 33 | if ($count > 0) { 34 | event(new ModelsPruned(static::class, $total)); 35 | } 36 | } while ($count > 0); 37 | 38 | return $total; 39 | } 40 | 41 | /** 42 | * Get the prunable model query. 43 | * 44 | * @return \Illuminate\Database\Eloquent\Builder 45 | */ 46 | public function prunable() 47 | { 48 | throw new LogicException('Please implement the prunable method on your model.'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Eloquent/MissingAttributeException.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | protected $model; 19 | 20 | /** 21 | * The affected model IDs. 22 | * 23 | * @var array 24 | */ 25 | protected $ids; 26 | 27 | /** 28 | * Set the affected Eloquent model and instance ids. 29 | * 30 | * @param class-string $model 31 | * @param array|int|string $ids 32 | * @return $this 33 | */ 34 | public function setModel($model, $ids = []) 35 | { 36 | $this->model = $model; 37 | $this->ids = Arr::wrap($ids); 38 | 39 | $this->message = "No query results for model [{$model}]"; 40 | 41 | if (count($this->ids) > 0) { 42 | $this->message .= ' '.implode(', ', $this->ids); 43 | } else { 44 | $this->message .= '.'; 45 | } 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * Get the affected Eloquent model. 52 | * 53 | * @return class-string 54 | */ 55 | public function getModel() 56 | { 57 | return $this->model; 58 | } 59 | 60 | /** 61 | * Get the affected Eloquent model IDs. 62 | * 63 | * @return array 64 | */ 65 | public function getIds() 66 | { 67 | return $this->ids; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Eloquent/Prunable.php: -------------------------------------------------------------------------------- 1 | prunable() 23 | ->when(in_array(SoftDeletes::class, class_uses_recursive(static::class)), function ($query) { 24 | $query->withTrashed(); 25 | })->chunkById($chunkSize, function ($models) use (&$total) { 26 | $models->each(function ($model) use (&$total) { 27 | try { 28 | $model->prune(); 29 | 30 | $total++; 31 | } catch (Throwable $e) { 32 | $handler = app(ExceptionHandler::class); 33 | 34 | if ($handler) { 35 | $handler->report($e); 36 | } else { 37 | throw $e; 38 | } 39 | } 40 | }); 41 | 42 | event(new ModelsPruned(static::class, $total)); 43 | }); 44 | 45 | return $total; 46 | } 47 | 48 | /** 49 | * Get the prunable model query. 50 | * 51 | * @return \Illuminate\Database\Eloquent\Builder 52 | */ 53 | public function prunable() 54 | { 55 | throw new LogicException('Please implement the prunable method on your model.'); 56 | } 57 | 58 | /** 59 | * Prune the model in the database. 60 | * 61 | * @return bool|null 62 | */ 63 | public function prune() 64 | { 65 | $this->pruning(); 66 | 67 | return in_array(SoftDeletes::class, class_uses_recursive(static::class)) 68 | ? $this->forceDelete() 69 | : $this->delete(); 70 | } 71 | 72 | /** 73 | * Prepare the model for pruning. 74 | * 75 | * @return void 76 | */ 77 | protected function pruning() 78 | { 79 | // 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Eloquent/QueueEntityResolver.php: -------------------------------------------------------------------------------- 1 | find($id); 22 | 23 | if ($instance) { 24 | return $instance; 25 | } 26 | 27 | throw new EntityNotFoundException($type, $id); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Eloquent/RelationNotFoundException.php: -------------------------------------------------------------------------------- 1 | model = $class; 42 | $instance->relation = $relation; 43 | 44 | return $instance; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Eloquent/Relations/Concerns/ComparesRelatedModels.php: -------------------------------------------------------------------------------- 1 | compareKeys($this->getParentKey(), $this->getRelatedKeyFrom($model)) && 20 | $this->related->getTable() === $model->getTable() && 21 | $this->related->getConnectionName() === $model->getConnectionName(); 22 | 23 | if ($match && $this instanceof SupportsPartialRelations && $this->isOneOfMany()) { 24 | return $this->query 25 | ->whereKey($model->getKey()) 26 | ->exists(); 27 | } 28 | 29 | return $match; 30 | } 31 | 32 | /** 33 | * Determine if the model is not the related instance of the relationship. 34 | * 35 | * @param \Illuminate\Database\Eloquent\Model|null $model 36 | * @return bool 37 | */ 38 | public function isNot($model) 39 | { 40 | return ! $this->is($model); 41 | } 42 | 43 | /** 44 | * Get the value of the parent model's key. 45 | * 46 | * @return mixed 47 | */ 48 | abstract public function getParentKey(); 49 | 50 | /** 51 | * Get the value of the model's related key. 52 | * 53 | * @param \Illuminate\Database\Eloquent\Model $model 54 | * @return mixed 55 | */ 56 | abstract protected function getRelatedKeyFrom(Model $model); 57 | 58 | /** 59 | * Compare the parent key with the related key. 60 | * 61 | * @param mixed $parentKey 62 | * @param mixed $relatedKey 63 | * @return bool 64 | */ 65 | protected function compareKeys($parentKey, $relatedKey) 66 | { 67 | if (empty($parentKey) || empty($relatedKey)) { 68 | return false; 69 | } 70 | 71 | if (is_int($parentKey) || is_int($relatedKey)) { 72 | return (int) $parentKey === (int) $relatedKey; 73 | } 74 | 75 | return $parentKey === $relatedKey; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Eloquent/Relations/Concerns/InteractsWithDictionary.php: -------------------------------------------------------------------------------- 1 | __toString(); 25 | } 26 | 27 | if ($attribute instanceof UnitEnum) { 28 | return enum_value($attribute); 29 | } 30 | 31 | throw new InvalidArgumentException('Model attribute value is an object but does not have a __toString method.'); 32 | } 33 | 34 | return $attribute; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Eloquent/Relations/Concerns/SupportsDefaultModels.php: -------------------------------------------------------------------------------- 1 | withDefault = $callback; 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * Get the default value for this relation. 41 | * 42 | * @param \Illuminate\Database\Eloquent\Model $parent 43 | * @return \Illuminate\Database\Eloquent\Model|null 44 | */ 45 | protected function getDefaultFor(Model $parent) 46 | { 47 | if (! $this->withDefault) { 48 | return; 49 | } 50 | 51 | $instance = $this->newRelatedInstanceFor($parent); 52 | 53 | if (is_callable($this->withDefault)) { 54 | return call_user_func($this->withDefault, $instance, $parent) ?: $instance; 55 | } 56 | 57 | if (is_array($this->withDefault)) { 58 | $instance->forceFill($this->withDefault); 59 | } 60 | 61 | return $instance; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Eloquent/Relations/HasMany.php: -------------------------------------------------------------------------------- 1 | > 12 | */ 13 | class HasMany extends HasOneOrMany 14 | { 15 | /** 16 | * Convert the relationship to a "has one" relationship. 17 | * 18 | * @return \Illuminate\Database\Eloquent\Relations\HasOne 19 | */ 20 | public function one() 21 | { 22 | return HasOne::noConstraints(fn () => tap( 23 | new HasOne( 24 | $this->getQuery(), 25 | $this->parent, 26 | $this->foreignKey, 27 | $this->localKey 28 | ), 29 | function ($hasOne) { 30 | if ($inverse = $this->getInverseRelationship()) { 31 | $hasOne->inverse($inverse); 32 | } 33 | } 34 | )); 35 | } 36 | 37 | /** @inheritDoc */ 38 | public function getResults() 39 | { 40 | return ! is_null($this->getParentKey()) 41 | ? $this->query->get() 42 | : $this->related->newCollection(); 43 | } 44 | 45 | /** @inheritDoc */ 46 | public function initRelation(array $models, $relation) 47 | { 48 | foreach ($models as $model) { 49 | $model->setRelation($relation, $this->related->newCollection()); 50 | } 51 | 52 | return $models; 53 | } 54 | 55 | /** @inheritDoc */ 56 | public function match(array $models, EloquentCollection $results, $relation) 57 | { 58 | return $this->matchMany($models, $results, $relation); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Eloquent/Relations/HasManyThrough.php: -------------------------------------------------------------------------------- 1 | > 15 | */ 16 | class HasManyThrough extends HasOneOrManyThrough 17 | { 18 | use InteractsWithDictionary; 19 | 20 | /** 21 | * Convert the relationship to a "has one through" relationship. 22 | * 23 | * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough 24 | */ 25 | public function one() 26 | { 27 | return HasOneThrough::noConstraints(fn () => new HasOneThrough( 28 | tap($this->getQuery(), fn (Builder $query) => $query->getQuery()->joins = []), 29 | $this->farParent, 30 | $this->throughParent, 31 | $this->getFirstKeyName(), 32 | $this->getForeignKeyName(), 33 | $this->getLocalKeyName(), 34 | $this->getSecondLocalKeyName(), 35 | )); 36 | } 37 | 38 | /** @inheritDoc */ 39 | public function initRelation(array $models, $relation) 40 | { 41 | foreach ($models as $model) { 42 | $model->setRelation($relation, $this->related->newCollection()); 43 | } 44 | 45 | return $models; 46 | } 47 | 48 | /** @inheritDoc */ 49 | public function match(array $models, EloquentCollection $results, $relation) 50 | { 51 | $dictionary = $this->buildDictionary($results); 52 | 53 | // Once we have the dictionary we can simply spin through the parent models to 54 | // link them up with their children using the keyed dictionary to make the 55 | // matching very convenient and easy work. Then we'll just return them. 56 | foreach ($models as $model) { 57 | if (isset($dictionary[$key = $this->getDictionaryKey($model->getAttribute($this->localKey))])) { 58 | $model->setRelation( 59 | $relation, $this->related->newCollection($dictionary[$key]) 60 | ); 61 | } 62 | } 63 | 64 | return $models; 65 | } 66 | 67 | /** @inheritDoc */ 68 | public function getResults() 69 | { 70 | return ! is_null($this->farParent->{$this->localKey}) 71 | ? $this->get() 72 | : $this->related->newCollection(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Eloquent/Relations/MorphMany.php: -------------------------------------------------------------------------------- 1 | > 12 | */ 13 | class MorphMany extends MorphOneOrMany 14 | { 15 | /** 16 | * Convert the relationship to a "morph one" relationship. 17 | * 18 | * @return \Illuminate\Database\Eloquent\Relations\MorphOne 19 | */ 20 | public function one() 21 | { 22 | return MorphOne::noConstraints(fn () => tap( 23 | new MorphOne( 24 | $this->getQuery(), 25 | $this->getParent(), 26 | $this->morphType, 27 | $this->foreignKey, 28 | $this->localKey 29 | ), 30 | function ($morphOne) { 31 | if ($inverse = $this->getInverseRelationship()) { 32 | $morphOne->inverse($inverse); 33 | } 34 | } 35 | )); 36 | } 37 | 38 | /** @inheritDoc */ 39 | public function getResults() 40 | { 41 | return ! is_null($this->getParentKey()) 42 | ? $this->query->get() 43 | : $this->related->newCollection(); 44 | } 45 | 46 | /** @inheritDoc */ 47 | public function initRelation(array $models, $relation) 48 | { 49 | foreach ($models as $model) { 50 | $model->setRelation($relation, $this->related->newCollection()); 51 | } 52 | 53 | return $models; 54 | } 55 | 56 | /** @inheritDoc */ 57 | public function match(array $models, EloquentCollection $results, $relation) 58 | { 59 | return $this->matchMany($models, $results, $relation); 60 | } 61 | 62 | /** @inheritDoc */ 63 | public function forceCreate(array $attributes = []) 64 | { 65 | $attributes[$this->getMorphType()] = $this->morphClass; 66 | 67 | return parent::forceCreate($attributes); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Eloquent/Relations/Pivot.php: -------------------------------------------------------------------------------- 1 | |bool 23 | */ 24 | protected $guarded = []; 25 | } 26 | -------------------------------------------------------------------------------- /Eloquent/Scope.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 29 | $this->connectionName = $connection->getName(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Events/DatabaseBusy.php: -------------------------------------------------------------------------------- 1 | method = $method; 33 | $this->migration = $migration; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Events/MigrationStarted.php: -------------------------------------------------------------------------------- 1 | $options The options provided when the migration method was invoked. 14 | */ 15 | public function __construct( 16 | public $method, 17 | public array $options = [], 18 | ) { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Events/MigrationsPruned.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 39 | $this->connectionName = $connection->getName(); 40 | $this->path = $path; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Events/MigrationsStarted.php: -------------------------------------------------------------------------------- 1 | $models The class names of the models that were pruned. 11 | */ 12 | public function __construct( 13 | public $models, 14 | ) { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Events/ModelPruningStarting.php: -------------------------------------------------------------------------------- 1 | $models The class names of the models that will be pruned. 11 | */ 12 | public function __construct( 13 | public $models 14 | ) { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Events/ModelsPruned.php: -------------------------------------------------------------------------------- 1 | sql = $sql; 53 | $this->time = $time; 54 | $this->bindings = $bindings; 55 | $this->connection = $connection; 56 | $this->connectionName = $connection->getName(); 57 | } 58 | 59 | /** 60 | * Get the raw SQL representation of the query with embedded bindings. 61 | * 62 | * @return string 63 | */ 64 | public function toRawSql() 65 | { 66 | return $this->connection 67 | ->query() 68 | ->getGrammar() 69 | ->substituteBindingsIntoRawSql($this->sql, $this->connection->prepareBindings($this->bindings)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Events/SchemaDumped.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 37 | $this->connectionName = $connection->getName(); 38 | $this->path = $path; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Events/SchemaLoaded.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 37 | $this->connectionName = $connection->getName(); 38 | $this->path = $path; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Events/StatementPrepared.php: -------------------------------------------------------------------------------- 1 | model = $class; 36 | $this->relation = $relation; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LostConnectionException.php: -------------------------------------------------------------------------------- 1 | schemaGrammar)) { 61 | $this->useDefaultSchemaGrammar(); 62 | } 63 | 64 | return new MariaDbBuilder($this); 65 | } 66 | 67 | /** 68 | * Get the default schema grammar instance. 69 | * 70 | * @return \Illuminate\Database\Schema\Grammars\MariaDbGrammar 71 | */ 72 | protected function getDefaultSchemaGrammar() 73 | { 74 | return new SchemaGrammar($this); 75 | } 76 | 77 | /** 78 | * Get the schema state for the connection. 79 | * 80 | * @param \Illuminate\Filesystem\Filesystem|null $files 81 | * @param callable|null $processFactory 82 | * @return \Illuminate\Database\Schema\MariaDbSchemaState 83 | */ 84 | public function getSchemaState(?Filesystem $files = null, ?callable $processFactory = null) 85 | { 86 | return new MariaDbSchemaState($this, $files, $processFactory); 87 | } 88 | 89 | /** 90 | * Get the default post processor instance. 91 | * 92 | * @return \Illuminate\Database\Query\Processors\MariaDbProcessor 93 | */ 94 | protected function getDefaultPostProcessor() 95 | { 96 | return new MariaDbProcessor; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Migrations/Migration.php: -------------------------------------------------------------------------------- 1 | connection; 29 | } 30 | 31 | /** 32 | * Determine if this migration should run. 33 | * 34 | * @return bool 35 | */ 36 | public function shouldRun(): bool 37 | { 38 | return true; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Migrations/MigrationRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->timestamps(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::dropIfExists('{{ table }}'); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /Migrations/stubs/migration.stub: -------------------------------------------------------------------------------- 1 | count = $count; 26 | 27 | parent::__construct("$count records were found.", $code, $previous); 28 | } 29 | 30 | /** 31 | * Get the number of records found. 32 | * 33 | * @return int 34 | */ 35 | public function getCount() 36 | { 37 | return $this->count; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PostgresConnection.php: -------------------------------------------------------------------------------- 1 | getCode(); 56 | } 57 | 58 | /** 59 | * Get the default query grammar instance. 60 | * 61 | * @return \Illuminate\Database\Query\Grammars\PostgresGrammar 62 | */ 63 | protected function getDefaultQueryGrammar() 64 | { 65 | return new QueryGrammar($this); 66 | } 67 | 68 | /** 69 | * Get a schema builder instance for the connection. 70 | * 71 | * @return \Illuminate\Database\Schema\PostgresBuilder 72 | */ 73 | public function getSchemaBuilder() 74 | { 75 | if (is_null($this->schemaGrammar)) { 76 | $this->useDefaultSchemaGrammar(); 77 | } 78 | 79 | return new PostgresBuilder($this); 80 | } 81 | 82 | /** 83 | * Get the default schema grammar instance. 84 | * 85 | * @return \Illuminate\Database\Schema\Grammars\PostgresGrammar 86 | */ 87 | protected function getDefaultSchemaGrammar() 88 | { 89 | return new SchemaGrammar($this); 90 | } 91 | 92 | /** 93 | * Get the schema state for the connection. 94 | * 95 | * @param \Illuminate\Filesystem\Filesystem|null $files 96 | * @param callable|null $processFactory 97 | * @return \Illuminate\Database\Schema\PostgresSchemaState 98 | */ 99 | public function getSchemaState(?Filesystem $files = null, ?callable $processFactory = null) 100 | { 101 | return new PostgresSchemaState($this, $files, $processFactory); 102 | } 103 | 104 | /** 105 | * Get the default post processor instance. 106 | * 107 | * @return \Illuminate\Database\Query\Processors\PostgresProcessor 108 | */ 109 | protected function getDefaultPostProcessor() 110 | { 111 | return new PostgresProcessor; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Query/Expression.php: -------------------------------------------------------------------------------- 1 | value; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Query/Grammars/MariaDbGrammar.php: -------------------------------------------------------------------------------- 1 | type = $type; 30 | $this->index = $index; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Query/JoinLateralClause.php: -------------------------------------------------------------------------------- 1 | column_name; 21 | }, $results); 22 | } 23 | 24 | /** 25 | * Process an "insert get ID" query. 26 | * 27 | * @param \Illuminate\Database\Query\Builder $query 28 | * @param string $sql 29 | * @param array $values 30 | * @param string|null $sequence 31 | * @return int 32 | */ 33 | public function processInsertGetId(Builder $query, $sql, $values, $sequence = null) 34 | { 35 | $query->getConnection()->insert($sql, $values, $sequence); 36 | 37 | $id = $query->getConnection()->getLastInsertId(); 38 | 39 | return is_numeric($id) ? (int) $id : $id; 40 | } 41 | 42 | /** @inheritDoc */ 43 | public function processColumns($results) 44 | { 45 | return array_map(function ($result) { 46 | $result = (object) $result; 47 | 48 | return [ 49 | 'name' => $result->name, 50 | 'type_name' => $result->type_name, 51 | 'type' => $result->type, 52 | 'collation' => $result->collation, 53 | 'nullable' => $result->nullable === 'YES', 54 | 'default' => $result->default, 55 | 'auto_increment' => $result->extra === 'auto_increment', 56 | 'comment' => $result->comment ?: null, 57 | 'generation' => $result->expression ? [ 58 | 'type' => match ($result->extra) { 59 | 'STORED GENERATED' => 'stored', 60 | 'VIRTUAL GENERATED' => 'virtual', 61 | default => null, 62 | }, 63 | 'expression' => $result->expression, 64 | ] : null, 65 | ]; 66 | }, $results); 67 | } 68 | 69 | /** @inheritDoc */ 70 | public function processIndexes($results) 71 | { 72 | return array_map(function ($result) { 73 | $result = (object) $result; 74 | 75 | return [ 76 | 'name' => $name = strtolower($result->name), 77 | 'columns' => $result->columns ? explode(',', $result->columns) : [], 78 | 'type' => strtolower($result->type), 79 | 'unique' => (bool) $result->unique, 80 | 'primary' => $name === 'primary', 81 | ]; 82 | }, $results); 83 | } 84 | 85 | /** @inheritDoc */ 86 | public function processForeignKeys($results) 87 | { 88 | return array_map(function ($result) { 89 | $result = (object) $result; 90 | 91 | return [ 92 | 'name' => $result->name, 93 | 'columns' => explode(',', $result->columns), 94 | 'foreign_schema' => $result->foreign_schema, 95 | 'foreign_table' => $result->foreign_table, 96 | 'foreign_columns' => explode(',', $result->foreign_columns), 97 | 'on_update' => strtolower($result->on_update), 98 | 'on_delete' => strtolower($result->on_delete), 99 | ]; 100 | }, $results); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Query/Processors/SQLiteProcessor.php: -------------------------------------------------------------------------------- 1 | type); 16 | 17 | $safeName = preg_quote($result->name, '/'); 18 | 19 | $collation = preg_match( 20 | '/\b'.$safeName.'\b[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:default|check|as)\s*(?:\(.*?\))?[^,]*)*collate\s+["\'`]?(\w+)/i', 21 | $sql, 22 | $matches 23 | ) === 1 ? strtolower($matches[1]) : null; 24 | 25 | $isGenerated = in_array($result->extra, [2, 3]); 26 | 27 | $expression = $isGenerated && preg_match( 28 | '/\b'.$safeName.'\b[^,]+\s+as\s+\(((?:[^()]+|\((?:[^()]+|\([^()]*\))*\))*)\)/i', 29 | $sql, 30 | $matches 31 | ) === 1 ? $matches[1] : null; 32 | 33 | return [ 34 | 'name' => $result->name, 35 | 'type_name' => strtok($type, '(') ?: '', 36 | 'type' => $type, 37 | 'collation' => $collation, 38 | 'nullable' => (bool) $result->nullable, 39 | 'default' => $result->default, 40 | 'auto_increment' => $hasPrimaryKey && $result->primary && $type === 'integer', 41 | 'comment' => null, 42 | 'generation' => $isGenerated ? [ 43 | 'type' => match ((int) $result->extra) { 44 | 3 => 'stored', 45 | 2 => 'virtual', 46 | default => null, 47 | }, 48 | 'expression' => $expression, 49 | ] : null, 50 | ]; 51 | }, $results); 52 | } 53 | 54 | /** @inheritDoc */ 55 | public function processIndexes($results) 56 | { 57 | $primaryCount = 0; 58 | 59 | $indexes = array_map(function ($result) use (&$primaryCount) { 60 | $result = (object) $result; 61 | 62 | if ($isPrimary = (bool) $result->primary) { 63 | $primaryCount += 1; 64 | } 65 | 66 | return [ 67 | 'name' => strtolower($result->name), 68 | 'columns' => $result->columns ? explode(',', $result->columns) : [], 69 | 'type' => null, 70 | 'unique' => (bool) $result->unique, 71 | 'primary' => $isPrimary, 72 | ]; 73 | }, $results); 74 | 75 | if ($primaryCount > 1) { 76 | $indexes = array_filter($indexes, fn ($index) => $index['name'] !== 'primary'); 77 | } 78 | 79 | return $indexes; 80 | } 81 | 82 | /** @inheritDoc */ 83 | public function processForeignKeys($results) 84 | { 85 | return array_map(function ($result) { 86 | $result = (object) $result; 87 | 88 | return [ 89 | 'name' => null, 90 | 'columns' => explode(',', $result->columns), 91 | 'foreign_schema' => $result->foreign_schema, 92 | 'foreign_table' => $result->foreign_table, 93 | 'foreign_columns' => explode(',', $result->foreign_columns), 94 | 'on_update' => strtolower($result->on_update), 95 | 'on_delete' => strtolower($result->on_delete), 96 | ]; 97 | }, $results); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /QueryException.php: -------------------------------------------------------------------------------- 1 | connectionName = $connectionName; 46 | $this->sql = $sql; 47 | $this->bindings = $bindings; 48 | $this->code = $previous->getCode(); 49 | $this->message = $this->formatMessage($connectionName, $sql, $bindings, $previous); 50 | 51 | if ($previous instanceof PDOException) { 52 | $this->errorInfo = $previous->errorInfo; 53 | } 54 | } 55 | 56 | /** 57 | * Format the SQL error message. 58 | * 59 | * @param string $connectionName 60 | * @param string $sql 61 | * @param array $bindings 62 | * @param \Throwable $previous 63 | * @return string 64 | */ 65 | protected function formatMessage($connectionName, $sql, $bindings, Throwable $previous) 66 | { 67 | return $previous->getMessage().' (Connection: '.$connectionName.', SQL: '.Str::replaceArray('?', $bindings, $sql).')'; 68 | } 69 | 70 | /** 71 | * Get the connection name for the query. 72 | * 73 | * @return string 74 | */ 75 | public function getConnectionName() 76 | { 77 | return $this->connectionName; 78 | } 79 | 80 | /** 81 | * Get the SQL for the query. 82 | * 83 | * @return string 84 | */ 85 | public function getSql() 86 | { 87 | return $this->sql; 88 | } 89 | 90 | /** 91 | * Get the raw SQL representation of the query with embedded bindings. 92 | */ 93 | public function getRawSql(): string 94 | { 95 | return DB::connection($this->getConnectionName()) 96 | ->getQueryGrammar() 97 | ->substituteBindingsIntoRawSql($this->getSql(), $this->getBindings()); 98 | } 99 | 100 | /** 101 | * Get the bindings for the query. 102 | * 103 | * @return array 104 | */ 105 | public function getBindings() 106 | { 107 | return $this->bindings; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Illuminate Database 2 | 3 | The Illuminate Database component is a full database toolkit for PHP, providing an expressive query builder, ActiveRecord style ORM, and schema builder. It currently supports MySQL, Postgres, SQL Server, and SQLite. It also serves as the database layer of the Laravel PHP framework. 4 | 5 | ### Usage Instructions 6 | 7 | First, create a new "Capsule" manager instance. Capsule aims to make configuring the library for usage outside of the Laravel framework as easy as possible. 8 | 9 | ```PHP 10 | use Illuminate\Database\Capsule\Manager as Capsule; 11 | 12 | $capsule = new Capsule; 13 | 14 | $capsule->addConnection([ 15 | 'driver' => 'mysql', 16 | 'host' => 'localhost', 17 | 'database' => 'database', 18 | 'username' => 'root', 19 | 'password' => 'password', 20 | 'charset' => 'utf8', 21 | 'collation' => 'utf8_unicode_ci', 22 | 'prefix' => '', 23 | ]); 24 | 25 | // Set the event dispatcher used by Eloquent models... (optional) 26 | use Illuminate\Events\Dispatcher; 27 | use Illuminate\Container\Container; 28 | $capsule->setEventDispatcher(new Dispatcher(new Container)); 29 | 30 | // Make this Capsule instance available globally via static methods... (optional) 31 | $capsule->setAsGlobal(); 32 | 33 | // Setup the Eloquent ORM... (optional; unless you've used setEventDispatcher()) 34 | $capsule->bootEloquent(); 35 | ``` 36 | 37 | > `composer require "illuminate/events"` required when you need to use observers with Eloquent. 38 | 39 | Once the Capsule instance has been registered. You may use it like so: 40 | 41 | **Using The Query Builder** 42 | 43 | ```PHP 44 | $users = Capsule::table('users')->where('votes', '>', 100)->get(); 45 | ``` 46 | Other core methods may be accessed directly from the Capsule in the same manner as from the DB facade: 47 | ```PHP 48 | $results = Capsule::select('select * from users where id = ?', [1]); 49 | ``` 50 | 51 | **Using The Schema Builder** 52 | 53 | ```PHP 54 | Capsule::schema()->create('users', function ($table) { 55 | $table->increments('id'); 56 | $table->string('email')->unique(); 57 | $table->timestamps(); 58 | }); 59 | ``` 60 | 61 | **Using The Eloquent ORM** 62 | 63 | ```PHP 64 | class User extends Illuminate\Database\Eloquent\Model {} 65 | 66 | $users = User::where('votes', '>', 1)->get(); 67 | ``` 68 | 69 | For further documentation on using the various database facilities this library provides, consult the [Laravel framework documentation](https://laravel.com/docs). 70 | -------------------------------------------------------------------------------- /RecordNotFoundException.php: -------------------------------------------------------------------------------- 1 | getMessage())); 45 | } 46 | 47 | /** 48 | * Get the default query grammar instance. 49 | * 50 | * @return \Illuminate\Database\Query\Grammars\SQLiteGrammar 51 | */ 52 | protected function getDefaultQueryGrammar() 53 | { 54 | return new QueryGrammar($this); 55 | } 56 | 57 | /** 58 | * Get a schema builder instance for the connection. 59 | * 60 | * @return \Illuminate\Database\Schema\SQLiteBuilder 61 | */ 62 | public function getSchemaBuilder() 63 | { 64 | if (is_null($this->schemaGrammar)) { 65 | $this->useDefaultSchemaGrammar(); 66 | } 67 | 68 | return new SQLiteBuilder($this); 69 | } 70 | 71 | /** 72 | * Get the default schema grammar instance. 73 | * 74 | * @return \Illuminate\Database\Schema\Grammars\SQLiteGrammar 75 | */ 76 | protected function getDefaultSchemaGrammar() 77 | { 78 | return new SchemaGrammar($this); 79 | } 80 | 81 | /** 82 | * Get the schema state for the connection. 83 | * 84 | * @param \Illuminate\Filesystem\Filesystem|null $files 85 | * @param callable|null $processFactory 86 | * 87 | * @throws \RuntimeException 88 | */ 89 | public function getSchemaState(?Filesystem $files = null, ?callable $processFactory = null) 90 | { 91 | return new SqliteSchemaState($this, $files, $processFactory); 92 | } 93 | 94 | /** 95 | * Get the default post processor instance. 96 | * 97 | * @return \Illuminate\Database\Query\Processors\SQLiteProcessor 98 | */ 99 | protected function getDefaultPostProcessor() 100 | { 101 | return new SQLiteProcessor; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /SQLiteDatabaseDoesNotExistException.php: -------------------------------------------------------------------------------- 1 | path = $path; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Schema/ColumnDefinition.php: -------------------------------------------------------------------------------- 1 | blueprint = $blueprint; 27 | } 28 | 29 | /** 30 | * Create a foreign key constraint on this column referencing the "id" column of the conventionally related table. 31 | * 32 | * @param string|null $table 33 | * @param string|null $column 34 | * @param string|null $indexName 35 | * @return \Illuminate\Database\Schema\ForeignKeyDefinition 36 | */ 37 | public function constrained($table = null, $column = null, $indexName = null) 38 | { 39 | $table ??= $this->table; 40 | $column ??= $this->referencesModelColumn ?? 'id'; 41 | 42 | return $this->references($column, $indexName)->on($table ?? (new Stringable($this->name))->beforeLast('_'.$column)->plural()); 43 | } 44 | 45 | /** 46 | * Specify which column this foreign ID references on another table. 47 | * 48 | * @param string $column 49 | * @param string $indexName 50 | * @return \Illuminate\Database\Schema\ForeignKeyDefinition 51 | */ 52 | public function references($column, $indexName = null) 53 | { 54 | return $this->blueprint->foreign($this->name, $indexName)->references($column); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Schema/ForeignKeyDefinition.php: -------------------------------------------------------------------------------- 1 | onUpdate('cascade'); 25 | } 26 | 27 | /** 28 | * Indicate that updates should be restricted. 29 | * 30 | * @return $this 31 | */ 32 | public function restrictOnUpdate() 33 | { 34 | return $this->onUpdate('restrict'); 35 | } 36 | 37 | /** 38 | * Indicate that updates should set the foreign key value to null. 39 | * 40 | * @return $this 41 | */ 42 | public function nullOnUpdate() 43 | { 44 | return $this->onUpdate('set null'); 45 | } 46 | 47 | /** 48 | * Indicate that updates should have "no action". 49 | * 50 | * @return $this 51 | */ 52 | public function noActionOnUpdate() 53 | { 54 | return $this->onUpdate('no action'); 55 | } 56 | 57 | /** 58 | * Indicate that deletes should cascade. 59 | * 60 | * @return $this 61 | */ 62 | public function cascadeOnDelete() 63 | { 64 | return $this->onDelete('cascade'); 65 | } 66 | 67 | /** 68 | * Indicate that deletes should be restricted. 69 | * 70 | * @return $this 71 | */ 72 | public function restrictOnDelete() 73 | { 74 | return $this->onDelete('restrict'); 75 | } 76 | 77 | /** 78 | * Indicate that deletes should set the foreign key value to null. 79 | * 80 | * @return $this 81 | */ 82 | public function nullOnDelete() 83 | { 84 | return $this->onDelete('set null'); 85 | } 86 | 87 | /** 88 | * Indicate that deletes should have "no action". 89 | * 90 | * @return $this 91 | */ 92 | public function noActionOnDelete() 93 | { 94 | return $this->onDelete('no action'); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Schema/Grammars/MariaDbGrammar.php: -------------------------------------------------------------------------------- 1 | connection->getServerVersion(), '10.5.2', '<')) { 14 | return $this->compileLegacyRenameColumn($blueprint, $command); 15 | } 16 | 17 | return parent::compileRenameColumn($blueprint, $command); 18 | } 19 | 20 | /** 21 | * Create the column definition for a uuid type. 22 | * 23 | * @param \Illuminate\Support\Fluent $column 24 | * @return string 25 | */ 26 | protected function typeUuid(Fluent $column) 27 | { 28 | if (version_compare($this->connection->getServerVersion(), '10.7.0', '<')) { 29 | return 'char(36)'; 30 | } 31 | 32 | return 'uuid'; 33 | } 34 | 35 | /** 36 | * Create the column definition for a spatial Geometry type. 37 | * 38 | * @param \Illuminate\Support\Fluent $column 39 | * @return string 40 | */ 41 | protected function typeGeometry(Fluent $column) 42 | { 43 | $subtype = $column->subtype ? strtolower($column->subtype) : null; 44 | 45 | if (! in_array($subtype, ['point', 'linestring', 'polygon', 'geometrycollection', 'multipoint', 'multilinestring', 'multipolygon'])) { 46 | $subtype = null; 47 | } 48 | 49 | return sprintf('%s%s', 50 | $subtype ?? 'geometry', 51 | $column->srid ? ' ref_system_id='.$column->srid : '' 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Schema/IndexDefinition.php: -------------------------------------------------------------------------------- 1 | connectionString().' --database="${:LARAVEL_LOAD_DATABASE}" < "${:LARAVEL_LOAD_PATH}"'; 16 | 17 | $process = $this->makeProcess($command)->setTimeout(null); 18 | 19 | $process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [ 20 | 'LARAVEL_LOAD_PATH' => $path, 21 | ])); 22 | } 23 | 24 | /** 25 | * Get the base dump command arguments for MariaDB as a string. 26 | * 27 | * @return string 28 | */ 29 | protected function baseDumpCommand() 30 | { 31 | $command = 'mariadb-dump '.$this->connectionString().' --no-tablespaces --skip-add-locks --skip-comments --skip-set-charset --tz-utc'; 32 | 33 | return $command.' "${:LARAVEL_LOAD_DATABASE}"'; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Schema/MySqlBuilder.php: -------------------------------------------------------------------------------- 1 | getTableListing($this->getCurrentSchemaListing()); 15 | 16 | if (empty($tables)) { 17 | return; 18 | } 19 | 20 | $this->disableForeignKeyConstraints(); 21 | 22 | try { 23 | $this->connection->statement( 24 | $this->grammar->compileDropAllTables($tables) 25 | ); 26 | } finally { 27 | $this->enableForeignKeyConstraints(); 28 | } 29 | } 30 | 31 | /** 32 | * Drop all views from the database. 33 | * 34 | * @return void 35 | */ 36 | public function dropAllViews() 37 | { 38 | $views = array_column($this->getViews($this->getCurrentSchemaListing()), 'schema_qualified_name'); 39 | 40 | if (empty($views)) { 41 | return; 42 | } 43 | 44 | $this->connection->statement( 45 | $this->grammar->compileDropAllViews($views) 46 | ); 47 | } 48 | 49 | /** 50 | * Get the names of current schemas for the connection. 51 | * 52 | * @return string[]|null 53 | */ 54 | public function getCurrentSchemaListing() 55 | { 56 | return [$this->connection->getDatabaseName()]; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Schema/PostgresBuilder.php: -------------------------------------------------------------------------------- 1 | connection->getConfig('dont_drop') ?? ['spatial_ref_sys']; 21 | 22 | foreach ($this->getTables($this->getCurrentSchemaListing()) as $table) { 23 | if (empty(array_intersect([$table['name'], $table['schema_qualified_name']], $excludedTables))) { 24 | $tables[] = $table['schema_qualified_name']; 25 | } 26 | } 27 | 28 | if (empty($tables)) { 29 | return; 30 | } 31 | 32 | $this->connection->statement( 33 | $this->grammar->compileDropAllTables($tables) 34 | ); 35 | } 36 | 37 | /** 38 | * Drop all views from the database. 39 | * 40 | * @return void 41 | */ 42 | public function dropAllViews() 43 | { 44 | $views = array_column($this->getViews($this->getCurrentSchemaListing()), 'schema_qualified_name'); 45 | 46 | if (empty($views)) { 47 | return; 48 | } 49 | 50 | $this->connection->statement( 51 | $this->grammar->compileDropAllViews($views) 52 | ); 53 | } 54 | 55 | /** 56 | * Drop all types from the database. 57 | * 58 | * @return void 59 | */ 60 | public function dropAllTypes() 61 | { 62 | $types = []; 63 | $domains = []; 64 | 65 | foreach ($this->getTypes($this->getCurrentSchemaListing()) as $type) { 66 | if (! $type['implicit']) { 67 | if ($type['type'] === 'domain') { 68 | $domains[] = $type['schema_qualified_name']; 69 | } else { 70 | $types[] = $type['schema_qualified_name']; 71 | } 72 | } 73 | } 74 | 75 | if (! empty($types)) { 76 | $this->connection->statement($this->grammar->compileDropAllTypes($types)); 77 | } 78 | 79 | if (! empty($domains)) { 80 | $this->connection->statement($this->grammar->compileDropAllDomains($domains)); 81 | } 82 | } 83 | 84 | /** 85 | * Get the current schemas for the connection. 86 | * 87 | * @return string[] 88 | */ 89 | public function getCurrentSchemaListing() 90 | { 91 | return array_map( 92 | fn ($schema) => $schema === '$user' ? $this->connection->getConfig('username') : $schema, 93 | $this->parseSearchPath( 94 | $this->connection->getConfig('search_path') 95 | ?: $this->connection->getConfig('schema') 96 | ?: 'public' 97 | ) 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Schema/PostgresSchemaState.php: -------------------------------------------------------------------------------- 1 | baseDumpCommand().' --schema-only > '.$path, 21 | ]); 22 | 23 | if ($this->hasMigrationTable()) { 24 | $commands->push($this->baseDumpCommand().' -t '.$this->getMigrationTable().' --data-only >> '.$path); 25 | } 26 | 27 | $commands->map(function ($command, $path) { 28 | $this->makeProcess($command)->mustRun($this->output, array_merge($this->baseVariables($this->connection->getConfig()), [ 29 | 'LARAVEL_LOAD_PATH' => $path, 30 | ])); 31 | }); 32 | } 33 | 34 | /** 35 | * Load the given schema file into the database. 36 | * 37 | * @param string $path 38 | * @return void 39 | */ 40 | public function load($path) 41 | { 42 | $command = 'pg_restore --no-owner --no-acl --clean --if-exists --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}" "${:LARAVEL_LOAD_PATH}"'; 43 | 44 | if (str_ends_with($path, '.sql')) { 45 | $command = 'psql --file="${:LARAVEL_LOAD_PATH}" --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}"'; 46 | } 47 | 48 | $process = $this->makeProcess($command); 49 | 50 | $process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [ 51 | 'LARAVEL_LOAD_PATH' => $path, 52 | ])); 53 | } 54 | 55 | /** 56 | * Get the name of the application's migration table. 57 | * 58 | * @return string 59 | */ 60 | protected function getMigrationTable(): string 61 | { 62 | [$schema, $table] = $this->connection->getSchemaBuilder()->parseSchemaAndTable($this->migrationTable, withDefaultSchema: true); 63 | 64 | return $schema.'.'.$this->connection->getTablePrefix().$table; 65 | } 66 | 67 | /** 68 | * Get the base dump command arguments for PostgreSQL as a string. 69 | * 70 | * @return string 71 | */ 72 | protected function baseDumpCommand() 73 | { 74 | return 'pg_dump --no-owner --no-acl --host="${:LARAVEL_LOAD_HOST}" --port="${:LARAVEL_LOAD_PORT}" --username="${:LARAVEL_LOAD_USER}" --dbname="${:LARAVEL_LOAD_DATABASE}"'; 75 | } 76 | 77 | /** 78 | * Get the base variables for a dump / load command. 79 | * 80 | * @param array $config 81 | * @return array 82 | */ 83 | protected function baseVariables(array $config) 84 | { 85 | $config['host'] ??= ''; 86 | 87 | return [ 88 | 'LARAVEL_LOAD_HOST' => is_array($config['host']) ? $config['host'][0] : $config['host'], 89 | 'LARAVEL_LOAD_PORT' => $config['port'] ?? '', 90 | 'LARAVEL_LOAD_USER' => $config['username'], 91 | 'PGPASSWORD' => $config['password'], 92 | 'LARAVEL_LOAD_DATABASE' => $config['database'], 93 | ]; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Schema/SchemaState.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 56 | 57 | $this->files = $files ?: new Filesystem; 58 | 59 | $this->processFactory = $processFactory ?: function (...$arguments) { 60 | return Process::fromShellCommandline(...$arguments)->setTimeout(null); 61 | }; 62 | 63 | $this->handleOutputUsing(function () { 64 | // 65 | }); 66 | } 67 | 68 | /** 69 | * Dump the database's schema into a file. 70 | * 71 | * @param \Illuminate\Database\Connection $connection 72 | * @param string $path 73 | * @return void 74 | */ 75 | abstract public function dump(Connection $connection, $path); 76 | 77 | /** 78 | * Load the given schema file into the database. 79 | * 80 | * @param string $path 81 | * @return void 82 | */ 83 | abstract public function load($path); 84 | 85 | /** 86 | * Create a new process instance. 87 | * 88 | * @param mixed ...$arguments 89 | * @return \Symfony\Component\Process\Process 90 | */ 91 | public function makeProcess(...$arguments) 92 | { 93 | return call_user_func($this->processFactory, ...$arguments); 94 | } 95 | 96 | /** 97 | * Determine if the current connection has a migration table. 98 | * 99 | * @return bool 100 | */ 101 | public function hasMigrationTable(): bool 102 | { 103 | return $this->connection->getSchemaBuilder()->hasTable($this->migrationTable); 104 | } 105 | 106 | /** 107 | * Get the name of the application's migration table. 108 | * 109 | * @return string 110 | */ 111 | protected function getMigrationTable(): string 112 | { 113 | return $this->connection->getTablePrefix().$this->migrationTable; 114 | } 115 | 116 | /** 117 | * Specify the name of the application's migration table. 118 | * 119 | * @param string $table 120 | * @return $this 121 | */ 122 | public function withMigrationTable(string $table) 123 | { 124 | $this->migrationTable = $table; 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * Specify the callback that should be used to handle process output. 131 | * 132 | * @param callable $output 133 | * @return $this 134 | */ 135 | public function handleOutputUsing(callable $output) 136 | { 137 | $this->output = $output; 138 | 139 | return $this; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Schema/SqlServerBuilder.php: -------------------------------------------------------------------------------- 1 | connection->statement($this->grammar->compileDropAllForeignKeys()); 17 | 18 | $this->connection->statement($this->grammar->compileDropAllTables()); 19 | } 20 | 21 | /** 22 | * Drop all views from the database. 23 | * 24 | * @return void 25 | */ 26 | public function dropAllViews() 27 | { 28 | $this->connection->statement($this->grammar->compileDropAllViews()); 29 | } 30 | 31 | /** 32 | * Get the default schema name for the connection. 33 | * 34 | * @return string|null 35 | */ 36 | public function getCurrentSchemaName() 37 | { 38 | return Arr::first($this->getSchemas(), fn ($schema) => $schema['default'])['name']; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Schema/SqliteSchemaState.php: -------------------------------------------------------------------------------- 1 | makeProcess( 20 | $this->baseCommand().' ".schema --indent"' 21 | ))->setTimeout(null)->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [ 22 | // 23 | ])); 24 | 25 | $migrations = preg_replace('/CREATE TABLE sqlite_.+?\);[\r\n]+/is', '', $process->getOutput()); 26 | 27 | $this->files->put($path, $migrations.PHP_EOL); 28 | 29 | if ($this->hasMigrationTable()) { 30 | $this->appendMigrationData($path); 31 | } 32 | } 33 | 34 | /** 35 | * Append the migration data to the schema dump. 36 | * 37 | * @param string $path 38 | * @return void 39 | */ 40 | protected function appendMigrationData(string $path) 41 | { 42 | with($process = $this->makeProcess( 43 | $this->baseCommand().' ".dump \''.$this->getMigrationTable().'\'"' 44 | ))->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [ 45 | // 46 | ])); 47 | 48 | $migrations = (new Collection(preg_split("/\r\n|\n|\r/", $process->getOutput()))) 49 | ->filter(fn ($line) => preg_match('/^\s*(--|INSERT\s)/iu', $line) === 1 && strlen($line) > 0) 50 | ->all(); 51 | 52 | $this->files->append($path, implode(PHP_EOL, $migrations).PHP_EOL); 53 | } 54 | 55 | /** 56 | * Load the given schema file into the database. 57 | * 58 | * @param string $path 59 | * @return void 60 | */ 61 | public function load($path) 62 | { 63 | $database = $this->connection->getDatabaseName(); 64 | 65 | if ($database === ':memory:' || 66 | str_contains($database, '?mode=memory') || 67 | str_contains($database, '&mode=memory') 68 | ) { 69 | $this->connection->getPdo()->exec($this->files->get($path)); 70 | 71 | return; 72 | } 73 | 74 | $process = $this->makeProcess($this->baseCommand().' < "${:LARAVEL_LOAD_PATH}"'); 75 | 76 | $process->mustRun(null, array_merge($this->baseVariables($this->connection->getConfig()), [ 77 | 'LARAVEL_LOAD_PATH' => $path, 78 | ])); 79 | } 80 | 81 | /** 82 | * Get the base sqlite command arguments as a string. 83 | * 84 | * @return string 85 | */ 86 | protected function baseCommand() 87 | { 88 | return 'sqlite3 "${:LARAVEL_LOAD_DATABASE}"'; 89 | } 90 | 91 | /** 92 | * Get the base variables for a dump / load command. 93 | * 94 | * @param array $config 95 | * @return array 96 | */ 97 | protected function baseVariables(array $config) 98 | { 99 | return [ 100 | 'LARAVEL_LOAD_DATABASE' => $config['database'], 101 | ]; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /UniqueConstraintViolationException.php: -------------------------------------------------------------------------------- 1 |