From 51146b81e3cc0dd2c36c59093498803bf9a8acbc Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Wed, 3 Apr 2024 22:10:46 +0800 Subject: [PATCH 001/197] Laravel 10 changes Co-authored-by: Luke Towers --- .github/workflows/tests.yml | 2 +- composer.json | 12 ++-- src/Cache/CacheManager.php | 19 ++++++ src/Console/Traits/HandlesCleanup.php | 6 +- src/Database/Concerns/HasAttributes.php | 34 ++++++++++ src/Database/Model.php | 1 + .../Relations/Concerns/DeferOneOrMany.php | 9 ++- src/Flash/FlashBag.php | 7 +- src/Foundation/Application.php | 7 +- src/Foundation/Http/Kernel.php | 2 +- src/Halcyon/Builder.php | 5 +- src/Halcyon/Model.php | 6 +- src/Support/Facades/DB.php | 2 +- src/Support/Facades/Mail.php | 2 +- src/Translation/FileLoader.php | 67 ++++++++++--------- tests/Database/ModelTest.php | 2 +- tests/Database/UpdaterTest.php | 2 +- tests/Foundation/ApplicationTest.php | 2 +- tests/Html/BlockBuilderTest.php | 2 +- tests/Support/EventFakeTest.php | 2 +- tests/Support/MailFakeTest.php | 9 ++- tests/Translation/FileLoaderTest.php | 1 + 22 files changed, 137 insertions(+), 64 deletions(-) create mode 100644 src/Cache/CacheManager.php create mode 100644 src/Database/Concerns/HasAttributes.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fb3073e6..c6c5c7cd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: max-parallel: 6 matrix: operatingSystem: [ubuntu-latest, windows-latest] - phpVersion: ['8.0', '8.1', '8.2'] + phpVersion: ['8.1', '8.2'] fail-fast: false runs-on: ${{ matrix.operatingSystem }} name: ${{ matrix.operatingSystem }} / PHP ${{ matrix.phpVersion }} diff --git a/composer.json b/composer.json index dca52f73..d0e7e891 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } ], "require": { - "php": "^8.0.2", + "php": "^8.1", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", @@ -36,15 +36,15 @@ "ext-zip": "*", "assetic/framework": "~3.0", - "doctrine/dbal": "^2.6", + "doctrine/dbal": "^3.0", "enshrined/svg-sanitize": "~0.16", - "laravel/framework": "^9.1", - "laravel/tinker": "^2.7", + "laravel/framework": "^10.0", + "laravel/tinker": "^2.8.1", "league/csv": "~9.1", "nesbot/carbon": "^2.0", "nikic/php-parser": "^4.10", "scssphp/scssphp": "~1.0", - "symfony/console": ">=6.0.9 <6.3.0", + "symfony/console": "^6.3", "symfony/yaml": "^6.0", "twig/twig": "~3.0", "wikimedia/less.php": "~3.0", @@ -59,7 +59,7 @@ "meyfa/phpunit-assert-gd": "^2.0.0|^3.0.0", "dms/phpunit-arraysubset-asserts": "^0.1.0|^0.2.1", "larastan/larastan": "^2.8.1", - "orchestra/testbench": "^7.1.0" + "orchestra/testbench": "^8.0.0" }, "suggest": { "ext-pdo_dblib": "Required to use MS SQL Server databases", diff --git a/src/Cache/CacheManager.php b/src/Cache/CacheManager.php new file mode 100644 index 00000000..6d0375d7 --- /dev/null +++ b/src/Cache/CacheManager.php @@ -0,0 +1,19 @@ + 'datetime', + * ]; + * ``` + * + * @return array + */ + public function getDates() + { + if (! $this->usesTimestamps()) { + return $this->dates; + } + + $defaults = [ + $this->getCreatedAtColumn(), + $this->getUpdatedAtColumn(), + ]; + + return array_unique(array_merge($this->dates, $defaults)); + } +} diff --git a/src/Database/Model.php b/src/Database/Model.php index 40390596..57ae23c6 100644 --- a/src/Database/Model.php +++ b/src/Database/Model.php @@ -22,6 +22,7 @@ */ class Model extends EloquentModel implements ModelInterface { + use Concerns\HasAttributes; use Concerns\GuardsAttributes; use Concerns\HasRelationships; use Concerns\HidesAttributes; diff --git a/src/Database/Relations/Concerns/DeferOneOrMany.php b/src/Database/Relations/Concerns/DeferOneOrMany.php index 519c9d8b..4db33a2e 100644 --- a/src/Database/Relations/Concerns/DeferOneOrMany.php +++ b/src/Database/Relations/Concerns/DeferOneOrMany.php @@ -94,8 +94,8 @@ public function withDeferred($sessionKey) ->where('master_type', get_class($this->parent)) ->where('session_key', $sessionKey) ->where('is_bind', 0) - ->whereRaw(DbDongle::parse('id > ifnull((select max(id) from '.DbDongle::getTablePrefix().'deferred_bindings where - slave_id = '.$this->getWithDeferredQualifiedKeyName().' and + ->whereRaw(DbDongle::parse('id > ifnull((select max(id) from ' . DbDongle::getTablePrefix() . 'deferred_bindings where + slave_id = ' . $this->getWithDeferredQualifiedKeyName() . ' and master_field = ? and master_type = ? and session_key = ? and @@ -122,13 +122,12 @@ public function withDeferred($sessionKey) /** * Returns the related "slave id" key in a database friendly format. - * @return \Illuminate\Database\Query\Expression */ - protected function getWithDeferredQualifiedKeyName() + protected function getWithDeferredQualifiedKeyName(): string { return $this->parent->getConnection()->raw(DbDongle::cast( DbDongle::getTablePrefix() . $this->related->getQualifiedKeyName(), 'TEXT' - )); + ))->getValue($this->parent->getGrammar()); } } diff --git a/src/Flash/FlashBag.php b/src/Flash/FlashBag.php index 61abb1f3..bdcacfb6 100644 --- a/src/Flash/FlashBag.php +++ b/src/Flash/FlashBag.php @@ -166,15 +166,16 @@ public function store() /** * Removes an object with a specified key or erases the flash data. + * * @param string $key Specifies a key to remove, optional + * @return $this */ public function forget($key = null) { if ($key === null) { $this->newMessages = $this->messages = []; $this->purge(); - } - else { + } else { if (isset($this->messages[$key])) { unset($this->messages[$key]); } @@ -185,6 +186,8 @@ public function forget($key = null) $this->store(); } + + return $this; } /* diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index 2bccf1e5..ef8b633b 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -68,11 +68,12 @@ public function version() /** * Get the path to the public / web directory. * + * @param string $path * @return string */ - public function publicPath() + public function publicPath($path = '') { - return $this->basePath; + return $this->joinPaths($this->basePath, $path); } /** @@ -479,7 +480,7 @@ public function registerCoreContainerAliases() $aliases = [ 'app' => [\Winter\Storm\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class], 'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class], - 'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class], + 'cache' => [\Winter\Storm\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class], 'cache.store' => [\Illuminate\Cache\Repository::class, \Illuminate\Contracts\Cache\Repository::class], 'config' => [\Illuminate\Config\Repository::class, \Illuminate\Contracts\Config\Repository::class], 'cookie' => [\Illuminate\Cookie\CookieJar::class, \Illuminate\Contracts\Cookie\Factory::class, \Illuminate\Contracts\Cookie\QueueingFactory::class], diff --git a/src/Foundation/Http/Kernel.php b/src/Foundation/Http/Kernel.php index 1c4218cf..e314e8a9 100644 --- a/src/Foundation/Http/Kernel.php +++ b/src/Foundation/Http/Kernel.php @@ -31,7 +31,7 @@ class Kernel extends HttpKernel /** * {@inheritDoc} */ - protected $routeMiddleware = [ + protected $middlewareAliases = [ // 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, // 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, diff --git a/src/Halcyon/Builder.php b/src/Halcyon/Builder.php index 2abd7f19..2df82754 100644 --- a/src/Halcyon/Builder.php +++ b/src/Halcyon/Builder.php @@ -699,12 +699,11 @@ protected function isCacheBusted($result) /** * Get the cache object with tags assigned, if applicable. * - * @return \Illuminate\Contracts\Cache\Repository + * @return \Illuminate\Cache\Repository */ protected function getCache() { - /** @var \Illuminate\Cache\Repository */ - $cache = $this->model->getCacheManager()->driver($this->cacheDriver); + $cache = $this->model->getCacheManager()->store($this->cacheDriver); return $this->cacheTags ? $cache->tags($this->cacheTags) : $cache; } diff --git a/src/Halcyon/Model.php b/src/Halcyon/Model.php index f51c3ac4..1458906a 100644 --- a/src/Halcyon/Model.php +++ b/src/Halcyon/Model.php @@ -109,7 +109,7 @@ class Model extends Extendable implements ModelInterface, ArrayAccess, Arrayable /** * The cache manager instance. * - * @var \Illuminate\Cache\CacheManager|null + * @var \Winter\Storm\Cache\CacheManager|null */ protected static $cache; @@ -1512,7 +1512,7 @@ public static function unsetEventDispatcher() /** * Get the cache manager instance. * - * @return \Illuminate\Cache\CacheManager|null + * @return \Winter\Storm\Cache\CacheManager|null */ public static function getCacheManager() { @@ -1522,7 +1522,7 @@ public static function getCacheManager() /** * Set the cache manager instance. * - * @param \Illuminate\Cache\CacheManager $cache + * @param \Winter\Storm\Cache\CacheManager $cache * @return void */ public static function setCacheManager($cache) diff --git a/src/Support/Facades/DB.php b/src/Support/Facades/DB.php index d2c0bb8b..15bbc8c5 100644 --- a/src/Support/Facades/DB.php +++ b/src/Support/Facades/DB.php @@ -5,7 +5,7 @@ /** * @method static \Doctrine\DBAL\Driver\PDOConnection getPdo() * @method static \Illuminate\Database\Connection connection(string $name = null) - * @method static \Winter\Storm\Database\QueryBuilder table(string $table, string $as = null) + * @method static \Winter\Storm\Database\QueryBuilder table(\Closure|\Illuminate\Database\Query\Builder|\Illuminate\Contracts\Database\Query\Expression|string $table, string|null $as = null) * @method static \Illuminate\Database\Query\Expression raw($value) * @method static array getQueryLog() * @method static array prepareBindings(array $bindings) diff --git a/src/Support/Facades/Mail.php b/src/Support/Facades/Mail.php index 04714713..8a394e07 100755 --- a/src/Support/Facades/Mail.php +++ b/src/Support/Facades/Mail.php @@ -34,7 +34,7 @@ class Mail extends Facade */ public static function fake() { - static::swap($fake = new MailFake); + static::swap($fake = new MailFake(static::getFacadeRoot())); return $fake; } diff --git a/src/Translation/FileLoader.php b/src/Translation/FileLoader.php index 86e65954..3dba1cc1 100644 --- a/src/Translation/FileLoader.php +++ b/src/Translation/FileLoader.php @@ -18,26 +18,31 @@ class FileLoader extends FileLoaderBase */ protected function loadNamespaceOverrides(array $lines, $locale, $group, $namespace) { - $namespace = str_replace('.', '/', $namespace); + return collect($this->paths) + ->reduce(function ($output, $path) use ($lines, $locale, $group, $namespace) { + $winterNamespace = str_replace('.', '/', $namespace); - $file = "{$this->path}/{$locale}/{$namespace}/{$group}.php"; + // Look for a Winter-managed namespace + $file = "{$path}/{$locale}/{$winterNamespace}/{$group}.php"; + if ($this->files->exists($file)) { + return array_replace_recursive($lines, $this->files->getRequire($file)); + } - if ($this->files->exists($file)) { - return array_replace_recursive($lines, $this->files->getRequire($file)); - } + // Look for a Winter-managed namespace with a Winter-formatted locale (xx-xx instead of xx_XX) + $dashLocale = str_replace('_', '-', strtolower($locale)); + $dashFile = "{$path}/{$dashLocale}/{$winterNamespace}/{$group}.php"; + if ($dashFile !== $file && $this->files->exists($dashFile)) { + return array_replace_recursive($lines, $this->files->getRequire($dashFile)); + } - // Try "xx-xx" format - $locale = str_replace('_', '-', strtolower($locale)); + // Look for a vendor-managed namespace + $file = "{$path}/vendor/{$namespace}/{$locale}/{$group}.php"; + if ($this->files->exists($file)) { + return array_replace_recursive($lines, $this->files->getRequire($file)); + } - if ("{$this->path}/{$locale}/{$namespace}/{$group}.php" !== $file) { - $file = "{$this->path}/{$locale}/{$namespace}/{$group}.php"; - - if ($this->files->exists($file)) { - return array_replace_recursive($lines, $this->files->getRequire($file)); - } - } - - return $lines; + return $lines; + }, []); } /** @@ -46,26 +51,28 @@ protected function loadNamespaceOverrides(array $lines, $locale, $group, $namesp * This is an override from the base Laravel functionality that allows "xx-xx" locale format * files as well as "xx_XX" locale format files. The "xx_XX" format is considered authorative. * - * @param string $path + * @param array $paths * @param string $locale * @param string $group * @return array */ - protected function loadPath($path, $locale, $group) + protected function loadPaths(array $paths, $locale, $group) { - if ($this->files->exists($full = "{$path}/{$locale}/{$group}.php")) { - return $this->files->getRequire($full); - } - - // Try "xx-xx" format - $locale = str_replace('_', '-', strtolower($locale)); + return collect($paths) + ->reduce(function ($output, $path) use ($locale, $group) { + $file = "{$path}/{$locale}/{$group}.php"; + if ($this->files->exists($file)) { + return array_replace_recursive($output, $this->files->getRequire($file)); + } - if ("{$path}/{$locale}/{$group}.php" !== $full) { - if ($this->files->exists($full = "{$path}/{$locale}/{$group}.php")) { - return $this->files->getRequire($full); - } - } + // Try "xx-xx" format + $dashLocale = str_replace('_', '-', strtolower($locale)); + $dashFile = "{$path}/{$dashLocale}/{$group}.php"; + if ($dashFile !== $file && $this->files->exists($dashFile)) { + return $this->files->getRequire($dashFile); + } - return []; + return $output; + }, []); } } diff --git a/tests/Database/ModelTest.php b/tests/Database/ModelTest.php index 276384ed..93c6d61d 100644 --- a/tests/Database/ModelTest.php +++ b/tests/Database/ModelTest.php @@ -471,7 +471,7 @@ public function testAddCasts() public function testStringIsTrimmed() { $name = "Name"; - $nameWithSpace = " ${name} "; + $nameWithSpace = " {$name} "; $model = new TestModelGuarded(); $model->name = $nameWithSpace; diff --git a/tests/Database/UpdaterTest.php b/tests/Database/UpdaterTest.php index ef60d457..eb94125e 100644 --- a/tests/Database/UpdaterTest.php +++ b/tests/Database/UpdaterTest.php @@ -4,7 +4,7 @@ class UpdaterTest extends TestCase { - protected Updater $updater; + protected ?Updater $updater = null; public function setUp(): void { diff --git a/tests/Foundation/ApplicationTest.php b/tests/Foundation/ApplicationTest.php index f138de05..7f5127e6 100644 --- a/tests/Foundation/ApplicationTest.php +++ b/tests/Foundation/ApplicationTest.php @@ -5,7 +5,7 @@ class ApplicationTest extends TestCase { - protected string $basePath; + protected string $basePath = ''; protected function setUp(): void { diff --git a/tests/Html/BlockBuilderTest.php b/tests/Html/BlockBuilderTest.php index a2e1fc6a..c39f5df9 100644 --- a/tests/Html/BlockBuilderTest.php +++ b/tests/Html/BlockBuilderTest.php @@ -4,7 +4,7 @@ class BlockBuilderTest extends TestCase { - protected BlockBuilder $Block; + protected ?BlockBuilder $Block = null; public function setUp(): void { diff --git a/tests/Support/EventFakeTest.php b/tests/Support/EventFakeTest.php index 5e74b680..403551fd 100644 --- a/tests/Support/EventFakeTest.php +++ b/tests/Support/EventFakeTest.php @@ -5,7 +5,7 @@ class EventFakeTest extends TestCase { - protected EventFake $faker; + protected ?EventFake $faker = null; public function setUp(): void { diff --git a/tests/Support/MailFakeTest.php b/tests/Support/MailFakeTest.php index 144a1452..b777d2e8 100644 --- a/tests/Support/MailFakeTest.php +++ b/tests/Support/MailFakeTest.php @@ -1,17 +1,24 @@ andReturn('en/US'); - Mail::swap(new MailFake()); + $this->manager = m::mock(MailManager::class); + Mail::swap(new MailFake($this->manager)); $this->recipient = 'fake@localhost'; $this->subject = 'MailFake test'; diff --git a/tests/Translation/FileLoaderTest.php b/tests/Translation/FileLoaderTest.php index 6b9fa349..0f1848db 100644 --- a/tests/Translation/FileLoaderTest.php +++ b/tests/Translation/FileLoaderTest.php @@ -31,6 +31,7 @@ public function testLoadMethodWithNamespacesProperlyCallsLoader() $loader = new FileLoader($files = m::mock(Filesystem::class), __DIR__); $files->shouldReceive('exists')->once()->with('bar/en/foo.php')->andReturn(true); $files->shouldReceive('exists')->once()->with(__DIR__.'/en/namespace/foo.php')->andReturn(false); + $files->shouldReceive('exists')->once()->with(__DIR__.'/vendor/namespace/en/foo.php')->andReturn(false); $files->shouldReceive('getRequire')->once()->with('bar/en/foo.php')->andReturn(['foo' => 'bar']); $loader->addNamespace('namespace', 'bar'); From 0a6b24e1fd0d14454ec899b93a31536ac02e9cb6 Mon Sep 17 00:00:00 2001 From: wverhoogt Date: Wed, 3 Apr 2024 16:32:12 +0200 Subject: [PATCH 002/197] Laravel 10 changes Co-authored-by: Wim Verhoogt Co-authored-by: Ben Thomson --- .github/workflows/code-analysis.yaml | 15 ++++++++ .github/workflows/code-quality.yaml | 2 -- .github/workflows/tests.yml | 4 +-- .gitignore | 1 + composer.json | 8 ++--- src/Auth/Models/Throttle.php | 9 +++-- src/Database/Concerns/HasRelationships.php | 17 ++++++--- src/Database/Model.php | 1 - src/Database/QueryBuilder.php | 2 +- .../Relations/Concerns/DeferOneOrMany.php | 3 +- src/Database/Relations/MorphToMany.php | 14 ++++---- src/Database/Traits/Revisionable.php | 3 +- src/Foundation/Application.php | 2 +- .../Middleware/CheckForMaintenanceMode.php | 35 +++++++++---------- src/Support/Facades/Flash.php | 2 +- src/Support/Facades/Mail.php | 6 ++-- 16 files changed, 76 insertions(+), 48 deletions(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 9a4c87c4..0cfd2511 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -23,6 +23,21 @@ jobs: - name: Checkout changes uses: actions/checkout@v4 + - name: Setup extension cache + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: '8.2' + extensions: ${{ env.extensions }} + key: ${{ env.key }} + + - name: Cache extensions + uses: actions/cache@v2 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + - name: Install PHP uses: shivammathur/setup-php@v2 with: diff --git a/.github/workflows/code-quality.yaml b/.github/workflows/code-quality.yaml index 065447d7..840f47e5 100644 --- a/.github/workflows/code-quality.yaml +++ b/.github/workflows/code-quality.yaml @@ -4,8 +4,6 @@ on: pull_request: push: branches: - - 1.0 - - 1.1 - develop concurrency: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c6c5c7cd..0625b7c3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,8 +3,6 @@ name: Tests on: push: branches: - - "1.0" - - "1.1" - develop pull_request: @@ -15,7 +13,7 @@ concurrency: jobs: unitTests: strategy: - max-parallel: 6 + max-parallel: 4 matrix: operatingSystem: [ubuntu-latest, windows-latest] phpVersion: ['8.1', '8.2'] diff --git a/.gitignore b/.gitignore index bdee57b0..06c24a82 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ composer.lock # Editor files .idea .vscode +*.code-workspace # Other files .DS_Store diff --git a/composer.json b/composer.json index d0e7e891..7c0260ad 100644 --- a/composer.json +++ b/composer.json @@ -36,9 +36,9 @@ "ext-zip": "*", "assetic/framework": "~3.0", - "doctrine/dbal": "^3.0", + "doctrine/dbal": "^3.6", "enshrined/svg-sanitize": "~0.16", - "laravel/framework": "^10.0", + "laravel/framework": "^10.1", "laravel/tinker": "^2.8.1", "league/csv": "~9.1", "nesbot/carbon": "^2.0", @@ -58,8 +58,8 @@ "php-parallel-lint/php-parallel-lint": "^1.0", "meyfa/phpunit-assert-gd": "^2.0.0|^3.0.0", "dms/phpunit-arraysubset-asserts": "^0.1.0|^0.2.1", - "larastan/larastan": "^2.8.1", - "orchestra/testbench": "^8.0.0" + "nunomaduro/larastan": "2.x-dev", + "orchestra/testbench": "^8.4" }, "suggest": { "ext-pdo_dblib": "Required to use MS SQL Server databases", diff --git a/src/Auth/Models/Throttle.php b/src/Auth/Models/Throttle.php index a4cdbea5..ba03df86 100644 --- a/src/Auth/Models/Throttle.php +++ b/src/Auth/Models/Throttle.php @@ -37,10 +37,13 @@ class Throttle extends Model /** * The attributes that should be mutated to dates. * - * @var array + * @var array */ - protected $dates = ['last_attempt_at', 'suspended_at', 'banned_at']; - + protected $casts = [ + 'suspended_at' => 'datetime', + 'last_attempt_at' => 'datetime', + 'banned_at' => 'datetime', + ]; /** * @var int Attempt limit. */ diff --git a/src/Database/Concerns/HasRelationships.php b/src/Database/Concerns/HasRelationships.php index 069c8e94..bdae519f 100644 --- a/src/Database/Concerns/HasRelationships.php +++ b/src/Database/Concerns/HasRelationships.php @@ -340,7 +340,7 @@ protected function handleRelation($relationName) case 'morphToMany': $relation = $this->validateRelationArgs($relationName, ['table', 'key', 'otherKey', 'parentKey', 'relatedKey', 'pivot', 'timestamps'], ['name']); - $relationObj = $this->$relationType($relation[0], $relation['name'], $relation['table'], $relation['key'], $relation['otherKey'], $relation['parentKey'], $relation['relatedKey'], false, $relationName); + $relationObj = $this->$relationType($relation[0], $relation['name'], $relation['table'], $relation['key'], $relation['otherKey'], $relation['parentKey'], $relation['relatedKey'], $relationName, false); if (isset($relation['pivotModel'])) { $relationObj->using($relation['pivotModel']); @@ -667,9 +667,18 @@ public function belongsToMany($related, $table = null, $primaryKey = null, $fore /** * Define a polymorphic many-to-many relationship. * This code is almost a duplicate of Eloquent but uses a Storm relation class. + * @param string $related + * @param string $name + * @param string|null $table + * @param string|null $primaryKey + * @param string|null $foreignKey + * @param string|null $parentKey + * @param string|null $relatedKey + * @param string|null $relationName + * @param bool $inverse * @return \Winter\Storm\Database\Relations\MorphToMany */ - public function morphToMany($related, $name, $table = null, $primaryKey = null, $foreignKey = null, $parentKey = null, $relatedKey = null, $inverse = false, $relationName = null) + public function morphToMany($related, $name, $table = null, $primaryKey = null, $foreignKey = null, $parentKey = null, $relatedKey = null, $relationName = null, $inverse = false) { if (is_null($relationName)) { $relationName = $this->getRelationCaller(); @@ -720,8 +729,8 @@ public function morphedByMany($related, $name, $table = null, $primaryKey = null $foreignKey, $parentKey, $relatedKey, - true, - $relationName + $relationName, + true ); } diff --git a/src/Database/Model.php b/src/Database/Model.php index 57ae23c6..5e3d9fce 100644 --- a/src/Database/Model.php +++ b/src/Database/Model.php @@ -106,7 +106,6 @@ public function isDatabaseReady(): bool } // Resolver hasn't been set yet - /** @phpstan-ignore-next-line */ if (!static::getConnectionResolver()) { return false; } diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index 14289c59..bad7d16f 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -463,7 +463,7 @@ protected function runPaginationCountQuery($columns = ['*']) } return $this->newQuery() - ->from(new Expression('(' . $clone->toSql() . ') as ' . $this->grammar->wrap('aggregate_table'))) + ->from($clone, $this->grammar->wrap('aggregate_table')) ->mergeBindings($clone) ->setAggregate('count', $this->withoutSelectAliases($columns)) ->get() diff --git a/src/Database/Relations/Concerns/DeferOneOrMany.php b/src/Database/Relations/Concerns/DeferOneOrMany.php index 4db33a2e..bfb8bb3a 100644 --- a/src/Database/Relations/Concerns/DeferOneOrMany.php +++ b/src/Database/Relations/Concerns/DeferOneOrMany.php @@ -1,5 +1,6 @@ where('session_key', $sessionKey) ->where('is_bind', 0) ->whereRaw(DbDongle::parse('id > ifnull((select max(id) from ' . DbDongle::getTablePrefix() . 'deferred_bindings where - slave_id = ' . $this->getWithDeferredQualifiedKeyName() . ' and + slave_id = ' . $this->getWithDeferredQualifiedKeyName()->getValue(new Grammar) . ' and master_field = ? and master_type = ? and session_key = ? and diff --git a/src/Database/Relations/MorphToMany.php b/src/Database/Relations/MorphToMany.php index 2487139c..58b20399 100644 --- a/src/Database/Relations/MorphToMany.php +++ b/src/Database/Relations/MorphToMany.php @@ -27,8 +27,10 @@ class MorphToMany extends BaseMorphToMany * @param \Illuminate\Database\Eloquent\Model $parent * @param string $name * @param string $table - * @param string $foreignKey - * @param string $otherKey + * @param string $foreignPivotKey + * @param string $relatedPivotKey + * @param string $parentKey + * @param string $relatedKey * @param string $relationName * @param bool $inverse * @return void @@ -38,8 +40,8 @@ public function __construct( Model $parent, $name, $table, - $foreignKey, - $otherKey, + $foreignPivotKey, + $relatedPivotKey, $parentKey, $relatedKey, $relationName = null, @@ -50,8 +52,8 @@ public function __construct( $parent, $name, $table, - $foreignKey, - $otherKey, + $foreignPivotKey, + $relatedPivotKey, $parentKey, $relatedKey, $relationName, diff --git a/src/Database/Traits/Revisionable.php b/src/Database/Traits/Revisionable.php index 9dcdc459..4e2b5763 100644 --- a/src/Database/Traits/Revisionable.php +++ b/src/Database/Traits/Revisionable.php @@ -157,7 +157,8 @@ protected function revisionableCleanUp() protected function revisionableGetCastType($attribute) { - if (in_array($attribute, $this->getDates())) { + // dates property deprecated in Laravel 10, no longer used in getDates() + if (in_array($attribute, $this->getDates()) || $this->isDateCastable($attribute) || in_array($attribute, $this->dates)) { return 'date'; } diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index ef8b633b..6f594345 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -428,7 +428,7 @@ public function registerConfiguredProviders($isRetry = false) }); if (Config::get('app.loadDiscoveredPackages', false)) { - $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]); + $providers->splice(1, 0, $this->make(PackageManifest::class)->providers()); } $filesystem = new Filesystem; diff --git a/src/Foundation/Http/Middleware/CheckForMaintenanceMode.php b/src/Foundation/Http/Middleware/CheckForMaintenanceMode.php index 6a82797b..76a452b3 100644 --- a/src/Foundation/Http/Middleware/CheckForMaintenanceMode.php +++ b/src/Foundation/Http/Middleware/CheckForMaintenanceMode.php @@ -23,26 +23,25 @@ public function handle($request, Closure $next) { try { return parent::handle($request, $next); - } catch (\Illuminate\Foundation\Http\Exceptions\MaintenanceModeException $ex) { - // Smoothly handle AJAX requests - if (request()->ajax()) { - return Response::make(Lang::get('system::lang.page.maintenance.help') . "\r\n" . $ex->getMessage(), 503); - } + } catch (\Symfony\Component\HttpKernel\Exception\HttpException $ex) { + if ($ex->getStatusCode() === 503) { + // Smoothly handle AJAX requests + if (request()->ajax()) { + return Response::make(Lang::get('system::lang.page.maintenance.help') . "\r\n" . $ex->getMessage(), 503); + } - // Check if there is a project level view to override the system one - View::addNamespace('base', base_path()); - if (View::exists('base::maintenance')) { - $view = 'base::maintenance'; - } else { - $view = 'system::maintenance'; - } + // Check if there is a project level view to override the system one + View::addNamespace('base', base_path()); + if (View::exists('base::maintenance')) { + $view = 'base::maintenance'; + } else { + $view = 'system::maintenance'; + } - return Response::make(View::make($view, [ - 'message' => $ex->getMessage(), - 'wentDownAt' => $ex->wentDownAt, - 'retryAfter' => $ex->retryAfter, - 'willBeAvailableAt' => $ex->willBeAvailableAt, - ]), 503); + return Response::make(View::make($view, [ + 'message' => $ex->getMessage() + ]), 503); + } } } } diff --git a/src/Support/Facades/Flash.php b/src/Support/Facades/Flash.php index 7e1e1b30..b5df487f 100644 --- a/src/Support/Facades/Flash.php +++ b/src/Support/Facades/Flash.php @@ -12,7 +12,7 @@ * @method static array|\Winter\Storm\Flash\FlashBag info(string $message = null) * @method static \Winter\Storm\Flash\FlashBag add(string $key, string $message) * @method static void store() - * @method static void forget(string $key = null) + * @method static \Winter\Storm\Flash\FlashBag forget(string $key = null) * @method static void purge(); * * @see \Winter\Storm\Flash\FlashBag diff --git a/src/Support/Facades/Mail.php b/src/Support/Facades/Mail.php index 8a394e07..f9e04c4b 100755 --- a/src/Support/Facades/Mail.php +++ b/src/Support/Facades/Mail.php @@ -1,5 +1,6 @@ Date: Wed, 3 Apr 2024 10:38:21 -0400 Subject: [PATCH 003/197] apply latest changes done by @bennothommo to fix code analysis --- .github/workflows/code-analysis.yaml | 11 ----------- .github/workflows/tests.yml | 11 ----------- phpstan.neon | 2 ++ 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 0cfd2511..0432f6f6 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -45,17 +45,6 @@ jobs: extensions: ${{ env.extensions }} coverage: none - - name: Setup dependency cache - id: composercache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composercache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ runner.os }}-composer- - - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0625b7c3..87d2e7a2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,17 +34,6 @@ jobs: extensions: ${{ env.extensions }} coverage: none - - name: Setup dependency cache - id: composercache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composercache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} - restore-keys: ${{ runner.os }}-composer- - - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts diff --git a/phpstan.neon b/phpstan.neon index 54338d86..a7a8abc0 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -10,6 +10,8 @@ parameters: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php - src/Parse/PHP/ArrayPrinter.php + - src/Foundation/Console/KeyGenerateCommand.php + disableSchemaScan: true databaseMigrationsPath: - src/Auth/Migrations - src/Database/Migrations From 416c6b31a545a724923261270d74f14bb8dbd6b3 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 10:47:36 -0400 Subject: [PATCH 004/197] restore initial larastan package --- composer.json | 2 +- src/Cache/CacheManager.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 7c0260ad..6e80c514 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,7 @@ "php-parallel-lint/php-parallel-lint": "^1.0", "meyfa/phpunit-assert-gd": "^2.0.0|^3.0.0", "dms/phpunit-arraysubset-asserts": "^0.1.0|^0.2.1", - "nunomaduro/larastan": "2.x-dev", + "larastan/larastan": "^2.8.1", "orchestra/testbench": "^8.4" }, "suggest": { diff --git a/src/Cache/CacheManager.php b/src/Cache/CacheManager.php index 6d0375d7..0c1de974 100644 --- a/src/Cache/CacheManager.php +++ b/src/Cache/CacheManager.php @@ -16,4 +16,4 @@ public function store($name = null) { return parent::store($name); } -} \ No newline at end of file +} From 4c7a7afd22a0544c5eb5ba6f2a8b42e8026dcc23 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 11:06:02 -0400 Subject: [PATCH 005/197] adjust $fillable signature --- src/Database/Attach/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Attach/File.php b/src/Database/Attach/File.php index 45c56f2a..af476ee1 100644 --- a/src/Database/Attach/File.php +++ b/src/Database/Attach/File.php @@ -40,7 +40,7 @@ class File extends Model ]; /** - * @var string[] The attributes that are mass assignable. + * @var array The attributes that are mass assignable. */ protected $fillable = [ 'file_name', From d58d3146926cf5988b9a7ab076709ad30dbe7935 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 11:06:36 -0400 Subject: [PATCH 006/197] adjust store() method return type --- src/Cache/CacheManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cache/CacheManager.php b/src/Cache/CacheManager.php index 0c1de974..0a2249fd 100644 --- a/src/Cache/CacheManager.php +++ b/src/Cache/CacheManager.php @@ -10,7 +10,7 @@ class CacheManager extends BaseCacheManager * Get a cache store instance by name, wrapped in a repository. * * @param string|null $name - * @return \Illuminate\Cache\Repository + * @return \Illuminate\Contracts\Cache\Repository */ public function store($name = null) { From 3c4013c936c5c54fc1750b55788a0406fd53fc72 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 11:35:33 -0400 Subject: [PATCH 007/197] fix getCache() return type --- src/Halcyon/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Halcyon/Builder.php b/src/Halcyon/Builder.php index 2df82754..de7313be 100644 --- a/src/Halcyon/Builder.php +++ b/src/Halcyon/Builder.php @@ -699,11 +699,11 @@ protected function isCacheBusted($result) /** * Get the cache object with tags assigned, if applicable. * - * @return \Illuminate\Cache\Repository + * @return \Illuminate\Contracts\Cache\Repository */ protected function getCache() { - $cache = $this->model->getCacheManager()->store($this->cacheDriver); + $cache = $this->model->getCacheManager()->driver($this->cacheDriver); return $this->cacheTags ? $cache->tags($this->cacheTags) : $cache; } From f5e4b94f55c69f91d819423955df234950e01996 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 11:36:00 -0400 Subject: [PATCH 008/197] remove invalid getValue() call --- src/Database/Relations/Concerns/DeferOneOrMany.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/Relations/Concerns/DeferOneOrMany.php b/src/Database/Relations/Concerns/DeferOneOrMany.php index bfb8bb3a..3bd9cc66 100644 --- a/src/Database/Relations/Concerns/DeferOneOrMany.php +++ b/src/Database/Relations/Concerns/DeferOneOrMany.php @@ -96,7 +96,7 @@ public function withDeferred($sessionKey) ->where('session_key', $sessionKey) ->where('is_bind', 0) ->whereRaw(DbDongle::parse('id > ifnull((select max(id) from ' . DbDongle::getTablePrefix() . 'deferred_bindings where - slave_id = ' . $this->getWithDeferredQualifiedKeyName()->getValue(new Grammar) . ' and + slave_id = ' . $this->getWithDeferredQualifiedKeyName() . ' and master_field = ? and master_type = ? and session_key = ? and From 00513c966af69680759a7d135f3427badd5afc33 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:13:27 -0400 Subject: [PATCH 009/197] migrate phpunit xml config --- phpunit.xml.dist | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5db8edbf..504abbbd 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,23 +1,13 @@ - - - - ./src/ - - - - - ./tests - - + + + + ./tests + + + + + ./src/ + + From 3a86f6c2e02aedeeac8f560ae1fa4c1a7b024d4e Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:13:52 -0400 Subject: [PATCH 010/197] update dependencies for laravel 11 --- composer.json | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index 6e80c514..bbb5b8e0 100644 --- a/composer.json +++ b/composer.json @@ -35,32 +35,37 @@ "ext-pdo": "*", "ext-zip": "*", - "assetic/framework": "~3.0", + "assetic/framework": "dev-master", "doctrine/dbal": "^3.6", "enshrined/svg-sanitize": "~0.16", - "laravel/framework": "^10.1", + "laravel/framework": "^11.0", "laravel/tinker": "^2.8.1", "league/csv": "~9.1", "nesbot/carbon": "^2.0", "nikic/php-parser": "^4.10", "scssphp/scssphp": "~1.0", - "symfony/console": "^6.3", - "symfony/yaml": "^6.0", + "symfony/console": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", "twig/twig": "~3.0", "wikimedia/less.php": "~3.0", "wikimedia/minify": "~2.2", "winter/laravel-config-writer": "^1.0.1" }, "require-dev": { - "phpunit/phpunit": "^9.5.8", - "mockery/mockery": "^1.4.4", "squizlabs/php_codesniffer": "^3.2", "php-parallel-lint/php-parallel-lint": "^1.0", - "meyfa/phpunit-assert-gd": "^2.0.0|^3.0.0", - "dms/phpunit-arraysubset-asserts": "^0.1.0|^0.2.1", - "larastan/larastan": "^2.8.1", - "orchestra/testbench": "^8.4" + "meyfa/phpunit-assert-gd": "^4.0", + "dms/phpunit-arraysubset-asserts": "^0.5", + "orchestra/testbench": "^9.0", + "larastan/larastan": "^2.8.1" }, + "repositories": [ + { + "type": "path", + "url": "vendor/assetic/framework" + } + ], + "suggest": { "ext-pdo_dblib": "Required to use MS SQL Server databases", "ext-pdo_mysql": "Required to use MySQL databases", From ea41b896f8516e2274cdd446b378dc6f3f0134c3 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:14:37 -0400 Subject: [PATCH 011/197] allign with new Authenticatable contract --- src/Auth/Models/User.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Auth/Models/User.php b/src/Auth/Models/User.php index 3865d0a9..64f6246e 100644 --- a/src/Auth/Models/User.php +++ b/src/Auth/Models/User.php @@ -110,6 +110,13 @@ class User extends Model implements \Illuminate\Contracts\Auth\Authenticatable */ protected $rememberTokenName = 'persist_code'; + /** + * The column name of the password field using during authentication. + * + * @var string + */ + protected $authPasswordName = 'password'; + /** * @var array The user merged permissions. */ @@ -248,7 +255,7 @@ public function attemptActivation($activationCode) */ public function checkPassword($password) { - return Hash::check($password, $this->password); + return Hash::check($password, $this->getAuthPassword()); } /** @@ -285,7 +292,7 @@ public function checkResetPasswordCode($resetCode) public function attemptResetPassword($resetCode, $newPassword) { if ($this->checkResetPasswordCode($resetCode)) { - $this->password = $newPassword; + $this->{$this->getAuthPasswordName()} = $newPassword; $this->reset_password_code = null; return $this->forceSave(); } @@ -600,13 +607,23 @@ public function getAuthIdentifier() return $this->{$this->getAuthIdentifierName()}; } + /** + * Get the name of the password attribute for the user. + * + * @return string + */ + public function getAuthPasswordName() + { + return $this->authPasswordName; + } + /** * Get the password for the user. * @return string */ public function getAuthPassword() { - return $this->password; + return $this->{$this->getAuthPasswordName()}; } /** From 3f5e612ead375da44662dee970b317bba9577052 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:16:05 -0400 Subject: [PATCH 012/197] match symfony SignalableCommandInterface signature --- src/Console/Traits/HandlesCleanup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Traits/HandlesCleanup.php b/src/Console/Traits/HandlesCleanup.php index 641f5cb4..a4bd72cf 100644 --- a/src/Console/Traits/HandlesCleanup.php +++ b/src/Console/Traits/HandlesCleanup.php @@ -39,7 +39,7 @@ public function getSubscribedSignals(): array /** * Handle the provided Unix process signal */ - public function handleSignal(int $signal): int|false + public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false { // Handle the signal if (method_exists($this, 'handleCleanup')) { From 9f9dc7a6ff27bbd5a2a4f95d8cb329d7f6a69af5 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:16:41 -0400 Subject: [PATCH 013/197] match new builder paginate signature --- src/Database/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Builder.php b/src/Database/Builder.php index f42dcf2a..cd7c632d 100644 --- a/src/Database/Builder.php +++ b/src/Database/Builder.php @@ -128,12 +128,12 @@ protected function searchWhereInternal($term, $columns, $mode, $boolean) * @param string|int|null $pageName * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function paginate($perPage = null, $currentPage = null, $columns = ['*'], $pageName = 'page') + public function paginate($perPage = null, $currentPage = null, $columns = ['*'], $pageName = 'page', $total = 0) { /* * Engage Laravel signature support * - * paginate($perPage, $columns, $pageName, $currentPage) + * paginate($perPage, $columns, $pageName, $currentPage, $total) */ if (is_array($currentPage)) { $_columns = $columns; From da18666015c3af0c87b71f5d44f8d243f913ede6 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:17:35 -0400 Subject: [PATCH 014/197] update CacheManager signature --- src/Halcyon/MemoryCacheManager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Halcyon/MemoryCacheManager.php b/src/Halcyon/MemoryCacheManager.php index ccb5ea9c..62422cdc 100644 --- a/src/Halcyon/MemoryCacheManager.php +++ b/src/Halcyon/MemoryCacheManager.php @@ -7,9 +7,9 @@ class MemoryCacheManager extends CacheManager { - public function repository(Store $store) + public function repository(Store $store, array $config = []) { - return new MemoryRepository($store); + return new MemoryRepository($store, $config); } public static function isEnabled() From b18d3e52aa3c5155fbf143f1db13c20803189bb2 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:20:24 -0400 Subject: [PATCH 015/197] fix assetic/framework dependency --- composer.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/composer.json b/composer.json index bbb5b8e0..ebac1a44 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "ext-pdo": "*", "ext-zip": "*", - "assetic/framework": "dev-master", + "assetic/framework": "^3.1", "doctrine/dbal": "^3.6", "enshrined/svg-sanitize": "~0.16", "laravel/framework": "^11.0", @@ -59,12 +59,6 @@ "orchestra/testbench": "^9.0", "larastan/larastan": "^2.8.1" }, - "repositories": [ - { - "type": "path", - "url": "vendor/assetic/framework" - } - ], "suggest": { "ext-pdo_dblib": "Required to use MS SQL Server databases", From 98b0e6afaf985528e40e36041b271a3f9548fa75 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:51:00 -0400 Subject: [PATCH 016/197] fix mockery dependency --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index ebac1a44..3c9638db 100644 --- a/composer.json +++ b/composer.json @@ -55,6 +55,7 @@ "squizlabs/php_codesniffer": "^3.2", "php-parallel-lint/php-parallel-lint": "^1.0", "meyfa/phpunit-assert-gd": "^4.0", + "mockery/mockery": "^1.6|^2.0", "dms/phpunit-arraysubset-asserts": "^0.5", "orchestra/testbench": "^9.0", "larastan/larastan": "^2.8.1" From 7aee65986ba5dbe6b1b2f0d6c04761cb69d48a78 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 13:51:44 -0400 Subject: [PATCH 017/197] replace setMethods() with add/onlyMethods() --- src/Support/Testing/MocksClassLoader.php | 2 +- tests/Database/QueryBuilderTest.php | 5 ++++- .../Http/Middleware/CheckForTrustedHostTest.php | 2 +- .../Http/Middleware/CheckForTrustedProxiesTest.php | 2 +- tests/Halcyon/HalcyonModelTest.php | 9 ++++++--- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Support/Testing/MocksClassLoader.php b/src/Support/Testing/MocksClassLoader.php index f2291209..46f756f8 100644 --- a/src/Support/Testing/MocksClassLoader.php +++ b/src/Support/Testing/MocksClassLoader.php @@ -49,7 +49,7 @@ protected function registerMockClassLoader($basePath = null, $manifestPath = nul protected function mockClassLoader($class) { $subject = $this->getMockBuilder($class) - ->setMethods(['extensionGetClassLoader']) + ->onlyMethods(['extensionGetClassLoader']) ->disableOriginalConstructor() ->getMock(); diff --git a/tests/Database/QueryBuilderTest.php b/tests/Database/QueryBuilderTest.php index 0d2387ce..4018e584 100644 --- a/tests/Database/QueryBuilderTest.php +++ b/tests/Database/QueryBuilderTest.php @@ -105,9 +105,10 @@ protected function getConnection($connection = null, $table = null) ->disableOriginalClone() ->disableArgumentCloning() ->disallowMockingUnknownTypes() - ->setMethods([ + ->onlyMethods([ 'table', 'raw', + 'scalar', 'selectOne', 'select', 'cursor', @@ -125,6 +126,8 @@ protected function getConnection($connection = null, $table = null) 'transactionLevel', 'pretend', 'getDatabaseName', + ]) + ->addMethods([ 'getConfig', ]) ->getMock(); diff --git a/tests/Foundation/Http/Middleware/CheckForTrustedHostTest.php b/tests/Foundation/Http/Middleware/CheckForTrustedHostTest.php index 758a4b28..d1bb6ff7 100644 --- a/tests/Foundation/Http/Middleware/CheckForTrustedHostTest.php +++ b/tests/Foundation/Http/Middleware/CheckForTrustedHostTest.php @@ -182,7 +182,7 @@ protected function createUrlGenerator($trustedHosts = [], $headers = [], $server { $middleware = $this->getMockBuilder(CheckForTrustedHost::class) ->disableOriginalConstructor() - ->setMethods(['hosts', 'shouldSpecifyTrustedHosts']) + ->onlyMethods(['hosts', 'shouldSpecifyTrustedHosts']) ->getMock(); $middleware->expects($this->any()) diff --git a/tests/Foundation/Http/Middleware/CheckForTrustedProxiesTest.php b/tests/Foundation/Http/Middleware/CheckForTrustedProxiesTest.php index cbde74f1..8f0cb549 100644 --- a/tests/Foundation/Http/Middleware/CheckForTrustedProxiesTest.php +++ b/tests/Foundation/Http/Middleware/CheckForTrustedProxiesTest.php @@ -432,7 +432,7 @@ protected function createTrustedProxyMock($trustedProxies = [], $trustedHeaders { $middleware = $this->getMockBuilder(CheckForTrustedProxies::class) ->disableOriginalConstructor() - ->setMethods(['proxies', 'headers']) + ->onlyMethods(['proxies', 'headers']) ->getMock(); $middleware->expects($this->any()) diff --git a/tests/Halcyon/HalcyonModelTest.php b/tests/Halcyon/HalcyonModelTest.php index 2bee0dee..6813750c 100644 --- a/tests/Halcyon/HalcyonModelTest.php +++ b/tests/Halcyon/HalcyonModelTest.php @@ -377,13 +377,16 @@ protected function setDatasourceResolver() protected function setValidatorOnModel() { - $translator = $this->getMockBuilder('Illuminate\Contracts\Translation\Translator')->setMethods([ + $translator = $this->getMockBuilder('Illuminate\Contracts\Translation\Translator') + ->onlyMethods([ 'get', 'choice', - 'trans', - 'transChoice', 'setLocale', 'getLocale' + ]) + ->addMethods([ + 'trans', + 'transChoice', ])->getMock(); $translator->expects($this->any())->method('get')->will($this->returnArgument(0)); From 79e2747c9dbd229abcbd309518111055d9eaca43 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 14:07:46 -0400 Subject: [PATCH 018/197] fix deprecation errors --- tests/Filesystem/FilesystemTest.php | 2 +- tests/Parse/MarkdownTest.php | 28 ++++++++++++------------ tests/Router/RoutingUrlGeneratorTest.php | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/Filesystem/FilesystemTest.php b/tests/Filesystem/FilesystemTest.php index 3ffa3636..8b85a147 100644 --- a/tests/Filesystem/FilesystemTest.php +++ b/tests/Filesystem/FilesystemTest.php @@ -25,7 +25,7 @@ public function testIsAbsolutePath($path, $expectedResult) $this->assertEquals($expectedResult, $result); } - public function providePathsForIsAbsolutePath() + public static function providePathsForIsAbsolutePath() { return [ ['/var/lib', true], diff --git a/tests/Parse/MarkdownTest.php b/tests/Parse/MarkdownTest.php index 82fb2441..9f87d0de 100644 --- a/tests/Parse/MarkdownTest.php +++ b/tests/Parse/MarkdownTest.php @@ -7,7 +7,7 @@ class MarkdownTest extends TestCase /** * Test fixtures that should be skipped by the data provider. */ - protected array $specialTests = [ + protected static array $specialTests = [ 'front_matter', ]; @@ -20,15 +20,15 @@ class MarkdownTest extends TestCase */ public function testParse($name, $markdown, $html) { - $this->assertEquals($html, $this->removeLineEndings(Markdown::parse($markdown)), 'Markdown test case "' . $name . '" failed'); + $this->assertEquals($html, static::removeLineEndings(Markdown::parse($markdown)), 'Markdown test case "' . $name . '" failed'); } public function testParseSafe() { $markdown = file_get_contents(dirname(__DIR__) . '/fixtures/markdown/code_block.md'); - $html = $this->removeLineEndings(file_get_contents(dirname(__DIR__) . '/fixtures/markdown/code_block_disabled.html')); + $html = static::removeLineEndings(file_get_contents(dirname(__DIR__) . '/fixtures/markdown/code_block_disabled.html')); - $this->assertEquals($html, $this->removeLineEndings(Markdown::parseSafe($markdown))); + $this->assertEquals($html, static::removeLineEndings(Markdown::parseSafe($markdown))); } public function testDisableAndEnableMethods() @@ -36,16 +36,16 @@ public function testDisableAndEnableMethods() $parser = Markdown::disableAutolinking()->disableTables(); $markdown = file_get_contents(dirname(__DIR__) . '/fixtures/markdown/table_inline_markdown.md'); - $html = $this->removeLineEndings(file_get_contents(dirname(__DIR__) . '/fixtures/markdown/table_inline_markdown_disabled.html')); + $html = static::removeLineEndings(file_get_contents(dirname(__DIR__) . '/fixtures/markdown/table_inline_markdown_disabled.html')); - $this->assertEquals($html, $this->removeLineEndings($parser->parse($markdown))); + $this->assertEquals($html, static::removeLineEndings($parser->parse($markdown))); // Re-enable tables and autolinking $parser->enableAutolinking()->enableTables(); - $html = $this->removeLineEndings(file_get_contents(dirname(__DIR__) . '/fixtures/markdown/table_inline_markdown.html')); + $html = static::removeLineEndings(file_get_contents(dirname(__DIR__) . '/fixtures/markdown/table_inline_markdown.html')); - $this->assertEquals($html, $this->removeLineEndings($parser->parse($markdown))); + $this->assertEquals($html, static::removeLineEndings($parser->parse($markdown))); } public function testFrontMatter() @@ -53,9 +53,9 @@ public function testFrontMatter() $parser = Markdown::enableFrontMatter(); $markdown = file_get_contents(dirname(__DIR__) . '/fixtures/markdown/front_matter.md'); - $html = $this->removeLineEndings(file_get_contents(dirname(__DIR__) . '/fixtures/markdown/front_matter.html')); + $html = static::removeLineEndings(file_get_contents(dirname(__DIR__) . '/fixtures/markdown/front_matter.html')); - $this->assertEquals($html, $this->removeLineEndings($parser->parse($markdown))); + $this->assertEquals($html, static::removeLineEndings($parser->parse($markdown))); $this->assertEquals([ 'title' => 'My document', 'group' => 'Documents', @@ -75,7 +75,7 @@ public function testFrontMatter() * * @return array */ - public function markdownData() + public static function markdownData() { $dirName = dirname(__DIR__) . '/fixtures/markdown'; $dir = new DirectoryIterator($dirName); @@ -87,19 +87,19 @@ public function markdownData() if ($name === 'README') { continue; } - if (in_array($name, $this->specialTests)) { + if (in_array($name, static::$specialTests)) { continue; } $markdown = file_get_contents($dirName . DIRECTORY_SEPARATOR . $name . '.md'); - $html = $this->removeLineEndings(file_get_contents($dirName . DIRECTORY_SEPARATOR . $name . '.html')); + $html = static::removeLineEndings(file_get_contents($dirName . DIRECTORY_SEPARATOR . $name . '.html')); yield [$name, $markdown, $html]; } } } - protected function removeLineEndings(string $text) + protected static function removeLineEndings(string $text) { return str_replace(["\r\n", "\r", "\n"], '', $text); } diff --git a/tests/Router/RoutingUrlGeneratorTest.php b/tests/Router/RoutingUrlGeneratorTest.php index e3d55892..b28f926c 100644 --- a/tests/Router/RoutingUrlGeneratorTest.php +++ b/tests/Router/RoutingUrlGeneratorTest.php @@ -526,7 +526,7 @@ public function testRoutesWithDomainsThroughProxy() $this->assertSame('http://sub.foo.com/foo/bar', $url->route('foo')); } - public function providerRouteParameters() + public static function providerRouteParameters() { return [ [['test' => 123]], @@ -555,7 +555,7 @@ public function testUrlGenerationForControllersRequiresPassingOfRequiredParamete $this->assertSame('http://www.foo.com:8080/foo?test=123', $url->route('foo', $parameters)); } - public function provideParametersAndExpectedMeaningfulExceptionMessages() + public static function provideParametersAndExpectedMeaningfulExceptionMessages() { return [ 'Missing parameters "one", "two" and "three"' => [ From 7b588e2eab5e2009793384cbbc183f9e043d938f Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 14:19:56 -0400 Subject: [PATCH 019/197] we need to extend TestCase now --- tests/fixtures/events/EventTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/fixtures/events/EventTest.php b/tests/fixtures/events/EventTest.php index 91b01695..1774b07a 100644 --- a/tests/fixtures/events/EventTest.php +++ b/tests/fixtures/events/EventTest.php @@ -1,5 +1,8 @@ Date: Wed, 3 Apr 2024 14:23:18 -0400 Subject: [PATCH 020/197] call parent constructor --- tests/fixtures/events/EventTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fixtures/events/EventTest.php b/tests/fixtures/events/EventTest.php index 1774b07a..002ddb20 100644 --- a/tests/fixtures/events/EventTest.php +++ b/tests/fixtures/events/EventTest.php @@ -4,5 +4,6 @@ class EventTest extends TestCase { public function __construct() { + parent::__construct("dummy"); } } From 16d43fa9c51a45e51dcaf8189bf4513aca2f6de3 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 14:59:01 -0400 Subject: [PATCH 021/197] return class instance --- src/Auth/Manager.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Auth/Manager.php b/src/Auth/Manager.php index fe4ff754..57cecad1 100644 --- a/src/Auth/Manager.php +++ b/src/Auth/Manager.php @@ -171,9 +171,10 @@ public function hasUser() * Sets the user * @phpstan-param Models\User $user */ - public function setUser(Authenticatable $user) + public function setUser(Authenticatable $user) : self { $this->user = $user; + return $this; } /** From 397081980876b607d0402f6b7a9bf9d0e6292335 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 14:59:16 -0400 Subject: [PATCH 022/197] Remove argument removed in Laravel 11 --- src/Support/helpers-array.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Support/helpers-array.php b/src/Support/helpers-array.php index e07c42f0..d5af6374 100644 --- a/src/Support/helpers-array.php +++ b/src/Support/helpers-array.php @@ -333,7 +333,7 @@ function array_set(&$array, $key, $value) */ function array_shuffle($array, $seed = null) { - return Arr::shuffle($array, $seed); + return Arr::shuffle($array); } } From 148ea7ccc84aa50528d3c09a7e9cfffb4368a12b Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 15:00:20 -0400 Subject: [PATCH 023/197] PDO driver classes removed in Laravel 11 --- src/Database/Connections/MySqlConnection.php | 11 ----------- src/Database/Connections/PostgresConnection.php | 11 ----------- src/Database/Connections/SQLiteConnection.php | 11 ----------- src/Database/Connections/SqlServerConnection.php | 11 ----------- 4 files changed, 44 deletions(-) diff --git a/src/Database/Connections/MySqlConnection.php b/src/Database/Connections/MySqlConnection.php index 2458a9ee..39185ad4 100644 --- a/src/Database/Connections/MySqlConnection.php +++ b/src/Database/Connections/MySqlConnection.php @@ -1,7 +1,6 @@ Date: Wed, 3 Apr 2024 15:08:11 -0400 Subject: [PATCH 024/197] try to make EventTest work --- tests/fixtures/events/EventTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/fixtures/events/EventTest.php b/tests/fixtures/events/EventTest.php index 002ddb20..07de9214 100644 --- a/tests/fixtures/events/EventTest.php +++ b/tests/fixtures/events/EventTest.php @@ -4,6 +4,11 @@ class EventTest extends TestCase { public function __construct() { - parent::__construct("dummy"); + parent::__construct("testDummy"); + } + + public function testDummy() + { + $this->assertTrue(true, true); } } From 5bddeb65800d73daa860ada73c4a81afd16d19c5 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 15:11:49 -0400 Subject: [PATCH 025/197] remove upload test artifact --- .github/workflows/tests.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 87d2e7a2..04d34c84 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -43,11 +43,3 @@ jobs: - name: Run tests run: ./vendor/bin/phpunit ./tests - - - name: Upload test artifacts on failure - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: ResizerTest-${{ matrix.operatingSystem }}-PHP${{ matrix.phpVersion }} - path: tests/artifacts/ResizerTest/ - if-no-files-found: error From 88f16390c652641a8341edb2334536ac71bbb06b Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Apr 2024 15:16:42 -0400 Subject: [PATCH 026/197] update php test versions --- .github/workflows/tests.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 04d34c84..cc94e8ba 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: max-parallel: 4 matrix: operatingSystem: [ubuntu-latest, windows-latest] - phpVersion: ['8.1', '8.2'] + phpVersion: ['8.2', '8.3'] fail-fast: false runs-on: ${{ matrix.operatingSystem }} name: ${{ matrix.operatingSystem }} / PHP ${{ matrix.phpVersion }} @@ -37,9 +37,5 @@ jobs: - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts - - name: Setup problem matchers for PHPUnit - if: matrix.phpVersion == '8.2' - run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - - name: Run tests run: ./vendor/bin/phpunit ./tests From cccf1b6a5839362950e5c7185f038fb617644888 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Thu, 4 Apr 2024 15:03:36 -0400 Subject: [PATCH 027/197] add comment about deprecated $dates property; remove doctrine/dbal --- composer.json | 1 - src/Database/Model.php | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3c9638db..27cd5cc2 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,6 @@ "ext-zip": "*", "assetic/framework": "^3.1", - "doctrine/dbal": "^3.6", "enshrined/svg-sanitize": "~0.16", "laravel/framework": "^11.0", "laravel/tinker": "^2.8.1", diff --git a/src/Database/Model.php b/src/Database/Model.php index 5e3d9fce..be501ddb 100644 --- a/src/Database/Model.php +++ b/src/Database/Model.php @@ -78,6 +78,8 @@ class Model extends EloquentModel implements ModelInterface */ public function __construct(array $attributes = []) { + //$dates property has been removed in Laravel 11 + //$this->casts = array_merge($this->casts ?? [], $this->dates); parent::__construct(); $this->bootNicerEvents(); From ec6d8a66e297a60864b31e50b3736e212618b28e Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Thu, 4 Apr 2024 15:56:16 -0400 Subject: [PATCH 028/197] remove unnecessary override of Illuminate\Cache\CacheManager --- composer.json | 1 + src/Cache/CacheManager.php | 10 ---------- src/Foundation/Application.php | 2 +- src/Halcyon/Builder.php | 2 +- src/Halcyon/Model.php | 6 +++--- 5 files changed, 6 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index 27cd5cc2..b8787c1e 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "ext-zip": "*", "assetic/framework": "^3.1", + "doctrine/dbal": "^3.6", "enshrined/svg-sanitize": "~0.16", "laravel/framework": "^11.0", "laravel/tinker": "^2.8.1", diff --git a/src/Cache/CacheManager.php b/src/Cache/CacheManager.php index 0a2249fd..b6b40d72 100644 --- a/src/Cache/CacheManager.php +++ b/src/Cache/CacheManager.php @@ -6,14 +6,4 @@ class CacheManager extends BaseCacheManager { - /** - * Get a cache store instance by name, wrapped in a repository. - * - * @param string|null $name - * @return \Illuminate\Contracts\Cache\Repository - */ - public function store($name = null) - { - return parent::store($name); - } } diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index 6f594345..b8e97e4c 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -480,7 +480,7 @@ public function registerCoreContainerAliases() $aliases = [ 'app' => [\Winter\Storm\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class], 'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class], - 'cache' => [\Winter\Storm\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class], + 'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class], 'cache.store' => [\Illuminate\Cache\Repository::class, \Illuminate\Contracts\Cache\Repository::class], 'config' => [\Illuminate\Config\Repository::class, \Illuminate\Contracts\Config\Repository::class], 'cookie' => [\Illuminate\Cookie\CookieJar::class, \Illuminate\Contracts\Cookie\Factory::class, \Illuminate\Contracts\Cookie\QueueingFactory::class], diff --git a/src/Halcyon/Builder.php b/src/Halcyon/Builder.php index de7313be..0b0f729e 100644 --- a/src/Halcyon/Builder.php +++ b/src/Halcyon/Builder.php @@ -703,7 +703,7 @@ protected function isCacheBusted($result) */ protected function getCache() { - $cache = $this->model->getCacheManager()->driver($this->cacheDriver); + $cache = $this->model->getCacheManager()->store($this->cacheDriver); return $this->cacheTags ? $cache->tags($this->cacheTags) : $cache; } diff --git a/src/Halcyon/Model.php b/src/Halcyon/Model.php index 1458906a..f51c3ac4 100644 --- a/src/Halcyon/Model.php +++ b/src/Halcyon/Model.php @@ -109,7 +109,7 @@ class Model extends Extendable implements ModelInterface, ArrayAccess, Arrayable /** * The cache manager instance. * - * @var \Winter\Storm\Cache\CacheManager|null + * @var \Illuminate\Cache\CacheManager|null */ protected static $cache; @@ -1512,7 +1512,7 @@ public static function unsetEventDispatcher() /** * Get the cache manager instance. * - * @return \Winter\Storm\Cache\CacheManager|null + * @return \Illuminate\Cache\CacheManager|null */ public static function getCacheManager() { @@ -1522,7 +1522,7 @@ public static function getCacheManager() /** * Set the cache manager instance. * - * @param \Winter\Storm\Cache\CacheManager $cache + * @param \Illuminate\Cache\CacheManager $cache * @return void */ public static function setCacheManager($cache) From 388fe35193b2528f458c96611be2d58591848e55 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Thu, 4 Apr 2024 16:16:19 -0400 Subject: [PATCH 029/197] no need to add dates to cast, this is handled in HasAttribute trait --- src/Database/Model.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Database/Model.php b/src/Database/Model.php index be501ddb..5e3d9fce 100644 --- a/src/Database/Model.php +++ b/src/Database/Model.php @@ -78,8 +78,6 @@ class Model extends EloquentModel implements ModelInterface */ public function __construct(array $attributes = []) { - //$dates property has been removed in Laravel 11 - //$this->casts = array_merge($this->casts ?? [], $this->dates); parent::__construct(); $this->bootNicerEvents(); From f71ad1ea68d298b679a8691a185fe72ba3692148 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Thu, 4 Apr 2024 16:46:25 -0400 Subject: [PATCH 030/197] ignore phpstan error on abstract base class --- composer.json | 1 - phpstan-baseline.neon | 5 +++++ src/Halcyon/Builder.php | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index b8787c1e..27cd5cc2 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,6 @@ "ext-zip": "*", "assetic/framework": "^3.1", - "doctrine/dbal": "^3.6", "enshrined/svg-sanitize": "~0.16", "laravel/framework": "^11.0", "laravel/tinker": "^2.8.1", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ace12018..d85ee361 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -764,3 +764,8 @@ parameters: message: "#^Call to function is_null\\(\\) with Closure will always evaluate to false\\.$#" count: 1 path: src/Validation/Factory.php + + - + message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Cache\\\\Repository\\:\\:tags\\(\\).$#" + count: 1 + path: src/Halcyon/Builder.php diff --git a/src/Halcyon/Builder.php b/src/Halcyon/Builder.php index 0b0f729e..de7313be 100644 --- a/src/Halcyon/Builder.php +++ b/src/Halcyon/Builder.php @@ -703,7 +703,7 @@ protected function isCacheBusted($result) */ protected function getCache() { - $cache = $this->model->getCacheManager()->store($this->cacheDriver); + $cache = $this->model->getCacheManager()->driver($this->cacheDriver); return $this->cacheTags ? $cache->tags($this->cacheTags) : $cache; } From 60f676f1bf8df3047b07e586708926163062dbee Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Thu, 4 Apr 2024 16:50:20 -0400 Subject: [PATCH 031/197] remove ignore rule --- phpstan-baseline.neon | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d85ee361..ace12018 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -764,8 +764,3 @@ parameters: message: "#^Call to function is_null\\(\\) with Closure will always evaluate to false\\.$#" count: 1 path: src/Validation/Factory.php - - - - message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Cache\\\\Repository\\:\\:tags\\(\\).$#" - count: 1 - path: src/Halcyon/Builder.php From 8dcf119b3480b5aed79470b11cac4fae4410824c Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 5 Apr 2024 17:15:02 -0400 Subject: [PATCH 032/197] restore migration behavior --- src/Database/Connections/MySqlConnection.php | 3 +- .../Connections/PostgresConnection.php | 3 +- src/Database/Connections/SQLiteConnection.php | 3 +- .../Connections/SqlServerConnection.php | 3 +- src/Database/Connectors/ConnectionFactory.php | 3 + .../Schema/Grammars/MariaDbGrammar.php | 7 + src/Database/Schema/Grammars/MySqlGrammar.php | 55 + .../Schema/Grammars/PostgresGrammar.php | 53 + .../Schema/Grammars/SQLiteGrammar.php | 117 ++ .../Schema/Grammars/SqlServerGrammar.php | 49 + .../Grammars/MySqlSchemaGrammarTest.php | 1459 +++++++++++++++++ .../Grammars/PostgresSchemaGrammarTest.php | 1238 ++++++++++++++ .../Grammars/SQLiteSchemaGrammarTest.php | 937 +++++++++++ .../Grammars/SqlServerSchemaGrammarTest.php | 932 +++++++++++ 14 files changed, 4858 insertions(+), 4 deletions(-) create mode 100755 src/Database/Schema/Grammars/MariaDbGrammar.php create mode 100755 src/Database/Schema/Grammars/MySqlGrammar.php create mode 100755 src/Database/Schema/Grammars/PostgresGrammar.php create mode 100755 src/Database/Schema/Grammars/SQLiteGrammar.php create mode 100755 src/Database/Schema/Grammars/SqlServerGrammar.php create mode 100644 tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php create mode 100644 tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php create mode 100644 tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php create mode 100644 tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php diff --git a/src/Database/Connections/MySqlConnection.php b/src/Database/Connections/MySqlConnection.php index 39185ad4..e8f80901 100644 --- a/src/Database/Connections/MySqlConnection.php +++ b/src/Database/Connections/MySqlConnection.php @@ -3,7 +3,8 @@ use PDO; use Illuminate\Database\Schema\MySqlBuilder; use Illuminate\Database\Query\Processors\MySqlProcessor; -use Illuminate\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar; + +use Winter\Storm\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar; use Winter\Storm\Database\Query\Grammars\MySqlGrammar as QueryGrammar; /** diff --git a/src/Database/Connections/PostgresConnection.php b/src/Database/Connections/PostgresConnection.php index 16bc0524..e1b11dc0 100644 --- a/src/Database/Connections/PostgresConnection.php +++ b/src/Database/Connections/PostgresConnection.php @@ -2,8 +2,9 @@ use Illuminate\Database\Schema\PostgresBuilder; use Illuminate\Database\Query\Processors\PostgresProcessor; + use Winter\Storm\Database\Query\Grammars\PostgresGrammar as QueryGrammar; -use Illuminate\Database\Schema\Grammars\PostgresGrammar as SchemaGrammar; +use Winter\Storm\Database\Schema\Grammars\PostgresGrammar as SchemaGrammar; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar diff --git a/src/Database/Connections/SQLiteConnection.php b/src/Database/Connections/SQLiteConnection.php index cda9f395..0e22ff93 100644 --- a/src/Database/Connections/SQLiteConnection.php +++ b/src/Database/Connections/SQLiteConnection.php @@ -2,8 +2,9 @@ use Illuminate\Database\Schema\SQLiteBuilder; use Illuminate\Database\Query\Processors\SQLiteProcessor; + use Winter\Storm\Database\Query\Grammars\SQLiteGrammar as QueryGrammar; -use Illuminate\Database\Schema\Grammars\SQLiteGrammar as SchemaGrammar; +use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar as SchemaGrammar; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar diff --git a/src/Database/Connections/SqlServerConnection.php b/src/Database/Connections/SqlServerConnection.php index 1a764669..640e3181 100644 --- a/src/Database/Connections/SqlServerConnection.php +++ b/src/Database/Connections/SqlServerConnection.php @@ -5,7 +5,8 @@ use Throwable; use Illuminate\Database\Schema\SqlServerBuilder; use Illuminate\Database\Query\Processors\SqlServerProcessor; -use Illuminate\Database\Schema\Grammars\SqlServerGrammar as SchemaGrammar; + +use Winter\Storm\Database\Schema\Grammars\SqlServerGrammar as SchemaGrammar; use Winter\Storm\Database\Query\Grammars\SqlServerGrammar as QueryGrammar; /** diff --git a/src/Database/Connectors/ConnectionFactory.php b/src/Database/Connectors/ConnectionFactory.php index 62963003..d9be1dc6 100644 --- a/src/Database/Connectors/ConnectionFactory.php +++ b/src/Database/Connectors/ConnectionFactory.php @@ -3,6 +3,7 @@ use Illuminate\Support\Arr; use Illuminate\Database\Connectors\ConnectionFactory as ConnectionFactoryBase; use Winter\Storm\Database\Connections\Connection; +use Winter\Storm\Database\Connections\MariaDbConnection; use Winter\Storm\Database\Connections\MySqlConnection; use Winter\Storm\Database\Connections\SQLiteConnection; use Winter\Storm\Database\Connections\PostgresConnection; @@ -57,6 +58,8 @@ protected function createConnection($driver, $connection, $database, $prefix = ' } switch ($driver) { + case 'mariadb': + return new MariaDbConnection($connection, $database, $prefix, $config); case 'mysql': return new MySqlConnection($connection, $database, $prefix, $config); case 'pgsql': diff --git a/src/Database/Schema/Grammars/MariaDbGrammar.php b/src/Database/Schema/Grammars/MariaDbGrammar.php new file mode 100755 index 00000000..9a574623 --- /dev/null +++ b/src/Database/Schema/Grammars/MariaDbGrammar.php @@ -0,0 +1,7 @@ +getSchemaBuilder()->getColumns($blueprint->getTable())); + + foreach ($blueprint->getChangedColumns() as $column) { + $sql = sprintf('%s %s%s %s', + is_null($column->renameTo) ? 'modify' : 'change', + $this->wrap($column), + is_null($column->renameTo) ? '' : ' '.$this->wrap($column->renameTo), + $this->getType($column) + ); + + $oldColum = new Fluent($prevColumns->where('name', $name)->first()); + $columns[] = $this->addLegacyModifiers($sql, $blueprint, $column, $oldColumn); + } + + return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); + } + + protected function addLegacyModifiers($sql, Blueprint $blueprint, Fluent $column, Fluent $oldColumn) + { + foreach ($this->modifiers as $modifier) { + if (method_exists($this, $method = "modify{$modifier}")) { + $mod = strtolower($modifier); + $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $sql .= $this->{$method}($blueprint, $col); + } + } + + return $sql; + } + +} diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php new file mode 100755 index 00000000..e1f1d0b7 --- /dev/null +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -0,0 +1,53 @@ +getSchemaBuilder()->getColumns($blueprint->getTable())); + + foreach ($blueprint->getChangedColumns() as $column) { + $changes = ['type '.$this->getType($column).$this->modifyCollate($blueprint, $column)]; + + $oldColum = new Fluent($prevColumns->where('name', $name)->first()); + foreach ($this->modifiers as $modifier) { + if ($modifier === 'Collate') { + continue; + } + + if (method_exists($this, $method = "modify{$modifier}")) { + $mod = strtolower($modifier); + $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $constraints = (array) $this->{$method}($blueprint, $col); + + foreach ($constraints as $constraint) { + $changes[] = $constraint; + } + } + } + + $columns[] = implode(', ', $this->prefixArray('alter column '.$this->wrap($column), $changes)); + } + + return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); + } + +} diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php new file mode 100755 index 00000000..f411649c --- /dev/null +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -0,0 +1,117 @@ +getSchemaBuilder(); + $table = $blueprint->getTable(); + + $changedColumns = collect($blueprint->getChangedColumns()); + $columnNames = []; + $autoIncrementColumn = null; + + $columns = collect($schema->getColumns($table)) + ->map(function ($column) use ($blueprint, $changedColumns, &$columnNames, &$autoIncrementColumn) { + $column = $changedColumns->first(fn ($col) => $col->name === $column['name'], $column); + + if ($column instanceof Fluent) { + $name = $this->wrap($column); + $autoIncrementColumn = $column->autoIncrement ? $column->name : $autoIncrementColumn; + + if (is_null($column->virtualAs) && is_null($column->virtualAsJson) && + is_null($column->storedAs) && is_null($column->storedAsJson)) { + $columnNames[] = $name; + } + + return $this->addModifiers($name.' '.$this->getType($column), $blueprint, $column); + } else { + $name = $this->wrap($column['name']); + $autoIncrementColumn = $column['auto_increment'] ? $column['name'] : $autoIncrementColumn; + $isGenerated = ! is_null($column['generation']); + + if (! $isGenerated) { + $columnNames[] = $name; + } + + return $this->addModifiers($name.' '.$column['type'], $blueprint, + new ColumnDefinition([ + 'change' => true, + 'type' => $column['type_name'], + 'nullable' => $column['nullable'], + 'default' => $column['default'] ? new Expression($column['default']) : null, + 'autoIncrement' => $column['auto_increment'], + 'collation' => $column['collation'], + 'comment' => $column['comment'], + 'virtualAs' => $isGenerated && $column['generation']['type'] === 'virtual' + ? $column['generation']['expression'] : null, + 'storedAs' => $isGenerated && $column['generation']['type'] === 'stored' + ? $column['generation']['expression'] : null, + ]) + ); + } + })->all(); + + $foreignKeys = collect($schema->getForeignKeys($table))->map(fn ($foreignKey) => new ForeignKeyDefinition([ + 'columns' => $foreignKey['columns'], + 'on' => $foreignKey['foreign_table'], + 'references' => $foreignKey['foreign_columns'], + 'onUpdate' => $foreignKey['on_update'], + 'onDelete' => $foreignKey['on_delete'], + ]))->all(); + + [$primary, $indexes] = collect($schema->getIndexes($table))->map(fn ($index) => new IndexDefinition([ + 'name' => match (true) { + $index['primary'] => 'primary', + $index['unique'] => 'unique', + default => 'index', + }, + 'index' => $index['name'], + 'columns' => $index['columns'], + ]))->partition(fn ($index) => $index->name === 'primary'); + + $indexes = collect($indexes)->reject(fn ($index) => str_starts_with('sqlite_', $index->index))->map( + fn ($index) => $this->{'compile'.ucfirst($index->name)}($blueprint, $index) + )->all(); + + $tempTable = $this->wrap('__temp__'.$blueprint->getPrefix().$table); + $table = $this->wrapTable($blueprint); + $columnNames = implode(', ', $columnNames); + + $foreignKeyConstraintsEnabled = $connection->scalar('pragma foreign_keys'); + + return array_filter(array_merge([ + $foreignKeyConstraintsEnabled ? $this->compileDisableForeignKeyConstraints() : null, + sprintf('create table %s (%s%s%s)', + $tempTable, + implode(', ', $columns), + $this->addForeignKeys($foreignKeys), + $autoIncrementColumn ? '' : $this->addPrimaryKeys($primary->first()) + ), + sprintf('insert into %s (%s) select %s from %s', $tempTable, $columnNames, $columnNames, $table), + sprintf('drop table %s', $table), + sprintf('alter table %s rename to %s', $tempTable, $table), + ], $indexes, [$foreignKeyConstraintsEnabled ? $this->compileEnableForeignKeyConstraints() : null])); + } +} diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php new file mode 100755 index 00000000..01c885c7 --- /dev/null +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -0,0 +1,49 @@ +compileDropDefaultConstraint($blueprint, $command)]; + $oldColumns = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())); + + foreach ($blueprint->getChangedColumns() as $column) { + $sql = sprintf('alter table %s alter column %s %s', + $this->wrapTable($blueprint), + $this->wrap($column), + $this->getType($column) + ); + + $oldColum = new Fluent($prevColumns->where('name', $name)->first()); + + foreach ($this->modifiers as $modifier) { + if (method_exists($this, $method = "modify{$modifier}")) { + $mod = strtolower($modifier); + $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $sql .= $this->{$method}($blueprint, $col); + } + } + + $changes[] = $sql; + } + + return $changes; + } +} diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php new file mode 100644 index 00000000..167637c7 --- /dev/null +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -0,0 +1,1459 @@ +create(); + $blueprint->increments('id'); + $blueprint->string('email'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->increments('id'); + $blueprint->string('email'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `id` int unsigned not null auto_increment primary key, add `email` varchar(255) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->uuid('id')->primary(); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table `users` (`id` char(36) not null, primary key (`id`))', $statements[0]); + } + + public function testAutoIncrementStartingValue() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id')->startingValue(1000); + $blueprint->string('email'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); + $this->assertSame('alter table `users` auto_increment = 1000', $statements[1]); + } + + public function testAddColumnsWithMultipleAutoIncrementStartingValue() + { + $blueprint = new Blueprint('users'); + $blueprint->id()->from(100); + $blueprint->string('name')->from(200); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals([ + 'alter table `users` add `id` bigint unsigned not null auto_increment primary key, add `name` varchar(255) not null', + 'alter table `users` auto_increment = 100', + ], $statements); + } + + public function testEngineCreateTable() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id'); + $blueprint->string('email'); + $blueprint->engine('InnoDB'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate 'utf8_unicode_ci' engine = InnoDB", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id'); + $blueprint->string('email'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn('InnoDB'); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate 'utf8_unicode_ci' engine = InnoDB", $statements[0]); + } + + public function testCharsetCollationCreateTable() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id'); + $blueprint->string('email'); + $blueprint->charset('utf8mb4'); + $blueprint->collation('utf8mb4_unicode_ci'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8mb4 collate 'utf8mb4_unicode_ci'", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id'); + $blueprint->string('email')->charset('utf8mb4')->collation('utf8mb4_unicode_ci'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) character set utf8mb4 collate 'utf8mb4_unicode_ci' not null) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); + } + + public function testBasicCreateTableWithPrefix() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id'); + $blueprint->string('email'); + $grammar = $this->getGrammar(); + $grammar->setTablePrefix('prefix_'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $grammar); + + $this->assertCount(1, $statements); + $this->assertSame('create table `prefix_users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null)', $statements[0]); + } + + public function testCreateTemporaryTable() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->temporary(); + $blueprint->increments('id'); + $blueprint->string('email'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create temporary table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null)', $statements[0]); + } + + public function testDropTable() + { + $blueprint = new Blueprint('users'); + $blueprint->drop(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop table `users`', $statements[0]); + } + + public function testDropTableIfExists() + { + $blueprint = new Blueprint('users'); + $blueprint->dropIfExists(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop table if exists `users`', $statements[0]); + } + + public function testDropColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->dropColumn('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop `foo`', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dropColumn(['foo', 'bar']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop `foo`, drop `bar`', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dropColumn('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop `foo`, drop `bar`', $statements[0]); + } + + public function testDropPrimary() + { + $blueprint = new Blueprint('users'); + $blueprint->dropPrimary(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop primary key', $statements[0]); + } + + public function testDropUnique() + { + $blueprint = new Blueprint('users'); + $blueprint->dropUnique('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop index `foo`', $statements[0]); + } + + public function testDropIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->dropIndex('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop index `foo`', $statements[0]); + } + + public function testDropSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->dropSpatialIndex(['coordinates']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` drop index `geo_coordinates_spatialindex`', $statements[0]); + } + + public function testDropForeign() + { + $blueprint = new Blueprint('users'); + $blueprint->dropForeign('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop foreign key `foo`', $statements[0]); + } + + public function testDropTimestamps() + { + $blueprint = new Blueprint('users'); + $blueprint->dropTimestamps(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop `created_at`, drop `updated_at`', $statements[0]); + } + + public function testDropTimestampsTz() + { + $blueprint = new Blueprint('users'); + $blueprint->dropTimestampsTz(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` drop `created_at`, drop `updated_at`', $statements[0]); + } + + public function testDropMorphs() + { + $blueprint = new Blueprint('photos'); + $blueprint->dropMorphs('imageable'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('alter table `photos` drop index `photos_imageable_type_imageable_id_index`', $statements[0]); + $this->assertSame('alter table `photos` drop `imageable_type`, drop `imageable_id`', $statements[1]); + } + + public function testRenameTable() + { + $blueprint = new Blueprint('users'); + $blueprint->rename('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('rename table `users` to `foo`', $statements[0]); + } + + public function testRenameIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->renameIndex('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` rename index `foo` to `bar`', $statements[0]); + } + + public function testAddingPrimaryKey() + { + $blueprint = new Blueprint('users'); + $blueprint->primary('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add primary key (`foo`)', $statements[0]); + } + + public function testAddingPrimaryKeyWithAlgorithm() + { + $blueprint = new Blueprint('users'); + $blueprint->primary('foo', 'bar', 'hash'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add primary key using hash(`foo`)', $statements[0]); + } + + public function testAddingUniqueKey() + { + $blueprint = new Blueprint('users'); + $blueprint->unique('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add unique `bar`(`foo`)', $statements[0]); + } + + public function testAddingIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->index(['foo', 'bar'], 'baz'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add index `baz`(`foo`, `bar`)', $statements[0]); + } + + public function testAddingIndexWithAlgorithm() + { + $blueprint = new Blueprint('users'); + $blueprint->index(['foo', 'bar'], 'baz', 'hash'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add index `baz` using hash(`foo`, `bar`)', $statements[0]); + } + + public function testAddingFulltextIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->fulltext('body'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add fulltext `users_body_fulltext`(`body`)', $statements[0]); + } + + public function testAddingSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->spatialIndex('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add spatial index `geo_coordinates_spatialindex`(`coordinates`)', $statements[0]); + } + + public function testAddingFluentSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'point')->spatialIndex(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('alter table `geo` add spatial index `geo_coordinates_spatialindex`(`coordinates`)', $statements[1]); + } + + public function testAddingRawIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->rawIndex('(function(column))', 'raw_index'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add index `raw_index`((function(column)))', $statements[0]); + } + + public function testAddingForeignKey() + { + $blueprint = new Blueprint('users'); + $blueprint->foreign('foo_id')->references('id')->on('orders'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add constraint `users_foo_id_foreign` foreign key (`foo_id`) references `orders` (`id`)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->foreign('foo_id')->references('id')->on('orders')->cascadeOnDelete(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add constraint `users_foo_id_foreign` foreign key (`foo_id`) references `orders` (`id`) on delete cascade', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->foreign('foo_id')->references('id')->on('orders')->cascadeOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add constraint `users_foo_id_foreign` foreign key (`foo_id`) references `orders` (`id`) on update cascade', $statements[0]); + } + + public function testAddingIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->increments('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `id` int unsigned not null auto_increment primary key', $statements[0]); + } + + public function testAddingSmallIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->smallIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `id` smallint unsigned not null auto_increment primary key', $statements[0]); + } + + public function testAddingID() + { + $blueprint = new Blueprint('users'); + $blueprint->id(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `id` bigint unsigned not null auto_increment primary key', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->id('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` bigint unsigned not null auto_increment primary key', $statements[0]); + } + + public function testAddingForeignID() + { + $blueprint = new Blueprint('users'); + $foreignId = $blueprint->foreignId('foo'); + $blueprint->foreignId('company_id')->constrained(); + $blueprint->foreignId('laravel_idea_id')->constrained(); + $blueprint->foreignId('team_id')->references('id')->on('teams'); + $blueprint->foreignId('team_column_id')->constrained('teams'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); + $this->assertSame([ + 'alter table `users` add `foo` bigint unsigned not null, add `company_id` bigint unsigned not null, add `laravel_idea_id` bigint unsigned not null, add `team_id` bigint unsigned not null, add `team_column_id` bigint unsigned not null', + 'alter table `users` add constraint `users_company_id_foreign` foreign key (`company_id`) references `companies` (`id`)', + 'alter table `users` add constraint `users_laravel_idea_id_foreign` foreign key (`laravel_idea_id`) references `laravel_ideas` (`id`)', + 'alter table `users` add constraint `users_team_id_foreign` foreign key (`team_id`) references `teams` (`id`)', + 'alter table `users` add constraint `users_team_column_id_foreign` foreign key (`team_column_id`) references `teams` (`id`)', + ], $statements); + } + + public function testAddingForeignIdSpecifyingIndexNameInConstraint() + { + $blueprint = new Blueprint('users'); + $blueprint->foreignId('company_id')->constrained(indexName: 'my_index'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertSame([ + 'alter table `users` add `company_id` bigint unsigned not null', + 'alter table `users` add constraint `my_index` foreign key (`company_id`) references `companies` (`id`)', + ], $statements); + } + + public function testAddingBigIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->bigIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `id` bigint unsigned not null auto_increment primary key', $statements[0]); + } + + public function testAddingColumnInTableFirst() + { + $blueprint = new Blueprint('users'); + $blueprint->string('name')->first(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `name` varchar(255) not null first', $statements[0]); + } + + public function testAddingColumnAfterAnotherColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->string('name')->after('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `name` varchar(255) not null after `foo`', $statements[0]); + } + + public function testAddingMultipleColumnsAfterAnotherColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->after('foo', function ($blueprint) { + $blueprint->string('one'); + $blueprint->string('two'); + }); + $blueprint->string('three'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `one` varchar(255) not null after `foo`, add `two` varchar(255) not null after `one`, add `three` varchar(255) not null', $statements[0]); + } + + public function testAddingGeneratedColumn() + { + $blueprint = new Blueprint('products'); + $blueprint->integer('price'); + $blueprint->integer('discounted_virtual')->virtualAs('price - 5'); + $blueprint->integer('discounted_stored')->storedAs('price - 5'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `products` add `price` int not null, add `discounted_virtual` int as (price - 5), add `discounted_stored` int as (price - 5) stored', $statements[0]); + + $blueprint = new Blueprint('products'); + $blueprint->integer('price'); + $blueprint->integer('discounted_virtual')->virtualAs('price - 5')->nullable(false); + $blueprint->integer('discounted_stored')->storedAs('price - 5')->nullable(false); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `products` add `price` int not null, add `discounted_virtual` int as (price - 5) not null, add `discounted_stored` int as (price - 5) stored not null', $statements[0]); + } + + public function testAddingGeneratedColumnWithCharset() + { + $blueprint = new Blueprint('links'); + $blueprint->string('url', 2083)->charset('ascii'); + $blueprint->string('url_hash_virtual', 64)->virtualAs('sha2(url, 256)')->charset('ascii'); + $blueprint->string('url_hash_stored', 64)->storedAs('sha2(url, 256)')->charset('ascii'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `links` add `url` varchar(2083) character set ascii not null, add `url_hash_virtual` varchar(64) character set ascii as (sha2(url, 256)), add `url_hash_stored` varchar(64) character set ascii as (sha2(url, 256)) stored', $statements[0]); + } + + public function testAddingGeneratedColumnByExpression() + { + $blueprint = new Blueprint('products'); + $blueprint->integer('price'); + $blueprint->integer('discounted_virtual')->virtualAs(new Expression('price - 5')); + $blueprint->integer('discounted_stored')->storedAs(new Expression('price - 5')); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `products` add `price` int not null, add `discounted_virtual` int as (price - 5), add `discounted_stored` int as (price - 5) stored', $statements[0]); + } + + public function testAddingInvisibleColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->string('secret', 64)->nullable(false)->invisible(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `secret` varchar(64) not null invisible', $statements[0]); + } + + public function testAddingString() + { + $blueprint = new Blueprint('users'); + $blueprint->string('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` varchar(255) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` varchar(100) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100)->nullable()->default('bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` varchar(100) null default \'bar\'', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100)->nullable()->default(new Expression('CURRENT TIMESTAMP')); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` varchar(100) null default CURRENT TIMESTAMP', $statements[0]); + } + + public function testAddingText() + { + $blueprint = new Blueprint('users'); + $blueprint->text('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` text not null', $statements[0]); + } + + public function testAddingBigInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->bigInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` bigint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->bigInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` bigint not null auto_increment primary key', $statements[0]); + } + + public function testAddingInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->integer('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` int not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->integer('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` int not null auto_increment primary key', $statements[0]); + } + + public function testAddingIncrementsWithStartingValues() + { + $blueprint = new Blueprint('users'); + $blueprint->id()->startingValue(1000); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('alter table `users` add `id` bigint unsigned not null auto_increment primary key', $statements[0]); + $this->assertSame('alter table `users` auto_increment = 1000', $statements[1]); + } + + public function testAddingMediumInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->mediumInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` mediumint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->mediumInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` mediumint not null auto_increment primary key', $statements[0]); + } + + public function testAddingSmallInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->smallInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` smallint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->smallInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` smallint not null auto_increment primary key', $statements[0]); + } + + public function testAddingTinyInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->tinyInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` tinyint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->tinyInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` tinyint not null auto_increment primary key', $statements[0]); + } + + public function testAddingFloat() + { + $blueprint = new Blueprint('users'); + $blueprint->float('foo', 5); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` float(5) not null', $statements[0]); + } + + public function testAddingDouble() + { + $blueprint = new Blueprint('users'); + $blueprint->double('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` double not null', $statements[0]); + } + + public function testAddingDecimal() + { + $blueprint = new Blueprint('users'); + $blueprint->decimal('foo', 5, 2); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` decimal(5, 2) not null', $statements[0]); + } + + public function testAddingBoolean() + { + $blueprint = new Blueprint('users'); + $blueprint->boolean('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` tinyint(1) not null', $statements[0]); + } + + public function testAddingEnum() + { + $blueprint = new Blueprint('users'); + $blueprint->enum('role', ['member', 'admin']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `role` enum(\'member\', \'admin\') not null', $statements[0]); + } + + public function testAddingSet() + { + $blueprint = new Blueprint('users'); + $blueprint->set('role', ['member', 'admin']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `role` set(\'member\', \'admin\') not null', $statements[0]); + } + + public function testAddingJson() + { + $blueprint = new Blueprint('users'); + $blueprint->json('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` json not null', $statements[0]); + } + + public function testAddingJsonb() + { + $blueprint = new Blueprint('users'); + $blueprint->jsonb('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` json not null', $statements[0]); + } + + public function testAddingDate() + { + $blueprint = new Blueprint('users'); + $blueprint->date('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` date not null', $statements[0]); + } + + public function testAddingYear() + { + $blueprint = new Blueprint('users'); + $blueprint->year('birth_year'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `birth_year` year not null', $statements[0]); + } + + public function testAddingDateTime() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime(1) not null', $statements[0]); + } + + public function testAddingDateTimeWithDefaultCurrent() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo')->useCurrent(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime not null default CURRENT_TIMESTAMP', $statements[0]); + } + + public function testAddingDateTimeWithOnUpdateCurrent() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo')->useCurrentOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime not null on update CURRENT_TIMESTAMP', $statements[0]); + } + + public function testAddingDateTimeWithDefaultCurrentAndOnUpdateCurrent() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo')->useCurrent()->useCurrentOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP', $statements[0]); + } + + public function testAddingDateTimeWithDefaultCurrentOnUpdateCurrentAndPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('foo', 3)->useCurrent()->useCurrentOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime(3) not null default CURRENT_TIMESTAMP(3) on update CURRENT_TIMESTAMP(3)', $statements[0]); + } + + public function testAddingDateTimeTz() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('foo', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime(1) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` datetime not null', $statements[0]); + } + + public function testAddingTime() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` time not null', $statements[0]); + } + + public function testAddingTimeWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` time(1) not null', $statements[0]); + } + + public function testAddingTimeTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` time not null', $statements[0]); + } + + public function testAddingTimeTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` time(1) not null', $statements[0]); + } + + public function testAddingTimestamp() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp not null', $statements[0]); + } + + public function testAddingTimestampWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp(1) not null', $statements[0]); + } + + public function testAddingTimestampWithDefault() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at')->default('2015-07-22 11:43:17'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame("alter table `users` add `created_at` timestamp not null default '2015-07-22 11:43:17'", $statements[0]); + } + + public function testAddingTimestampWithDefaultCurrentSpecifyingPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at', 1)->useCurrent(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp(1) not null default CURRENT_TIMESTAMP(1)', $statements[0]); + } + + public function testAddingTimestampWithOnUpdateCurrentSpecifyingPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at', 1)->useCurrentOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp(1) not null on update CURRENT_TIMESTAMP(1)', $statements[0]); + } + + public function testAddingTimestampWithDefaultCurrentAndOnUpdateCurrentSpecifyingPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at', 1)->useCurrent()->useCurrentOnUpdate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp(1) not null default CURRENT_TIMESTAMP(1) on update CURRENT_TIMESTAMP(1)', $statements[0]); + } + + public function testAddingTimestampTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp not null', $statements[0]); + } + + public function testAddingTimestampTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp(1) not null', $statements[0]); + } + + public function testAddingTimeStampTzWithDefault() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at')->default('2015-07-22 11:43:17'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame("alter table `users` add `created_at` timestamp not null default '2015-07-22 11:43:17'", $statements[0]); + } + + public function testAddingTimestamps() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamps(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp null, add `updated_at` timestamp null', $statements[0]); + } + + public function testAddingTimestampsTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampsTz(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `created_at` timestamp null, add `updated_at` timestamp null', $statements[0]); + } + + public function testAddingRememberToken() + { + $blueprint = new Blueprint('users'); + $blueprint->rememberToken(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `remember_token` varchar(100) null', $statements[0]); + } + + public function testAddingBinary() + { + $blueprint = new Blueprint('users'); + $blueprint->binary('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` blob not null', $statements[0]); + } + + public function testAddingUuid() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` char(36) not null', $statements[0]); + } + + public function testAddingUuidDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `uuid` char(36) not null', $statements[0]); + } + + public function testAddingForeignUuid() + { + $blueprint = new Blueprint('users'); + $foreignUuid = $blueprint->foreignUuid('foo'); + $blueprint->foreignUuid('company_id')->constrained(); + $blueprint->foreignUuid('laravel_idea_id')->constrained(); + $blueprint->foreignUuid('team_id')->references('id')->on('teams'); + $blueprint->foreignUuid('team_column_id')->constrained('teams'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignUuid); + $this->assertSame([ + 'alter table `users` add `foo` char(36) not null, add `company_id` char(36) not null, add `laravel_idea_id` char(36) not null, add `team_id` char(36) not null, add `team_column_id` char(36) not null', + 'alter table `users` add constraint `users_company_id_foreign` foreign key (`company_id`) references `companies` (`id`)', + 'alter table `users` add constraint `users_laravel_idea_id_foreign` foreign key (`laravel_idea_id`) references `laravel_ideas` (`id`)', + 'alter table `users` add constraint `users_team_id_foreign` foreign key (`team_id`) references `teams` (`id`)', + 'alter table `users` add constraint `users_team_column_id_foreign` foreign key (`team_column_id`) references `teams` (`id`)', + ], $statements); + } + + public function testAddingIpAddress() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` varchar(45) not null', $statements[0]); + } + + public function testAddingIpAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `ip_address` varchar(45) not null', $statements[0]); + } + + public function testAddingMacAddress() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `foo` varchar(17) not null', $statements[0]); + } + + public function testAddingMacAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `mac_address` varchar(17) not null', $statements[0]); + } + + public function testAddingGeometry() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` geometry not null', $statements[0]); + } + + public function testAddingGeography() + { + $blueprint = new Blueprint('geo'); + $blueprint->geography('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` geometry srid 4326 not null', $statements[0]); + } + + public function testAddingPoint() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'point'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` point not null', $statements[0]); + } + + public function testAddingPointWithSrid() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'point', 4326); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` point srid 4326 not null', $statements[0]); + } + + public function testAddingPointWithSridColumn() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'point', 4326)->after('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` point srid 4326 not null after `id`', $statements[0]); + } + + public function testAddingLineString() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'linestring'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` linestring not null', $statements[0]); + } + + public function testAddingPolygon() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'polygon'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` polygon not null', $statements[0]); + } + + public function testAddingGeometryCollection() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'geometrycollection'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` geometrycollection not null', $statements[0]); + } + + public function testAddingMultiPoint() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'multipoint'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` multipoint not null', $statements[0]); + } + + public function testAddingMultiLineString() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'multilinestring'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` multilinestring not null', $statements[0]); + } + + public function testAddingMultiPolygon() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'multipolygon'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `geo` add `coordinates` multipolygon not null', $statements[0]); + } + + public function testAddingComment() + { + $blueprint = new Blueprint('users'); + $blueprint->string('foo')->comment("Escape ' when using words like it's"); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("alter table `users` add `foo` varchar(255) not null comment 'Escape \\' when using words like it\\'s'", $statements[0]); + } + + public function testCreateDatabase() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4_foo'); + $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci_foo'); + + $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); + + $this->assertSame( + 'create database `my_database_a` default character set `utf8mb4_foo` default collate `utf8mb4_unicode_ci_foo`', + $statement + ); + + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4_bar'); + $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci_bar'); + + $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); + + $this->assertSame( + 'create database `my_database_b` default character set `utf8mb4_bar` default collate `utf8mb4_unicode_ci_bar`', + $statement + ); + } + + public function testCreateTableWithVirtualAsColumn() + { + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_column'); + $blueprint->string('my_other_column')->virtualAs('my_column'); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_column` varchar(255) not null, `my_other_column` varchar(255) as (my_column)) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\"'))))", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute->nested'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\".\"nested\"'))))", $statements[0]); + } + + public function testCreateTableWithVirtualAsColumnWhenJsonColumnHasArrayKey() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column')->virtualAsJson('my_json_column->foo[0][1]'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"foo\"[0][1]'))))", $statements[0]); + } + + public function testCreateTableWithStoredAsColumn() + { + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); + $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_column'); + $blueprint->string('my_other_column')->storedAs('my_column'); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_column` varchar(255) not null, `my_other_column` varchar(255) as (my_column) stored) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\"'))) stored)", $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute->nested'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\".\"nested\"'))) stored)", $statements[0]); + } + + public function testDropDatabaseIfExists() + { + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); + + $this->assertSame( + 'drop database if exists `my_database_a`', + $statement + ); + + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); + + $this->assertSame( + 'drop database if exists `my_database_b`', + $statement + ); + } + + public function testDropAllTables() + { + $statement = $this->getGrammar()->compileDropAllTables(['alpha', 'beta', 'gamma']); + + $this->assertSame('drop table `alpha`,`beta`,`gamma`', $statement); + } + + public function testDropAllViews() + { + $statement = $this->getGrammar()->compileDropAllViews(['alpha', 'beta', 'gamma']); + + $this->assertSame('drop view `alpha`,`beta`,`gamma`', $statement); + } + + public function testGrammarsAreMacroable() + { + // compileReplace macro. + $this->getGrammar()::macro('compileReplace', function () { + return true; + }); + + $c = $this->getGrammar()::compileReplace(); + + $this->assertTrue($c); + } + + protected function getConnection() + { + return m::mock(Connection::class); + } + + public function getGrammar() + { + return new MySqlGrammar; + } +} diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php new file mode 100644 index 00000000..96b1222b --- /dev/null +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -0,0 +1,1238 @@ +create(); + $blueprint->increments('id'); + $blueprint->string('email'); + $blueprint->string('name')->collation('nb_NO.utf8'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null, "name" varchar(255) collate "nb_NO.utf8" not null)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" serial not null primary key, add column "email" varchar(255) not null', $statements[0]); + } + + public function testCreateTableWithAutoIncrementStartingValue() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id')->startingValue(1000); + $blueprint->string('email'); + $blueprint->string('name')->collation('nb_NO.utf8'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null, "name" varchar(255) collate "nb_NO.utf8" not null)', $statements[0]); + $this->assertSame('alter sequence users_id_seq restart with 1000', $statements[1]); + } + + public function testAddColumnsWithMultipleAutoIncrementStartingValue() + { + $blueprint = new Blueprint('users'); + $blueprint->id()->from(100); + $blueprint->increments('code')->from(200); + $blueprint->string('name')->from(300); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertEquals([ + 'alter table "users" add column "id" bigserial not null primary key, add column "code" serial not null primary key, add column "name" varchar(255) not null', + 'alter sequence users_id_seq restart with 100', + 'alter sequence users_code_seq restart with 200', + ], $statements); + } + + public function testCreateTableAndCommentColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id'); + $blueprint->string('email')->comment('my first comment'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null)', $statements[0]); + $this->assertSame('comment on column "users"."email" is \'my first comment\'', $statements[1]); + } + + public function testCreateTemporaryTable() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->temporary(); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create temporary table "users" ("id" serial not null primary key, "email" varchar(255) not null)', $statements[0]); + } + + public function testDropTable() + { + $blueprint = new Blueprint('users'); + $blueprint->drop(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop table "users"', $statements[0]); + } + + public function testDropTableIfExists() + { + $blueprint = new Blueprint('users'); + $blueprint->dropIfExists(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop table if exists "users"', $statements[0]); + } + + public function testDropColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->dropColumn('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop column "foo"', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dropColumn(['foo', 'bar']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop column "foo", drop column "bar"', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dropColumn('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop column "foo", drop column "bar"', $statements[0]); + } + + public function testDropPrimary() + { + $blueprint = new Blueprint('users'); + $blueprint->dropPrimary(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop constraint "users_pkey"', $statements[0]); + } + + public function testDropUnique() + { + $blueprint = new Blueprint('users'); + $blueprint->dropUnique('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop constraint "foo"', $statements[0]); + } + + public function testDropIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->dropIndex('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop index "foo"', $statements[0]); + } + + public function testDropSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->dropSpatialIndex(['coordinates']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop index "geo_coordinates_spatialindex"', $statements[0]); + } + + public function testDropForeign() + { + $blueprint = new Blueprint('users'); + $blueprint->dropForeign('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop constraint "foo"', $statements[0]); + } + + public function testDropTimestamps() + { + $blueprint = new Blueprint('users'); + $blueprint->dropTimestamps(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop column "created_at", drop column "updated_at"', $statements[0]); + } + + public function testDropTimestampsTz() + { + $blueprint = new Blueprint('users'); + $blueprint->dropTimestampsTz(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop column "created_at", drop column "updated_at"', $statements[0]); + } + + public function testDropMorphs() + { + $blueprint = new Blueprint('photos'); + $blueprint->dropMorphs('imageable'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('drop index "photos_imageable_type_imageable_id_index"', $statements[0]); + $this->assertSame('alter table "photos" drop column "imageable_type", drop column "imageable_id"', $statements[1]); + } + + public function testRenameTable() + { + $blueprint = new Blueprint('users'); + $blueprint->rename('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" rename to "foo"', $statements[0]); + } + + public function testRenameIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->renameIndex('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter index "foo" rename to "bar"', $statements[0]); + } + + public function testAddingPrimaryKey() + { + $blueprint = new Blueprint('users'); + $blueprint->primary('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add primary key ("foo")', $statements[0]); + } + + public function testAddingUniqueKey() + { + $blueprint = new Blueprint('users'); + $blueprint->unique('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add constraint "bar" unique ("foo")', $statements[0]); + } + + public function testAddingIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->index(['foo', 'bar'], 'baz'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "baz" on "users" ("foo", "bar")', $statements[0]); + } + + public function testAddingIndexWithAlgorithm() + { + $blueprint = new Blueprint('users'); + $blueprint->index(['foo', 'bar'], 'baz', 'hash'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "baz" on "users" using hash ("foo", "bar")', $statements[0]); + } + + public function testAddingFulltextIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->fulltext('body'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "users_body_fulltext" on "users" using gin ((to_tsvector(\'english\', "body")))', $statements[0]); + } + + public function testAddingFulltextIndexMultipleColumns() + { + $blueprint = new Blueprint('users'); + $blueprint->fulltext(['body', 'title']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "users_body_title_fulltext" on "users" using gin ((to_tsvector(\'english\', "body") || to_tsvector(\'english\', "title")))', $statements[0]); + } + + public function testAddingFulltextIndexWithLanguage() + { + $blueprint = new Blueprint('users'); + $blueprint->fulltext('body')->language('spanish'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "users_body_fulltext" on "users" using gin ((to_tsvector(\'spanish\', "body")))', $statements[0]); + } + + public function testAddingFulltextIndexWithFluency() + { + $blueprint = new Blueprint('users'); + $blueprint->string('body')->fulltext(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('create index "users_body_fulltext" on "users" using gin ((to_tsvector(\'english\', "body")))', $statements[1]); + } + + public function testAddingSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->spatialIndex('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "geo_coordinates_spatialindex" on "geo" using gist ("coordinates")', $statements[0]); + } + + public function testAddingFluentSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'point')->spatialIndex(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('create index "geo_coordinates_spatialindex" on "geo" using gist ("coordinates")', $statements[1]); + } + + public function testAddingRawIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->rawIndex('(function(column))', 'raw_index'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "raw_index" on "users" ((function(column)))', $statements[0]); + } + + public function testAddingIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->increments('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" serial not null primary key', $statements[0]); + } + + public function testAddingSmallIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->smallIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" smallserial not null primary key', $statements[0]); + } + + public function testAddingMediumIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->mediumIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" serial not null primary key', $statements[0]); + } + + public function testAddingID() + { + $blueprint = new Blueprint('users'); + $blueprint->id(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" bigserial not null primary key', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->id('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" bigserial not null primary key', $statements[0]); + } + + public function testAddingForeignID() + { + $blueprint = new Blueprint('users'); + $foreignId = $blueprint->foreignId('foo'); + $blueprint->foreignId('company_id')->constrained(); + $blueprint->foreignId('laravel_idea_id')->constrained(); + $blueprint->foreignId('team_id')->references('id')->on('teams'); + $blueprint->foreignId('team_column_id')->constrained('teams'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); + $this->assertSame([ + 'alter table "users" add column "foo" bigint not null, add column "company_id" bigint not null, add column "laravel_idea_id" bigint not null, add column "team_id" bigint not null, add column "team_column_id" bigint not null', + 'alter table "users" add constraint "users_company_id_foreign" foreign key ("company_id") references "companies" ("id")', + 'alter table "users" add constraint "users_laravel_idea_id_foreign" foreign key ("laravel_idea_id") references "laravel_ideas" ("id")', + 'alter table "users" add constraint "users_team_id_foreign" foreign key ("team_id") references "teams" ("id")', + 'alter table "users" add constraint "users_team_column_id_foreign" foreign key ("team_column_id") references "teams" ("id")', + ], $statements); + } + + public function testAddingForeignIdSpecifyingIndexNameInConstraint() + { + $blueprint = new Blueprint('users'); + $blueprint->foreignId('company_id')->constrained(indexName: 'my_index'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertSame([ + 'alter table "users" add column "company_id" bigint not null', + 'alter table "users" add constraint "my_index" foreign key ("company_id") references "companies" ("id")', + ], $statements); + } + + public function testAddingBigIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->bigIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" bigserial not null primary key', $statements[0]); + } + + public function testAddingString() + { + $blueprint = new Blueprint('users'); + $blueprint->string('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar(255) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar(100) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100)->nullable()->default('bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar(100) null default \'bar\'', $statements[0]); + } + + public function testAddingStringWithoutLengthLimit() + { + $blueprint = new Blueprint('users'); + $blueprint->string('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar(255) not null', $statements[0]); + + Builder::$defaultStringLength = null; + + $blueprint = new Blueprint('users'); + $blueprint->string('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + try { + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); + } finally { + Builder::$defaultStringLength = 255; + } + } + + public function testAddingCharWithoutLengthLimit() + { + $blueprint = new Blueprint('users'); + $blueprint->char('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" char(255) not null', $statements[0]); + + Builder::$defaultStringLength = null; + + $blueprint = new Blueprint('users'); + $blueprint->char('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + try { + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" char not null', $statements[0]); + } finally { + Builder::$defaultStringLength = 255; + } + } + + public function testAddingText() + { + $blueprint = new Blueprint('users'); + $blueprint->text('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" text not null', $statements[0]); + } + + public function testAddingBigInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->bigInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" bigint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->bigInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" bigserial not null primary key', $statements[0]); + } + + public function testAddingInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->integer('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->integer('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" serial not null primary key', $statements[0]); + } + + public function testAddingMediumInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->mediumInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->mediumInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" serial not null primary key', $statements[0]); + } + + public function testAddingTinyInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->tinyInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" smallint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->tinyInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" smallserial not null primary key', $statements[0]); + } + + public function testAddingSmallInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->smallInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" smallint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->smallInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" smallserial not null primary key', $statements[0]); + } + + public function testAddingFloat() + { + $blueprint = new Blueprint('users'); + $blueprint->float('foo', 5); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" float(5) not null', $statements[0]); + } + + public function testAddingDouble() + { + $blueprint = new Blueprint('users'); + $blueprint->double('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" double precision not null', $statements[0]); + } + + public function testAddingDecimal() + { + $blueprint = new Blueprint('users'); + $blueprint->decimal('foo', 5, 2); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" decimal(5, 2) not null', $statements[0]); + } + + public function testAddingBoolean() + { + $blueprint = new Blueprint('users'); + $blueprint->boolean('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" boolean not null', $statements[0]); + } + + public function testAddingEnum() + { + $blueprint = new Blueprint('users'); + $blueprint->enum('role', ['member', 'admin']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "role" varchar(255) check ("role" in (\'member\', \'admin\')) not null', $statements[0]); + } + + public function testAddingDate() + { + $blueprint = new Blueprint('users'); + $blueprint->date('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" date not null', $statements[0]); + } + + public function testAddingYear() + { + $blueprint = new Blueprint('users'); + $blueprint->year('birth_year'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "birth_year" integer not null', $statements[0]); + } + + public function testAddingJson() + { + $blueprint = new Blueprint('users'); + $blueprint->json('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" json not null', $statements[0]); + } + + public function testAddingJsonb() + { + $blueprint = new Blueprint('users'); + $blueprint->jsonb('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" jsonb not null', $statements[0]); + } + + public function testAddingDateTime() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(0) without time zone not null', $statements[0]); + } + + public function testAddingDateTimeWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(1) without time zone not null', $statements[0]); + } + + public function testAddingDateTimeWithNullPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('created_at', null); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp without time zone not null', $statements[0]); + } + + public function testAddingDateTimeTz() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(0) with time zone not null', $statements[0]); + } + + public function testAddingDateTimeTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(1) with time zone not null', $statements[0]); + } + + public function testAddingDateTimeTzWithNullPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('created_at', null); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp with time zone not null', $statements[0]); + } + + public function testAddingTime() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time(0) without time zone not null', $statements[0]); + } + + public function testAddingTimeWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time(1) without time zone not null', $statements[0]); + } + + public function testAddingTimeWithNullPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at', null); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time without time zone not null', $statements[0]); + } + + public function testAddingTimeTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time(0) with time zone not null', $statements[0]); + } + + public function testAddingTimeTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time(1) with time zone not null', $statements[0]); + } + + public function testAddingTimeTzWithNullPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at', null); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time with time zone not null', $statements[0]); + } + + public function testAddingTimestamp() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(0) without time zone not null', $statements[0]); + } + + public function testAddingTimestampWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(1) without time zone not null', $statements[0]); + } + + public function testAddingTimestampWithNullPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at', null); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp without time zone not null', $statements[0]); + } + + public function testAddingTimestampTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(0) with time zone not null', $statements[0]); + } + + public function testAddingTimestampTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(1) with time zone not null', $statements[0]); + } + + public function testAddingTimestampTzWithNullPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at', null); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp with time zone not null', $statements[0]); + } + + public function testAddingTimestamps() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamps(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(0) without time zone null, add column "updated_at" timestamp(0) without time zone null', $statements[0]); + } + + public function testAddingTimestampsTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampsTz(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" timestamp(0) with time zone null, add column "updated_at" timestamp(0) with time zone null', $statements[0]); + } + + public function testAddingBinary() + { + $blueprint = new Blueprint('users'); + $blueprint->binary('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" bytea not null', $statements[0]); + } + + public function testAddingUuid() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" uuid not null', $statements[0]); + } + + public function testAddingUuidDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "uuid" uuid not null', $statements[0]); + } + + public function testAddingForeignUuid() + { + $blueprint = new Blueprint('users'); + $foreignUuid = $blueprint->foreignUuid('foo'); + $blueprint->foreignUuid('company_id')->constrained(); + $blueprint->foreignUuid('laravel_idea_id')->constrained(); + $blueprint->foreignUuid('team_id')->references('id')->on('teams'); + $blueprint->foreignUuid('team_column_id')->constrained('teams'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignUuid); + $this->assertSame([ + 'alter table "users" add column "foo" uuid not null, add column "company_id" uuid not null, add column "laravel_idea_id" uuid not null, add column "team_id" uuid not null, add column "team_column_id" uuid not null', + 'alter table "users" add constraint "users_company_id_foreign" foreign key ("company_id") references "companies" ("id")', + 'alter table "users" add constraint "users_laravel_idea_id_foreign" foreign key ("laravel_idea_id") references "laravel_ideas" ("id")', + 'alter table "users" add constraint "users_team_id_foreign" foreign key ("team_id") references "teams" ("id")', + 'alter table "users" add constraint "users_team_column_id_foreign" foreign key ("team_column_id") references "teams" ("id")', + ], $statements); + } + + public function testAddingGeneratedAs() + { + $blueprint = new Blueprint('users'); + $blueprint->increments('foo')->generatedAs(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity primary key', $statements[0]); + // With always modifier + $blueprint = new Blueprint('users'); + $blueprint->increments('foo')->generatedAs()->always(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null generated always as identity primary key', $statements[0]); + // With sequence options + $blueprint = new Blueprint('users'); + $blueprint->increments('foo')->generatedAs('increment by 10 start with 100'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity (increment by 10 start with 100) primary key', $statements[0]); + // Not a primary key + $blueprint = new Blueprint('users'); + $blueprint->integer('foo')->generatedAs(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity', $statements[0]); + } + + public function testAddingVirtualAs() + { + $blueprint = new Blueprint('users'); + $blueprint->integer('foo')->nullable(); + $blueprint->boolean('bar')->virtualAs('foo is not null'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer null, add column "bar" boolean not null generated always as (foo is not null)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->integer('foo')->nullable(); + $blueprint->boolean('bar')->virtualAs(new Expression('foo is not null')); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer null, add column "bar" boolean not null generated always as (foo is not null)', $statements[0]); + } + + public function testAddingStoredAs() + { + $blueprint = new Blueprint('users'); + $blueprint->integer('foo')->nullable(); + $blueprint->boolean('bar')->storedAs('foo is not null'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer null, add column "bar" boolean not null generated always as (foo is not null) stored', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->integer('foo')->nullable(); + $blueprint->boolean('bar')->storedAs(new Expression('foo is not null')); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer null, add column "bar" boolean not null generated always as (foo is not null) stored', $statements[0]); + } + + public function testAddingIpAddress() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" inet not null', $statements[0]); + } + + public function testAddingIpAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "ip_address" inet not null', $statements[0]); + } + + public function testAddingMacAddress() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" macaddr not null', $statements[0]); + } + + public function testAddingMacAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "mac_address" macaddr not null', $statements[0]); + } + + public function testCompileForeign() + { + $blueprint = new Blueprint('users'); + $blueprint->foreign('parent_id')->references('id')->on('parents')->onDelete('cascade')->deferrable(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add constraint "users_parent_id_foreign" foreign key ("parent_id") references "parents" ("id") on delete cascade deferrable', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->foreign('parent_id')->references('id')->on('parents')->onDelete('cascade')->deferrable(false)->initiallyImmediate(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add constraint "users_parent_id_foreign" foreign key ("parent_id") references "parents" ("id") on delete cascade not deferrable', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->foreign('parent_id')->references('id')->on('parents')->onDelete('cascade')->deferrable()->initiallyImmediate(false); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add constraint "users_parent_id_foreign" foreign key ("parent_id") references "parents" ("id") on delete cascade deferrable initially deferred', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->foreign('parent_id')->references('id')->on('parents')->onDelete('cascade')->deferrable()->notValid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add constraint "users_parent_id_foreign" foreign key ("parent_id") references "parents" ("id") on delete cascade deferrable not valid', $statements[0]); + } + + public function testAddingGeometry() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry not null', $statements[0]); + } + + public function testAddingGeography() + { + $blueprint = new Blueprint('geo'); + $blueprint->geography('coordinates', 'pointzm', 4269); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geography(pointzm,4269) not null', $statements[0]); + } + + public function testAddingPoint() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'point'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry(point) not null', $statements[0]); + } + + public function testAddingPointWithSrid() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'point', 4269); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry(point,4269) not null', $statements[0]); + } + + public function testAddingLineString() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'linestring'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry(linestring) not null', $statements[0]); + } + + public function testAddingPolygon() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'polygon'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry(polygon) not null', $statements[0]); + } + + public function testAddingGeometryCollection() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'geometrycollection'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry(geometrycollection) not null', $statements[0]); + } + + public function testAddingMultiPoint() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'multipoint'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry(multipoint) not null', $statements[0]); + } + + public function testAddingMultiLineString() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'multilinestring'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry(multilinestring) not null', $statements[0]); + } + + public function testAddingMultiPolygon() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'multipolygon'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry(multipolygon) not null', $statements[0]); + } + + public function testCreateDatabase() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8_foo'); + $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); + + $this->assertSame( + 'create database "my_database_a" encoding "utf8_foo"', + $statement + ); + + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8_bar'); + $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); + + $this->assertSame( + 'create database "my_database_b" encoding "utf8_bar"', + $statement + ); + } + + public function testDropDatabaseIfExists() + { + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); + + $this->assertSame( + 'drop database if exists "my_database_a"', + $statement + ); + + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); + + $this->assertSame( + 'drop database if exists "my_database_b"', + $statement + ); + } + + public function testDropAllTablesEscapesTableNames() + { + $statement = $this->getGrammar()->compileDropAllTables(['alpha', 'beta', 'gamma']); + + $this->assertSame('drop table "alpha","beta","gamma" cascade', $statement); + } + + public function testDropAllViewsEscapesTableNames() + { + $statement = $this->getGrammar()->compileDropAllViews(['alpha', 'beta', 'gamma']); + + $this->assertSame('drop view "alpha","beta","gamma" cascade', $statement); + } + + public function testDropAllTypesEscapesTableNames() + { + $statement = $this->getGrammar()->compileDropAllTypes(['alpha', 'beta', 'gamma']); + + $this->assertSame('drop type "alpha","beta","gamma" cascade', $statement); + } + + protected function getConnection() + { + return m::mock(Connection::class); + } + + public function getGrammar() + { + return new PostgresGrammar; + } + + public function testGrammarsAreMacroable() + { + // compileReplace macro. + $this->getGrammar()::macro('compileReplace', function () { + return true; + }); + + $c = $this->getGrammar()::compileReplace(); + + $this->assertTrue($c); + } +} diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php new file mode 100644 index 00000000..a120e88b --- /dev/null +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -0,0 +1,937 @@ +create(); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("id" integer primary key autoincrement not null, "email" varchar not null)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $expected = [ + 'alter table "users" add column "id" integer primary key autoincrement not null', + 'alter table "users" add column "email" varchar not null', + ]; + $this->assertEquals($expected, $statements); + } + + public function testCreateTemporaryTable() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->temporary(); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create temporary table "users" ("id" integer primary key autoincrement not null, "email" varchar not null)', $statements[0]); + } + + public function testDropTable() + { + $blueprint = new Blueprint('users'); + $blueprint->drop(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop table "users"', $statements[0]); + } + + public function testDropTableIfExists() + { + $blueprint = new Blueprint('users'); + $blueprint->dropIfExists(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop table if exists "users"', $statements[0]); + } + + public function testDropUnique() + { + $blueprint = new Blueprint('users'); + $blueprint->dropUnique('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop index "foo"', $statements[0]); + } + + public function testDropIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->dropIndex('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop index "foo"', $statements[0]); + } + + public function testDropColumn() + { + $db = new Manager; + + $db->addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => 'prefix_', + ]); + + $schema = $db->getConnection()->getSchemaBuilder(); + + $schema->create('users', function (Blueprint $table) { + $table->string('email'); + $table->string('name'); + }); + + $this->assertTrue($schema->hasTable('users')); + $this->assertTrue($schema->hasColumn('users', 'name')); + + $schema->table('users', function (Blueprint $table) { + $table->dropColumn('name'); + }); + + $this->assertFalse($schema->hasColumn('users', 'name')); + } + + public function testDropSpatialIndex() + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The database driver in use does not support spatial indexes.'); + + $blueprint = new Blueprint('geo'); + $blueprint->dropSpatialIndex(['coordinates']); + $blueprint->toSql($this->getConnection(), $this->getGrammar()); + } + + public function testRenameTable() + { + $blueprint = new Blueprint('users'); + $blueprint->rename('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" rename to "foo"', $statements[0]); + } + + public function testRenameIndex() + { + $db = new Manager; + + $db->addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => 'prefix_', + ]); + + $schema = $db->getConnection()->getSchemaBuilder(); + + $schema->create('users', function (Blueprint $table) { + $table->string('name'); + $table->string('email'); + }); + + $schema->table('users', function (Blueprint $table) { + $table->index(['name', 'email'], 'index1'); + }); + + $indexes = $schema->getIndexListing('users'); + + $this->assertContains('index1', $indexes); + $this->assertNotContains('index2', $indexes); + + $schema->table('users', function (Blueprint $table) { + $table->renameIndex('index1', 'index2'); + }); + + $this->assertFalse($schema->hasIndex('users', 'index1')); + $this->assertTrue(collect($schema->getIndexes('users'))->contains( + fn ($index) => $index['name'] === 'index2' && $index['columns'] === ['name', 'email'] + )); + } + + public function testAddingPrimaryKey() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('foo')->primary(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("foo" varchar not null, primary key ("foo"))', $statements[0]); + } + + public function testAddingForeignKey() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('foo')->primary(); + $blueprint->string('order_id'); + $blueprint->foreign('order_id')->references('id')->on('orders'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("foo" varchar not null, "order_id" varchar not null, foreign key("order_id") references "orders"("id"), primary key ("foo"))', $statements[0]); + } + + public function testAddingUniqueKey() + { + $blueprint = new Blueprint('users'); + $blueprint->unique('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create unique index "bar" on "users" ("foo")', $statements[0]); + } + + public function testAddingIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->index(['foo', 'bar'], 'baz'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "baz" on "users" ("foo", "bar")', $statements[0]); + } + + public function testAddingSpatialIndex() + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The database driver in use does not support spatial indexes.'); + + $blueprint = new Blueprint('geo'); + $blueprint->spatialIndex('coordinates'); + $blueprint->toSql($this->getConnection(), $this->getGrammar()); + } + + public function testAddingFluentSpatialIndex() + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The database driver in use does not support spatial indexes.'); + + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates')->spatialIndex(); + $blueprint->toSql($this->getConnection(), $this->getGrammar()); + } + + public function testAddingRawIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->rawIndex('(function(column))', 'raw_index'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "raw_index" on "users" ((function(column)))', $statements[0]); + } + + public function testAddingIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->increments('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingSmallIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->smallIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingMediumIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->mediumIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingID() + { + $blueprint = new Blueprint('users'); + $blueprint->id(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->id('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingForeignID() + { + $blueprint = new Blueprint('users'); + $foreignId = $blueprint->foreignId('foo'); + $blueprint->foreignId('company_id')->constrained(); + $blueprint->foreignId('laravel_idea_id')->constrained(); + $blueprint->foreignId('team_id')->references('id')->on('teams'); + $blueprint->foreignId('team_column_id')->constrained('teams'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); + $this->assertSame([ + 'alter table "users" add column "foo" integer not null', + 'alter table "users" add column "company_id" integer not null', + 'alter table "users" add column "laravel_idea_id" integer not null', + 'alter table "users" add column "team_id" integer not null', + 'alter table "users" add column "team_column_id" integer not null', + ], $statements); + } + + public function testAddingForeignIdSpecifyingIndexNameInConstraint() + { + $blueprint = new Blueprint('users'); + $blueprint->foreignId('company_id')->constrained(indexName: 'my_index'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertSame([ + 'alter table "users" add column "company_id" integer not null', + ], $statements); + } + + public function testAddingBigIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->bigIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingString() + { + $blueprint = new Blueprint('users'); + $blueprint->string('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100)->nullable()->default('bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar default \'bar\'', $statements[0]); + } + + public function testAddingText() + { + $blueprint = new Blueprint('users'); + $blueprint->text('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" text not null', $statements[0]); + } + + public function testAddingBigInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->bigInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->bigInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->integer('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->integer('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingMediumInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->mediumInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->mediumInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingTinyInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->tinyInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->tinyInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingSmallInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->smallInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->smallInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); + } + + public function testAddingFloat() + { + $blueprint = new Blueprint('users'); + $blueprint->float('foo', 5); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" float not null', $statements[0]); + } + + public function testAddingDouble() + { + $blueprint = new Blueprint('users'); + $blueprint->double('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" double not null', $statements[0]); + } + + public function testAddingDecimal() + { + $blueprint = new Blueprint('users'); + $blueprint->decimal('foo', 5, 2); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" numeric not null', $statements[0]); + } + + public function testAddingBoolean() + { + $blueprint = new Blueprint('users'); + $blueprint->boolean('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" tinyint(1) not null', $statements[0]); + } + + public function testAddingEnum() + { + $blueprint = new Blueprint('users'); + $blueprint->enum('role', ['member', 'admin']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "role" varchar check ("role" in (\'member\', \'admin\')) not null', $statements[0]); + } + + public function testAddingJson() + { + $blueprint = new Blueprint('users'); + $blueprint->json('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" text not null', $statements[0]); + } + + public function testAddingJsonb() + { + $blueprint = new Blueprint('users'); + $blueprint->jsonb('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" text not null', $statements[0]); + } + + public function testAddingDate() + { + $blueprint = new Blueprint('users'); + $blueprint->date('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" date not null', $statements[0]); + } + + public function testAddingYear() + { + $blueprint = new Blueprint('users'); + $blueprint->year('birth_year'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "birth_year" integer not null', $statements[0]); + } + + public function testAddingDateTime() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); + } + + public function testAddingDateTimeWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); + } + + public function testAddingDateTimeTz() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); + } + + public function testAddingDateTimeTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); + } + + public function testAddingTime() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time not null', $statements[0]); + } + + public function testAddingTimeWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time not null', $statements[0]); + } + + public function testAddingTimeTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time not null', $statements[0]); + } + + public function testAddingTimeTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" time not null', $statements[0]); + } + + public function testAddingTimestamp() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); + } + + public function testAddingTimestampWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); + } + + public function testAddingTimestampTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); + } + + public function testAddingTimestampTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); + } + + public function testAddingTimestamps() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamps(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(2, $statements); + $this->assertEquals([ + 'alter table "users" add column "created_at" datetime', + 'alter table "users" add column "updated_at" datetime', + ], $statements); + } + + public function testAddingTimestampsTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampsTz(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(2, $statements); + $this->assertEquals([ + 'alter table "users" add column "created_at" datetime', + 'alter table "users" add column "updated_at" datetime', + ], $statements); + } + + public function testAddingRememberToken() + { + $blueprint = new Blueprint('users'); + $blueprint->rememberToken(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "remember_token" varchar', $statements[0]); + } + + public function testAddingBinary() + { + $blueprint = new Blueprint('users'); + $blueprint->binary('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" blob not null', $statements[0]); + } + + public function testAddingUuid() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); + } + + public function testAddingUuidDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "uuid" varchar not null', $statements[0]); + } + + public function testAddingForeignUuid() + { + $blueprint = new Blueprint('users'); + $foreignUuid = $blueprint->foreignUuid('foo'); + $blueprint->foreignUuid('company_id')->constrained(); + $blueprint->foreignUuid('laravel_idea_id')->constrained(); + $blueprint->foreignUuid('team_id')->references('id')->on('teams'); + $blueprint->foreignUuid('team_column_id')->constrained('teams'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignUuid); + $this->assertSame([ + 'alter table "users" add column "foo" varchar not null', + 'alter table "users" add column "company_id" varchar not null', + 'alter table "users" add column "laravel_idea_id" varchar not null', + 'alter table "users" add column "team_id" varchar not null', + 'alter table "users" add column "team_column_id" varchar not null', + ], $statements); + } + + public function testAddingIpAddress() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); + } + + public function testAddingIpAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "ip_address" varchar not null', $statements[0]); + } + + public function testAddingMacAddress() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); + } + + public function testAddingMacAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "mac_address" varchar not null', $statements[0]); + } + + public function testAddingGeometry() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add column "coordinates" geometry not null', $statements[0]); + } + + public function testAddingGeneratedColumn() + { + $blueprint = new Blueprint('products'); + $blueprint->create(); + $blueprint->integer('price'); + $blueprint->integer('discounted_virtual')->virtualAs('"price" - 5'); + $blueprint->integer('discounted_stored')->storedAs('"price" - 5'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "products" ("price" integer not null, "discounted_virtual" integer as ("price" - 5), "discounted_stored" integer as ("price" - 5) stored)', $statements[0]); + + $blueprint = new Blueprint('products'); + $blueprint->integer('price'); + $blueprint->integer('discounted_virtual')->virtualAs('"price" - 5')->nullable(false); + $blueprint->integer('discounted_stored')->storedAs('"price" - 5')->nullable(false); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(3, $statements); + $expected = [ + 'alter table "products" add column "price" integer not null', + 'alter table "products" add column "discounted_virtual" integer not null as ("price" - 5)', + 'alter table "products" add column "discounted_stored" integer not null as ("price" - 5) stored', + ]; + $this->assertSame($expected, $statements); + } + + public function testAddingGeneratedColumnByExpression() + { + $blueprint = new Blueprint('products'); + $blueprint->create(); + $blueprint->integer('price'); + $blueprint->integer('discounted_virtual')->virtualAs(new Expression('"price" - 5')); + $blueprint->integer('discounted_stored')->storedAs(new Expression('"price" - 5')); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "products" ("price" integer not null, "discounted_virtual" integer as ("price" - 5), "discounted_stored" integer as ("price" - 5) stored)', $statements[0]); + } + + public function testGrammarsAreMacroable() + { + // compileReplace macro. + $this->getGrammar()::macro('compileReplace', function () { + return true; + }); + + $c = $this->getGrammar()::compileReplace(); + + $this->assertTrue($c); + } + + public function testCreateTableWithVirtualAsColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_column'); + $blueprint->string('my_other_column')->virtualAs('my_column'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_column" varchar not null, "my_other_column" varchar as (my_column))', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"\')))', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute->nested'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"."nested"\')))', $statements[0]); + } + + public function testCreateTableWithVirtualAsColumnWhenJsonColumnHasArrayKey() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column')->virtualAsJson('my_json_column->foo[0][1]'); + + $conn = $this->getConnection(); + $conn->shouldReceive('getConfig')->andReturn(null); + + $statements = $blueprint->toSql($conn, $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("create table \"users\" (\"my_json_column\" varchar as (json_extract(\"my_json_column\", '$.\"foo\"[0][1]')))", $statements[0]); + } + + public function testCreateTableWithStoredAsColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_column'); + $blueprint->string('my_other_column')->storedAs('my_column'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_column" varchar not null, "my_other_column" varchar as (my_column) stored)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"\')) stored)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->string('my_json_column'); + $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute->nested'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"."nested"\')) stored)', $statements[0]); + } + + protected function getConnection() + { + return m::mock(Connection::class); + } + + public function getGrammar() + { + return new SQLiteGrammar; + } +} diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php new file mode 100644 index 00000000..3e9a50a0 --- /dev/null +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -0,0 +1,932 @@ +create(); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "id" int not null identity primary key, "email" nvarchar(255) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()->setTablePrefix('prefix_')); + + $this->assertCount(1, $statements); + $this->assertSame('create table "prefix_users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); + } + + public function testCreateTemporaryTable() + { + $blueprint = new Blueprint('users'); + $blueprint->create(); + $blueprint->temporary(); + $blueprint->increments('id'); + $blueprint->string('email'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create table "#users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); + } + + public function testDropTable() + { + $blueprint = new Blueprint('users'); + $blueprint->drop(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop table "users"', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->drop(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()->setTablePrefix('prefix_')); + + $this->assertCount(1, $statements); + $this->assertSame('drop table "prefix_users"', $statements[0]); + } + + public function testDropTableIfExists() + { + $blueprint = new Blueprint('users'); + $blueprint->dropIfExists(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('if object_id(N\'"users"\', \'U\') is not null drop table "users"', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dropIfExists(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()->setTablePrefix('prefix_')); + + $this->assertCount(1, $statements); + $this->assertSame('if object_id(N\'"prefix_users"\', \'U\') is not null drop table "prefix_users"', $statements[0]); + } + + public function testDropColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->dropColumn('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertStringContainsString('alter table "users" drop column "foo"', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dropColumn(['foo', 'bar']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertStringContainsString('alter table "users" drop column "foo", "bar"', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->dropColumn('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertStringContainsString('alter table "users" drop column "foo", "bar"', $statements[0]); + } + + public function testDropColumnDropsCreatesSqlToDropDefaultConstraints() + { + $blueprint = new Blueprint('foo'); + $blueprint->dropColumn('bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame("DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"foo\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'\"foo\"') AND [name] in ('bar') AND [default_object_id] <> 0;EXEC(@sql);alter table \"foo\" drop column \"bar\"", $statements[0]); + } + + public function testDropPrimary() + { + $blueprint = new Blueprint('users'); + $blueprint->dropPrimary('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop constraint "foo"', $statements[0]); + } + + public function testDropUnique() + { + $blueprint = new Blueprint('users'); + $blueprint->dropUnique('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop index "foo" on "users"', $statements[0]); + } + + public function testDropIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->dropIndex('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop index "foo" on "users"', $statements[0]); + } + + public function testDropSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->dropSpatialIndex(['coordinates']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('drop index "geo_coordinates_spatialindex" on "geo"', $statements[0]); + } + + public function testDropForeign() + { + $blueprint = new Blueprint('users'); + $blueprint->dropForeign('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" drop constraint "foo"', $statements[0]); + } + + public function testDropConstrainedForeignId() + { + $blueprint = new Blueprint('users'); + $blueprint->dropConstrainedForeignId('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('alter table "users" drop constraint "users_foo_foreign"', $statements[0]); + $this->assertSame('DECLARE @sql NVARCHAR(MAX) = \'\';SELECT @sql += \'ALTER TABLE "users" DROP CONSTRAINT \' + OBJECT_NAME([default_object_id]) + \';\' FROM sys.columns WHERE [object_id] = OBJECT_ID(N\'"users"\') AND [name] in (\'foo\') AND [default_object_id] <> 0;EXEC(@sql);alter table "users" drop column "foo"', $statements[1]); + } + + public function testDropTimestamps() + { + $blueprint = new Blueprint('users'); + $blueprint->dropTimestamps(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertStringContainsString('alter table "users" drop column "created_at", "updated_at"', $statements[0]); + } + + public function testDropTimestampsTz() + { + $blueprint = new Blueprint('users'); + $blueprint->dropTimestampsTz(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertStringContainsString('alter table "users" drop column "created_at", "updated_at"', $statements[0]); + } + + public function testDropMorphs() + { + $blueprint = new Blueprint('photos'); + $blueprint->dropMorphs('imageable'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('drop index "photos_imageable_type_imageable_id_index" on "photos"', $statements[0]); + $this->assertStringContainsString('alter table "photos" drop column "imageable_type", "imageable_id"', $statements[1]); + } + + public function testRenameTable() + { + $blueprint = new Blueprint('users'); + $blueprint->rename('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('sp_rename N\'"users"\', "foo"', $statements[0]); + } + + public function testRenameIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->renameIndex('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('sp_rename N\'"users"."foo"\', "bar", N\'INDEX\'', $statements[0]); + } + + public function testAddingPrimaryKey() + { + $blueprint = new Blueprint('users'); + $blueprint->primary('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add constraint "bar" primary key ("foo")', $statements[0]); + } + + public function testAddingUniqueKey() + { + $blueprint = new Blueprint('users'); + $blueprint->unique('foo', 'bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create unique index "bar" on "users" ("foo")', $statements[0]); + } + + public function testAddingIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->index(['foo', 'bar'], 'baz'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "baz" on "users" ("foo", "bar")', $statements[0]); + } + + public function testAddingSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->spatialIndex('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create spatial index "geo_coordinates_spatialindex" on "geo" ("coordinates")', $statements[0]); + } + + public function testAddingFluentSpatialIndex() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates', 'point')->spatialIndex(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(2, $statements); + $this->assertSame('create spatial index "geo_coordinates_spatialindex" on "geo" ("coordinates")', $statements[1]); + } + + public function testAddingRawIndex() + { + $blueprint = new Blueprint('users'); + $blueprint->rawIndex('(function(column))', 'raw_index'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('create index "raw_index" on "users" ((function(column)))', $statements[0]); + } + + public function testAddingIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->increments('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "id" int not null identity primary key', $statements[0]); + } + + public function testAddingSmallIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->smallIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "id" smallint not null identity primary key', $statements[0]); + } + + public function testAddingMediumIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->mediumIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "id" int not null identity primary key', $statements[0]); + } + + public function testAddingID() + { + $blueprint = new Blueprint('users'); + $blueprint->id(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "id" bigint not null identity primary key', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->id('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" bigint not null identity primary key', $statements[0]); + } + + public function testAddingForeignID() + { + $blueprint = new Blueprint('users'); + $foreignId = $blueprint->foreignId('foo'); + $blueprint->foreignId('company_id')->constrained(); + $blueprint->foreignId('laravel_idea_id')->constrained(); + $blueprint->foreignId('team_id')->references('id')->on('teams'); + $blueprint->foreignId('team_column_id')->constrained('teams'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); + $this->assertSame([ + 'alter table "users" add "foo" bigint not null, "company_id" bigint not null, "laravel_idea_id" bigint not null, "team_id" bigint not null, "team_column_id" bigint not null', + 'alter table "users" add constraint "users_company_id_foreign" foreign key ("company_id") references "companies" ("id")', + 'alter table "users" add constraint "users_laravel_idea_id_foreign" foreign key ("laravel_idea_id") references "laravel_ideas" ("id")', + 'alter table "users" add constraint "users_team_id_foreign" foreign key ("team_id") references "teams" ("id")', + 'alter table "users" add constraint "users_team_column_id_foreign" foreign key ("team_column_id") references "teams" ("id")', + ], $statements); + } + + public function testAddingForeignIdSpecifyingIndexNameInConstraint() + { + $blueprint = new Blueprint('users'); + $blueprint->foreignId('company_id')->constrained(indexName: 'my_index'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertSame([ + 'alter table "users" add "company_id" bigint not null', + 'alter table "users" add constraint "my_index" foreign key ("company_id") references "companies" ("id")', + ], $statements); + } + + public function testAddingBigIncrementingID() + { + $blueprint = new Blueprint('users'); + $blueprint->bigIncrements('id'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "id" bigint not null identity primary key', $statements[0]); + } + + public function testAddingString() + { + $blueprint = new Blueprint('users'); + $blueprint->string('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" nvarchar(255) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" nvarchar(100) not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->string('foo', 100)->nullable()->default('bar'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" nvarchar(100) null default \'bar\'', $statements[0]); + } + + public function testAddingText() + { + $blueprint = new Blueprint('users'); + $blueprint->text('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" nvarchar(max) not null', $statements[0]); + } + + public function testAddingBigInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->bigInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" bigint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->bigInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" bigint not null identity primary key', $statements[0]); + } + + public function testAddingInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->integer('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" int not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->integer('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" int not null identity primary key', $statements[0]); + } + + public function testAddingMediumInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->mediumInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" int not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->mediumInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" int not null identity primary key', $statements[0]); + } + + public function testAddingTinyInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->tinyInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" tinyint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->tinyInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" tinyint not null identity primary key', $statements[0]); + } + + public function testAddingSmallInteger() + { + $blueprint = new Blueprint('users'); + $blueprint->smallInteger('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" smallint not null', $statements[0]); + + $blueprint = new Blueprint('users'); + $blueprint->smallInteger('foo', true); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" smallint not null identity primary key', $statements[0]); + } + + public function testAddingFloat() + { + $blueprint = new Blueprint('users'); + $blueprint->float('foo', 5); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" float(5) not null', $statements[0]); + } + + public function testAddingDouble() + { + $blueprint = new Blueprint('users'); + $blueprint->double('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" double precision not null', $statements[0]); + } + + public function testAddingDecimal() + { + $blueprint = new Blueprint('users'); + $blueprint->decimal('foo', 5, 2); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" decimal(5, 2) not null', $statements[0]); + } + + public function testAddingBoolean() + { + $blueprint = new Blueprint('users'); + $blueprint->boolean('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" bit not null', $statements[0]); + } + + public function testAddingEnum() + { + $blueprint = new Blueprint('users'); + $blueprint->enum('role', ['member', 'admin']); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "role" nvarchar(255) check ("role" in (N\'member\', N\'admin\')) not null', $statements[0]); + } + + public function testAddingJson() + { + $blueprint = new Blueprint('users'); + $blueprint->json('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" nvarchar(max) not null', $statements[0]); + } + + public function testAddingJsonb() + { + $blueprint = new Blueprint('users'); + $blueprint->jsonb('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" nvarchar(max) not null', $statements[0]); + } + + public function testAddingDate() + { + $blueprint = new Blueprint('users'); + $blueprint->date('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" date not null', $statements[0]); + } + + public function testAddingYear() + { + $blueprint = new Blueprint('users'); + $blueprint->year('birth_year'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "birth_year" int not null', $statements[0]); + } + + public function testAddingDateTime() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" datetime not null', $statements[0]); + } + + public function testAddingDateTimeWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTime('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" datetime2(1) not null', $statements[0]); + } + + public function testAddingDateTimeTz() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" datetimeoffset not null', $statements[0]); + } + + public function testAddingDateTimeTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->dateTimeTz('foo', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" datetimeoffset(1) not null', $statements[0]); + } + + public function testAddingTime() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" time not null', $statements[0]); + } + + public function testAddingTimeWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->time('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" time(1) not null', $statements[0]); + } + + public function testAddingTimeTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" time not null', $statements[0]); + } + + public function testAddingTimeTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timeTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" time(1) not null', $statements[0]); + } + + public function testAddingTimestamp() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" datetime not null', $statements[0]); + } + + public function testAddingTimestampWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamp('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" datetime2(1) not null', $statements[0]); + } + + public function testAddingTimestampTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" datetimeoffset not null', $statements[0]); + } + + public function testAddingTimestampTzWithPrecision() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampTz('created_at', 1); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" datetimeoffset(1) not null', $statements[0]); + } + + public function testAddingTimestamps() + { + $blueprint = new Blueprint('users'); + $blueprint->timestamps(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" datetime null, "updated_at" datetime null', $statements[0]); + } + + public function testAddingTimestampsTz() + { + $blueprint = new Blueprint('users'); + $blueprint->timestampsTz(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "created_at" datetimeoffset null, "updated_at" datetimeoffset null', $statements[0]); + } + + public function testAddingRememberToken() + { + $blueprint = new Blueprint('users'); + $blueprint->rememberToken(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "remember_token" nvarchar(100) null', $statements[0]); + } + + public function testAddingBinary() + { + $blueprint = new Blueprint('users'); + $blueprint->binary('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" varbinary(max) not null', $statements[0]); + } + + public function testAddingUuid() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" uniqueidentifier not null', $statements[0]); + } + + public function testAddingUuidDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->uuid(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "uuid" uniqueidentifier not null', $statements[0]); + } + + public function testAddingForeignUuid() + { + $blueprint = new Blueprint('users'); + $foreignId = $blueprint->foreignUuid('foo'); + $blueprint->foreignUuid('company_id')->constrained(); + $blueprint->foreignUuid('laravel_idea_id')->constrained(); + $blueprint->foreignUuid('team_id')->references('id')->on('teams'); + $blueprint->foreignUuid('team_column_id')->constrained('teams'); + + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); + $this->assertSame([ + 'alter table "users" add "foo" uniqueidentifier not null, "company_id" uniqueidentifier not null, "laravel_idea_id" uniqueidentifier not null, "team_id" uniqueidentifier not null, "team_column_id" uniqueidentifier not null', + 'alter table "users" add constraint "users_company_id_foreign" foreign key ("company_id") references "companies" ("id")', + 'alter table "users" add constraint "users_laravel_idea_id_foreign" foreign key ("laravel_idea_id") references "laravel_ideas" ("id")', + 'alter table "users" add constraint "users_team_id_foreign" foreign key ("team_id") references "teams" ("id")', + 'alter table "users" add constraint "users_team_column_id_foreign" foreign key ("team_column_id") references "teams" ("id")', + ], $statements); + } + + public function testAddingIpAddress() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" nvarchar(45) not null', $statements[0]); + } + + public function testAddingIpAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->ipAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "ip_address" nvarchar(45) not null', $statements[0]); + } + + public function testAddingMacAddress() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress('foo'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "foo" nvarchar(17) not null', $statements[0]); + } + + public function testAddingMacAddressDefaultsColumnName() + { + $blueprint = new Blueprint('users'); + $blueprint->macAddress(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "mac_address" nvarchar(17) not null', $statements[0]); + } + + public function testAddingGeometry() + { + $blueprint = new Blueprint('geo'); + $blueprint->geometry('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add "coordinates" geometry not null', $statements[0]); + } + + public function testAddingGeography() + { + $blueprint = new Blueprint('geo'); + $blueprint->geography('coordinates'); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "geo" add "coordinates" geography not null', $statements[0]); + } + + public function testAddingGeneratedColumn() + { + $blueprint = new Blueprint('products'); + $blueprint->integer('price'); + $blueprint->computed('discounted_virtual', 'price - 5'); + $blueprint->computed('discounted_stored', 'price - 5')->persisted(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "products" add "price" int not null, "discounted_virtual" as (price - 5), "discounted_stored" as (price - 5) persisted', $statements[0]); + + $blueprint = new Blueprint('products'); + $blueprint->integer('price'); + $blueprint->computed('discounted_virtual', new Expression('price - 5')); + $blueprint->computed('discounted_stored', new Expression('price - 5'))->persisted(); + $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $this->assertCount(1, $statements); + $this->assertSame('alter table "products" add "price" int not null, "discounted_virtual" as (price - 5), "discounted_stored" as (price - 5) persisted', $statements[0]); + } + + public function testGrammarsAreMacroable() + { + // compileReplace macro. + $this->getGrammar()::macro('compileReplace', function () { + return true; + }); + + $c = $this->getGrammar()::compileReplace(); + + $this->assertTrue($c); + } + + public function testQuoteString() + { + $this->assertSame("N'中文測試'", $this->getGrammar()->quoteString('中文測試')); + } + + public function testQuoteStringOnArray() + { + $this->assertSame("N'中文', N'測試'", $this->getGrammar()->quoteString(['中文', '測試'])); + } + + public function testCreateDatabase() + { + $connection = $this->getConnection(); + + $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); + + $this->assertSame( + 'create database "my_database_a"', + $statement + ); + + $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); + + $this->assertSame( + 'create database "my_database_b"', + $statement + ); + } + + public function testDropDatabaseIfExists() + { + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); + + $this->assertSame( + 'drop database if exists "my_database_a"', + $statement + ); + + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); + + $this->assertSame( + 'drop database if exists "my_database_b"', + $statement + ); + } + + protected function getConnection() + { + return m::mock(Connection::class); + } + + public function getGrammar() + { + return new SqlServerGrammar; + } +} From f6bd0bede1ce462cd4851c2930384ef7e9af5f93 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 5 Apr 2024 17:37:33 -0400 Subject: [PATCH 033/197] add MariaDb Connection and associated classes --- .../Connections/MariaDbConnection.php | 76 +++++++++++++++++++ .../Query/Grammars/MariaDbGrammar.php | 9 +++ src/Database/Schema/Grammars/MySqlGrammar.php | 4 +- .../Schema/Grammars/PostgresGrammar.php | 1 - .../Schema/Grammars/SQLiteGrammar.php | 7 +- .../Schema/Grammars/SqlServerGrammar.php | 3 +- 6 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 src/Database/Connections/MariaDbConnection.php create mode 100644 src/Database/Query/Grammars/MariaDbGrammar.php diff --git a/src/Database/Connections/MariaDbConnection.php b/src/Database/Connections/MariaDbConnection.php new file mode 100644 index 00000000..9d06b2bf --- /dev/null +++ b/src/Database/Connections/MariaDbConnection.php @@ -0,0 +1,76 @@ +withTablePrefix(new QueryGrammar); + } + + /** + * Get a schema builder instance for the connection. + * + * @return \Illuminate\Database\Schema\MariaDbBuilder + */ + public function getSchemaBuilder() + { + if (!isset($this->schemaGrammar)) { + $this->useDefaultSchemaGrammar(); + } + + return new MariaDbBuilder($this); + } + + /** + * Get the default schema grammar instance. + * + * @return \Illuminate\Database\Grammar + */ + protected function getDefaultSchemaGrammar() + { + return $this->withTablePrefix(new SchemaGrammar); + } + + /** + * Get the default post processor instance. + * + * @return \Illuminate\Database\Query\Processors\MariaDbProcessor + */ + protected function getDefaultPostProcessor() + { + return new MariaDbProcessor; + } + + /** + * Bind values to their parameters in the given statement. + * + * @param \PDOStatement $statement + * @param array $bindings + * @return void + */ + public function bindValues($statement, $bindings) + { + foreach ($bindings as $key => $value) { + $statement->bindValue( + is_string($key) ? $key : $key + 1, + $value, + is_int($value) || is_float($value) ? PDO::PARAM_INT : PDO::PARAM_STR + ); + } + } +} diff --git a/src/Database/Query/Grammars/MariaDbGrammar.php b/src/Database/Query/Grammars/MariaDbGrammar.php new file mode 100644 index 00000000..11eccd72 --- /dev/null +++ b/src/Database/Query/Grammars/MariaDbGrammar.php @@ -0,0 +1,9 @@ +getSchemaBuilder()->getColumns($blueprint->getTable())); foreach ($blueprint->getChangedColumns() as $column) { - $sql = sprintf('%s %s%s %s', + $sql = sprintf( + '%s %s%s %s', is_null($column->renameTo) ? 'modify' : 'change', $this->wrap($column), is_null($column->renameTo) ? '' : ' '.$this->wrap($column->renameTo), @@ -51,5 +52,4 @@ protected function addLegacyModifiers($sql, Blueprint $blueprint, Fluent $column return $sql; } - } diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index e1f1d0b7..62acdde9 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -49,5 +49,4 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); } - } diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index f411649c..52356a12 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -55,7 +55,9 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $columnNames[] = $name; } - return $this->addModifiers($name.' '.$column['type'], $blueprint, + return $this->addModifiers( + $name.' '.$column['type'], + $blueprint, new ColumnDefinition([ 'change' => true, 'type' => $column['type_name'], @@ -101,7 +103,8 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $foreignKeyConstraintsEnabled = $connection->scalar('pragma foreign_keys'); - return array_filter(array_merge([ + return array_filter(array_merge( + [ $foreignKeyConstraintsEnabled ? $this->compileDisableForeignKeyConstraints() : null, sprintf('create table %s (%s%s%s)', $tempTable, diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 01c885c7..607eaf07 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -25,7 +25,8 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $oldColumns = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())); foreach ($blueprint->getChangedColumns() as $column) { - $sql = sprintf('alter table %s alter column %s %s', + $sql = sprintf( + 'alter table %s alter column %s %s', $this->wrapTable($blueprint), $this->wrap($column), $this->getType($column) From e9915c9715a087a296f2d05e2fd7c81b3f82e70c Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 5 Apr 2024 17:49:26 -0400 Subject: [PATCH 034/197] try to appease code quality --- .../Schema/Grammars/SQLiteGrammar.php | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 52356a12..0b0d2de0 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -103,18 +103,35 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $foreignKeyConstraintsEnabled = $connection->scalar('pragma foreign_keys'); - return array_filter(array_merge( - [ - $foreignKeyConstraintsEnabled ? $this->compileDisableForeignKeyConstraints() : null, - sprintf('create table %s (%s%s%s)', - $tempTable, - implode(', ', $columns), - $this->addForeignKeys($foreignKeys), - $autoIncrementColumn ? '' : $this->addPrimaryKeys($primary->first()) - ), - sprintf('insert into %s (%s) select %s from %s', $tempTable, $columnNames, $columnNames, $table), - sprintf('drop table %s', $table), - sprintf('alter table %s rename to %s', $tempTable, $table), - ], $indexes, [$foreignKeyConstraintsEnabled ? $this->compileEnableForeignKeyConstraints() : null])); + return array_filter( + array_merge( + [ + $foreignKeyConstraintsEnabled ? $this->compileDisableForeignKeyConstraints() : null, + sprintf('create table %s (%s%s%s)', + $tempTable, + implode(', ', $columns), + $this->addForeignKeys($foreignKeys), + $autoIncrementColumn ? '' : $this->addPrimaryKeys($primary->first()) + ), + sprintf('insert into %s (%s) select %s from %s', + $tempTable, + $columnNames, + $columnNames, + $table + ), + sprintf('drop table %s', + $table + ), + sprintf('alter table %s rename to %s', + $tempTable, + $table + ), + ], + $indexes, + [ + $foreignKeyConstraintsEnabled ? $this->compileEnableForeignKeyConstraints() : null + ] + ) + ); } } From 0a35475e3c6ef1ce0525f3f7af92754a107b32a4 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 5 Apr 2024 19:46:09 -0400 Subject: [PATCH 035/197] fix phpstan ignore rules; fix overriden Grammars --- composer.json | 2 +- phpstan-baseline.neon | 293 +++++++++--------- src/Database/Connections/MySqlConnection.php | 2 +- src/Database/Schema/Grammars/MySqlGrammar.php | 2 +- .../Schema/Grammars/PostgresGrammar.php | 2 +- .../Schema/Grammars/SQLiteGrammar.php | 12 +- .../Schema/Grammars/SqlServerGrammar.php | 2 +- 7 files changed, 152 insertions(+), 163 deletions(-) diff --git a/composer.json b/composer.json index 27cd5cc2..c2595600 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ "mockery/mockery": "^1.6|^2.0", "dms/phpunit-arraysubset-asserts": "^0.5", "orchestra/testbench": "^9.0", - "larastan/larastan": "^2.8.1" + "larastan/larastan": "^2.9.2" }, "suggest": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ace12018..1dc54b1a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,27 +1,27 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\)\\:\\:getOriginalEncryptableValues\\(\\)\\.$#" + message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\)::getOriginalEncryptableValues\\(\\)\\.$#" count: 1 path: src/Auth/Models/Group.php - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\)\\:\\:getOriginalHashValues\\(\\)\\.$#" + message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\)::getOriginalHashValues\\(\\)\\.$#" count: 1 path: src/Auth/Models/Group.php - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\)\\:\\:getOriginalEncryptableValues\\(\\)\\.$#" + message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\)::getOriginalEncryptableValues\\(\\)\\.$#" count: 1 path: src/Auth/Models/Role.php - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\)\\:\\:getOriginalHashValues\\(\\)\\.$#" + message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\)::getOriginalHashValues\\(\\)\\.$#" count: 1 path: src/Auth/Models/Role.php - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\User\\)\\:\\:getOriginalEncryptableValues\\(\\)\\.$#" + message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\User\\)::getOriginalEncryptableValues\\(\\)\\.$#" count: 1 path: src/Auth/Models/User.php @@ -31,57 +31,57 @@ parameters: path: src/Database/Capsule/Manager.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface\\:\\:getName\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface::getName\\(\\)\\.$#" count: 1 path: src/Database/MemoryCache.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$is_bind\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$is_bind\\.$#" count: 1 path: src/Database/Model.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$master_field\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$master_field\\.$#" count: 1 path: src/Database/Model.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$pivot_data\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$pivot_data\\.$#" count: 1 path: src/Database/Model.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$slave_id\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$slave_id\\.$#" count: 1 path: src/Database/Model.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$slave_type\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$slave_type\\.$#" count: 1 path: src/Database/Model.php - - message: "#^Method Winter\\\\Storm\\\\Database\\\\Model\\:\\:getDeferredBindingRecords\\(\\) should return Winter\\\\Storm\\\\Database\\\\Collection but returns Illuminate\\\\Database\\\\Eloquent\\\\Collection\\\\.$#" + message: "#^Method Winter\\\\Storm\\\\Database\\\\Model::getDeferredBindingRecords\\(\\) should return Winter\\\\Storm\\\\Database\\\\Collection but returns Illuminate\\\\Database\\\\Eloquent\\\\Collection\\\\.$#" count: 1 path: src/Database/Model.php - - message: "#^Return type \\(Winter\\\\Storm\\\\Database\\\\Pivot\\) of method Winter\\\\Storm\\\\Database\\\\Model\\:\\:newPivot\\(\\) should be compatible with return type \\(Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Pivot\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:newPivot\\(\\)$#" + message: "#^Return type \\(Winter\\\\Storm\\\\Database\\\\Pivot\\) of method Winter\\\\Storm\\\\Database\\\\Model::newPivot\\(\\) should be compatible with return type \\(Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Pivot\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Model::newPivot\\(\\)$#" count: 1 path: src/Database/Model.php - - message: "#^Static property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$dispatcher \\(Illuminate\\\\Contracts\\\\Events\\\\Dispatcher\\) in isset\\(\\) is not nullable\\.$#" + message: "#^Static property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$dispatcher \\(Illuminate\\\\Contracts\\\\Events\\\\Dispatcher\\) in isset\\(\\) is not nullable\\.$#" count: 1 path: src/Database/Model.php - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Model\\:\\:errors\\(\\)\\.$#" + message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Model::errors\\(\\)\\.$#" count: 1 path: src/Database/ModelException.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:deleteCancel\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::deleteCancel\\(\\)\\.$#" count: 2 path: src/Database/Models/DeferredBinding.php @@ -91,7 +91,7 @@ parameters: path: src/Database/MorphPivot.php - - message: "#^Parameter \\#1 \\$ids of method Winter\\\\Storm\\\\Database\\\\Pivot\\:\\:newQueryForRestoration\\(\\) expects array\\\\|string, int given\\.$#" + message: "#^Parameter \\#1 \\$ids of method Winter\\\\Storm\\\\Database\\\\Pivot::newQueryForRestoration\\(\\) expects array\\\\|string, int given\\.$#" count: 1 path: src/Database/MorphPivot.php @@ -101,107 +101,92 @@ parameters: path: src/Database/MorphPivot.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getLeftColumnName\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getLeftColumnName\\(\\)\\.$#" count: 1 path: src/Database/NestedTreeScope.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$concats\\.$#" - count: 2 - path: src/Database/Query/Grammars/MySqlGrammar.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$concats\\.$#" - count: 2 - path: src/Database/Query/Grammars/PostgresGrammar.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$concats\\.$#" - count: 2 - path: src/Database/Query/Grammars/SQLiteGrammar.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$concats\\.$#" - count: 2 - path: src/Database/Query/Grammars/SqlServerGrammar.php + message: "#^Access to an undefined property Illuminate.+(Builder|(Column|Index)Definition|Fluent)::\\$(.+)\\.$#" + paths: + - src/Database/*/Grammars/*Grammar.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface\\:\\:getName\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface::getName\\(\\)\\.$#" count: 1 path: src/Database/QueryBuilder.php - - message: "#^Property Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:\\$orders \\(array\\) does not accept null\\.$#" + message: "#^Property Illuminate\\\\Database\\\\Query\\\\Builder::\\$orders \\(array\\) does not accept null\\.$#" count: 1 path: src/Database/QueryBuilder.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/AttachMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/AttachMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachMany.php - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\AttachMany\\:\\:getRelationExistenceQueryForSelfJoin\\(\\)\\.$#" + message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\AttachMany::getRelationExistenceQueryForSelfJoin\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachMany.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/AttachOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/AttachOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachOne.php - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\AttachOne\\:\\:getRelationExistenceQueryForSelfJoin\\(\\)\\.$#" + message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\AttachOne::getRelationExistenceQueryForSelfJoin\\(\\)\\.$#" count: 1 path: src/Database/Relations/AttachOne.php @@ -211,172 +196,172 @@ parameters: path: src/Database/Relations/AttachOne.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/BelongsTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:bindEventOnce\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::bindEventOnce\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/BelongsTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsTo.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$sessionKey\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$sessionKey\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:bindDeferred\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::bindDeferred\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:bindEventOnce\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::bindEventOnce\\(\\)\\.$#" count: 2 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:fireEvent\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::fireEvent\\(\\)\\.$#" count: 4 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getRelationDefinition\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getRelationDefinition\\(\\)\\.$#" count: 3 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:newRelationPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::newRelationPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:reloadRelations\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::reloadRelations\\(\\)\\.$#" count: 2 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:unbindDeferred\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::unbindDeferred\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:lists\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany\\:\\:flushDuplicateCache\\(\\)\\.$#" + message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::flushDuplicateCache\\(\\)\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:paginate\\(\\) expects array, int\\|null given\\.$#" + message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects array, int\\|null given\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#" + message: "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#" + message: "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:paginate\\(\\) expects string, array given\\.$#" + message: "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects string, array given\\.$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#" + message: "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" count: 1 path: src/Database/Relations/BelongsToMany.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/HasMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/HasMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasMany.php @@ -396,62 +381,62 @@ parameters: path: src/Database/Relations/HasMany.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/HasManyThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/HasManyThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasManyThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasManyThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasManyThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasManyThrough.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/HasOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/HasOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasOne.php @@ -461,62 +446,62 @@ parameters: path: src/Database/Relations/HasOne.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/HasOneThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/HasOneThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasOneThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasOneThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasOneThrough.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/HasOneThrough.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/MorphMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/MorphMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphMany.php @@ -536,47 +521,47 @@ parameters: path: src/Database/Relations/MorphMany.php - - message: "#^Parameter \\#1 \\$query of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOneOrMany\\\\:\\:__construct\\(\\) expects Illuminate\\\\Database\\\\Eloquent\\\\Builder\\, Illuminate\\\\Database\\\\Eloquent\\\\Builder\\ given\\.$#" + message: "#^Parameter \\#1 \\$query of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOneOrMany\\::__construct\\(\\) expects Illuminate\\\\Database\\\\Eloquent\\\\Builder\\, Illuminate\\\\Database\\\\Eloquent\\\\Builder\\ given\\.$#" count: 1 path: src/Database/Relations/MorphMany.php - - message: "#^Parameter \\$parent of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphMany\\:\\:__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" + message: "#^Parameter \\$parent of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphMany::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" count: 1 path: src/Database/Relations/MorphMany.php - - message: "#^Parameter \\$query of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphMany\\:\\:__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" + message: "#^Parameter \\$query of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphMany::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" count: 1 path: src/Database/Relations/MorphMany.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/MorphOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/MorphOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphOne.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphOne.php @@ -586,177 +571,177 @@ parameters: path: src/Database/Relations/MorphOne.php - - message: "#^Parameter \\#1 \\$query of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOneOrMany\\\\:\\:__construct\\(\\) expects Illuminate\\\\Database\\\\Eloquent\\\\Builder\\, Illuminate\\\\Database\\\\Eloquent\\\\Builder\\ given\\.$#" + message: "#^Parameter \\#1 \\$query of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOneOrMany\\::__construct\\(\\) expects Illuminate\\\\Database\\\\Eloquent\\\\Builder\\, Illuminate\\\\Database\\\\Eloquent\\\\Builder\\ given\\.$#" count: 1 path: src/Database/Relations/MorphOne.php - - message: "#^Parameter \\$parent of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphOne\\:\\:__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" + message: "#^Parameter \\$parent of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphOne::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" count: 1 path: src/Database/Relations/MorphOne.php - - message: "#^Parameter \\$query of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphOne\\:\\:__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" + message: "#^Parameter \\$query of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphOne::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" count: 1 path: src/Database/Relations/MorphOne.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/MorphTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:bindEventOnce\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::bindEventOnce\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/MorphTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphTo.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphTo.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\:\\:\\$countMode\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getForeignKey\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" count: 3 path: src/Database/Relations/MorphToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:select\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withDefault\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withPivot\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:withTimestamps\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:lists\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany\\:\\:flushDuplicateCache\\(\\)\\.$#" + message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany::flushDuplicateCache\\(\\)\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:paginate\\(\\) expects array, int\\|null given\\.$#" + message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects array, int\\|null given\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#" + message: "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany::paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#" + message: "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany::paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:paginate\\(\\) expects string, array given\\.$#" + message: "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects string, array given\\.$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany\\:\\:paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\\\:\\:paginate\\(\\)$#" + message: "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany::paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" count: 1 path: src/Database/Relations/MorphToMany.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getSortOrderColumn\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getSortOrderColumn\\(\\)\\.$#" count: 1 path: src/Database/SortableScope.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$children\\.$#" + message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$children\\.$#" count: 1 path: src/Database/TreeCollection.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:getParentId\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getParentId\\(\\)\\.$#" count: 1 path: src/Database/TreeCollection.php - - message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue\\:\\:queue\\(\\)$#" + message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" count: 1 path: src/Mail/Mailer.php - - message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer\\:\\:queue\\(\\)$#" + message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::queue\\(\\)$#" count: 1 path: src/Mail/Mailer.php - - message: "#^Parameter \\#2 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:queueOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer\\:\\:queueOn\\(\\)$#" + message: "#^Parameter \\#2 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queueOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::queueOn\\(\\)$#" count: 1 path: src/Mail/Mailer.php - - message: "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue\\:\\:later\\(\\)$#" + message: "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::later\\(\\)$#" count: 1 path: src/Mail/Mailer.php - - message: "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer\\:\\:later\\(\\)$#" + message: "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::later\\(\\)$#" count: 1 path: src/Mail/Mailer.php - - message: "#^Parameter \\#3 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer\\:\\:laterOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer\\:\\:laterOn\\(\\)$#" + message: "#^Parameter \\#3 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::laterOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::laterOn\\(\\)$#" count: 1 path: src/Mail/Mailer.php - - message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue\\:\\:queue\\(\\)$#" + message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" count: 1 path: src/Support/Testing/Fakes/MailFake.php - - message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake\\:\\:queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Support\\\\Testing\\\\Fakes\\\\MailFake\\:\\:queue\\(\\)$#" + message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\)$#" count: 1 path: src/Support/Testing/Fakes/MailFake.php - - message: "#^Return type \\(void\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake\\:\\:send\\(\\) should be compatible with return type \\(Illuminate\\\\Mail\\\\SentMessage\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\Mailer\\:\\:send\\(\\)$#" + message: "#^Return type \\(void\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::send\\(\\) should be compatible with return type \\(Illuminate\\\\Mail\\\\SentMessage\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\Mailer::send\\(\\)$#" count: 1 path: src/Support/Testing/Fakes/MailFake.php diff --git a/src/Database/Connections/MySqlConnection.php b/src/Database/Connections/MySqlConnection.php index e8f80901..dbcf04df 100644 --- a/src/Database/Connections/MySqlConnection.php +++ b/src/Database/Connections/MySqlConnection.php @@ -4,8 +4,8 @@ use Illuminate\Database\Schema\MySqlBuilder; use Illuminate\Database\Query\Processors\MySqlProcessor; -use Winter\Storm\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar; use Winter\Storm\Database\Query\Grammars\MySqlGrammar as QueryGrammar; +use Winter\Storm\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar diff --git a/src/Database/Schema/Grammars/MySqlGrammar.php b/src/Database/Schema/Grammars/MySqlGrammar.php index c4d777c8..1cb8d2a0 100755 --- a/src/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Database/Schema/Grammars/MySqlGrammar.php @@ -33,7 +33,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $this->getType($column) ); - $oldColum = new Fluent($prevColumns->where('name', $name)->first()); + $oldColumn = new Fluent($oldColumns->where('name', $column->name)->first()); $columns[] = $this->addLegacyModifiers($sql, $blueprint, $column, $oldColumn); } diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 62acdde9..bdeb1ab7 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -27,7 +27,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection foreach ($blueprint->getChangedColumns() as $column) { $changes = ['type '.$this->getType($column).$this->modifyCollate($blueprint, $column)]; - $oldColum = new Fluent($prevColumns->where('name', $name)->first()); + $oldColumn = new Fluent($oldColumns->where('name', $column->name)->first()); foreach ($this->modifiers as $modifier) { if ($modifier === 'Collate') { continue; diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 0b0d2de0..4ee06bec 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -107,22 +107,26 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection array_merge( [ $foreignKeyConstraintsEnabled ? $this->compileDisableForeignKeyConstraints() : null, - sprintf('create table %s (%s%s%s)', + sprintf( + 'create table %s (%s%s%s)', $tempTable, implode(', ', $columns), $this->addForeignKeys($foreignKeys), $autoIncrementColumn ? '' : $this->addPrimaryKeys($primary->first()) ), - sprintf('insert into %s (%s) select %s from %s', + sprintf( + 'insert into %s (%s) select %s from %s', $tempTable, $columnNames, $columnNames, $table ), - sprintf('drop table %s', + sprintf( + 'drop table %s', $table ), - sprintf('alter table %s rename to %s', + sprintf( + 'alter table %s rename to %s', $tempTable, $table ), diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 607eaf07..11b013c4 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -32,7 +32,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $this->getType($column) ); - $oldColum = new Fluent($prevColumns->where('name', $name)->first()); + $oldColumn = new Fluent($oldColumns->where('name', $column->name)->first()); foreach ($this->modifiers as $modifier) { if (method_exists($this, $method = "modify{$modifier}")) { From 9a6233d4801f3d0cbc18761f98cadaa95d0e837b Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 5 Apr 2024 19:52:13 -0400 Subject: [PATCH 036/197] downgrade larastan --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c2595600..27cd5cc2 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ "mockery/mockery": "^1.6|^2.0", "dms/phpunit-arraysubset-asserts": "^0.5", "orchestra/testbench": "^9.0", - "larastan/larastan": "^2.9.2" + "larastan/larastan": "^2.8.1" }, "suggest": { From 5dca01ebec53d87e041c13fd44b0e0ae628d76cc Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 08:53:50 -0400 Subject: [PATCH 037/197] simplify phpstan baseline rules --- phpstan-baseline.neon | 265 ++++-------------------------------------- 1 file changed, 21 insertions(+), 244 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1dc54b1a..db79a587 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,29 +1,11 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\)::getOriginalEncryptableValues\\(\\)\\.$#" - count: 1 - path: src/Auth/Models/Group.php - - - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Group\\)::getOriginalHashValues\\(\\)\\.$#" - count: 1 - path: src/Auth/Models/Group.php - - - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\)::getOriginalEncryptableValues\\(\\)\\.$#" - count: 1 - path: src/Auth/Models/Role.php - - - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\Role\\)::getOriginalHashValues\\(\\)\\.$#" - count: 1 - path: src/Auth/Models/Role.php - - - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\User\\)::getOriginalEncryptableValues\\(\\)\\.$#" - count: 1 - path: src/Auth/Models/User.php + message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\(Group|Role|User)\\)::getOriginal(Encryptable|Hash)Values\\(\\)\\.$#" + paths: + - src/Auth/Models/Group.php + - src/Auth/Models/Role.php + - src/Auth/Models/User.php - message: "#^Parameter \\#1 \\$app of class Illuminate\\\\Database\\\\DatabaseManager constructor expects Illuminate\\\\Contracts\\\\Foundation\\\\Application, Illuminate\\\\Contracts\\\\Container\\\\Container given\\.$#" @@ -36,43 +18,11 @@ parameters: path: src/Database/MemoryCache.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$is_bind\\.$#" - count: 1 - path: src/Database/Model.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$master_field\\.$#" - count: 1 - path: src/Database/Model.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$pivot_data\\.$#" - count: 1 - path: src/Database/Model.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$slave_id\\.$#" - count: 1 - path: src/Database/Model.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$slave_type\\.$#" - count: 1 - path: src/Database/Model.php - - - - message: "#^Method Winter\\\\Storm\\\\Database\\\\Model::getDeferredBindingRecords\\(\\) should return Winter\\\\Storm\\\\Database\\\\Collection but returns Illuminate\\\\Database\\\\Eloquent\\\\Collection\\\\.$#" - count: 1 - path: src/Database/Model.php - - - - message: "#^Return type \\(Winter\\\\Storm\\\\Database\\\\Pivot\\) of method Winter\\\\Storm\\\\Database\\\\Model::newPivot\\(\\) should be compatible with return type \\(Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Pivot\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Model::newPivot\\(\\)$#" - count: 1 - path: src/Database/Model.php - - - - message: "#^Static property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$dispatcher \\(Illuminate\\\\Contracts\\\\Events\\\\Dispatcher\\) in isset\\(\\) is not nullable\\.$#" - count: 1 + messages: + - "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$(is_bind|master_field|pivot_data|slave_(id|type))\\.$#" + - "#^Method Winter\\\\Storm\\\\Database\\\\Model::getDeferredBindingRecords\\(\\) should return Winter\\\\Storm\\\\Database\\\\Collection but returns Illuminate\\\\Database\\\\Eloquent\\\\Collection\\\\.$#" + - "#^Return type \\(Winter\\\\Storm\\\\Database\\\\Pivot\\) of method Winter\\\\Storm\\\\Database\\\\Model::newPivot\\(\\) should be compatible with return type \\(Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Pivot\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Model::newPivot\\(\\)$#" + - "#^Static property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$dispatcher \\(Illuminate\\\\Contracts\\\\Events\\\\Dispatcher\\) in isset\\(\\) is not nullable\\.$#" path: src/Database/Model.php - @@ -108,7 +58,8 @@ parameters: - message: "#^Access to an undefined property Illuminate.+(Builder|(Column|Index)Definition|Fluent)::\\$(.+)\\.$#" paths: - - src/Database/*/Grammars/*Grammar.php + - src/Database/Schema/Grammars/*Grammar.php + - src/Database/Query/Grammars/*Grammar.php - message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface::getName\\(\\)\\.$#" @@ -121,189 +72,15 @@ parameters: path: src/Database/QueryBuilder.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/AttachMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/AttachMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachMany.php - - - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\AttachMany::getRelationExistenceQueryForSelfJoin\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachMany.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/AttachOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/AttachOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachOne.php - - - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\AttachOne::getRelationExistenceQueryForSelfJoin\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/AttachOne.php - - - - message: "#^Call to private method delete\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne\\\\.$#" - count: 3 - path: src/Database/Relations/AttachOne.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/BelongsTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::bindEventOnce\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/BelongsTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsTo.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$sessionKey\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::bindDeferred\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::bindEventOnce\\(\\)\\.$#" - count: 2 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::fireEvent\\(\\)\\.$#" - count: 4 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getRelationDefinition\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::newRelationPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::reloadRelations\\(\\)\\.$#" - count: 2 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::unbindDeferred\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php + messages: + - "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$sessionKey\\.$#" + - "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" + - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::(getForeignKey|select|with(Default|Pivot|Timestamps))\\(\\)\\.$#" + - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::((un)?bind(Deferred|EventOnce)|fireEvent|getRelationDefinition|newRelationPivot|reloadRelations)\\(\\)\\.$#" + - "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" + - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\Attach(Many|One)::getRelationExistenceQueryForSelfJoin\\(\\)\\.$#" + - "#^Call to private method delete\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne\\\\.$#" + path: src/Database/Relations/*.php - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::flushDuplicateCache\\(\\)\\.$#" From f136a78e147bc351ca088755bb3a38f16d037446 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 08:56:36 -0400 Subject: [PATCH 038/197] simplify phpstan baseline rules --- phpstan-baseline.neon | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index db79a587..96125446 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -78,15 +78,10 @@ parameters: - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::(getForeignKey|select|with(Default|Pivot|Timestamps))\\(\\)\\.$#" - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::((un)?bind(Deferred|EventOnce)|fireEvent|getRelationDefinition|newRelationPivot|reloadRelations)\\(\\)\\.$#" - "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" - - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\Attach(Many|One)::getRelationExistenceQueryForSelfJoin\\(\\)\\.$#" + - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Attach|BelongsTo)(Many|One)::(flushDuplicateCache|getRelationExistenceQueryForSelfJoin)\\(\\)\\.$#" - "#^Call to private method delete\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne\\\\.$#" path: src/Database/Relations/*.php - - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::flushDuplicateCache\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects array, int\\|null given\\.$#" count: 1 From d4f7389fe53a00f3c1a74b5f8332d0b21e93273e Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 09:39:30 -0400 Subject: [PATCH 039/197] major simplifications to phpstan baseline rules --- phpstan-baseline.neon | 478 ++++-------------------------------------- 1 file changed, 40 insertions(+), 438 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 96125446..c5f878a9 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -36,18 +36,10 @@ parameters: path: src/Database/Models/DeferredBinding.php - - message: "#^Parameter \\#1 \\$haystack of function str_contains expects string, int given\\.$#" - count: 1 - path: src/Database/MorphPivot.php - - - - message: "#^Parameter \\#1 \\$ids of method Winter\\\\Storm\\\\Database\\\\Pivot::newQueryForRestoration\\(\\) expects array\\\\|string, int given\\.$#" - count: 1 - path: src/Database/MorphPivot.php - - - - message: "#^Parameter \\#2 \\$string of function explode expects string, int given\\.$#" - count: 1 + messages: + - "#^Parameter \\#1 \\$haystack of function str_contains expects string, int given\\.$#" + - "#^Parameter \\#1 \\$ids of method Winter\\\\Storm\\\\Database\\\\Pivot::newQueryForRestoration\\(\\) expects array\\\\|string, int given\\.$#" + - "#^Parameter \\#2 \\$string of function explode expects string, int given\\.$#" path: src/Database/MorphPivot.php - @@ -62,13 +54,9 @@ parameters: - src/Database/Query/Grammars/*Grammar.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface::getName\\(\\)\\.$#" - count: 1 - path: src/Database/QueryBuilder.php - - - - message: "#^Property Illuminate\\\\Database\\\\Query\\\\Builder::\\$orders \\(array\\) does not accept null\\.$#" - count: 1 + messages: + - "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface::getName\\(\\)\\.$#" + - "#^Property Illuminate\\\\Database\\\\Query\\\\Builder::\\$orders \\(array\\) does not accept null\\.$#" path: src/Database/QueryBuilder.php - @@ -78,384 +66,30 @@ parameters: - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::(getForeignKey|select|with(Default|Pivot|Timestamps))\\(\\)\\.$#" - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::((un)?bind(Deferred|EventOnce)|fireEvent|getRelationDefinition|newRelationPivot|reloadRelations)\\(\\)\\.$#" - "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" - - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Attach|BelongsTo)(Many|One)::(flushDuplicateCache|getRelationExistenceQueryForSelfJoin)\\(\\)\\.$#" - - "#^Call to private method delete\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne\\\\.$#" + - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Attach|(Belongs|Morph)To)(One|Many)::(flushDuplicateCache|getRelationExistenceQueryForSelfJoin)\\(\\)\\.$#" + - "#^Call to private method .+ of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Has|Morph)(One|Many)\\\\.$#" + - "#^If condition is always true\\.$#" path: src/Database/Relations/*.php - - message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects array, int\\|null given\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects string, array given\\.$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\BelongsToMany::paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" - count: 1 - path: src/Database/Relations/BelongsToMany.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/HasMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/HasMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasMany.php - - - - message: "#^Call to private method update\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany\\\\.$#" - count: 1 - path: src/Database/Relations/HasMany.php - - - - message: "#^Call to private method whereNotIn\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany\\\\.$#" - count: 1 - path: src/Database/Relations/HasMany.php - - - - message: "#^If condition is always true\\.$#" - count: 1 - path: src/Database/Relations/HasMany.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/HasManyThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/HasManyThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasManyThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasManyThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasManyThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasManyThrough.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/HasOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/HasOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasOne.php - - - - message: "#^Call to private method update\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasOne\\\\.$#" - count: 2 - path: src/Database/Relations/HasOne.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/HasOneThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/HasOneThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasOneThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasOneThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasOneThrough.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/HasOneThrough.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Call to private method update\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphMany\\\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Call to private method whereNotIn\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphMany\\\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^If condition is always true\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Parameter \\#1 \\$query of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOneOrMany\\::__construct\\(\\) expects Illuminate\\\\Database\\\\Eloquent\\\\Builder\\, Illuminate\\\\Database\\\\Eloquent\\\\Builder\\ given\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Parameter \\$parent of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphMany::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Parameter \\$query of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphMany::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" - count: 1 - path: src/Database/Relations/MorphMany.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Call to private method update\\(\\) of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne\\\\.$#" - count: 2 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Parameter \\#1 \\$query of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOneOrMany\\::__construct\\(\\) expects Illuminate\\\\Database\\\\Eloquent\\\\Builder\\, Illuminate\\\\Database\\\\Eloquent\\\\Builder\\ given\\.$#" - count: 1 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Parameter \\$parent of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphOne::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" - count: 1 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Parameter \\$query of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphOne::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" - count: 1 - path: src/Database/Relations/MorphOne.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/MorphTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::bindEventOnce\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/MorphTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphTo.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphTo.php - - - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany::\\$countMode\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getForeignKey\\(\\)\\.$#" - count: 3 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::select\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withDefault\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withPivot\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::withTimestamps\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany::flushDuplicateCache\\(\\)\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects array, int\\|null given\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany::paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany::paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" - count: 1 - path: src/Database/Relations/MorphToMany.php - - - - message: "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects string, array given\\.$#" - count: 1 - path: src/Database/Relations/MorphToMany.php + messages: + - "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects array, int\\|null given\\.$#" + - "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::paginate\\(\\)$#" + - "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::paginate\\(\\)$#" + - "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects string, array given\\.$#" + - "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::paginate\\(\\)$#" + paths: + - src/Database/Relations/BelongsToMany.php + - src/Database/Relations/MorphToMany.php - - message: "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\MorphToMany::paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany\\::paginate\\(\\)$#" - count: 1 - path: src/Database/Relations/MorphToMany.php + messages: + - "#^Parameter \\#1 \\$query of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOneOrMany\\::__construct\\(\\) expects Illuminate\\\\Database\\\\Eloquent\\\\Builder\\, Illuminate\\\\Database\\\\Eloquent\\\\Builder\\ given\\.$#" + - "#^Parameter \\$parent of method Winter\\\\Storm\\\\Database\\\\Relations\\\\Morph(One|Many)::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" + - "#^Parameter \\$query of method Winter\\\\Storm\\\\Database\\\\Relations\\\\Morph(One|Many)::__construct\\(\\) has invalid type Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\TRelatedModel\\.$#" + paths: + - src/Database/Relations/MorphOne.php + - src/Database/Relations/MorphMany.php - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getSortOrderColumn\\(\\)\\.$#" @@ -463,58 +97,26 @@ parameters: path: src/Database/SortableScope.php - - message: "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$children\\.$#" - count: 1 - path: src/Database/TreeCollection.php - - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getParentId\\(\\)\\.$#" - count: 1 + messages: + - "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$children\\.$#" + - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getParentId\\(\\)\\.$#" path: src/Database/TreeCollection.php - - message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" - count: 1 - path: src/Mail/Mailer.php - - - - message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::queue\\(\\)$#" - count: 1 - path: src/Mail/Mailer.php - - - - message: "#^Parameter \\#2 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queueOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::queueOn\\(\\)$#" - count: 1 - path: src/Mail/Mailer.php - - - - message: "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::later\\(\\)$#" - count: 1 - path: src/Mail/Mailer.php - - - - message: "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::later\\(\\)$#" - count: 1 - path: src/Mail/Mailer.php - - - - message: "#^Parameter \\#3 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::laterOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::laterOn\\(\\)$#" - count: 1 + messages: + - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" + - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::queue\\(\\)$#" + - "#^Parameter \\#2 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queueOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::queueOn\\(\\)$#" + - "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::later\\(\\)$#" + - "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::later\\(\\)$#" + - "#^Parameter \\#3 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::laterOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::laterOn\\(\\)$#" path: src/Mail/Mailer.php - - message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" - count: 1 - path: src/Support/Testing/Fakes/MailFake.php - - - - message: "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\)$#" - count: 1 - path: src/Support/Testing/Fakes/MailFake.php - - - - message: "#^Return type \\(void\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::send\\(\\) should be compatible with return type \\(Illuminate\\\\Mail\\\\SentMessage\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\Mailer::send\\(\\)$#" - count: 1 + messages: + - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" + - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\)$#" + - "#^Return type \\(void\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::send\\(\\) should be compatible with return type \\(Illuminate\\\\Mail\\\\SentMessage\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\Mailer::send\\(\\)$#" path: src/Support/Testing/Fakes/MailFake.php - From 158573ecfc1d67addbcd19c328ae83a4b2978b3f Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 09:55:09 -0400 Subject: [PATCH 040/197] ignore src/Scaffold/GeneratorCommand.php --- phpstan.neon | 1 + 1 file changed, 1 insertion(+) diff --git a/phpstan.neon b/phpstan.neon index a7a8abc0..8ea42470 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -11,6 +11,7 @@ parameters: - src/Parse/PHP/ArrayFile.php - src/Parse/PHP/ArrayPrinter.php - src/Foundation/Console/KeyGenerateCommand.php + - src/Scaffold/GeneratorCommand.php disableSchemaScan: true databaseMigrationsPath: - src/Auth/Migrations From eb133026bd61cc9cf7fb309f836961e6d5e55526 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 09:56:51 -0400 Subject: [PATCH 041/197] only use spaces in phpstan.neon --- phpstan.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index 8ea42470..291360af 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -11,7 +11,7 @@ parameters: - src/Parse/PHP/ArrayFile.php - src/Parse/PHP/ArrayPrinter.php - src/Foundation/Console/KeyGenerateCommand.php - - src/Scaffold/GeneratorCommand.php + - src/Scaffold/GeneratorCommand.php disableSchemaScan: true databaseMigrationsPath: - src/Auth/Migrations From 7d668136772d55a0e125cd3b3ab329d41b87187a Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 11:59:46 -0400 Subject: [PATCH 042/197] no need to wrap in Fluent --- src/Database/Schema/Grammars/MySqlGrammar.php | 2 +- src/Database/Schema/Grammars/PostgresGrammar.php | 2 +- src/Database/Schema/Grammars/SqlServerGrammar.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Database/Schema/Grammars/MySqlGrammar.php b/src/Database/Schema/Grammars/MySqlGrammar.php index 1cb8d2a0..15d6f51c 100755 --- a/src/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Database/Schema/Grammars/MySqlGrammar.php @@ -33,7 +33,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $this->getType($column) ); - $oldColumn = new Fluent($oldColumns->where('name', $column->name)->first()); + $oldColumn = $oldColumns->where('name', $column->name)->first(); $columns[] = $this->addLegacyModifiers($sql, $blueprint, $column, $oldColumn); } diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index bdeb1ab7..205644d6 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -27,7 +27,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection foreach ($blueprint->getChangedColumns() as $column) { $changes = ['type '.$this->getType($column).$this->modifyCollate($blueprint, $column)]; - $oldColumn = new Fluent($oldColumns->where('name', $column->name)->first()); + $oldColumn = $oldColumns->where('name', $column->name)->first(); foreach ($this->modifiers as $modifier) { if ($modifier === 'Collate') { continue; diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 11b013c4..f01815ca 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -32,7 +32,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $this->getType($column) ); - $oldColumn = new Fluent($oldColumns->where('name', $column->name)->first()); + $oldColumn = $oldColumns->where('name', $column->name)->first(); foreach ($this->modifiers as $modifier) { if (method_exists($this, $method = "modify{$modifier}")) { From 58d5b83777b2df2fc3c977280470637af44a27ce Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 12:03:38 -0400 Subject: [PATCH 043/197] add test for Blueprint::change() method --- .../Grammars/MySqlSchemaGrammarTest.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index 167637c7..e308f613 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -5,9 +5,11 @@ use Illuminate\Database\Connection; use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\Builder; use Illuminate\Database\Schema\ForeignIdColumnDefinition; use Winter\Storm\Database\Schema\Grammars\MySqlGrammar; use Mockery as m; +use PDO; use PHPUnit\Framework\TestCase; class MySqlSchemaGrammarTest extends TestCase @@ -1276,6 +1278,32 @@ public function testAddingComment() $this->assertSame("alter table `users` add `foo` varchar(255) not null comment 'Escape \\' when using words like it\\'s'", $statements[0]); } + public function testChangingColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->string('name')->nullable(); + + $connection = $this->getConnection(); + $grammar = $this->getGrammar(); + + $schemaBuilder = m::mock(Builder::class); + $schemaBuilder->shouldReceive('getColumns')->once()->andReturn($blueprint->getColumns()); + + $connection->shouldReceive('getSchemaBuilder')->once()->andReturn($schemaBuilder); + + $statements = $blueprint->toSql($connection, $grammar); + + $this->assertCount(1, $statements); + $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); + + $changeBlueprint = new Blueprint('users'); + $changeBlueprint->string('name')->default('admin')->change(); + $statements = $changeBlueprint->toSql($connection, $grammar); + + $this->assertCount(1, $statements); + $this->assertSame("alter table `users` modify `name` varchar(255) null default 'admin'", $statements[0]); + } + public function testCreateDatabase() { $connection = $this->getConnection(); From 01248282b937134537ad477ab7e51a3afeb4fa63 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 13:43:34 -0400 Subject: [PATCH 044/197] add test for Blueprint change() command for SqlServer & Postgres --- .../Grammars/Concerns/MySqlBasedGrammar.php | 52 +++++++++++++++++++ .../Schema/Grammars/MariaDbGrammar.php | 5 +- src/Database/Schema/Grammars/MySqlGrammar.php | 47 +---------------- .../Schema/Grammars/PostgresGrammar.php | 4 ++ .../Grammars/PostgresSchemaGrammarTest.php | 30 +++++++++++ .../Grammars/SqlServerSchemaGrammarTest.php | 27 ++++++++++ 6 files changed, 118 insertions(+), 47 deletions(-) create mode 100644 src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php diff --git a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php new file mode 100644 index 00000000..da1b79a6 --- /dev/null +++ b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php @@ -0,0 +1,52 @@ +getSchemaBuilder()->getColumns($blueprint->getTable())); + + foreach ($blueprint->getChangedColumns() as $column) { + $sql = sprintf( + '%s %s%s %s', + is_null($column->renameTo) ? 'modify' : 'change', + $this->wrap($column), + is_null($column->renameTo) ? '' : ' '.$this->wrap($column->renameTo), + $this->getType($column) + ); + + $oldColumn = $oldColumns->where('name', $column->name)->first(); + foreach ($this->modifiers as $modifier) { + if (method_exists($this, $method = "modify{$modifier}")) { + $mod = strtolower($modifier); + $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $sql .= $this->{$method}($blueprint, $col); + } + } + $columns[] = $sql; + } + + return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); + } +} diff --git a/src/Database/Schema/Grammars/MariaDbGrammar.php b/src/Database/Schema/Grammars/MariaDbGrammar.php index 9a574623..fbc4f6ca 100755 --- a/src/Database/Schema/Grammars/MariaDbGrammar.php +++ b/src/Database/Schema/Grammars/MariaDbGrammar.php @@ -2,6 +2,9 @@ namespace Winter\Storm\Database\Schema\Grammars; -class MariaDbGrammar extends MySqlGrammar +use Illuminate\Database\Schema\Grammars\MariaDbGrammar as MariaDbGrammarBase; + +class MariaDbGrammar extends MariaDbGrammarBase { + use Concerns\MySqlBasedGrammar; } diff --git a/src/Database/Schema/Grammars/MySqlGrammar.php b/src/Database/Schema/Grammars/MySqlGrammar.php index 15d6f51c..207f58a3 100755 --- a/src/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Database/Schema/Grammars/MySqlGrammar.php @@ -2,54 +2,9 @@ namespace Winter\Storm\Database\Schema\Grammars; -use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Grammars\MySqlGrammar as MySqlGrammarBase; -use Illuminate\Database\Connection; -use Illuminate\Support\Fluent; class MySqlGrammar extends MySqlGrammarBase { - /** - * Compile a change column command into a series of SQL statements. - * - * @param \Illuminate\Database\Schema\Blueprint $blueprint - * @param \Illuminate\Support\Fluent $command - * @param \Illuminate\Database\Connection $connection - * @return array|string - * - * @throws \RuntimeException - */ - public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) - { - $columns = []; - $oldColumns = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())); - - foreach ($blueprint->getChangedColumns() as $column) { - $sql = sprintf( - '%s %s%s %s', - is_null($column->renameTo) ? 'modify' : 'change', - $this->wrap($column), - is_null($column->renameTo) ? '' : ' '.$this->wrap($column->renameTo), - $this->getType($column) - ); - - $oldColumn = $oldColumns->where('name', $column->name)->first(); - $columns[] = $this->addLegacyModifiers($sql, $blueprint, $column, $oldColumn); - } - - return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); - } - - protected function addLegacyModifiers($sql, Blueprint $blueprint, Fluent $column, Fluent $oldColumn) - { - foreach ($this->modifiers as $modifier) { - if (method_exists($this, $method = "modify{$modifier}")) { - $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; - $sql .= $this->{$method}($blueprint, $col); - } - } - - return $sql; - } + use Concerns\MySqlBasedGrammar; } diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 205644d6..7bf58965 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -11,6 +11,10 @@ class PostgresGrammar extends PostgresGrammarBase { /** * Compile a change column command into a series of SQL statements. + * + * Starting with Laravel 11, previous column attributes do not persist when changing a column. + * This restores Laravel previous behavior where existing column attributes are kept + * unless they get changed by the new Blueprint. * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index 96b1222b..a861b0c0 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -1155,6 +1155,36 @@ public function testAddingMultiPolygon() $this->assertSame('alter table "geo" add column "coordinates" geometry(multipolygon) not null', $statements[0]); } + public function testChangingColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->string('name')->nullable(); + + $connection = $this->getConnection(); + $grammar = $this->getGrammar(); + + $schemaBuilder = m::mock(Builder::class); + $schemaBuilder->shouldReceive('getColumns')->once()->andReturn($blueprint->getColumns()); + + $connection->shouldReceive('getSchemaBuilder')->once()->andReturn($schemaBuilder); + + $statements = $blueprint->toSql($connection, $grammar); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "name" varchar(255) null', $statements[0]); + + $changeBlueprint = new Blueprint('users'); + $changeBlueprint->string('name')->default('admin')->change(); + $statements = $changeBlueprint->toSql($connection, $grammar); + + $this->assertCount(2, $statements); + + $parts = explode(', ', $statements[0]); + $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); + $this->assertSame('alter column "name" null', $parts[1]); + $this->assertSame("alter column \"name\" set default 'admin'", $parts[2]); + } + public function testCreateDatabase() { $connection = $this->getConnection(); diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index 3e9a50a0..aea3d716 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -862,6 +862,33 @@ public function testAddingGeneratedColumn() $this->assertSame('alter table "products" add "price" int not null, "discounted_virtual" as (price - 5), "discounted_stored" as (price - 5) persisted', $statements[0]); } + public function testChangingColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->string('name')->nullable(); + + $connection = $this->getConnection(); + $grammar = $this->getGrammar(); + + $schemaBuilder = m::mock(Builder::class); + $schemaBuilder->shouldReceive('getColumns')->once()->andReturn($blueprint->getColumns()); + + $connection->shouldReceive('getSchemaBuilder')->once()->andReturn($schemaBuilder); + + $statements = $blueprint->toSql($connection, $grammar); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add "name" nvarchar(255) null', $statements[0]); + + $changeBlueprint = new Blueprint('users'); + $changeBlueprint->string('name')->default('admin')->change(); + $statements = $changeBlueprint->toSql($connection, $grammar); + + $this->assertCount(3, $statements); + $this->assertSame('alter table "users" alter column "name" nvarchar(255) null', $statements[1]); + $this->assertSame('alter table "users" add default \'admin\' for "name"', $statements[2]); + } + public function testGrammarsAreMacroable() { // compileReplace macro. From fad6c26ca1d0b7015b0d5f07cb8d06fec47db86b Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 13:57:54 -0400 Subject: [PATCH 045/197] remove extra white space --- src/Database/Schema/Grammars/PostgresGrammar.php | 2 +- src/Database/Schema/Grammars/SqlServerGrammar.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 7bf58965..e62b2f36 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -11,7 +11,7 @@ class PostgresGrammar extends PostgresGrammarBase { /** * Compile a change column command into a series of SQL statements. - * + * * Starting with Laravel 11, previous column attributes do not persist when changing a column. * This restores Laravel previous behavior where existing column attributes are kept * unless they get changed by the new Blueprint. diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index f01815ca..52e80237 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -12,6 +12,10 @@ class SqlServerGrammar extends SqlServerGrammarBase /** * Compile a change column command into a series of SQL statements. * + * Starting with Laravel 11, previous column attributes do not persist when changing a column. + * This restores Laravel previous behavior where existing column attributes are kept + * unless they get changed by the new Blueprint. + * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command * @param \Illuminate\Database\Connection $connection From 0ed6f64676151dcb74e51a1e1c87c797a95b99e3 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 14:33:33 -0400 Subject: [PATCH 046/197] add compileChange override --- .../Schema/Grammars/SQLiteGrammar.php | 60 +++++++++---------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 4ee06bec..b8b0ecf8 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -16,6 +16,10 @@ class SQLiteGrammar extends SQLiteGrammarBase /** * Compile a change column command into a series of SQL statements. * + * Starting with Laravel 11, previous column attributes do not persist when changing a column. + * This restores Laravel previous behavior where existing column attributes are kept + * unless they get changed by the new Blueprint. + * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command * @param \Illuminate\Database\Connection $connection @@ -32,47 +36,37 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $columnNames = []; $autoIncrementColumn = null; + $oldColumns = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())); + $columns = collect($schema->getColumns($table)) - ->map(function ($column) use ($blueprint, $changedColumns, &$columnNames, &$autoIncrementColumn) { + ->map(function ($column) use ($blueprint, $changedColumns, &$columnNames, &$autoIncrementColumn, $oldColumns) { $column = $changedColumns->first(fn ($col) => $col->name === $column['name'], $column); - if ($column instanceof Fluent) { - $name = $this->wrap($column); - $autoIncrementColumn = $column->autoIncrement ? $column->name : $autoIncrementColumn; + if (! $column instanceof Fluent) { + $column = new Fluent($column); + } - if (is_null($column->virtualAs) && is_null($column->virtualAsJson) && - is_null($column->storedAs) && is_null($column->storedAsJson)) { - $columnNames[] = $name; - } + $name = $this->wrap($column); + $autoIncrementColumn = $column->autoIncrement ? $column->name : $autoIncrementColumn; - return $this->addModifiers($name.' '.$this->getType($column), $blueprint, $column); - } else { - $name = $this->wrap($column['name']); - $autoIncrementColumn = $column['auto_increment'] ? $column['name'] : $autoIncrementColumn; - $isGenerated = ! is_null($column['generation']); + if (is_null($column->virtualAs) && is_null($column->virtualAsJson) && + is_null($column->storedAs) && is_null($column->storedAsJson) && + is_null($column->generation) + ) { + $columnNames[] = $name; + } - if (! $isGenerated) { - $columnNames[] = $name; - } + $oldColumn = $oldColumns->where('name', $column->name)->first(); + $sql = $name.' '.$this->getType($column); - return $this->addModifiers( - $name.' '.$column['type'], - $blueprint, - new ColumnDefinition([ - 'change' => true, - 'type' => $column['type_name'], - 'nullable' => $column['nullable'], - 'default' => $column['default'] ? new Expression($column['default']) : null, - 'autoIncrement' => $column['auto_increment'], - 'collation' => $column['collation'], - 'comment' => $column['comment'], - 'virtualAs' => $isGenerated && $column['generation']['type'] === 'virtual' - ? $column['generation']['expression'] : null, - 'storedAs' => $isGenerated && $column['generation']['type'] === 'stored' - ? $column['generation']['expression'] : null, - ]) - ); + foreach ($this->modifiers as $modifier) { + if (method_exists($this, $method = "modify{$modifier}")) { + $mod = strtolower($modifier); + $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $sql .= $this->{$method}($blueprint, $col); + } } + return $sql; })->all(); $foreignKeys = collect($schema->getForeignKeys($table))->map(fn ($foreignKey) => new ForeignKeyDefinition([ From 157451c430762600a44358992373f60bbe2c2627 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 14:33:43 -0400 Subject: [PATCH 047/197] add SQLite Blueprint change() unit test --- .../Grammars/MySqlSchemaGrammarTest.php | 4 +-- .../Grammars/PostgresSchemaGrammarTest.php | 4 +-- .../Grammars/SQLiteSchemaGrammarTest.php | 29 +++++++++++++++++++ .../Grammars/SqlServerSchemaGrammarTest.php | 4 +-- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index e308f613..1cc7c406 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -1287,9 +1287,9 @@ public function testChangingColumn() $grammar = $this->getGrammar(); $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->once()->andReturn($blueprint->getColumns()); + $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - $connection->shouldReceive('getSchemaBuilder')->once()->andReturn($schemaBuilder); + $connection->shouldReceive('getSchemaBuilder')->andReturn($schemaBuilder); $statements = $blueprint->toSql($connection, $grammar); diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index a861b0c0..2c634b07 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -1164,9 +1164,9 @@ public function testChangingColumn() $grammar = $this->getGrammar(); $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->once()->andReturn($blueprint->getColumns()); + $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - $connection->shouldReceive('getSchemaBuilder')->once()->andReturn($schemaBuilder); + $connection->shouldReceive('getSchemaBuilder')->andReturn($schemaBuilder); $statements = $blueprint->toSql($connection, $grammar); diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index a120e88b..b5d6fd91 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -832,6 +832,35 @@ public function testAddingGeneratedColumnByExpression() $this->assertSame('create table "products" ("price" integer not null, "discounted_virtual" integer as ("price" - 5), "discounted_stored" integer as ("price" - 5) stored)', $statements[0]); } + public function testChangingColumn() + { + $blueprint = new Blueprint('users'); + $blueprint->string('name')->nullable(); + + $connection = $this->getConnection(); + $grammar = $this->getGrammar(); + + $schemaBuilder = m::mock(Builder::class); + $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); + $schemaBuilder->shouldReceive('getForeignKeys')->andReturn([]); + $schemaBuilder->shouldReceive('getIndexes')->andReturn([]); + + $connection->shouldReceive('getSchemaBuilder')->andReturn($schemaBuilder); + $connection->shouldReceive('scalar')->andReturn(''); + + $statements = $blueprint->toSql($connection, $grammar); + + $this->assertCount(1, $statements); + $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); + + $changeBlueprint = new Blueprint('users'); + $changeBlueprint->string('name')->default('admin')->change(); + $statements = $changeBlueprint->toSql($connection, $grammar); + + $this->assertCount(4, $statements); + $this->assertStringContainsString("varchar default 'admin'", $statements[0]); + } + public function testGrammarsAreMacroable() { // compileReplace macro. diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index aea3d716..37ccd6f0 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -871,9 +871,9 @@ public function testChangingColumn() $grammar = $this->getGrammar(); $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->once()->andReturn($blueprint->getColumns()); + $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - $connection->shouldReceive('getSchemaBuilder')->once()->andReturn($schemaBuilder); + $connection->shouldReceive('getSchemaBuilder')->andReturn($schemaBuilder); $statements = $blueprint->toSql($connection, $grammar); From 8a7f0a37cd8bfbf636cd66dfb0e104cfdc56e22c Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 6 Apr 2024 14:45:18 -0400 Subject: [PATCH 048/197] be safe and properly instantiate a new ColumnDefinition object --- src/Database/Schema/Grammars/SQLiteGrammar.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index b8b0ecf8..9b01e5a1 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -43,15 +43,27 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $column = $changedColumns->first(fn ($col) => $col->name === $column['name'], $column); if (! $column instanceof Fluent) { - $column = new Fluent($column); + $isGenerated = ! is_null($column['generation']); + $column = new ColumnDefinition([ + 'change' => true, + 'type' => $column['type_name'], + 'nullable' => $column['nullable'], + 'default' => $column['default'] ? new Expression($column['default']) : null, + 'autoIncrement' => $column['auto_increment'], + 'collation' => $column['collation'], + 'comment' => $column['comment'], + 'virtualAs' => $isGenerated && $column['generation']['type'] === 'virtual' + ? $column['generation']['expression'] : null, + 'storedAs' => $isGenerated && $column['generation']['type'] === 'stored' + ? $column['generation']['expression'] : null, + ]); } $name = $this->wrap($column); $autoIncrementColumn = $column->autoIncrement ? $column->name : $autoIncrementColumn; if (is_null($column->virtualAs) && is_null($column->virtualAsJson) && - is_null($column->storedAs) && is_null($column->storedAsJson) && - is_null($column->generation) + is_null($column->storedAs) && is_null($column->storedAsJson) ) { $columnNames[] = $name; } From 6d8bd9dd8dd7824ff18690ac1943a3f7d83a26eb Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 7 Apr 2024 09:19:08 -0400 Subject: [PATCH 049/197] only use previous modifiers if not set in current Blueprint --- .../Schema/Grammars/Concerns/MySqlBasedGrammar.php | 2 +- src/Database/Schema/Grammars/PostgresGrammar.php | 2 +- src/Database/Schema/Grammars/SQLiteGrammar.php | 2 +- src/Database/Schema/Grammars/SqlServerGrammar.php | 2 +- .../Schema/Grammars/MySqlSchemaGrammarTest.php | 7 +++++++ .../Schema/Grammars/PostgresSchemaGrammarTest.php | 10 ++++++++++ .../Schema/Grammars/SQLiteSchemaGrammarTest.php | 7 +++++++ .../Schema/Grammars/SqlServerSchemaGrammarTest.php | 7 +++++++ 8 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php index da1b79a6..1a83fb5e 100644 --- a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php +++ b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php @@ -40,7 +40,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection foreach ($this->modifiers as $modifier) { if (method_exists($this, $method = "modify{$modifier}")) { $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $col = isset($oldColumn->{$mod}) && !isset($column->{$mod}) ? $oldColumn : $column; $sql .= $this->{$method}($blueprint, $col); } } diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index e62b2f36..16c81542 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -39,7 +39,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection if (method_exists($this, $method = "modify{$modifier}")) { $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $col = isset($oldColumn->{$mod}) && ! isset($column->{$mod}) ? $oldColumn : $column; $constraints = (array) $this->{$method}($blueprint, $col); foreach ($constraints as $constraint) { diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 9b01e5a1..095a96eb 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -74,7 +74,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection foreach ($this->modifiers as $modifier) { if (method_exists($this, $method = "modify{$modifier}")) { $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $col = isset($oldColumn->{$mod}) && !isset($column->{$mod}) ? $oldColumn : $column; $sql .= $this->{$method}($blueprint, $col); } } diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 52e80237..c2212c11 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -41,7 +41,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection foreach ($this->modifiers as $modifier) { if (method_exists($this, $method = "modify{$modifier}")) { $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) ? $oldColumn : $column; + $col = isset($oldColumn->{$mod}) && !isset($column->{$mod}) ? $oldColumn : $column; $sql .= $this->{$method}($blueprint, $col); } } diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index 1cc7c406..7100c67b 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -1302,6 +1302,13 @@ public function testChangingColumn() $this->assertCount(1, $statements); $this->assertSame("alter table `users` modify `name` varchar(255) null default 'admin'", $statements[0]); + + $otherChangeBlueprint = new Blueprint('users'); + $otherChangeBlueprint->string('name')->nullable(false)->change(); + $statements = $otherChangeBlueprint->toSql($connection, $grammar); + + $this->assertCount(1, $statements); + $this->assertSame("alter table `users` modify `name` varchar(255) not null", $statements[0]); } public function testCreateDatabase() diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index 2c634b07..5fa8a174 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -1183,6 +1183,16 @@ public function testChangingColumn() $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); $this->assertSame('alter column "name" null', $parts[1]); $this->assertSame("alter column \"name\" set default 'admin'", $parts[2]); + + $otherChangeBlueprint = new Blueprint('users'); + $otherChangeBlueprint->string('name')->nullable(false)->change(); + $statements = $otherChangeBlueprint->toSql($connection, $grammar); + + $this->assertCount(2, $statements); + + $parts = explode(', ', $statements[0]); + $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); + $this->assertSame('alter column "name" set not null', $parts[1]); } public function testCreateDatabase() diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index b5d6fd91..c43c7d6f 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -859,6 +859,13 @@ public function testChangingColumn() $this->assertCount(4, $statements); $this->assertStringContainsString("varchar default 'admin'", $statements[0]); + + $otherChangeBlueprint = new Blueprint('users'); + $otherChangeBlueprint->string('name')->nullable(false)->change(); + $statements = $otherChangeBlueprint->toSql($connection, $grammar); + + $this->assertCount(4, $statements); + $this->assertStringContainsString("varchar not null", $statements[0]); } public function testGrammarsAreMacroable() diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index 37ccd6f0..0adf8dc4 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -887,6 +887,13 @@ public function testChangingColumn() $this->assertCount(3, $statements); $this->assertSame('alter table "users" alter column "name" nvarchar(255) null', $statements[1]); $this->assertSame('alter table "users" add default \'admin\' for "name"', $statements[2]); + + $otherChangeBlueprint = new Blueprint('users'); + $otherChangeBlueprint->string('name')->nullable(false)->change(); + $statements = $otherChangeBlueprint->toSql($connection, $grammar); + + $this->assertCount(2, $statements); + $this->assertSame('alter table "users" alter column "name" nvarchar(255) not null', $statements[1]); } public function testGrammarsAreMacroable() From 75555345fe9ad7fd1323f6a2e4b32696baec6556 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 7 Apr 2024 10:19:24 -0400 Subject: [PATCH 050/197] only test change() Blueprint method --- .../Grammars/MySqlSchemaGrammarTest.php | 1478 +---------------- 1 file changed, 39 insertions(+), 1439 deletions(-) diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index 7100c67b..e8c89f64 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -19,1476 +19,76 @@ protected function tearDown(): void m::close(); } - public function testBasicCreateTable() + protected function setupConnection(Blueprint $blueprint) { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); - $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); - $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $blueprint->string('email'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `id` int unsigned not null auto_increment primary key, add `email` varchar(255) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->uuid('id')->primary(); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table `users` (`id` char(36) not null, primary key (`id`))', $statements[0]); - } - - public function testAutoIncrementStartingValue() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id')->startingValue(1000); - $blueprint->string('email'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); - $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); - $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); - $this->assertSame('alter table `users` auto_increment = 1000', $statements[1]); - } - - public function testAddColumnsWithMultipleAutoIncrementStartingValue() - { - $blueprint = new Blueprint('users'); - $blueprint->id()->from(100); - $blueprint->string('name')->from(200); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertEquals([ - 'alter table `users` add `id` bigint unsigned not null auto_increment primary key, add `name` varchar(255) not null', - 'alter table `users` auto_increment = 100', - ], $statements); - } - - public function testEngineCreateTable() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $blueprint->engine('InnoDB'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); - $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate 'utf8_unicode_ci' engine = InnoDB", $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); - $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); - $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn('InnoDB'); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate 'utf8_unicode_ci' engine = InnoDB", $statements[0]); - } - - public function testCharsetCollationCreateTable() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $blueprint->charset('utf8mb4'); - $blueprint->collation('utf8mb4_unicode_ci'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8mb4 collate 'utf8mb4_unicode_ci'", $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email')->charset('utf8mb4')->collation('utf8mb4_unicode_ci'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); - $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); - $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) character set utf8mb4 collate 'utf8mb4_unicode_ci' not null) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); - } - - public function testBasicCreateTableWithPrefix() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $grammar = $this->getGrammar(); - $grammar->setTablePrefix('prefix_'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $grammar); - - $this->assertCount(1, $statements); - $this->assertSame('create table `prefix_users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null)', $statements[0]); - } - - public function testCreateTemporaryTable() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->temporary(); - $blueprint->increments('id'); - $blueprint->string('email'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create temporary table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null)', $statements[0]); - } - - public function testDropTable() - { - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop table `users`', $statements[0]); - } - - public function testDropTableIfExists() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIfExists(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop table if exists `users`', $statements[0]); - } - - public function testDropColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop `foo`', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn(['foo', 'bar']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop `foo`, drop `bar`', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop `foo`, drop `bar`', $statements[0]); - } - - public function testDropPrimary() - { - $blueprint = new Blueprint('users'); - $blueprint->dropPrimary(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop primary key', $statements[0]); - } - - public function testDropUnique() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop index `foo`', $statements[0]); - } - - public function testDropIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIndex('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop index `foo`', $statements[0]); - } - - public function testDropSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->dropSpatialIndex(['coordinates']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` drop index `geo_coordinates_spatialindex`', $statements[0]); - } - - public function testDropForeign() - { - $blueprint = new Blueprint('users'); - $blueprint->dropForeign('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop foreign key `foo`', $statements[0]); - } - - public function testDropTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop `created_at`, drop `updated_at`', $statements[0]); - } - - public function testDropTimestampsTz() - { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestampsTz(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` drop `created_at`, drop `updated_at`', $statements[0]); - } - - public function testDropMorphs() - { - $blueprint = new Blueprint('photos'); - $blueprint->dropMorphs('imageable'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('alter table `photos` drop index `photos_imageable_type_imageable_id_index`', $statements[0]); - $this->assertSame('alter table `photos` drop `imageable_type`, drop `imageable_id`', $statements[1]); - } - - public function testRenameTable() - { - $blueprint = new Blueprint('users'); - $blueprint->rename('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('rename table `users` to `foo`', $statements[0]); - } - - public function testRenameIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->renameIndex('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` rename index `foo` to `bar`', $statements[0]); - } - - public function testAddingPrimaryKey() - { - $blueprint = new Blueprint('users'); - $blueprint->primary('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add primary key (`foo`)', $statements[0]); - } - - public function testAddingPrimaryKeyWithAlgorithm() - { - $blueprint = new Blueprint('users'); - $blueprint->primary('foo', 'bar', 'hash'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add primary key using hash(`foo`)', $statements[0]); - } - - public function testAddingUniqueKey() - { - $blueprint = new Blueprint('users'); - $blueprint->unique('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add unique `bar`(`foo`)', $statements[0]); - } - - public function testAddingIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add index `baz`(`foo`, `bar`)', $statements[0]); - } - - public function testAddingIndexWithAlgorithm() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz', 'hash'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add index `baz` using hash(`foo`, `bar`)', $statements[0]); - } - - public function testAddingFulltextIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->fulltext('body'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add fulltext `users_body_fulltext`(`body`)', $statements[0]); - } - - public function testAddingSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->spatialIndex('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add spatial index `geo_coordinates_spatialindex`(`coordinates`)', $statements[0]); - } - - public function testAddingFluentSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'point')->spatialIndex(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('alter table `geo` add spatial index `geo_coordinates_spatialindex`(`coordinates`)', $statements[1]); - } - - public function testAddingRawIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->rawIndex('(function(column))', 'raw_index'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add index `raw_index`((function(column)))', $statements[0]); - } - - public function testAddingForeignKey() - { - $blueprint = new Blueprint('users'); - $blueprint->foreign('foo_id')->references('id')->on('orders'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add constraint `users_foo_id_foreign` foreign key (`foo_id`) references `orders` (`id`)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->foreign('foo_id')->references('id')->on('orders')->cascadeOnDelete(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add constraint `users_foo_id_foreign` foreign key (`foo_id`) references `orders` (`id`) on delete cascade', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->foreign('foo_id')->references('id')->on('orders')->cascadeOnUpdate(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add constraint `users_foo_id_foreign` foreign key (`foo_id`) references `orders` (`id`) on update cascade', $statements[0]); - } - - public function testAddingIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `id` int unsigned not null auto_increment primary key', $statements[0]); - } - - public function testAddingSmallIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->smallIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `id` smallint unsigned not null auto_increment primary key', $statements[0]); - } - - public function testAddingID() - { - $blueprint = new Blueprint('users'); - $blueprint->id(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `id` bigint unsigned not null auto_increment primary key', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->id('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` bigint unsigned not null auto_increment primary key', $statements[0]); - } - - public function testAddingForeignID() - { - $blueprint = new Blueprint('users'); - $foreignId = $blueprint->foreignId('foo'); - $blueprint->foreignId('company_id')->constrained(); - $blueprint->foreignId('laravel_idea_id')->constrained(); - $blueprint->foreignId('team_id')->references('id')->on('teams'); - $blueprint->foreignId('team_column_id')->constrained('teams'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); - $this->assertSame([ - 'alter table `users` add `foo` bigint unsigned not null, add `company_id` bigint unsigned not null, add `laravel_idea_id` bigint unsigned not null, add `team_id` bigint unsigned not null, add `team_column_id` bigint unsigned not null', - 'alter table `users` add constraint `users_company_id_foreign` foreign key (`company_id`) references `companies` (`id`)', - 'alter table `users` add constraint `users_laravel_idea_id_foreign` foreign key (`laravel_idea_id`) references `laravel_ideas` (`id`)', - 'alter table `users` add constraint `users_team_id_foreign` foreign key (`team_id`) references `teams` (`id`)', - 'alter table `users` add constraint `users_team_column_id_foreign` foreign key (`team_column_id`) references `teams` (`id`)', - ], $statements); - } - - public function testAddingForeignIdSpecifyingIndexNameInConstraint() - { - $blueprint = new Blueprint('users'); - $blueprint->foreignId('company_id')->constrained(indexName: 'my_index'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertSame([ - 'alter table `users` add `company_id` bigint unsigned not null', - 'alter table `users` add constraint `my_index` foreign key (`company_id`) references `companies` (`id`)', - ], $statements); - } - - public function testAddingBigIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->bigIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `id` bigint unsigned not null auto_increment primary key', $statements[0]); - } - - public function testAddingColumnInTableFirst() - { - $blueprint = new Blueprint('users'); - $blueprint->string('name')->first(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `name` varchar(255) not null first', $statements[0]); - } - - public function testAddingColumnAfterAnotherColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->string('name')->after('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `name` varchar(255) not null after `foo`', $statements[0]); - } - - public function testAddingMultipleColumnsAfterAnotherColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->after('foo', function ($blueprint) { - $blueprint->string('one'); - $blueprint->string('two'); - }); - $blueprint->string('three'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `one` varchar(255) not null after `foo`, add `two` varchar(255) not null after `one`, add `three` varchar(255) not null', $statements[0]); - } - - public function testAddingGeneratedColumn() - { - $blueprint = new Blueprint('products'); - $blueprint->integer('price'); - $blueprint->integer('discounted_virtual')->virtualAs('price - 5'); - $blueprint->integer('discounted_stored')->storedAs('price - 5'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `products` add `price` int not null, add `discounted_virtual` int as (price - 5), add `discounted_stored` int as (price - 5) stored', $statements[0]); - - $blueprint = new Blueprint('products'); - $blueprint->integer('price'); - $blueprint->integer('discounted_virtual')->virtualAs('price - 5')->nullable(false); - $blueprint->integer('discounted_stored')->storedAs('price - 5')->nullable(false); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `products` add `price` int not null, add `discounted_virtual` int as (price - 5) not null, add `discounted_stored` int as (price - 5) stored not null', $statements[0]); - } - - public function testAddingGeneratedColumnWithCharset() - { - $blueprint = new Blueprint('links'); - $blueprint->string('url', 2083)->charset('ascii'); - $blueprint->string('url_hash_virtual', 64)->virtualAs('sha2(url, 256)')->charset('ascii'); - $blueprint->string('url_hash_stored', 64)->storedAs('sha2(url, 256)')->charset('ascii'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `links` add `url` varchar(2083) character set ascii not null, add `url_hash_virtual` varchar(64) character set ascii as (sha2(url, 256)), add `url_hash_stored` varchar(64) character set ascii as (sha2(url, 256)) stored', $statements[0]); - } - - public function testAddingGeneratedColumnByExpression() - { - $blueprint = new Blueprint('products'); - $blueprint->integer('price'); - $blueprint->integer('discounted_virtual')->virtualAs(new Expression('price - 5')); - $blueprint->integer('discounted_stored')->storedAs(new Expression('price - 5')); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `products` add `price` int not null, add `discounted_virtual` int as (price - 5), add `discounted_stored` int as (price - 5) stored', $statements[0]); - } - - public function testAddingInvisibleColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->string('secret', 64)->nullable(false)->invisible(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `secret` varchar(64) not null invisible', $statements[0]); - } - - public function testAddingString() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` varchar(255) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` varchar(100) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` varchar(100) null default \'bar\'', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default(new Expression('CURRENT TIMESTAMP')); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` varchar(100) null default CURRENT TIMESTAMP', $statements[0]); - } - - public function testAddingText() - { - $blueprint = new Blueprint('users'); - $blueprint->text('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` text not null', $statements[0]); - } - - public function testAddingBigInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` bigint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` bigint not null auto_increment primary key', $statements[0]); - } - - public function testAddingInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` int not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` int not null auto_increment primary key', $statements[0]); - } - - public function testAddingIncrementsWithStartingValues() - { - $blueprint = new Blueprint('users'); - $blueprint->id()->startingValue(1000); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('alter table `users` add `id` bigint unsigned not null auto_increment primary key', $statements[0]); - $this->assertSame('alter table `users` auto_increment = 1000', $statements[1]); - } - - public function testAddingMediumInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` mediumint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` mediumint not null auto_increment primary key', $statements[0]); - } - - public function testAddingSmallInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` smallint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` smallint not null auto_increment primary key', $statements[0]); - } - - public function testAddingTinyInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` tinyint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` tinyint not null auto_increment primary key', $statements[0]); - } - - public function testAddingFloat() - { - $blueprint = new Blueprint('users'); - $blueprint->float('foo', 5); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` float(5) not null', $statements[0]); - } - - public function testAddingDouble() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` double not null', $statements[0]); - } - - public function testAddingDecimal() - { - $blueprint = new Blueprint('users'); - $blueprint->decimal('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` decimal(5, 2) not null', $statements[0]); - } - - public function testAddingBoolean() - { - $blueprint = new Blueprint('users'); - $blueprint->boolean('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` tinyint(1) not null', $statements[0]); - } - - public function testAddingEnum() - { - $blueprint = new Blueprint('users'); - $blueprint->enum('role', ['member', 'admin']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `role` enum(\'member\', \'admin\') not null', $statements[0]); - } - - public function testAddingSet() - { - $blueprint = new Blueprint('users'); - $blueprint->set('role', ['member', 'admin']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `role` set(\'member\', \'admin\') not null', $statements[0]); - } - - public function testAddingJson() - { - $blueprint = new Blueprint('users'); - $blueprint->json('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` json not null', $statements[0]); - } - - public function testAddingJsonb() - { - $blueprint = new Blueprint('users'); - $blueprint->jsonb('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` json not null', $statements[0]); - } - - public function testAddingDate() - { - $blueprint = new Blueprint('users'); - $blueprint->date('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` date not null', $statements[0]); - } - - public function testAddingYear() - { - $blueprint = new Blueprint('users'); - $blueprint->year('birth_year'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `birth_year` year not null', $statements[0]); - } - - public function testAddingDateTime() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime(1) not null', $statements[0]); - } - - public function testAddingDateTimeWithDefaultCurrent() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo')->useCurrent(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime not null default CURRENT_TIMESTAMP', $statements[0]); - } - - public function testAddingDateTimeWithOnUpdateCurrent() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo')->useCurrentOnUpdate(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime not null on update CURRENT_TIMESTAMP', $statements[0]); - } - - public function testAddingDateTimeWithDefaultCurrentAndOnUpdateCurrent() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo')->useCurrent()->useCurrentOnUpdate(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP', $statements[0]); - } - - public function testAddingDateTimeWithDefaultCurrentOnUpdateCurrentAndPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo', 3)->useCurrent()->useCurrentOnUpdate(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime(3) not null default CURRENT_TIMESTAMP(3) on update CURRENT_TIMESTAMP(3)', $statements[0]); - } - - public function testAddingDateTimeTz() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('foo', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime(1) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` datetime not null', $statements[0]); - } - - public function testAddingTime() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` time not null', $statements[0]); - } - - public function testAddingTimeWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` time(1) not null', $statements[0]); - } - - public function testAddingTimeTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` time not null', $statements[0]); - } - - public function testAddingTimeTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` time(1) not null', $statements[0]); - } - - public function testAddingTimestamp() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp not null', $statements[0]); - } - - public function testAddingTimestampWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp(1) not null', $statements[0]); - } - - public function testAddingTimestampWithDefault() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at')->default('2015-07-22 11:43:17'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame("alter table `users` add `created_at` timestamp not null default '2015-07-22 11:43:17'", $statements[0]); - } - - public function testAddingTimestampWithDefaultCurrentSpecifyingPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at', 1)->useCurrent(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp(1) not null default CURRENT_TIMESTAMP(1)', $statements[0]); - } - - public function testAddingTimestampWithOnUpdateCurrentSpecifyingPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at', 1)->useCurrentOnUpdate(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp(1) not null on update CURRENT_TIMESTAMP(1)', $statements[0]); - } - - public function testAddingTimestampWithDefaultCurrentAndOnUpdateCurrentSpecifyingPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at', 1)->useCurrent()->useCurrentOnUpdate(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp(1) not null default CURRENT_TIMESTAMP(1) on update CURRENT_TIMESTAMP(1)', $statements[0]); - } - - public function testAddingTimestampTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp not null', $statements[0]); - } - - public function testAddingTimestampTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp(1) not null', $statements[0]); - } - - public function testAddingTimeStampTzWithDefault() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at')->default('2015-07-22 11:43:17'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame("alter table `users` add `created_at` timestamp not null default '2015-07-22 11:43:17'", $statements[0]); - } - - public function testAddingTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp null, add `updated_at` timestamp null', $statements[0]); - } - - public function testAddingTimestampsTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampsTz(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `created_at` timestamp null, add `updated_at` timestamp null', $statements[0]); - } - - public function testAddingRememberToken() - { - $blueprint = new Blueprint('users'); - $blueprint->rememberToken(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `remember_token` varchar(100) null', $statements[0]); - } - - public function testAddingBinary() - { - $blueprint = new Blueprint('users'); - $blueprint->binary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` blob not null', $statements[0]); - } - - public function testAddingUuid() - { - $blueprint = new Blueprint('users'); - $blueprint->uuid('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` char(36) not null', $statements[0]); - } - - public function testAddingUuidDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->uuid(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `uuid` char(36) not null', $statements[0]); - } - - public function testAddingForeignUuid() - { - $blueprint = new Blueprint('users'); - $foreignUuid = $blueprint->foreignUuid('foo'); - $blueprint->foreignUuid('company_id')->constrained(); - $blueprint->foreignUuid('laravel_idea_id')->constrained(); - $blueprint->foreignUuid('team_id')->references('id')->on('teams'); - $blueprint->foreignUuid('team_column_id')->constrained('teams'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignUuid); - $this->assertSame([ - 'alter table `users` add `foo` char(36) not null, add `company_id` char(36) not null, add `laravel_idea_id` char(36) not null, add `team_id` char(36) not null, add `team_column_id` char(36) not null', - 'alter table `users` add constraint `users_company_id_foreign` foreign key (`company_id`) references `companies` (`id`)', - 'alter table `users` add constraint `users_laravel_idea_id_foreign` foreign key (`laravel_idea_id`) references `laravel_ideas` (`id`)', - 'alter table `users` add constraint `users_team_id_foreign` foreign key (`team_id`) references `teams` (`id`)', - 'alter table `users` add constraint `users_team_column_id_foreign` foreign key (`team_column_id`) references `teams` (`id`)', - ], $statements); - } - - public function testAddingIpAddress() - { - $blueprint = new Blueprint('users'); - $blueprint->ipAddress('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` varchar(45) not null', $statements[0]); - } - - public function testAddingIpAddressDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->ipAddress(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `ip_address` varchar(45) not null', $statements[0]); - } - - public function testAddingMacAddress() - { - $blueprint = new Blueprint('users'); - $blueprint->macAddress('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `foo` varchar(17) not null', $statements[0]); - } - - public function testAddingMacAddressDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->macAddress(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `mac_address` varchar(17) not null', $statements[0]); - } - - public function testAddingGeometry() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` geometry not null', $statements[0]); - } - - public function testAddingGeography() - { - $blueprint = new Blueprint('geo'); - $blueprint->geography('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` geometry srid 4326 not null', $statements[0]); - } - - public function testAddingPoint() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'point'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` point not null', $statements[0]); - } - - public function testAddingPointWithSrid() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'point', 4326); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` point srid 4326 not null', $statements[0]); - } - - public function testAddingPointWithSridColumn() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'point', 4326)->after('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` point srid 4326 not null after `id`', $statements[0]); - } - - public function testAddingLineString() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'linestring'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` linestring not null', $statements[0]); - } - - public function testAddingPolygon() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'polygon'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` polygon not null', $statements[0]); - } - - public function testAddingGeometryCollection() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'geometrycollection'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` geometrycollection not null', $statements[0]); - } - - public function testAddingMultiPoint() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'multipoint'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` multipoint not null', $statements[0]); + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); + return $connection; } - public function testAddingMultiLineString() + protected function getSchemaBuilder(Blueprint $blueprint) { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'multilinestring'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` multilinestring not null', $statements[0]); - } - - public function testAddingMultiPolygon() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'multipolygon'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `geo` add `coordinates` multipolygon not null', $statements[0]); - } - - public function testAddingComment() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo')->comment("Escape ' when using words like it's"); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("alter table `users` add `foo` varchar(255) not null comment 'Escape \\' when using words like it\\'s'", $statements[0]); - } - - public function testChangingColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->string('name')->nullable(); - - $connection = $this->getConnection(); - $grammar = $this->getGrammar(); - $schemaBuilder = m::mock(Builder::class); $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - $connection->shouldReceive('getSchemaBuilder')->andReturn($schemaBuilder); - - $statements = $blueprint->toSql($connection, $grammar); - - $this->assertCount(1, $statements); - $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); - - $changeBlueprint = new Blueprint('users'); - $changeBlueprint->string('name')->default('admin')->change(); - $statements = $changeBlueprint->toSql($connection, $grammar); - - $this->assertCount(1, $statements); - $this->assertSame("alter table `users` modify `name` varchar(255) null default 'admin'", $statements[0]); - - $otherChangeBlueprint = new Blueprint('users'); - $otherChangeBlueprint->string('name')->nullable(false)->change(); - $statements = $otherChangeBlueprint->toSql($connection, $grammar); - - $this->assertCount(1, $statements); - $this->assertSame("alter table `users` modify `name` varchar(255) not null", $statements[0]); + return $schemaBuilder; } - public function testCreateDatabase() + protected function runBlueprint(Blueprint $initialBlueprint, Blueprint $blueprint = null) { - $connection = $this->getConnection(); - $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4_foo'); - $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci_foo'); - - $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); - - $this->assertSame( - 'create database `my_database_a` default character set `utf8mb4_foo` default collate `utf8mb4_unicode_ci_foo`', - $statement - ); - - $connection = $this->getConnection(); - $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4_bar'); - $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci_bar'); - - $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); - - $this->assertSame( - 'create database `my_database_b` default character set `utf8mb4_bar` default collate `utf8mb4_unicode_ci_bar`', - $statement - ); + $connection = $this->setupConnection($initialBlueprint); + if (is_null($blueprint)) { + $blueprint = $initialBlueprint; + } + return $blueprint->toSql($connection, new MySqlGrammar); } - public function testCreateTableWithVirtualAsColumn() + public function testNoInitialModifiersAddNullable() { - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); - $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); - $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_column'); - $blueprint->string('my_other_column')->virtualAs('my_column'); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`my_column` varchar(255) not null, `my_other_column` varchar(255) as (my_column)) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column'); - $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name'); + $statements = $this->runBlueprint($initialBlueprint); $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\"'))))", $statements[0]); + $this->assertSame('alter table `users` add `name` varchar(255) not null', $statements[0]); - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column'); - $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute->nested'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->nullable()->change(); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\".\"nested\"'))))", $statements[0]); + $this->assertSame("alter table `users` modify `name` varchar(255) null", $statements[0]); } - public function testCreateTableWithVirtualAsColumnWhenJsonColumnHasArrayKey() + public function testNullableInitialModifierAddDefault() { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column')->virtualAsJson('my_json_column->foo[0][1]'); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name')->nullable(); - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); - $statements = $blueprint->toSql($conn, $this->getGrammar()); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->default('admin')->change(); - $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`my_json_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"foo\"[0][1]'))))", $statements[0]); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $this->assertSame("alter table `users` modify `name` varchar(255) null default 'admin'", $statements[0]); } - public function testCreateTableWithStoredAsColumn() + public function testNullableInitialModifierAddDefaultNotNullable() { - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); - $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); - $conn->shouldReceive('getConfig')->once()->with('engine')->andReturn(null); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_column'); - $blueprint->string('my_other_column')->storedAs('my_column'); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`my_column` varchar(255) not null, `my_other_column` varchar(255) as (my_column) stored) default character set utf8 collate 'utf8_unicode_ci'", $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column'); - $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name')->nullable(); + $statements = $this->runBlueprint($initialBlueprint); $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\"'))) stored)", $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column'); - $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute->nested'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); + $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); - $statements = $blueprint->toSql($conn, $this->getGrammar()); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); $this->assertCount(1, $statements); - $this->assertSame("create table `users` (`my_json_column` varchar(255) not null, `my_other_column` varchar(255) as (json_unquote(json_extract(`my_json_column`, '$.\"some_attribute\".\"nested\"'))) stored)", $statements[0]); - } - - public function testDropDatabaseIfExists() - { - $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); - - $this->assertSame( - 'drop database if exists `my_database_a`', - $statement - ); - - $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); - - $this->assertSame( - 'drop database if exists `my_database_b`', - $statement - ); - } - - public function testDropAllTables() - { - $statement = $this->getGrammar()->compileDropAllTables(['alpha', 'beta', 'gamma']); - - $this->assertSame('drop table `alpha`,`beta`,`gamma`', $statement); - } - - public function testDropAllViews() - { - $statement = $this->getGrammar()->compileDropAllViews(['alpha', 'beta', 'gamma']); - - $this->assertSame('drop view `alpha`,`beta`,`gamma`', $statement); - } - - public function testGrammarsAreMacroable() - { - // compileReplace macro. - $this->getGrammar()::macro('compileReplace', function () { - return true; - }); - - $c = $this->getGrammar()::compileReplace(); - - $this->assertTrue($c); - } - - protected function getConnection() - { - return m::mock(Connection::class); - } - - public function getGrammar() - { - return new MySqlGrammar; + $this->assertSame("alter table `users` modify `name` varchar(255) not null default 'admin'", $statements[0]); } } From 5dc7fc9650bc1c5c15e3f82c0a5e34b0e5ab1140 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 7 Apr 2024 10:53:47 -0400 Subject: [PATCH 051/197] only test change() Blueprint method --- .../Grammars/MySqlSchemaGrammarTest.php | 4 - .../Grammars/PostgresSchemaGrammarTest.php | 1269 +---------------- .../Grammars/SQLiteSchemaGrammarTest.php | 960 +------------ .../Grammars/SqlServerSchemaGrammarTest.php | 956 +------------ 4 files changed, 124 insertions(+), 3065 deletions(-) diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index e8c89f64..ab796314 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -49,14 +49,12 @@ public function testNoInitialModifiersAddNullable() $initialBlueprint->string('name'); $statements = $this->runBlueprint($initialBlueprint); - $this->assertCount(1, $statements); $this->assertSame('alter table `users` add `name` varchar(255) not null', $statements[0]); $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->nullable()->change(); $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); - $this->assertCount(1, $statements); $this->assertSame("alter table `users` modify `name` varchar(255) null", $statements[0]); } @@ -81,14 +79,12 @@ public function testNullableInitialModifierAddDefaultNotNullable() $initialBlueprint->string('name')->nullable(); $statements = $this->runBlueprint($initialBlueprint); - $this->assertCount(1, $statements); $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); - $this->assertCount(1, $statements); $this->assertSame("alter table `users` modify `name` varchar(255) not null default 'admin'", $statements[0]); } } diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index 5fa8a174..474f0858 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -18,1261 +18,80 @@ protected function tearDown(): void m::close(); } - public function testBasicCreateTable() + protected function setupConnection(Blueprint $blueprint) { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $blueprint->string('name')->collation('nb_NO.utf8'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null, "name" varchar(255) collate "nb_NO.utf8" not null)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" serial not null primary key, add column "email" varchar(255) not null', $statements[0]); - } - - public function testCreateTableWithAutoIncrementStartingValue() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id')->startingValue(1000); - $blueprint->string('email'); - $blueprint->string('name')->collation('nb_NO.utf8'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null, "name" varchar(255) collate "nb_NO.utf8" not null)', $statements[0]); - $this->assertSame('alter sequence users_id_seq restart with 1000', $statements[1]); - } - - public function testAddColumnsWithMultipleAutoIncrementStartingValue() - { - $blueprint = new Blueprint('users'); - $blueprint->id()->from(100); - $blueprint->increments('code')->from(200); - $blueprint->string('name')->from(300); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertEquals([ - 'alter table "users" add column "id" bigserial not null primary key, add column "code" serial not null primary key, add column "name" varchar(255) not null', - 'alter sequence users_id_seq restart with 100', - 'alter sequence users_code_seq restart with 200', - ], $statements); - } - - public function testCreateTableAndCommentColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email')->comment('my first comment'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('create table "users" ("id" serial not null primary key, "email" varchar(255) not null)', $statements[0]); - $this->assertSame('comment on column "users"."email" is \'my first comment\'', $statements[1]); - } - - public function testCreateTemporaryTable() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->temporary(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create temporary table "users" ("id" serial not null primary key, "email" varchar(255) not null)', $statements[0]); - } - - public function testDropTable() - { - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop table "users"', $statements[0]); - } - - public function testDropTableIfExists() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIfExists(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop table if exists "users"', $statements[0]); - } - - public function testDropColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop column "foo"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn(['foo', 'bar']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop column "foo", drop column "bar"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop column "foo", drop column "bar"', $statements[0]); - } - - public function testDropPrimary() - { - $blueprint = new Blueprint('users'); - $blueprint->dropPrimary(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop constraint "users_pkey"', $statements[0]); - } - - public function testDropUnique() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop constraint "foo"', $statements[0]); - } - - public function testDropIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIndex('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop index "foo"', $statements[0]); - } - - public function testDropSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->dropSpatialIndex(['coordinates']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop index "geo_coordinates_spatialindex"', $statements[0]); - } - - public function testDropForeign() - { - $blueprint = new Blueprint('users'); - $blueprint->dropForeign('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop constraint "foo"', $statements[0]); + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); + return $connection; } - public function testDropTimestamps() + protected function getSchemaBuilder(Blueprint $blueprint) { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop column "created_at", drop column "updated_at"', $statements[0]); - } - - public function testDropTimestampsTz() - { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestampsTz(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop column "created_at", drop column "updated_at"', $statements[0]); - } - - public function testDropMorphs() - { - $blueprint = new Blueprint('photos'); - $blueprint->dropMorphs('imageable'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('drop index "photos_imageable_type_imageable_id_index"', $statements[0]); - $this->assertSame('alter table "photos" drop column "imageable_type", drop column "imageable_id"', $statements[1]); - } - - public function testRenameTable() - { - $blueprint = new Blueprint('users'); - $blueprint->rename('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" rename to "foo"', $statements[0]); - } - - public function testRenameIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->renameIndex('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter index "foo" rename to "bar"', $statements[0]); - } - - public function testAddingPrimaryKey() - { - $blueprint = new Blueprint('users'); - $blueprint->primary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add primary key ("foo")', $statements[0]); - } - - public function testAddingUniqueKey() - { - $blueprint = new Blueprint('users'); - $blueprint->unique('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add constraint "bar" unique ("foo")', $statements[0]); - } - - public function testAddingIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "baz" on "users" ("foo", "bar")', $statements[0]); - } - - public function testAddingIndexWithAlgorithm() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz', 'hash'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "baz" on "users" using hash ("foo", "bar")', $statements[0]); - } - - public function testAddingFulltextIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->fulltext('body'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "users_body_fulltext" on "users" using gin ((to_tsvector(\'english\', "body")))', $statements[0]); - } - - public function testAddingFulltextIndexMultipleColumns() - { - $blueprint = new Blueprint('users'); - $blueprint->fulltext(['body', 'title']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "users_body_title_fulltext" on "users" using gin ((to_tsvector(\'english\', "body") || to_tsvector(\'english\', "title")))', $statements[0]); - } - - public function testAddingFulltextIndexWithLanguage() - { - $blueprint = new Blueprint('users'); - $blueprint->fulltext('body')->language('spanish'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "users_body_fulltext" on "users" using gin ((to_tsvector(\'spanish\', "body")))', $statements[0]); - } - - public function testAddingFulltextIndexWithFluency() - { - $blueprint = new Blueprint('users'); - $blueprint->string('body')->fulltext(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('create index "users_body_fulltext" on "users" using gin ((to_tsvector(\'english\', "body")))', $statements[1]); - } - - public function testAddingSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->spatialIndex('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "geo_coordinates_spatialindex" on "geo" using gist ("coordinates")', $statements[0]); - } - - public function testAddingFluentSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'point')->spatialIndex(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('create index "geo_coordinates_spatialindex" on "geo" using gist ("coordinates")', $statements[1]); - } - - public function testAddingRawIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->rawIndex('(function(column))', 'raw_index'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "raw_index" on "users" ((function(column)))', $statements[0]); - } - - public function testAddingIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" serial not null primary key', $statements[0]); - } - - public function testAddingSmallIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->smallIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" smallserial not null primary key', $statements[0]); - } - - public function testAddingMediumIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" serial not null primary key', $statements[0]); - } - - public function testAddingID() - { - $blueprint = new Blueprint('users'); - $blueprint->id(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" bigserial not null primary key', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->id('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" bigserial not null primary key', $statements[0]); - } - - public function testAddingForeignID() - { - $blueprint = new Blueprint('users'); - $foreignId = $blueprint->foreignId('foo'); - $blueprint->foreignId('company_id')->constrained(); - $blueprint->foreignId('laravel_idea_id')->constrained(); - $blueprint->foreignId('team_id')->references('id')->on('teams'); - $blueprint->foreignId('team_column_id')->constrained('teams'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); - $this->assertSame([ - 'alter table "users" add column "foo" bigint not null, add column "company_id" bigint not null, add column "laravel_idea_id" bigint not null, add column "team_id" bigint not null, add column "team_column_id" bigint not null', - 'alter table "users" add constraint "users_company_id_foreign" foreign key ("company_id") references "companies" ("id")', - 'alter table "users" add constraint "users_laravel_idea_id_foreign" foreign key ("laravel_idea_id") references "laravel_ideas" ("id")', - 'alter table "users" add constraint "users_team_id_foreign" foreign key ("team_id") references "teams" ("id")', - 'alter table "users" add constraint "users_team_column_id_foreign" foreign key ("team_column_id") references "teams" ("id")', - ], $statements); - } - - public function testAddingForeignIdSpecifyingIndexNameInConstraint() - { - $blueprint = new Blueprint('users'); - $blueprint->foreignId('company_id')->constrained(indexName: 'my_index'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertSame([ - 'alter table "users" add column "company_id" bigint not null', - 'alter table "users" add constraint "my_index" foreign key ("company_id") references "companies" ("id")', - ], $statements); - } - - public function testAddingBigIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->bigIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" bigserial not null primary key', $statements[0]); - } - - public function testAddingString() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar(255) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar(100) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar(100) null default \'bar\'', $statements[0]); - } - - public function testAddingStringWithoutLengthLimit() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar(255) not null', $statements[0]); - - Builder::$defaultStringLength = null; - - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $schemaBuilder = m::mock(Builder::class); + $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - try { - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); - } finally { - Builder::$defaultStringLength = 255; - } + return $schemaBuilder; } - public function testAddingCharWithoutLengthLimit() + protected function runBlueprint(Blueprint $initialBlueprint, Blueprint $blueprint = null) { - $blueprint = new Blueprint('users'); - $blueprint->char('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" char(255) not null', $statements[0]); - - Builder::$defaultStringLength = null; - - $blueprint = new Blueprint('users'); - $blueprint->char('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - try { - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" char not null', $statements[0]); - } finally { - Builder::$defaultStringLength = 255; + $connection = $this->setupConnection($initialBlueprint); + if (is_null($blueprint)) { + $blueprint = $initialBlueprint; } + return $blueprint->toSql($connection, new PostgresGrammar); } - public function testAddingText() - { - $blueprint = new Blueprint('users'); - $blueprint->text('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" text not null', $statements[0]); - } - - public function testAddingBigInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" bigint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" bigserial not null primary key', $statements[0]); - } - - public function testAddingInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" serial not null primary key', $statements[0]); - } - - public function testAddingMediumInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" serial not null primary key', $statements[0]); - } - - public function testAddingTinyInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" smallint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" smallserial not null primary key', $statements[0]); - } - - public function testAddingSmallInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" smallint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" smallserial not null primary key', $statements[0]); - } - - public function testAddingFloat() - { - $blueprint = new Blueprint('users'); - $blueprint->float('foo', 5); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" float(5) not null', $statements[0]); - } - - public function testAddingDouble() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" double precision not null', $statements[0]); - } - - public function testAddingDecimal() - { - $blueprint = new Blueprint('users'); - $blueprint->decimal('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" decimal(5, 2) not null', $statements[0]); - } - - public function testAddingBoolean() - { - $blueprint = new Blueprint('users'); - $blueprint->boolean('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" boolean not null', $statements[0]); - } - - public function testAddingEnum() + public function testNoInitialModifiersAddNullable() { - $blueprint = new Blueprint('users'); - $blueprint->enum('role', ['member', 'admin']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name'); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "role" varchar(255) check ("role" in (\'member\', \'admin\')) not null', $statements[0]); - } - - public function testAddingDate() - { - $blueprint = new Blueprint('users'); - $blueprint->date('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" date not null', $statements[0]); - } - - public function testAddingYear() - { - $blueprint = new Blueprint('users'); - $blueprint->year('birth_year'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "birth_year" integer not null', $statements[0]); - } - - public function testAddingJson() - { - $blueprint = new Blueprint('users'); - $blueprint->json('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" json not null', $statements[0]); - } - - public function testAddingJsonb() - { - $blueprint = new Blueprint('users'); - $blueprint->jsonb('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" jsonb not null', $statements[0]); - } - - public function testAddingDateTime() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(0) without time zone not null', $statements[0]); - } - - public function testAddingDateTimeWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(1) without time zone not null', $statements[0]); - } - - public function testAddingDateTimeWithNullPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('created_at', null); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp without time zone not null', $statements[0]); - } - - public function testAddingDateTimeTz() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(0) with time zone not null', $statements[0]); - } - - public function testAddingDateTimeTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(1) with time zone not null', $statements[0]); - } - - public function testAddingDateTimeTzWithNullPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('created_at', null); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp with time zone not null', $statements[0]); - } - - public function testAddingTime() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time(0) without time zone not null', $statements[0]); - } - - public function testAddingTimeWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time(1) without time zone not null', $statements[0]); - } - - public function testAddingTimeWithNullPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at', null); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time without time zone not null', $statements[0]); - } - - public function testAddingTimeTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time(0) with time zone not null', $statements[0]); - } - - public function testAddingTimeTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time(1) with time zone not null', $statements[0]); - } - - public function testAddingTimeTzWithNullPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at', null); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time with time zone not null', $statements[0]); - } - - public function testAddingTimestamp() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(0) without time zone not null', $statements[0]); - } + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table "users" add column "name" varchar(255) not null', $statements[0]); - public function testAddingTimestampWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(1) without time zone not null', $statements[0]); - } - - public function testAddingTimestampWithNullPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at', null); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp without time zone not null', $statements[0]); - } - - public function testAddingTimestampTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(0) with time zone not null', $statements[0]); - } - - public function testAddingTimestampTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(1) with time zone not null', $statements[0]); - } - - public function testAddingTimestampTzWithNullPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at', null); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp with time zone not null', $statements[0]); - } - - public function testAddingTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(0) without time zone null, add column "updated_at" timestamp(0) without time zone null', $statements[0]); - } - - public function testAddingTimestampsTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampsTz(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" timestamp(0) with time zone null, add column "updated_at" timestamp(0) with time zone null', $statements[0]); - } - - public function testAddingBinary() - { - $blueprint = new Blueprint('users'); - $blueprint->binary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" bytea not null', $statements[0]); - } - - public function testAddingUuid() - { - $blueprint = new Blueprint('users'); - $blueprint->uuid('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" uuid not null', $statements[0]); - } - - public function testAddingUuidDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->uuid(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "uuid" uuid not null', $statements[0]); - } - - public function testAddingForeignUuid() - { - $blueprint = new Blueprint('users'); - $foreignUuid = $blueprint->foreignUuid('foo'); - $blueprint->foreignUuid('company_id')->constrained(); - $blueprint->foreignUuid('laravel_idea_id')->constrained(); - $blueprint->foreignUuid('team_id')->references('id')->on('teams'); - $blueprint->foreignUuid('team_column_id')->constrained('teams'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignUuid); - $this->assertSame([ - 'alter table "users" add column "foo" uuid not null, add column "company_id" uuid not null, add column "laravel_idea_id" uuid not null, add column "team_id" uuid not null, add column "team_column_id" uuid not null', - 'alter table "users" add constraint "users_company_id_foreign" foreign key ("company_id") references "companies" ("id")', - 'alter table "users" add constraint "users_laravel_idea_id_foreign" foreign key ("laravel_idea_id") references "laravel_ideas" ("id")', - 'alter table "users" add constraint "users_team_id_foreign" foreign key ("team_id") references "teams" ("id")', - 'alter table "users" add constraint "users_team_column_id_foreign" foreign key ("team_column_id") references "teams" ("id")', - ], $statements); - } - - public function testAddingGeneratedAs() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('foo')->generatedAs(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity primary key', $statements[0]); - // With always modifier - $blueprint = new Blueprint('users'); - $blueprint->increments('foo')->generatedAs()->always(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null generated always as identity primary key', $statements[0]); - // With sequence options - $blueprint = new Blueprint('users'); - $blueprint->increments('foo')->generatedAs('increment by 10 start with 100'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity (increment by 10 start with 100) primary key', $statements[0]); - // Not a primary key - $blueprint = new Blueprint('users'); - $blueprint->integer('foo')->generatedAs(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null generated by default as identity', $statements[0]); - } - - public function testAddingVirtualAs() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo')->nullable(); - $blueprint->boolean('bar')->virtualAs('foo is not null'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer null, add column "bar" boolean not null generated always as (foo is not null)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo')->nullable(); - $blueprint->boolean('bar')->virtualAs(new Expression('foo is not null')); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer null, add column "bar" boolean not null generated always as (foo is not null)', $statements[0]); - } - - public function testAddingStoredAs() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo')->nullable(); - $blueprint->boolean('bar')->storedAs('foo is not null'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer null, add column "bar" boolean not null generated always as (foo is not null) stored', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo')->nullable(); - $blueprint->boolean('bar')->storedAs(new Expression('foo is not null')); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer null, add column "bar" boolean not null generated always as (foo is not null) stored', $statements[0]); - } - - public function testAddingIpAddress() - { - $blueprint = new Blueprint('users'); - $blueprint->ipAddress('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" inet not null', $statements[0]); - } - - public function testAddingIpAddressDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->ipAddress(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->nullable()->change(); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "ip_address" inet not null', $statements[0]); - } - - public function testAddingMacAddress() - { - $blueprint = new Blueprint('users'); - $blueprint->macAddress('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" macaddr not null', $statements[0]); - } - - public function testAddingMacAddressDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->macAddress(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "mac_address" macaddr not null', $statements[0]); - } - - public function testCompileForeign() - { - $blueprint = new Blueprint('users'); - $blueprint->foreign('parent_id')->references('id')->on('parents')->onDelete('cascade')->deferrable(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add constraint "users_parent_id_foreign" foreign key ("parent_id") references "parents" ("id") on delete cascade deferrable', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->foreign('parent_id')->references('id')->on('parents')->onDelete('cascade')->deferrable(false)->initiallyImmediate(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add constraint "users_parent_id_foreign" foreign key ("parent_id") references "parents" ("id") on delete cascade not deferrable', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->foreign('parent_id')->references('id')->on('parents')->onDelete('cascade')->deferrable()->initiallyImmediate(false); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add constraint "users_parent_id_foreign" foreign key ("parent_id") references "parents" ("id") on delete cascade deferrable initially deferred', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->foreign('parent_id')->references('id')->on('parents')->onDelete('cascade')->deferrable()->notValid(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add constraint "users_parent_id_foreign" foreign key ("parent_id") references "parents" ("id") on delete cascade deferrable not valid', $statements[0]); - } - - public function testAddingGeometry() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry not null', $statements[0]); - } - - public function testAddingGeography() - { - $blueprint = new Blueprint('geo'); - $blueprint->geography('coordinates', 'pointzm', 4269); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geography(pointzm,4269) not null', $statements[0]); - } - - public function testAddingPoint() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'point'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry(point) not null', $statements[0]); - } - - public function testAddingPointWithSrid() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'point', 4269); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry(point,4269) not null', $statements[0]); - } - - public function testAddingLineString() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'linestring'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry(linestring) not null', $statements[0]); - } - - public function testAddingPolygon() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'polygon'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry(polygon) not null', $statements[0]); - } - - public function testAddingGeometryCollection() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'geometrycollection'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry(geometrycollection) not null', $statements[0]); - } - - public function testAddingMultiPoint() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'multipoint'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry(multipoint) not null', $statements[0]); - } - - public function testAddingMultiLineString() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'multilinestring'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry(multilinestring) not null', $statements[0]); - } - - public function testAddingMultiPolygon() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'multipolygon'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry(multipolygon) not null', $statements[0]); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $parts = explode(', ', $statements[0]); + $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); + $this->assertSame('alter column "name" drop not null', $parts[1]); + $this->assertSame("alter column \"name\" drop default", $parts[2]); } - - public function testChangingColumn() + public function testNullableInitialModifierAddDefault() { - $blueprint = new Blueprint('users'); - $blueprint->string('name')->nullable(); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name')->nullable(); - $connection = $this->getConnection(); - $grammar = $this->getGrammar(); - - $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - - $connection->shouldReceive('getSchemaBuilder')->andReturn($schemaBuilder); - - $statements = $blueprint->toSql($connection, $grammar); - - $this->assertCount(1, $statements); + $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar(255) null', $statements[0]); - $changeBlueprint = new Blueprint('users'); - $changeBlueprint->string('name')->default('admin')->change(); - $statements = $changeBlueprint->toSql($connection, $grammar); - - $this->assertCount(2, $statements); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->default('admin')->change(); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); $parts = explode(', ', $statements[0]); $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); $this->assertSame('alter column "name" null', $parts[1]); $this->assertSame("alter column \"name\" set default 'admin'", $parts[2]); - - $otherChangeBlueprint = new Blueprint('users'); - $otherChangeBlueprint->string('name')->nullable(false)->change(); - $statements = $otherChangeBlueprint->toSql($connection, $grammar); - - $this->assertCount(2, $statements); - - $parts = explode(', ', $statements[0]); - $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); - $this->assertSame('alter column "name" set not null', $parts[1]); - } - - public function testCreateDatabase() - { - $connection = $this->getConnection(); - $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8_foo'); - $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); - - $this->assertSame( - 'create database "my_database_a" encoding "utf8_foo"', - $statement - ); - - $connection = $this->getConnection(); - $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8_bar'); - $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); - - $this->assertSame( - 'create database "my_database_b" encoding "utf8_bar"', - $statement - ); - } - - public function testDropDatabaseIfExists() - { - $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); - - $this->assertSame( - 'drop database if exists "my_database_a"', - $statement - ); - - $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); - - $this->assertSame( - 'drop database if exists "my_database_b"', - $statement - ); - } - - public function testDropAllTablesEscapesTableNames() - { - $statement = $this->getGrammar()->compileDropAllTables(['alpha', 'beta', 'gamma']); - - $this->assertSame('drop table "alpha","beta","gamma" cascade', $statement); } - public function testDropAllViewsEscapesTableNames() + public function testNullableInitialModifierAddDefaultNotNullable() { - $statement = $this->getGrammar()->compileDropAllViews(['alpha', 'beta', 'gamma']); - - $this->assertSame('drop view "alpha","beta","gamma" cascade', $statement); - } + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name')->nullable(); - public function testDropAllTypesEscapesTableNames() - { - $statement = $this->getGrammar()->compileDropAllTypes(['alpha', 'beta', 'gamma']); - - $this->assertSame('drop type "alpha","beta","gamma" cascade', $statement); - } - - protected function getConnection() - { - return m::mock(Connection::class); - } - - public function getGrammar() - { - return new PostgresGrammar; - } - - public function testGrammarsAreMacroable() - { - // compileReplace macro. - $this->getGrammar()::macro('compileReplace', function () { - return true; - }); + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table "users" add column "name" varchar(255) null', $statements[0]); - $c = $this->getGrammar()::compileReplace(); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); - $this->assertTrue($c); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $parts = explode(', ', $statements[0]); + $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); + $this->assertSame('alter column "name" set not null', $parts[1]); + $this->assertSame("alter column \"name\" set default 'admin'", $parts[2]); } } diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index c43c7d6f..432d99ce 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -19,955 +19,75 @@ protected function tearDown(): void m::close(); } - public function testBasicCreateTable() + protected function setupConnection(Blueprint $blueprint) { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("id" integer primary key autoincrement not null, "email" varchar not null)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $expected = [ - 'alter table "users" add column "id" integer primary key autoincrement not null', - 'alter table "users" add column "email" varchar not null', - ]; - $this->assertEquals($expected, $statements); - } - - public function testCreateTemporaryTable() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->temporary(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create temporary table "users" ("id" integer primary key autoincrement not null, "email" varchar not null)', $statements[0]); - } - - public function testDropTable() - { - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop table "users"', $statements[0]); - } - - public function testDropTableIfExists() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIfExists(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop table if exists "users"', $statements[0]); - } - - public function testDropUnique() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop index "foo"', $statements[0]); - } - - public function testDropIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIndex('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop index "foo"', $statements[0]); - } - - public function testDropColumn() - { - $db = new Manager; - - $db->addConnection([ - 'driver' => 'sqlite', - 'database' => ':memory:', - 'prefix' => 'prefix_', - ]); - - $schema = $db->getConnection()->getSchemaBuilder(); - - $schema->create('users', function (Blueprint $table) { - $table->string('email'); - $table->string('name'); - }); - - $this->assertTrue($schema->hasTable('users')); - $this->assertTrue($schema->hasColumn('users', 'name')); - - $schema->table('users', function (Blueprint $table) { - $table->dropColumn('name'); - }); - - $this->assertFalse($schema->hasColumn('users', 'name')); - } - - public function testDropSpatialIndex() - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The database driver in use does not support spatial indexes.'); - - $blueprint = new Blueprint('geo'); - $blueprint->dropSpatialIndex(['coordinates']); - $blueprint->toSql($this->getConnection(), $this->getGrammar()); - } - - public function testRenameTable() - { - $blueprint = new Blueprint('users'); - $blueprint->rename('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" rename to "foo"', $statements[0]); - } - - public function testRenameIndex() - { - $db = new Manager; - - $db->addConnection([ - 'driver' => 'sqlite', - 'database' => ':memory:', - 'prefix' => 'prefix_', - ]); - - $schema = $db->getConnection()->getSchemaBuilder(); - - $schema->create('users', function (Blueprint $table) { - $table->string('name'); - $table->string('email'); - }); - - $schema->table('users', function (Blueprint $table) { - $table->index(['name', 'email'], 'index1'); - }); - - $indexes = $schema->getIndexListing('users'); - - $this->assertContains('index1', $indexes); - $this->assertNotContains('index2', $indexes); - - $schema->table('users', function (Blueprint $table) { - $table->renameIndex('index1', 'index2'); - }); - - $this->assertFalse($schema->hasIndex('users', 'index1')); - $this->assertTrue(collect($schema->getIndexes('users'))->contains( - fn ($index) => $index['name'] === 'index2' && $index['columns'] === ['name', 'email'] - )); - } - - public function testAddingPrimaryKey() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('foo')->primary(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("foo" varchar not null, primary key ("foo"))', $statements[0]); - } - - public function testAddingForeignKey() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('foo')->primary(); - $blueprint->string('order_id'); - $blueprint->foreign('order_id')->references('id')->on('orders'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("foo" varchar not null, "order_id" varchar not null, foreign key("order_id") references "orders"("id"), primary key ("foo"))', $statements[0]); - } - - public function testAddingUniqueKey() - { - $blueprint = new Blueprint('users'); - $blueprint->unique('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create unique index "bar" on "users" ("foo")', $statements[0]); - } - - public function testAddingIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "baz" on "users" ("foo", "bar")', $statements[0]); - } - - public function testAddingSpatialIndex() - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The database driver in use does not support spatial indexes.'); - - $blueprint = new Blueprint('geo'); - $blueprint->spatialIndex('coordinates'); - $blueprint->toSql($this->getConnection(), $this->getGrammar()); - } - - public function testAddingFluentSpatialIndex() - { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The database driver in use does not support spatial indexes.'); - - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates')->spatialIndex(); - $blueprint->toSql($this->getConnection(), $this->getGrammar()); - } - - public function testAddingRawIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->rawIndex('(function(column))', 'raw_index'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "raw_index" on "users" ((function(column)))', $statements[0]); - } - - public function testAddingIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingSmallIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->smallIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingMediumIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingID() - { - $blueprint = new Blueprint('users'); - $blueprint->id(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->id('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingForeignID() - { - $blueprint = new Blueprint('users'); - $foreignId = $blueprint->foreignId('foo'); - $blueprint->foreignId('company_id')->constrained(); - $blueprint->foreignId('laravel_idea_id')->constrained(); - $blueprint->foreignId('team_id')->references('id')->on('teams'); - $blueprint->foreignId('team_column_id')->constrained('teams'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); - $this->assertSame([ - 'alter table "users" add column "foo" integer not null', - 'alter table "users" add column "company_id" integer not null', - 'alter table "users" add column "laravel_idea_id" integer not null', - 'alter table "users" add column "team_id" integer not null', - 'alter table "users" add column "team_column_id" integer not null', - ], $statements); - } - - public function testAddingForeignIdSpecifyingIndexNameInConstraint() - { - $blueprint = new Blueprint('users'); - $blueprint->foreignId('company_id')->constrained(indexName: 'my_index'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertSame([ - 'alter table "users" add column "company_id" integer not null', - ], $statements); - } - - public function testAddingBigIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->bigIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "id" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingString() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar default \'bar\'', $statements[0]); - } - - public function testAddingText() - { - $blueprint = new Blueprint('users'); - $blueprint->text('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" text not null', $statements[0]); - } - - public function testAddingBigInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingMediumInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingTinyInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingSmallInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" integer primary key autoincrement not null', $statements[0]); - } - - public function testAddingFloat() - { - $blueprint = new Blueprint('users'); - $blueprint->float('foo', 5); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" float not null', $statements[0]); - } - - public function testAddingDouble() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" double not null', $statements[0]); - } - - public function testAddingDecimal() - { - $blueprint = new Blueprint('users'); - $blueprint->decimal('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" numeric not null', $statements[0]); - } - - public function testAddingBoolean() - { - $blueprint = new Blueprint('users'); - $blueprint->boolean('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" tinyint(1) not null', $statements[0]); - } - - public function testAddingEnum() - { - $blueprint = new Blueprint('users'); - $blueprint->enum('role', ['member', 'admin']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "role" varchar check ("role" in (\'member\', \'admin\')) not null', $statements[0]); - } - - public function testAddingJson() - { - $blueprint = new Blueprint('users'); - $blueprint->json('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" text not null', $statements[0]); - } - - public function testAddingJsonb() - { - $blueprint = new Blueprint('users'); - $blueprint->jsonb('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" text not null', $statements[0]); - } - - public function testAddingDate() - { - $blueprint = new Blueprint('users'); - $blueprint->date('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" date not null', $statements[0]); - } - - public function testAddingYear() - { - $blueprint = new Blueprint('users'); - $blueprint->year('birth_year'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "birth_year" integer not null', $statements[0]); - } - - public function testAddingDateTime() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); - } - - public function testAddingDateTimeWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); - } - - public function testAddingDateTimeTz() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); - } - - public function testAddingDateTimeTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); - } - - public function testAddingTime() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time not null', $statements[0]); - } - - public function testAddingTimeWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time not null', $statements[0]); - } - - public function testAddingTimeTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time not null', $statements[0]); - } - - public function testAddingTimeTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" time not null', $statements[0]); - } - - public function testAddingTimestamp() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); - } - - public function testAddingTimestampWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); - } - - public function testAddingTimestampTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); - } - - public function testAddingTimestampTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "created_at" datetime not null', $statements[0]); - } - - public function testAddingTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(2, $statements); - $this->assertEquals([ - 'alter table "users" add column "created_at" datetime', - 'alter table "users" add column "updated_at" datetime', - ], $statements); - } - - public function testAddingTimestampsTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampsTz(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(2, $statements); - $this->assertEquals([ - 'alter table "users" add column "created_at" datetime', - 'alter table "users" add column "updated_at" datetime', - ], $statements); - } - - public function testAddingRememberToken() - { - $blueprint = new Blueprint('users'); - $blueprint->rememberToken(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "remember_token" varchar', $statements[0]); - } - - public function testAddingBinary() - { - $blueprint = new Blueprint('users'); - $blueprint->binary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" blob not null', $statements[0]); - } - - public function testAddingUuid() - { - $blueprint = new Blueprint('users'); - $blueprint->uuid('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); - } - - public function testAddingUuidDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->uuid(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "uuid" varchar not null', $statements[0]); - } - - public function testAddingForeignUuid() - { - $blueprint = new Blueprint('users'); - $foreignUuid = $blueprint->foreignUuid('foo'); - $blueprint->foreignUuid('company_id')->constrained(); - $blueprint->foreignUuid('laravel_idea_id')->constrained(); - $blueprint->foreignUuid('team_id')->references('id')->on('teams'); - $blueprint->foreignUuid('team_column_id')->constrained('teams'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignUuid); - $this->assertSame([ - 'alter table "users" add column "foo" varchar not null', - 'alter table "users" add column "company_id" varchar not null', - 'alter table "users" add column "laravel_idea_id" varchar not null', - 'alter table "users" add column "team_id" varchar not null', - 'alter table "users" add column "team_column_id" varchar not null', - ], $statements); - } - - public function testAddingIpAddress() - { - $blueprint = new Blueprint('users'); - $blueprint->ipAddress('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); - } - - public function testAddingIpAddressDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->ipAddress(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "ip_address" varchar not null', $statements[0]); - } - - public function testAddingMacAddress() - { - $blueprint = new Blueprint('users'); - $blueprint->macAddress('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "foo" varchar not null', $statements[0]); - } - - public function testAddingMacAddressDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->macAddress(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "mac_address" varchar not null', $statements[0]); - } - - public function testAddingGeometry() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add column "coordinates" geometry not null', $statements[0]); - } - - public function testAddingGeneratedColumn() - { - $blueprint = new Blueprint('products'); - $blueprint->create(); - $blueprint->integer('price'); - $blueprint->integer('discounted_virtual')->virtualAs('"price" - 5'); - $blueprint->integer('discounted_stored')->storedAs('"price" - 5'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "products" ("price" integer not null, "discounted_virtual" integer as ("price" - 5), "discounted_stored" integer as ("price" - 5) stored)', $statements[0]); - - $blueprint = new Blueprint('products'); - $blueprint->integer('price'); - $blueprint->integer('discounted_virtual')->virtualAs('"price" - 5')->nullable(false); - $blueprint->integer('discounted_stored')->storedAs('"price" - 5')->nullable(false); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(3, $statements); - $expected = [ - 'alter table "products" add column "price" integer not null', - 'alter table "products" add column "discounted_virtual" integer not null as ("price" - 5)', - 'alter table "products" add column "discounted_stored" integer not null as ("price" - 5) stored', - ]; - $this->assertSame($expected, $statements); - } - - public function testAddingGeneratedColumnByExpression() - { - $blueprint = new Blueprint('products'); - $blueprint->create(); - $blueprint->integer('price'); - $blueprint->integer('discounted_virtual')->virtualAs(new Expression('"price" - 5')); - $blueprint->integer('discounted_stored')->storedAs(new Expression('"price" - 5')); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "products" ("price" integer not null, "discounted_virtual" integer as ("price" - 5), "discounted_stored" integer as ("price" - 5) stored)', $statements[0]); + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); + $connection->shouldReceive('scalar')->andReturn(''); + return $connection; } - public function testChangingColumn() + protected function getSchemaBuilder(Blueprint $blueprint) { - $blueprint = new Blueprint('users'); - $blueprint->string('name')->nullable(); - - $connection = $this->getConnection(); - $grammar = $this->getGrammar(); - $schemaBuilder = m::mock(Builder::class); $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); $schemaBuilder->shouldReceive('getForeignKeys')->andReturn([]); $schemaBuilder->shouldReceive('getIndexes')->andReturn([]); - $connection->shouldReceive('getSchemaBuilder')->andReturn($schemaBuilder); - $connection->shouldReceive('scalar')->andReturn(''); - - $statements = $blueprint->toSql($connection, $grammar); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); - - $changeBlueprint = new Blueprint('users'); - $changeBlueprint->string('name')->default('admin')->change(); - $statements = $changeBlueprint->toSql($connection, $grammar); - - $this->assertCount(4, $statements); - $this->assertStringContainsString("varchar default 'admin'", $statements[0]); - - $otherChangeBlueprint = new Blueprint('users'); - $otherChangeBlueprint->string('name')->nullable(false)->change(); - $statements = $otherChangeBlueprint->toSql($connection, $grammar); - - $this->assertCount(4, $statements); - $this->assertStringContainsString("varchar not null", $statements[0]); + return $schemaBuilder; } - public function testGrammarsAreMacroable() + protected function runBlueprint(Blueprint $initialBlueprint, Blueprint $blueprint = null) { - // compileReplace macro. - $this->getGrammar()::macro('compileReplace', function () { - return true; - }); - - $c = $this->getGrammar()::compileReplace(); - - $this->assertTrue($c); + $connection = $this->setupConnection($initialBlueprint); + if (is_null($blueprint)) { + $blueprint = $initialBlueprint; + } + return $blueprint->toSql($connection, new SQLiteGrammar); } - public function testCreateTableWithVirtualAsColumn() + public function testNoInitialModifiersAddNullable() { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_column'); - $blueprint->string('my_other_column')->virtualAs('my_column'); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table "users" add column "name" varchar not null', $statements[0]); - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("my_column" varchar not null, "my_other_column" varchar as (my_column))', $statements[0]); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->nullable()->change(); - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column'); - $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"\')))', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column'); - $blueprint->string('my_other_column')->virtualAsJson('my_json_column->some_attribute->nested'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"."nested"\')))', $statements[0]); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $this->assertStringContainsString('"name" varchar', $statements[0]); } - public function testCreateTableWithVirtualAsColumnWhenJsonColumnHasArrayKey() + public function testNullableInitialModifierAddDefault() { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column')->virtualAsJson('my_json_column->foo[0][1]'); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name')->nullable(); - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); - $statements = $blueprint->toSql($conn, $this->getGrammar()); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->default('admin')->change(); - $this->assertCount(1, $statements); - $this->assertSame("create table \"users\" (\"my_json_column\" varchar as (json_extract(\"my_json_column\", '$.\"foo\"[0][1]')))", $statements[0]); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $this->assertStringContainsString("varchar default 'admin'", $statements[0]); } - public function testCreateTableWithStoredAsColumn() + public function testNullableInitialModifierAddDefaultNotNullable() { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_column'); - $blueprint->string('my_other_column')->storedAs('my_column'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("my_column" varchar not null, "my_other_column" varchar as (my_column) stored)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column'); - $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute'); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name')->nullable(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"\')) stored)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('my_json_column'); - $blueprint->string('my_other_column')->storedAsJson('my_json_column->some_attribute->nested'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("my_json_column" varchar not null, "my_other_column" varchar as (json_extract("my_json_column", \'$."some_attribute"."nested"\')) stored)', $statements[0]); - } + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); - protected function getConnection() - { - return m::mock(Connection::class); - } + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); - public function getGrammar() - { - return new SQLiteGrammar; + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $this->assertStringContainsString("\"name\" varchar not null default 'admin'", $statements[0]); } } diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index 0adf8dc4..d68702df 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -17,950 +17,74 @@ protected function tearDown(): void m::close(); } - public function testBasicCreateTable() + protected function setupConnection(Blueprint $blueprint) { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" int not null identity primary key, "email" nvarchar(255) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()->setTablePrefix('prefix_')); - - $this->assertCount(1, $statements); - $this->assertSame('create table "prefix_users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); - } - - public function testCreateTemporaryTable() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->temporary(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create table "#users" ("id" int not null identity primary key, "email" nvarchar(255) not null)', $statements[0]); - } - - public function testDropTable() - { - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop table "users"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()->setTablePrefix('prefix_')); - - $this->assertCount(1, $statements); - $this->assertSame('drop table "prefix_users"', $statements[0]); - } - - public function testDropTableIfExists() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIfExists(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('if object_id(N\'"users"\', \'U\') is not null drop table "users"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropIfExists(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()->setTablePrefix('prefix_')); - - $this->assertCount(1, $statements); - $this->assertSame('if object_id(N\'"prefix_users"\', \'U\') is not null drop table "prefix_users"', $statements[0]); - } - - public function testDropColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertStringContainsString('alter table "users" drop column "foo"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn(['foo', 'bar']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertStringContainsString('alter table "users" drop column "foo", "bar"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertStringContainsString('alter table "users" drop column "foo", "bar"', $statements[0]); - } - - public function testDropColumnDropsCreatesSqlToDropDefaultConstraints() - { - $blueprint = new Blueprint('foo'); - $blueprint->dropColumn('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame("DECLARE @sql NVARCHAR(MAX) = '';SELECT @sql += 'ALTER TABLE \"foo\" DROP CONSTRAINT ' + OBJECT_NAME([default_object_id]) + ';' FROM sys.columns WHERE [object_id] = OBJECT_ID(N'\"foo\"') AND [name] in ('bar') AND [default_object_id] <> 0;EXEC(@sql);alter table \"foo\" drop column \"bar\"", $statements[0]); - } - - public function testDropPrimary() - { - $blueprint = new Blueprint('users'); - $blueprint->dropPrimary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop constraint "foo"', $statements[0]); - } - - public function testDropUnique() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop index "foo" on "users"', $statements[0]); + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); + return $connection; } - public function testDropIndex() + protected function getSchemaBuilder(Blueprint $blueprint) { - $blueprint = new Blueprint('users'); - $blueprint->dropIndex('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop index "foo" on "users"', $statements[0]); - } - - public function testDropSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->dropSpatialIndex(['coordinates']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('drop index "geo_coordinates_spatialindex" on "geo"', $statements[0]); - } - - public function testDropForeign() - { - $blueprint = new Blueprint('users'); - $blueprint->dropForeign('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" drop constraint "foo"', $statements[0]); - } - - public function testDropConstrainedForeignId() - { - $blueprint = new Blueprint('users'); - $blueprint->dropConstrainedForeignId('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('alter table "users" drop constraint "users_foo_foreign"', $statements[0]); - $this->assertSame('DECLARE @sql NVARCHAR(MAX) = \'\';SELECT @sql += \'ALTER TABLE "users" DROP CONSTRAINT \' + OBJECT_NAME([default_object_id]) + \';\' FROM sys.columns WHERE [object_id] = OBJECT_ID(N\'"users"\') AND [name] in (\'foo\') AND [default_object_id] <> 0;EXEC(@sql);alter table "users" drop column "foo"', $statements[1]); - } - - public function testDropTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertStringContainsString('alter table "users" drop column "created_at", "updated_at"', $statements[0]); - } - - public function testDropTimestampsTz() - { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestampsTz(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertStringContainsString('alter table "users" drop column "created_at", "updated_at"', $statements[0]); - } - - public function testDropMorphs() - { - $blueprint = new Blueprint('photos'); - $blueprint->dropMorphs('imageable'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('drop index "photos_imageable_type_imageable_id_index" on "photos"', $statements[0]); - $this->assertStringContainsString('alter table "photos" drop column "imageable_type", "imageable_id"', $statements[1]); - } - - public function testRenameTable() - { - $blueprint = new Blueprint('users'); - $blueprint->rename('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('sp_rename N\'"users"\', "foo"', $statements[0]); - } - - public function testRenameIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->renameIndex('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('sp_rename N\'"users"."foo"\', "bar", N\'INDEX\'', $statements[0]); - } - - public function testAddingPrimaryKey() - { - $blueprint = new Blueprint('users'); - $blueprint->primary('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add constraint "bar" primary key ("foo")', $statements[0]); - } - - public function testAddingUniqueKey() - { - $blueprint = new Blueprint('users'); - $blueprint->unique('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create unique index "bar" on "users" ("foo")', $statements[0]); - } - - public function testAddingIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "baz" on "users" ("foo", "bar")', $statements[0]); - } - - public function testAddingSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->spatialIndex('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create spatial index "geo_coordinates_spatialindex" on "geo" ("coordinates")', $statements[0]); - } - - public function testAddingFluentSpatialIndex() - { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates', 'point')->spatialIndex(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $this->assertSame('create spatial index "geo_coordinates_spatialindex" on "geo" ("coordinates")', $statements[1]); - } - - public function testAddingRawIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->rawIndex('(function(column))', 'raw_index'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('create index "raw_index" on "users" ((function(column)))', $statements[0]); - } - - public function testAddingIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" int not null identity primary key', $statements[0]); - } - - public function testAddingSmallIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->smallIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" smallint not null identity primary key', $statements[0]); - } - - public function testAddingMediumIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" int not null identity primary key', $statements[0]); - } - - public function testAddingID() - { - $blueprint = new Blueprint('users'); - $blueprint->id(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" bigint not null identity primary key', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->id('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" bigint not null identity primary key', $statements[0]); - } - - public function testAddingForeignID() - { - $blueprint = new Blueprint('users'); - $foreignId = $blueprint->foreignId('foo'); - $blueprint->foreignId('company_id')->constrained(); - $blueprint->foreignId('laravel_idea_id')->constrained(); - $blueprint->foreignId('team_id')->references('id')->on('teams'); - $blueprint->foreignId('team_column_id')->constrained('teams'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); - $this->assertSame([ - 'alter table "users" add "foo" bigint not null, "company_id" bigint not null, "laravel_idea_id" bigint not null, "team_id" bigint not null, "team_column_id" bigint not null', - 'alter table "users" add constraint "users_company_id_foreign" foreign key ("company_id") references "companies" ("id")', - 'alter table "users" add constraint "users_laravel_idea_id_foreign" foreign key ("laravel_idea_id") references "laravel_ideas" ("id")', - 'alter table "users" add constraint "users_team_id_foreign" foreign key ("team_id") references "teams" ("id")', - 'alter table "users" add constraint "users_team_column_id_foreign" foreign key ("team_column_id") references "teams" ("id")', - ], $statements); - } - - public function testAddingForeignIdSpecifyingIndexNameInConstraint() - { - $blueprint = new Blueprint('users'); - $blueprint->foreignId('company_id')->constrained(indexName: 'my_index'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertSame([ - 'alter table "users" add "company_id" bigint not null', - 'alter table "users" add constraint "my_index" foreign key ("company_id") references "companies" ("id")', - ], $statements); - } - - public function testAddingBigIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->bigIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "id" bigint not null identity primary key', $statements[0]); - } - - public function testAddingString() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" nvarchar(255) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" nvarchar(100) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" nvarchar(100) null default \'bar\'', $statements[0]); - } - - public function testAddingText() - { - $blueprint = new Blueprint('users'); - $blueprint->text('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" nvarchar(max) not null', $statements[0]); - } - - public function testAddingBigInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" bigint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" bigint not null identity primary key', $statements[0]); - } - - public function testAddingInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" int not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" int not null identity primary key', $statements[0]); - } - - public function testAddingMediumInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" int not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" int not null identity primary key', $statements[0]); - } - - public function testAddingTinyInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" tinyint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" tinyint not null identity primary key', $statements[0]); - } - - public function testAddingSmallInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" smallint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" smallint not null identity primary key', $statements[0]); - } - - public function testAddingFloat() - { - $blueprint = new Blueprint('users'); - $blueprint->float('foo', 5); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" float(5) not null', $statements[0]); - } - - public function testAddingDouble() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" double precision not null', $statements[0]); - } - - public function testAddingDecimal() - { - $blueprint = new Blueprint('users'); - $blueprint->decimal('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" decimal(5, 2) not null', $statements[0]); - } - - public function testAddingBoolean() - { - $blueprint = new Blueprint('users'); - $blueprint->boolean('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" bit not null', $statements[0]); - } - - public function testAddingEnum() - { - $blueprint = new Blueprint('users'); - $blueprint->enum('role', ['member', 'admin']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "role" nvarchar(255) check ("role" in (N\'member\', N\'admin\')) not null', $statements[0]); - } - - public function testAddingJson() - { - $blueprint = new Blueprint('users'); - $blueprint->json('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" nvarchar(max) not null', $statements[0]); - } - - public function testAddingJsonb() - { - $blueprint = new Blueprint('users'); - $blueprint->jsonb('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" nvarchar(max) not null', $statements[0]); - } - - public function testAddingDate() - { - $blueprint = new Blueprint('users'); - $blueprint->date('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" date not null', $statements[0]); - } - - public function testAddingYear() - { - $blueprint = new Blueprint('users'); - $blueprint->year('birth_year'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "birth_year" int not null', $statements[0]); - } - - public function testAddingDateTime() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" datetime not null', $statements[0]); - } - - public function testAddingDateTimeWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" datetime2(1) not null', $statements[0]); - } - - public function testAddingDateTimeTz() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" datetimeoffset not null', $statements[0]); - } - - public function testAddingDateTimeTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTimeTz('foo', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" datetimeoffset(1) not null', $statements[0]); - } - - public function testAddingTime() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" time not null', $statements[0]); - } - - public function testAddingTimeWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->time('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" time(1) not null', $statements[0]); - } - - public function testAddingTimeTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" time not null', $statements[0]); - } - - public function testAddingTimeTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timeTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" time(1) not null', $statements[0]); - } - - public function testAddingTimestamp() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" datetime not null', $statements[0]); - } - - public function testAddingTimestampWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" datetime2(1) not null', $statements[0]); - } - - public function testAddingTimestampTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" datetimeoffset not null', $statements[0]); - } - - public function testAddingTimestampTzWithPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampTz('created_at', 1); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" datetimeoffset(1) not null', $statements[0]); - } - - public function testAddingTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" datetime null, "updated_at" datetime null', $statements[0]); - } - - public function testAddingTimestampsTz() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampsTz(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "created_at" datetimeoffset null, "updated_at" datetimeoffset null', $statements[0]); - } - - public function testAddingRememberToken() - { - $blueprint = new Blueprint('users'); - $blueprint->rememberToken(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "remember_token" nvarchar(100) null', $statements[0]); - } - - public function testAddingBinary() - { - $blueprint = new Blueprint('users'); - $blueprint->binary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" varbinary(max) not null', $statements[0]); - } - - public function testAddingUuid() - { - $blueprint = new Blueprint('users'); - $blueprint->uuid('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" uniqueidentifier not null', $statements[0]); - } - - public function testAddingUuidDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->uuid(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "uuid" uniqueidentifier not null', $statements[0]); - } - - public function testAddingForeignUuid() - { - $blueprint = new Blueprint('users'); - $foreignId = $blueprint->foreignUuid('foo'); - $blueprint->foreignUuid('company_id')->constrained(); - $blueprint->foreignUuid('laravel_idea_id')->constrained(); - $blueprint->foreignUuid('team_id')->references('id')->on('teams'); - $blueprint->foreignUuid('team_column_id')->constrained('teams'); - - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertInstanceOf(ForeignIdColumnDefinition::class, $foreignId); - $this->assertSame([ - 'alter table "users" add "foo" uniqueidentifier not null, "company_id" uniqueidentifier not null, "laravel_idea_id" uniqueidentifier not null, "team_id" uniqueidentifier not null, "team_column_id" uniqueidentifier not null', - 'alter table "users" add constraint "users_company_id_foreign" foreign key ("company_id") references "companies" ("id")', - 'alter table "users" add constraint "users_laravel_idea_id_foreign" foreign key ("laravel_idea_id") references "laravel_ideas" ("id")', - 'alter table "users" add constraint "users_team_id_foreign" foreign key ("team_id") references "teams" ("id")', - 'alter table "users" add constraint "users_team_column_id_foreign" foreign key ("team_column_id") references "teams" ("id")', - ], $statements); - } - - public function testAddingIpAddress() - { - $blueprint = new Blueprint('users'); - $blueprint->ipAddress('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" nvarchar(45) not null', $statements[0]); - } - - public function testAddingIpAddressDefaultsColumnName() - { - $blueprint = new Blueprint('users'); - $blueprint->ipAddress(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "ip_address" nvarchar(45) not null', $statements[0]); - } - - public function testAddingMacAddress() - { - $blueprint = new Blueprint('users'); - $blueprint->macAddress('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $schemaBuilder = m::mock(Builder::class); + $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "foo" nvarchar(17) not null', $statements[0]); + return $schemaBuilder; } - public function testAddingMacAddressDefaultsColumnName() + protected function runBlueprint(Blueprint $initialBlueprint, Blueprint $blueprint = null) { - $blueprint = new Blueprint('users'); - $blueprint->macAddress(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertSame('alter table "users" add "mac_address" nvarchar(17) not null', $statements[0]); + $connection = $this->setupConnection($initialBlueprint); + if (is_null($blueprint)) { + $blueprint = $initialBlueprint; + } + return $blueprint->toSql($connection, new SqlServerGrammar); } - public function testAddingGeometry() + public function testNoInitialModifiersAddNullable() { - $blueprint = new Blueprint('geo'); - $blueprint->geometry('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name'); - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add "coordinates" geometry not null', $statements[0]); - } + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table "users" add "name" nvarchar(255) not null', $statements[0]); - public function testAddingGeography() - { - $blueprint = new Blueprint('geo'); - $blueprint->geography('coordinates'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->nullable()->change(); - $this->assertCount(1, $statements); - $this->assertSame('alter table "geo" add "coordinates" geography not null', $statements[0]); - } - - public function testAddingGeneratedColumn() - { - $blueprint = new Blueprint('products'); - $blueprint->integer('price'); - $blueprint->computed('discounted_virtual', 'price - 5'); - $blueprint->computed('discounted_stored', 'price - 5')->persisted(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "products" add "price" int not null, "discounted_virtual" as (price - 5), "discounted_stored" as (price - 5) persisted', $statements[0]); - - $blueprint = new Blueprint('products'); - $blueprint->integer('price'); - $blueprint->computed('discounted_virtual', new Expression('price - 5')); - $blueprint->computed('discounted_stored', new Expression('price - 5'))->persisted(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - $this->assertCount(1, $statements); - $this->assertSame('alter table "products" add "price" int not null, "discounted_virtual" as (price - 5), "discounted_stored" as (price - 5) persisted', $statements[0]); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $this->assertSame('alter table "users" alter column "name" nvarchar(255) null', $statements[1]); } - public function testChangingColumn() + public function testNullableInitialModifierAddDefault() { - $blueprint = new Blueprint('users'); - $blueprint->string('name')->nullable(); - - $connection = $this->getConnection(); - $grammar = $this->getGrammar(); - - $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - - $connection->shouldReceive('getSchemaBuilder')->andReturn($schemaBuilder); - - $statements = $blueprint->toSql($connection, $grammar); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name')->nullable(); - $this->assertCount(1, $statements); + $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add "name" nvarchar(255) null', $statements[0]); - $changeBlueprint = new Blueprint('users'); - $changeBlueprint->string('name')->default('admin')->change(); - $statements = $changeBlueprint->toSql($connection, $grammar); + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->default('admin')->change(); - $this->assertCount(3, $statements); + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); $this->assertSame('alter table "users" alter column "name" nvarchar(255) null', $statements[1]); $this->assertSame('alter table "users" add default \'admin\' for "name"', $statements[2]); - - $otherChangeBlueprint = new Blueprint('users'); - $otherChangeBlueprint->string('name')->nullable(false)->change(); - $statements = $otherChangeBlueprint->toSql($connection, $grammar); - - $this->assertCount(2, $statements); - $this->assertSame('alter table "users" alter column "name" nvarchar(255) not null', $statements[1]); - } - - public function testGrammarsAreMacroable() - { - // compileReplace macro. - $this->getGrammar()::macro('compileReplace', function () { - return true; - }); - - $c = $this->getGrammar()::compileReplace(); - - $this->assertTrue($c); - } - - public function testQuoteString() - { - $this->assertSame("N'中文測試'", $this->getGrammar()->quoteString('中文測試')); - } - - public function testQuoteStringOnArray() - { - $this->assertSame("N'中文', N'測試'", $this->getGrammar()->quoteString(['中文', '測試'])); - } - - public function testCreateDatabase() - { - $connection = $this->getConnection(); - - $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); - - $this->assertSame( - 'create database "my_database_a"', - $statement - ); - - $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); - - $this->assertSame( - 'create database "my_database_b"', - $statement - ); } - public function testDropDatabaseIfExists() + public function testNullableInitialModifierAddDefaultNotNullable() { - $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); - - $this->assertSame( - 'drop database if exists "my_database_a"', - $statement - ); - - $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); + $initialBlueprint = new Blueprint('users'); + $initialBlueprint->string('name')->nullable(); - $this->assertSame( - 'drop database if exists "my_database_b"', - $statement - ); - } + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table "users" add "name" nvarchar(255) null', $statements[0]); - protected function getConnection() - { - return m::mock(Connection::class); - } + $changedBlueprint = new Blueprint('users'); + $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); - public function getGrammar() - { - return new SqlServerGrammar; + $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $this->assertSame('alter table "users" alter column "name" nvarchar(255) not null', $statements[1]); + $this->assertSame('alter table "users" add default \'admin\' for "name"', $statements[2]); } } From 63427a3db51c7f7a8f04e0961c70e9903bb578c0 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 7 Apr 2024 12:25:15 -0400 Subject: [PATCH 052/197] use new GrammarTestCase to simplify tests --- composer.json | 3 +- .../Grammars/MySqlSchemaGrammarTest.php | 48 +++++------------- .../Grammars/PostgresSchemaGrammarTest.php | 45 ++++------------- .../Grammars/SQLiteSchemaGrammarTest.php | 49 ++++--------------- .../Grammars/SqlServerSchemaGrammarTest.php | 44 ++++------------- tests/GrammarTestCase.php | 42 ++++++++++++++++ 6 files changed, 85 insertions(+), 146 deletions(-) create mode 100644 tests/GrammarTestCase.php diff --git a/composer.json b/composer.json index 27cd5cc2..a178e491 100644 --- a/composer.json +++ b/composer.json @@ -84,7 +84,8 @@ "autoload-dev": { "classmap": [ "tests/TestCase.php", - "tests/DbTestCase.php" + "tests/DbTestCase.php", + "tests/GrammarTestCase.php" ], "psr-4": { "Winter\\Storm\\Tests\\": "tests/" diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index ab796314..38ed9281 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -1,52 +1,24 @@ shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); - return $connection; - } - - protected function getSchemaBuilder(Blueprint $blueprint) - { - $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - - return $schemaBuilder; - } - - protected function runBlueprint(Blueprint $initialBlueprint, Blueprint $blueprint = null) - { - $connection = $this->setupConnection($initialBlueprint); - if (is_null($blueprint)) { - $blueprint = $initialBlueprint; - } - return $blueprint->toSql($connection, new MySqlGrammar); + $this->grammar = new MySqlGrammar; } public function testNoInitialModifiersAddNullable() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name'); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table `users` add `name` varchar(255) not null', $statements[0]); @@ -54,7 +26,7 @@ public function testNoInitialModifiersAddNullable() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->nullable()->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertSame("alter table `users` modify `name` varchar(255) null", $statements[0]); } @@ -62,6 +34,7 @@ public function testNullableInitialModifierAddDefault() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name')->nullable(); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); @@ -69,7 +42,7 @@ public function testNullableInitialModifierAddDefault() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertSame("alter table `users` modify `name` varchar(255) null default 'admin'", $statements[0]); } @@ -77,6 +50,7 @@ public function testNullableInitialModifierAddDefaultNotNullable() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name')->nullable(); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); @@ -84,7 +58,7 @@ public function testNullableInitialModifierAddDefaultNotNullable() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertSame("alter table `users` modify `name` varchar(255) not null default 'admin'", $statements[0]); } } diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index 474f0858..dbf13715 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -2,50 +2,23 @@ namespace Winter\Storm\Tests\Database\Schema\Grammars; -use Illuminate\Database\Connection; -use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; -use Illuminate\Database\Schema\Builder; -use Illuminate\Database\Schema\ForeignIdColumnDefinition; use Winter\Storm\Database\Schema\Grammars\PostgresGrammar; -use Mockery as m; -use PHPUnit\Framework\TestCase; -class PostgresSchemaGrammarTest extends TestCase +class PostgresSchemaGrammarTest extends \GrammarTestCase { - protected function tearDown(): void + public function setUp(): void { - m::close(); - } + parent::setUp(); - protected function setupConnection(Blueprint $blueprint) - { - $connection = m::mock(Connection::class); - $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); - return $connection; - } - - protected function getSchemaBuilder(Blueprint $blueprint) - { - $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - - return $schemaBuilder; - } - - protected function runBlueprint(Blueprint $initialBlueprint, Blueprint $blueprint = null) - { - $connection = $this->setupConnection($initialBlueprint); - if (is_null($blueprint)) { - $blueprint = $initialBlueprint; - } - return $blueprint->toSql($connection, new PostgresGrammar); + $this->grammar = new PostgresGrammar; } public function testNoInitialModifiersAddNullable() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name'); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar(255) not null', $statements[0]); @@ -53,7 +26,7 @@ public function testNoInitialModifiersAddNullable() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->nullable()->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $parts = explode(', ', $statements[0]); $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); $this->assertSame('alter column "name" drop not null', $parts[1]); @@ -63,6 +36,7 @@ public function testNullableInitialModifierAddDefault() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name')->nullable(); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar(255) null', $statements[0]); @@ -70,7 +44,7 @@ public function testNullableInitialModifierAddDefault() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $parts = explode(', ', $statements[0]); $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); $this->assertSame('alter column "name" null', $parts[1]); @@ -81,6 +55,7 @@ public function testNullableInitialModifierAddDefaultNotNullable() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name')->nullable(); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar(255) null', $statements[0]); @@ -88,7 +63,7 @@ public function testNullableInitialModifierAddDefaultNotNullable() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $parts = explode(', ', $statements[0]); $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); $this->assertSame('alter column "name" set not null', $parts[1]); diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index 432d99ce..8d6b8c97 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -2,54 +2,23 @@ namespace Winter\Storm\Tests\Database\Schema\Grammars; -use Illuminate\Database\Capsule\Manager; -use Illuminate\Database\Connection; -use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; -use Illuminate\Database\Schema\ForeignIdColumnDefinition; use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar; -use Mockery as m; -use PHPUnit\Framework\TestCase; -use RuntimeException; -class SQLiteSchemaGrammarTest extends TestCase +class SQLiteSchemaGrammarTest extends \GrammarTestCase { - protected function tearDown(): void + public function setUp(): void { - m::close(); - } + parent::setUp(); - protected function setupConnection(Blueprint $blueprint) - { - $connection = m::mock(Connection::class); - $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); - $connection->shouldReceive('scalar')->andReturn(''); - return $connection; - } - - protected function getSchemaBuilder(Blueprint $blueprint) - { - $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - $schemaBuilder->shouldReceive('getForeignKeys')->andReturn([]); - $schemaBuilder->shouldReceive('getIndexes')->andReturn([]); - - return $schemaBuilder; - } - - protected function runBlueprint(Blueprint $initialBlueprint, Blueprint $blueprint = null) - { - $connection = $this->setupConnection($initialBlueprint); - if (is_null($blueprint)) { - $blueprint = $initialBlueprint; - } - return $blueprint->toSql($connection, new SQLiteGrammar); + $this->grammar = new SQLiteGrammar; } public function testNoInitialModifiersAddNullable() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name'); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar not null', $statements[0]); @@ -57,7 +26,7 @@ public function testNoInitialModifiersAddNullable() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->nullable()->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertStringContainsString('"name" varchar', $statements[0]); } @@ -65,6 +34,7 @@ public function testNullableInitialModifierAddDefault() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name')->nullable(); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); @@ -72,7 +42,7 @@ public function testNullableInitialModifierAddDefault() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertStringContainsString("varchar default 'admin'", $statements[0]); } @@ -80,6 +50,7 @@ public function testNullableInitialModifierAddDefaultNotNullable() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name')->nullable(); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); @@ -87,7 +58,7 @@ public function testNullableInitialModifierAddDefaultNotNullable() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertStringContainsString("\"name\" varchar not null default 'admin'", $statements[0]); } } diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index d68702df..60a42873 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -2,49 +2,23 @@ namespace Winter\Storm\Tests\Database\Schema\Grammars; -use Illuminate\Database\Connection; -use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; -use Illuminate\Database\Schema\ForeignIdColumnDefinition; use Winter\Storm\Database\Schema\Grammars\SqlServerGrammar; -use Mockery as m; -use PHPUnit\Framework\TestCase; -class SqlServerSchemaGrammarTest extends TestCase +class SqlServerSchemaGrammarTest extends \GrammarTestCase { - protected function tearDown(): void + public function setUp(): void { - m::close(); - } + parent::setUp(); - protected function setupConnection(Blueprint $blueprint) - { - $connection = m::mock(Connection::class); - $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); - return $connection; - } - - protected function getSchemaBuilder(Blueprint $blueprint) - { - $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - - return $schemaBuilder; - } - - protected function runBlueprint(Blueprint $initialBlueprint, Blueprint $blueprint = null) - { - $connection = $this->setupConnection($initialBlueprint); - if (is_null($blueprint)) { - $blueprint = $initialBlueprint; - } - return $blueprint->toSql($connection, new SqlServerGrammar); + $this->grammar = new SqlServerGrammar; } public function testNoInitialModifiersAddNullable() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name'); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add "name" nvarchar(255) not null', $statements[0]); @@ -52,7 +26,7 @@ public function testNoInitialModifiersAddNullable() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->nullable()->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertSame('alter table "users" alter column "name" nvarchar(255) null', $statements[1]); } @@ -60,6 +34,7 @@ public function testNullableInitialModifierAddDefault() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name')->nullable(); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add "name" nvarchar(255) null', $statements[0]); @@ -67,7 +42,7 @@ public function testNullableInitialModifierAddDefault() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertSame('alter table "users" alter column "name" nvarchar(255) null', $statements[1]); $this->assertSame('alter table "users" add default \'admin\' for "name"', $statements[2]); } @@ -76,6 +51,7 @@ public function testNullableInitialModifierAddDefaultNotNullable() { $initialBlueprint = new Blueprint('users'); $initialBlueprint->string('name')->nullable(); + $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add "name" nvarchar(255) null', $statements[0]); @@ -83,7 +59,7 @@ public function testNullableInitialModifierAddDefaultNotNullable() $changedBlueprint = new Blueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); - $statements = $this->runBlueprint($initialBlueprint, $changedBlueprint); + $statements = $this->runBlueprint($changedBlueprint); $this->assertSame('alter table "users" alter column "name" nvarchar(255) not null', $statements[1]); $this->assertSame('alter table "users" add default \'admin\' for "name"', $statements[2]); } diff --git a/tests/GrammarTestCase.php b/tests/GrammarTestCase.php new file mode 100644 index 00000000..cd195321 --- /dev/null +++ b/tests/GrammarTestCase.php @@ -0,0 +1,42 @@ +shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); + $connection->shouldReceive('scalar')->andReturn(''); + $this->connection = $connection; + } + + protected function getSchemaBuilder(Blueprint $blueprint) + { + $schemaBuilder = m::mock(Builder::class); + $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); + $schemaBuilder->shouldReceive('getForeignKeys')->andReturn([]); + $schemaBuilder->shouldReceive('getIndexes')->andReturn([]); + + return $schemaBuilder; + } + + protected function runBlueprint(Blueprint $blueprint) + { + return $blueprint->toSql($this->connection, $this->grammar); + } +} From a18f40b2dc60bd050b08a69c9beec61727c9fb5e Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 8 Apr 2024 13:20:32 -0400 Subject: [PATCH 053/197] extend Illuminate\Database\Connection --- src/Database/Connections/Connection.php | 2 +- src/Database/Connections/MySqlConnection.php | 4 +++- src/Database/Connections/PostgresConnection.php | 4 +++- src/Database/Connections/SQLiteConnection.php | 4 +++- src/Database/Connections/SqlServerConnection.php | 4 +++- src/Database/Connectors/ConnectionFactory.php | 2 +- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php index e42c91f5..80cccca2 100644 --- a/src/Database/Connections/Connection.php +++ b/src/Database/Connections/Connection.php @@ -4,7 +4,7 @@ use Winter\Storm\Database\QueryBuilder; use Illuminate\Database\Connection as ConnectionBase; -class Connection extends ConnectionBase +trait Connection { /** * Get a new query builder instance. diff --git a/src/Database/Connections/MySqlConnection.php b/src/Database/Connections/MySqlConnection.php index dbcf04df..514dfb15 100644 --- a/src/Database/Connections/MySqlConnection.php +++ b/src/Database/Connections/MySqlConnection.php @@ -10,8 +10,10 @@ /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ -class MySqlConnection extends Connection +class MySqlConnection extends \Illuminate\Database\MySqlConnection { + use Connection; + /** * Get the default query grammar instance. * diff --git a/src/Database/Connections/PostgresConnection.php b/src/Database/Connections/PostgresConnection.php index e1b11dc0..1730b532 100644 --- a/src/Database/Connections/PostgresConnection.php +++ b/src/Database/Connections/PostgresConnection.php @@ -9,8 +9,10 @@ /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ -class PostgresConnection extends Connection +class PostgresConnection extends \Illuminate\Database\PostgresConnection { + use Connection; + /** * Get the default query grammar instance. * diff --git a/src/Database/Connections/SQLiteConnection.php b/src/Database/Connections/SQLiteConnection.php index 0e22ff93..95631bae 100644 --- a/src/Database/Connections/SQLiteConnection.php +++ b/src/Database/Connections/SQLiteConnection.php @@ -9,8 +9,10 @@ /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ -class SQLiteConnection extends Connection +class SQLiteConnection extends \Illuminate\Database\SQLiteConnection { + use Connection; + /** * Get the default query grammar instance. * diff --git a/src/Database/Connections/SqlServerConnection.php b/src/Database/Connections/SqlServerConnection.php index 640e3181..cbe0728b 100644 --- a/src/Database/Connections/SqlServerConnection.php +++ b/src/Database/Connections/SqlServerConnection.php @@ -12,8 +12,10 @@ /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ -class SqlServerConnection extends Connection +class SqlServerConnection extends \Illuminate\Database\SqlServerConnection { + use Connection; + /** * Execute a Closure within a transaction. * diff --git a/src/Database/Connectors/ConnectionFactory.php b/src/Database/Connectors/ConnectionFactory.php index d9be1dc6..a193640f 100644 --- a/src/Database/Connectors/ConnectionFactory.php +++ b/src/Database/Connectors/ConnectionFactory.php @@ -2,7 +2,7 @@ use Illuminate\Support\Arr; use Illuminate\Database\Connectors\ConnectionFactory as ConnectionFactoryBase; -use Winter\Storm\Database\Connections\Connection; +use Illuminate\Database\Connection; use Winter\Storm\Database\Connections\MariaDbConnection; use Winter\Storm\Database\Connections\MySqlConnection; use Winter\Storm\Database\Connections\SQLiteConnection; From a98c60cedaee8fac1b0c2d2504ef1e40062da73d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 8 Apr 2024 13:27:27 -0400 Subject: [PATCH 054/197] fix MariaDbConnection parent --- src/Database/Connections/Connection.php | 1 - src/Database/Connections/MariaDbConnection.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php index 80cccca2..50e7e592 100644 --- a/src/Database/Connections/Connection.php +++ b/src/Database/Connections/Connection.php @@ -2,7 +2,6 @@ use Winter\Storm\Database\MemoryCache; use Winter\Storm\Database\QueryBuilder; -use Illuminate\Database\Connection as ConnectionBase; trait Connection { diff --git a/src/Database/Connections/MariaDbConnection.php b/src/Database/Connections/MariaDbConnection.php index 9d06b2bf..35e185b8 100644 --- a/src/Database/Connections/MariaDbConnection.php +++ b/src/Database/Connections/MariaDbConnection.php @@ -10,7 +10,7 @@ /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ -class MariaDbConnection extends Connection +class MariaDbConnection extends \Illuminate\Database\MariaDbConnection { /** * Get the default query grammar instance. From 2fb435da22c2f953eab003d9d58a90368472f8ad Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 9 Apr 2024 11:26:01 -0400 Subject: [PATCH 055/197] make sure we have a column definition --- src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php | 5 +++++ src/Database/Schema/Grammars/PostgresGrammar.php | 5 +++++ src/Database/Schema/Grammars/SQLiteGrammar.php | 3 +++ src/Database/Schema/Grammars/SqlServerGrammar.php | 4 ++++ 4 files changed, 17 insertions(+) diff --git a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php index 1a83fb5e..e990c58d 100644 --- a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php +++ b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php @@ -4,6 +4,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Support\Fluent; trait MySqlBasedGrammar @@ -37,6 +38,10 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection ); $oldColumn = $oldColumns->where('name', $column->name)->first(); + if (!$oldColumn instanceof ColumnDefinition) { + $oldColumn = new ColumnDefinition($oldColumn); + } + foreach ($this->modifiers as $modifier) { if (method_exists($this, $method = "modify{$modifier}")) { $mod = strtolower($modifier); diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 16c81542..98ab4bed 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -4,6 +4,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Database\Schema\Grammars\PostgresGrammar as PostgresGrammarBase; use Illuminate\Support\Fluent; @@ -32,6 +33,10 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $changes = ['type '.$this->getType($column).$this->modifyCollate($blueprint, $column)]; $oldColumn = $oldColumns->where('name', $column->name)->first(); + if (!$oldColumn instanceof ColumnDefinition) { + $oldColumn = new ColumnDefinition($oldColumn); + } + foreach ($this->modifiers as $modifier) { if ($modifier === 'Collate') { continue; diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 095a96eb..dcb79b25 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -69,6 +69,9 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection } $oldColumn = $oldColumns->where('name', $column->name)->first(); + if (!$oldColumn instanceof ColumnDefinition) { + $oldColumn = new ColumnDefinition($oldColumn); + } $sql = $name.' '.$this->getType($column); foreach ($this->modifiers as $modifier) { diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index c2212c11..2684a4d7 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -4,6 +4,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Database\Schema\Grammars\SqlServerGrammar as SqlServerGrammarBase; use Illuminate\Support\Fluent; @@ -37,6 +38,9 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection ); $oldColumn = $oldColumns->where('name', $column->name)->first(); + if (!$oldColumn instanceof ColumnDefinition) { + $oldColumn = new ColumnDefinition($oldColumn); + } foreach ($this->modifiers as $modifier) { if (method_exists($this, $method = "modify{$modifier}")) { From a4f0f49722e543d7794ef48b21d0c537358cfc7c Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 9 Apr 2024 11:36:03 -0400 Subject: [PATCH 056/197] hasAttribute() has been added in Eloquent HasAttributes trait --- src/Database/Model.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Database/Model.php b/src/Database/Model.php index 5e3d9fce..bf31daa0 100644 --- a/src/Database/Model.php +++ b/src/Database/Model.php @@ -1097,20 +1097,6 @@ public function addJsonable($attributes = null) // Getters // - /** - * Determine if the given attribute will be processed by getAttributeValue(). - */ - public function hasAttribute(string $key): bool - { - return ( - array_key_exists($key, $this->attributes) - || array_key_exists($key, $this->casts) - || $this->hasGetMutator($key) - || $this->hasAttributeMutator($key) - || $this->isClassCastable($key) - ); - } - /** * Get an attribute from the model. * Overrides {@link Eloquent} to support loading from property-defined relations. From 73aefbe07f7931ca6dc2339d76f0ca06e7c4f37a Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 9 Apr 2024 14:32:18 -0400 Subject: [PATCH 057/197] remove single quotes from value before processing it --- .../Schema/Grammars/Concerns/MySqlBasedGrammar.php | 9 +++++++++ src/Database/Schema/Grammars/PostgresGrammar.php | 9 +++++++++ src/Database/Schema/Grammars/SQLiteGrammar.php | 9 +++++++++ src/Database/Schema/Grammars/SqlServerGrammar.php | 9 +++++++++ 4 files changed, 36 insertions(+) diff --git a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php index e990c58d..963acdce 100644 --- a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php +++ b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php @@ -54,4 +54,13 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); } + + public function getDefaultValue($value) + { + if (is_string($value)) { + $value = preg_replace('#\'#', '', $value); + } + + return parent::getDefaultValue($value); + } } diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 98ab4bed..3c61f036 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -58,4 +58,13 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); } + + public function getDefaultValue($value) + { + if (is_string($value)) { + $value = preg_replace('#\'#', '', $value); + } + + return parent::getDefaultValue($value); + } } diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index dcb79b25..8a3e22e7 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -147,4 +147,13 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection ) ); } + + public function getDefaultValue($value) + { + if (is_string($value)) { + $value = preg_replace('#\'#', '', $value); + } + + return parent::getDefaultValue($value); + } } diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 2684a4d7..770b71da 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -55,4 +55,13 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection return $changes; } + + public function getDefaultValue($value) + { + if (is_string($value)) { + $value = preg_replace('#\'#', '', $value); + } + + return parent::getDefaultValue($value); + } } From a97f6552c86d916a78048753125f0d60d20b5e86 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 10 Apr 2024 11:39:21 -0400 Subject: [PATCH 058/197] add back doctrine functionality --- composer.json | 1 + phpstan-baseline.neon | 7 +- src/Database/Connections/Connection.php | 121 ++++++++++++ .../Connections/MariaDbConnection.php | 11 ++ src/Database/Connections/MySqlConnection.php | 11 ++ .../Connections/PostgresConnection.php | 11 ++ src/Database/Connections/SQLiteConnection.php | 11 ++ .../Connections/SqlServerConnection.php | 13 +- .../PDO/Concerns/ConnectsToDatabase.php | 30 +++ src/Database/PDO/Connection.php | 182 ++++++++++++++++++ src/Database/PDO/MySqlDriver.php | 19 ++ src/Database/PDO/PostgresDriver.php | 19 ++ src/Database/PDO/SQLiteDriver.php | 19 ++ src/Database/PDO/SqlServerConnection.php | 152 +++++++++++++++ src/Database/PDO/SqlServerDriver.php | 32 +++ 15 files changed, 635 insertions(+), 4 deletions(-) create mode 100644 src/Database/PDO/Concerns/ConnectsToDatabase.php create mode 100644 src/Database/PDO/Connection.php create mode 100644 src/Database/PDO/MySqlDriver.php create mode 100644 src/Database/PDO/PostgresDriver.php create mode 100644 src/Database/PDO/SQLiteDriver.php create mode 100644 src/Database/PDO/SqlServerConnection.php create mode 100644 src/Database/PDO/SqlServerDriver.php diff --git a/composer.json b/composer.json index a178e491..1ca2e936 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "ext-zip": "*", "assetic/framework": "^3.1", + "doctrine/dbal": "^3.0", "enshrined/svg-sanitize": "~0.16", "laravel/framework": "^11.0", "laravel/tinker": "^2.8.1", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index c5f878a9..e659a4ac 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -120,6 +120,7 @@ parameters: path: src/Support/Testing/Fakes/MailFake.php - - message: "#^Call to function is_null\\(\\) with Closure will always evaluate to false\\.$#" - count: 1 - path: src/Validation/Factory.php + message: "#^Call to function is_null\\(\\) with (Closure|Doctrine\\\\DBAL\\\\Connection) will always evaluate to false\\.$#" + paths: + - src/Validation/Factory.php + - src/Database/Connections/Connection.php diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php index 50e7e592..ce200c0b 100644 --- a/src/Database/Connections/Connection.php +++ b/src/Database/Connections/Connection.php @@ -1,10 +1,28 @@ + */ + protected $doctrineTypeMappings = []; + /** * Get a new query builder instance. * @@ -70,4 +88,107 @@ protected function fireEvent(string $event, array|object $attributes = []): void $eventManager->dispatch($event, $attributes); } + + /** + * Is Doctrine available? + * + * @return bool + */ + public function isDoctrineAvailable() + { + return class_exists('Doctrine\DBAL\Connection'); + } + + /** + * Indicates whether native alter operations will be used when dropping or renaming columns, even if Doctrine DBAL is installed. + * + * @return bool + */ + public function usingNativeSchemaOperations() + { + return ! $this->isDoctrineAvailable(); + } + + /** + * Get a Doctrine Schema Column instance. + * + * @param string $table + * @param string $column + * @return \Doctrine\DBAL\Schema\Column + */ + public function getDoctrineColumn($table, $column) + { + $schema = $this->getDoctrineSchemaManager(); + + return $schema->listTableDetails($table)->getColumn($column); + } + + /** + * Get the Doctrine DBAL schema manager for the connection. + * + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getDoctrineSchemaManager() + { + $connection = $this->getDoctrineConnection(); + + // Doctrine v2 expects one parameter while v3 expects two. 2nd will be ignored on v2... + return $this->getDoctrineDriver()->getSchemaManager( + $connection, + $connection->getDatabasePlatform() + ); + } + + /** + * Get the Doctrine DBAL database connection instance. + * + * @return \Doctrine\DBAL\Connection + */ + public function getDoctrineConnection() + { + if (is_null($this->doctrineConnection)) { + $driver = $this->getDoctrineDriver(); + + $this->doctrineConnection = new DoctrineConnection(array_filter([ + 'pdo' => $this->getPdo(), + 'dbname' => $this->getDatabaseName(), + 'driver' => $driver->getName(), + 'serverVersion' => $this->getConfig('server_version'), + ]), $driver); + + foreach ($this->doctrineTypeMappings as $name => $type) { + $this->doctrineConnection + ->getDatabasePlatform() + ->registerDoctrineTypeMapping($type, $name); + } + } + + return $this->doctrineConnection; + } + + /** + * Register a custom Doctrine mapping type. + * + * @param Type|class-string $class + * @param string $name + * @param string $type + * @return void + * + * @throws \RuntimeException + */ + public function registerDoctrineType(Type|string $class, string $name, string $type): void + { + if (! $this->isDoctrineAvailable()) { + throw new RuntimeException( + 'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).' + ); + } + + if (! Type::hasType($name)) { + Type::getTypeRegistry() + ->register($name, is_string($class) ? new $class() : $class); + } + + $this->doctrineTypeMappings[$name] = $type; + } } diff --git a/src/Database/Connections/MariaDbConnection.php b/src/Database/Connections/MariaDbConnection.php index 35e185b8..f03a425a 100644 --- a/src/Database/Connections/MariaDbConnection.php +++ b/src/Database/Connections/MariaDbConnection.php @@ -4,6 +4,7 @@ use Illuminate\Database\Schema\MariaDbBuilder; use Illuminate\Database\Query\Processors\MariaDbProcessor; +use Winter\Storm\Database\PDO\MySqlDriver; use Winter\Storm\Database\Schema\Grammars\MariaDbGrammar as SchemaGrammar; use Winter\Storm\Database\Query\Grammars\MariaDbGrammar as QueryGrammar; @@ -73,4 +74,14 @@ public function bindValues($statement, $bindings) ); } } + + /** + * Get the Doctrine DBAL driver. + * + * @return \Winter\Storm\Database\PDO\MySqlDriver + */ + protected function getDoctrineDriver() + { + return new MySqlDriver; + } } diff --git a/src/Database/Connections/MySqlConnection.php b/src/Database/Connections/MySqlConnection.php index 514dfb15..f77c0377 100644 --- a/src/Database/Connections/MySqlConnection.php +++ b/src/Database/Connections/MySqlConnection.php @@ -4,6 +4,7 @@ use Illuminate\Database\Schema\MySqlBuilder; use Illuminate\Database\Query\Processors\MySqlProcessor; +use Winter\Storm\Database\PDO\MySqlDriver; use Winter\Storm\Database\Query\Grammars\MySqlGrammar as QueryGrammar; use Winter\Storm\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar; @@ -75,4 +76,14 @@ public function bindValues($statement, $bindings) ); } } + + /** + * Get the Doctrine DBAL driver. + * + * @return \Winter\Storm\Database\PDO\MySqlDriver + */ + protected function getDoctrineDriver() + { + return new MySqlDriver; + } } diff --git a/src/Database/Connections/PostgresConnection.php b/src/Database/Connections/PostgresConnection.php index 1730b532..acda5bc7 100644 --- a/src/Database/Connections/PostgresConnection.php +++ b/src/Database/Connections/PostgresConnection.php @@ -3,6 +3,7 @@ use Illuminate\Database\Schema\PostgresBuilder; use Illuminate\Database\Query\Processors\PostgresProcessor; +use Winter\Storm\Database\PDO\PostgresDriver; use Winter\Storm\Database\Query\Grammars\PostgresGrammar as QueryGrammar; use Winter\Storm\Database\Schema\Grammars\PostgresGrammar as SchemaGrammar; @@ -56,4 +57,14 @@ protected function getDefaultPostProcessor() { return new PostgresProcessor; } + + /** + * Get the Doctrine DBAL driver. + * + * @return \Winter\Storm\Database\PDO\PostgresDriver + */ + protected function getDoctrineDriver() + { + return new PostgresDriver; + } } diff --git a/src/Database/Connections/SQLiteConnection.php b/src/Database/Connections/SQLiteConnection.php index 95631bae..8c2b77eb 100644 --- a/src/Database/Connections/SQLiteConnection.php +++ b/src/Database/Connections/SQLiteConnection.php @@ -3,6 +3,7 @@ use Illuminate\Database\Schema\SQLiteBuilder; use Illuminate\Database\Query\Processors\SQLiteProcessor; +use Winter\Storm\Database\PDO\SQLiteDriver; use Winter\Storm\Database\Query\Grammars\SQLiteGrammar as QueryGrammar; use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar as SchemaGrammar; @@ -56,4 +57,14 @@ protected function getDefaultPostProcessor() { return new SQLiteProcessor; } + + /** + * Get the Doctrine DBAL driver. + * + * @return \Winter\Storm\Database\PDO\SQLiteDriver + */ + protected function getDoctrineDriver() + { + return new SQLiteDriver; + } } diff --git a/src/Database/Connections/SqlServerConnection.php b/src/Database/Connections/SqlServerConnection.php index cbe0728b..a9d96969 100644 --- a/src/Database/Connections/SqlServerConnection.php +++ b/src/Database/Connections/SqlServerConnection.php @@ -6,8 +6,9 @@ use Illuminate\Database\Schema\SqlServerBuilder; use Illuminate\Database\Query\Processors\SqlServerProcessor; -use Winter\Storm\Database\Schema\Grammars\SqlServerGrammar as SchemaGrammar; +use Winter\Storm\Database\PDO\SqlServerDriver; use Winter\Storm\Database\Query\Grammars\SqlServerGrammar as QueryGrammar; +use Winter\Storm\Database\Schema\Grammars\SqlServerGrammar as SchemaGrammar; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar @@ -102,4 +103,14 @@ protected function getDefaultPostProcessor() { return new SqlServerProcessor; } + + /** + * Get the Doctrine DBAL driver. + * + * @return \Winter\Storm\Database\PDO\SqlServerDriver + */ + protected function getDoctrineDriver() + { + return new SqlServerDriver; + } } diff --git a/src/Database/PDO/Concerns/ConnectsToDatabase.php b/src/Database/PDO/Concerns/ConnectsToDatabase.php new file mode 100644 index 00000000..cdffb75a --- /dev/null +++ b/src/Database/PDO/Concerns/ConnectsToDatabase.php @@ -0,0 +1,30 @@ +connection = $connection; + } + + /** + * Execute an SQL statement. + * + * @param string $statement + * @return int + */ + public function exec(string $statement): int + { + try { + $result = $this->connection->exec($statement); + + \assert($result !== false); + + return $result; + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * Prepare a new SQL statement. + * + * @param string $sql + * @return \Doctrine\DBAL\Driver\PDO\Statement + */ + public function prepare(string $sql): StatementInterface + { + try { + return $this->createStatement( + $this->connection->prepare($sql) + ); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * Execute a new query against the connection. + * + * @param string $sql + * @return \Doctrine\DBAL\Driver\Result + */ + public function query(string $sql): ResultInterface + { + try { + $stmt = $this->connection->query($sql); + + \assert($stmt instanceof PDOStatement); + + return new Result($stmt); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * Get the last insert ID. + * + * @param string|null $name + * @return mixed + */ + public function lastInsertId($name = null) + { + try { + if ($name === null) { + return $this->connection->lastInsertId(); + } + + return $this->connection->lastInsertId($name); + } catch (PDOException $exception) { + throw Exception::new($exception); + } + } + + /** + * Create a new statement instance. + * + * @param \PDOStatement $stmt + * @return \Doctrine\DBAL\Driver\PDO\Statement + */ + protected function createStatement(PDOStatement $stmt): Statement + { + return new Statement($stmt); + } + + /** + * Begin a new database transaction. + * + * @return bool + */ + public function beginTransaction() + { + return $this->connection->beginTransaction(); + } + + /** + * Commit a database transaction. + * + * @return bool + */ + public function commit() + { + return $this->connection->commit(); + } + + /** + * Rollback a database transaction. + * + * @return bool + */ + public function rollBack() + { + return $this->connection->rollBack(); + } + + /** + * Wrap quotes around the given input. + * + * @param string $input + * @param int $type + * @return string + */ + public function quote($input, $type = ParameterType::STRING) + { + return $this->connection->quote($input, $type); + } + + /** + * Get the server version for the connection. + * + * @return string + */ + public function getServerVersion() + { + return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); + } + + /** + * Get the wrapped PDO connection. + * + * @return \PDO + */ + public function getWrappedConnection(): PDO + { + return $this->connection; + } +} diff --git a/src/Database/PDO/MySqlDriver.php b/src/Database/PDO/MySqlDriver.php new file mode 100644 index 00000000..5a3c449c --- /dev/null +++ b/src/Database/PDO/MySqlDriver.php @@ -0,0 +1,19 @@ +connection = $connection; + } + + /** + * Prepare a new SQL statement. + * + * @param string $sql + * @return \Doctrine\DBAL\Driver\Statement + */ + public function prepare(string $sql): StatementInterface + { + return new Statement( + $this->connection->prepare($sql) + ); + } + + /** + * Execute a new query against the connection. + * + * @param string $sql + * @return \Doctrine\DBAL\Driver\Result + */ + public function query(string $sql): Result + { + return $this->connection->query($sql); + } + + /** + * Execute an SQL statement. + * + * @param string $statement + * @return int + */ + public function exec(string $statement): int + { + return $this->connection->exec($statement); + } + + /** + * Get the last insert ID. + * + * @param string|null $name + * @return mixed + */ + public function lastInsertId($name = null) + { + if ($name === null) { + return $this->connection->lastInsertId($name); + } + + return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') + ->execute([$name]) + ->fetchOne(); + } + + /** + * Begin a new database transaction. + * + * @return bool + */ + public function beginTransaction() + { + return $this->connection->beginTransaction(); + } + + /** + * Commit a database transaction. + * + * @return bool + */ + public function commit() + { + return $this->connection->commit(); + } + + /** + * Rollback a database transaction. + * + * @return bool + */ + public function rollBack() + { + return $this->connection->rollBack(); + } + + /** + * Wrap quotes around the given input. + * + * @param string $value + * @param int $type + * @return string + */ + public function quote($value, $type = ParameterType::STRING) + { + $val = $this->connection->quote($value, $type); + + // Fix for a driver version terminating all values with null byte... + if (\is_string($val) && str_contains($val, "\0")) { + $val = \substr($val, 0, -1); + } + + return $val; + } + + /** + * Get the server version for the connection. + * + * @return string + */ + public function getServerVersion() + { + return $this->connection->getServerVersion(); + } + + /** + * Get the wrapped PDO connection. + * + * @return \PDO + */ + public function getWrappedConnection(): PDO + { + return $this->connection->getWrappedConnection(); + } +} diff --git a/src/Database/PDO/SqlServerDriver.php b/src/Database/PDO/SqlServerDriver.php new file mode 100644 index 00000000..633565d9 --- /dev/null +++ b/src/Database/PDO/SqlServerDriver.php @@ -0,0 +1,32 @@ + Date: Mon, 15 Apr 2024 02:51:00 -0600 Subject: [PATCH 059/197] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1ca2e936..a88836fd 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "ext-zip": "*", "assetic/framework": "^3.1", - "doctrine/dbal": "^3.0", + "doctrine/dbal": "^3.0", "enshrined/svg-sanitize": "~0.16", "laravel/framework": "^11.0", "laravel/tinker": "^2.8.1", From 713f258d979410b1c48f2df6415ffe020e1fe02a Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 15 Apr 2024 02:51:08 -0600 Subject: [PATCH 060/197] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a88836fd..de25bba4 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,7 @@ "squizlabs/php_codesniffer": "^3.2", "php-parallel-lint/php-parallel-lint": "^1.0", "meyfa/phpunit-assert-gd": "^4.0", - "mockery/mockery": "^1.6|^2.0", + "mockery/mockery": "^1.6|^2.0", "dms/phpunit-arraysubset-asserts": "^0.5", "orchestra/testbench": "^9.0", "larastan/larastan": "^2.8.1" From a9e164e9857a92f68e00d5d60cc0f1a059ffd98b Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 15 Apr 2024 02:51:15 -0600 Subject: [PATCH 061/197] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index de25bba4..c58600b6 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ "meyfa/phpunit-assert-gd": "^4.0", "mockery/mockery": "^1.6|^2.0", "dms/phpunit-arraysubset-asserts": "^0.5", - "orchestra/testbench": "^9.0", + "orchestra/testbench": "^9.0", "larastan/larastan": "^2.8.1" }, From f21710e00d8f1cddc0073dc8f42d105f5927ad2f Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 15 Apr 2024 02:51:28 -0600 Subject: [PATCH 062/197] Update composer.json --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index c58600b6..a8bf9f27 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,6 @@ "orchestra/testbench": "^9.0", "larastan/larastan": "^2.8.1" }, - "suggest": { "ext-pdo_dblib": "Required to use MS SQL Server databases", "ext-pdo_mysql": "Required to use MySQL databases", From 04882b5367441d2b8f93bd462887d8ca70b87724 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 10:10:58 -0400 Subject: [PATCH 063/197] fix composer dependencies --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a8bf9f27..f8ac38f8 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } ], "require": { - "php": "^8.1", + "php": "^8.2", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", @@ -52,6 +52,7 @@ "winter/laravel-config-writer": "^1.0.1" }, "require-dev": { + "phpunit/phpunit": "^10.5|^11.0", "squizlabs/php_codesniffer": "^3.2", "php-parallel-lint/php-parallel-lint": "^1.0", "meyfa/phpunit-assert-gd": "^4.0", From 478df3d9349096cb99c3795438e91943ad9e5f22 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 10:19:26 -0400 Subject: [PATCH 064/197] revert Connection trait into a class --- src/Database/Connections/Connection.php | 192 +---------------- .../Connections/MariaDbConnection.php | 3 + src/Database/Connections/MySqlConnection.php | 3 +- .../Connections/PostgresConnection.php | 3 +- src/Database/Connections/SQLiteConnection.php | 3 +- .../Connections/SqlServerConnection.php | 3 +- src/Database/Traits/HasConnection.php | 193 ++++++++++++++++++ 7 files changed, 207 insertions(+), 193 deletions(-) create mode 100644 src/Database/Traits/HasConnection.php diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php index ce200c0b..76022cb8 100644 --- a/src/Database/Connections/Connection.php +++ b/src/Database/Connections/Connection.php @@ -1,194 +1,8 @@ - */ - protected $doctrineTypeMappings = []; - - /** - * Get a new query builder instance. - * - * @return \Winter\Storm\Database\QueryBuilder - */ - public function query() - { - return new QueryBuilder( - $this, - $this->getQueryGrammar(), - $this->getPostProcessor() - ); - } - - /** - * Flush the memory cache. - * @return void - */ - public static function flushDuplicateCache() - { - MemoryCache::instance()->flush(); - } - - /** - * Log a query in the connection's query log. - * - * @param string $query - * @param array $bindings - * @param float|null $time - * @return void - */ - public function logQuery($query, $bindings, $time = null) - { - $this->fireEvent('illuminate.query', [$query, $bindings, $time, $this->getName()]); - - parent::logQuery($query, $bindings, $time); - } - - /** - * Fire an event for this connection. - * - * @param string $event - * @return array|null - */ - protected function fireConnectionEvent($event) - { - $this->fireEvent('connection.'.$this->getName().'.'.$event, $this); - - parent::fireConnectionEvent($event); - } - - /** - * Fire the given event if possible. - */ - protected function fireEvent(string $event, array|object $attributes = []): void - { - /** @var \Winter\Storm\Events\Dispatcher|null */ - $eventManager = $this->events; - - if (!isset($eventManager)) { - return; - } - - $eventManager->dispatch($event, $attributes); - } - - /** - * Is Doctrine available? - * - * @return bool - */ - public function isDoctrineAvailable() - { - return class_exists('Doctrine\DBAL\Connection'); - } - - /** - * Indicates whether native alter operations will be used when dropping or renaming columns, even if Doctrine DBAL is installed. - * - * @return bool - */ - public function usingNativeSchemaOperations() - { - return ! $this->isDoctrineAvailable(); - } - - /** - * Get a Doctrine Schema Column instance. - * - * @param string $table - * @param string $column - * @return \Doctrine\DBAL\Schema\Column - */ - public function getDoctrineColumn($table, $column) - { - $schema = $this->getDoctrineSchemaManager(); - - return $schema->listTableDetails($table)->getColumn($column); - } - - /** - * Get the Doctrine DBAL schema manager for the connection. - * - * @return \Doctrine\DBAL\Schema\AbstractSchemaManager - */ - public function getDoctrineSchemaManager() - { - $connection = $this->getDoctrineConnection(); - - // Doctrine v2 expects one parameter while v3 expects two. 2nd will be ignored on v2... - return $this->getDoctrineDriver()->getSchemaManager( - $connection, - $connection->getDatabasePlatform() - ); - } - - /** - * Get the Doctrine DBAL database connection instance. - * - * @return \Doctrine\DBAL\Connection - */ - public function getDoctrineConnection() - { - if (is_null($this->doctrineConnection)) { - $driver = $this->getDoctrineDriver(); - - $this->doctrineConnection = new DoctrineConnection(array_filter([ - 'pdo' => $this->getPdo(), - 'dbname' => $this->getDatabaseName(), - 'driver' => $driver->getName(), - 'serverVersion' => $this->getConfig('server_version'), - ]), $driver); - - foreach ($this->doctrineTypeMappings as $name => $type) { - $this->doctrineConnection - ->getDatabasePlatform() - ->registerDoctrineTypeMapping($type, $name); - } - } - - return $this->doctrineConnection; - } - - /** - * Register a custom Doctrine mapping type. - * - * @param Type|class-string $class - * @param string $name - * @param string $type - * @return void - * - * @throws \RuntimeException - */ - public function registerDoctrineType(Type|string $class, string $name, string $type): void - { - if (! $this->isDoctrineAvailable()) { - throw new RuntimeException( - 'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).' - ); - } - - if (! Type::hasType($name)) { - Type::getTypeRegistry() - ->register($name, is_string($class) ? new $class() : $class); - } - - $this->doctrineTypeMappings[$name] = $type; - } + use HasConnection; } diff --git a/src/Database/Connections/MariaDbConnection.php b/src/Database/Connections/MariaDbConnection.php index f03a425a..a1cb86d0 100644 --- a/src/Database/Connections/MariaDbConnection.php +++ b/src/Database/Connections/MariaDbConnection.php @@ -7,12 +7,15 @@ use Winter\Storm\Database\PDO\MySqlDriver; use Winter\Storm\Database\Schema\Grammars\MariaDbGrammar as SchemaGrammar; use Winter\Storm\Database\Query\Grammars\MariaDbGrammar as QueryGrammar; +use Winter\Storm\Database\Traits\HasConnection; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ class MariaDbConnection extends \Illuminate\Database\MariaDbConnection { + use HasConnection; + /** * Get the default query grammar instance. * diff --git a/src/Database/Connections/MySqlConnection.php b/src/Database/Connections/MySqlConnection.php index f77c0377..aab13d27 100644 --- a/src/Database/Connections/MySqlConnection.php +++ b/src/Database/Connections/MySqlConnection.php @@ -7,13 +7,14 @@ use Winter\Storm\Database\PDO\MySqlDriver; use Winter\Storm\Database\Query\Grammars\MySqlGrammar as QueryGrammar; use Winter\Storm\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar; +use Winter\Storm\Database\Traits\HasConnection; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ class MySqlConnection extends \Illuminate\Database\MySqlConnection { - use Connection; + use HasConnection; /** * Get the default query grammar instance. diff --git a/src/Database/Connections/PostgresConnection.php b/src/Database/Connections/PostgresConnection.php index acda5bc7..24ade075 100644 --- a/src/Database/Connections/PostgresConnection.php +++ b/src/Database/Connections/PostgresConnection.php @@ -6,13 +6,14 @@ use Winter\Storm\Database\PDO\PostgresDriver; use Winter\Storm\Database\Query\Grammars\PostgresGrammar as QueryGrammar; use Winter\Storm\Database\Schema\Grammars\PostgresGrammar as SchemaGrammar; +use Winter\Storm\Database\Traits\HasConnection; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ class PostgresConnection extends \Illuminate\Database\PostgresConnection { - use Connection; + use HasConnection; /** * Get the default query grammar instance. diff --git a/src/Database/Connections/SQLiteConnection.php b/src/Database/Connections/SQLiteConnection.php index 8c2b77eb..8bb67dec 100644 --- a/src/Database/Connections/SQLiteConnection.php +++ b/src/Database/Connections/SQLiteConnection.php @@ -6,13 +6,14 @@ use Winter\Storm\Database\PDO\SQLiteDriver; use Winter\Storm\Database\Query\Grammars\SQLiteGrammar as QueryGrammar; use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar as SchemaGrammar; +use Winter\Storm\Database\Traits\HasConnection; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ class SQLiteConnection extends \Illuminate\Database\SQLiteConnection { - use Connection; + use HasConnection; /** * Get the default query grammar instance. diff --git a/src/Database/Connections/SqlServerConnection.php b/src/Database/Connections/SqlServerConnection.php index a9d96969..afcbc5b1 100644 --- a/src/Database/Connections/SqlServerConnection.php +++ b/src/Database/Connections/SqlServerConnection.php @@ -9,13 +9,14 @@ use Winter\Storm\Database\PDO\SqlServerDriver; use Winter\Storm\Database\Query\Grammars\SqlServerGrammar as QueryGrammar; use Winter\Storm\Database\Schema\Grammars\SqlServerGrammar as SchemaGrammar; +use Winter\Storm\Database\Traits\HasConnection; /** * @phpstan-property \Illuminate\Database\Schema\Grammars\Grammar|null $schemaGrammar */ class SqlServerConnection extends \Illuminate\Database\SqlServerConnection { - use Connection; + use HasConnection; /** * Execute a Closure within a transaction. diff --git a/src/Database/Traits/HasConnection.php b/src/Database/Traits/HasConnection.php new file mode 100644 index 00000000..905ad264 --- /dev/null +++ b/src/Database/Traits/HasConnection.php @@ -0,0 +1,193 @@ + + */ + protected $doctrineTypeMappings = []; + + /** + * Get a new query builder instance. + * + * @return \Winter\Storm\Database\QueryBuilder + */ + public function query() + { + return new QueryBuilder( + $this, + $this->getQueryGrammar(), + $this->getPostProcessor() + ); + } + + /** + * Flush the memory cache. + * @return void + */ + public static function flushDuplicateCache() + { + MemoryCache::instance()->flush(); + } + + /** + * Log a query in the connection's query log. + * + * @param string $query + * @param array $bindings + * @param float|null $time + * @return void + */ + public function logQuery($query, $bindings, $time = null) + { + $this->fireEvent('illuminate.query', [$query, $bindings, $time, $this->getName()]); + + parent::logQuery($query, $bindings, $time); + } + + /** + * Fire an event for this connection. + * + * @param string $event + * @return array|null + */ + protected function fireConnectionEvent($event) + { + $this->fireEvent('connection.'.$this->getName().'.'.$event, $this); + + parent::fireConnectionEvent($event); + } + + /** + * Fire the given event if possible. + */ + protected function fireEvent(string $event, array|object $attributes = []): void + { + /** @var \Winter\Storm\Events\Dispatcher|null */ + $eventManager = $this->events; + + if (!isset($eventManager)) { + return; + } + + $eventManager->dispatch($event, $attributes); + } + + /** + * Is Doctrine available? + * + * @return bool + */ + public function isDoctrineAvailable() + { + return class_exists('Doctrine\DBAL\Connection'); + } + + /** + * Indicates whether native alter operations will be used when dropping or renaming columns, even if Doctrine DBAL is installed. + * + * @return bool + */ + public function usingNativeSchemaOperations() + { + return ! $this->isDoctrineAvailable(); + } + + /** + * Get a Doctrine Schema Column instance. + * + * @param string $table + * @param string $column + * @return \Doctrine\DBAL\Schema\Column + */ + public function getDoctrineColumn($table, $column) + { + $schema = $this->getDoctrineSchemaManager(); + + return $schema->listTableDetails($table)->getColumn($column); + } + + /** + * Get the Doctrine DBAL schema manager for the connection. + * + * @return \Doctrine\DBAL\Schema\AbstractSchemaManager + */ + public function getDoctrineSchemaManager() + { + $connection = $this->getDoctrineConnection(); + + // Doctrine v2 expects one parameter while v3 expects two. 2nd will be ignored on v2... + return $this->getDoctrineDriver()->getSchemaManager( + $connection, + $connection->getDatabasePlatform() + ); + } + + /** + * Get the Doctrine DBAL database connection instance. + * + * @return \Doctrine\DBAL\Connection + */ + public function getDoctrineConnection() + { + if (is_null($this->doctrineConnection)) { + $driver = $this->getDoctrineDriver(); + + $this->doctrineConnection = new DoctrineConnection(array_filter([ + 'pdo' => $this->getPdo(), + 'dbname' => $this->getDatabaseName(), + 'driver' => $driver->getName(), + 'serverVersion' => $this->getConfig('server_version'), + ]), $driver); + + foreach ($this->doctrineTypeMappings as $name => $type) { + $this->doctrineConnection + ->getDatabasePlatform() + ->registerDoctrineTypeMapping($type, $name); + } + } + + return $this->doctrineConnection; + } + + /** + * Register a custom Doctrine mapping type. + * + * @param Type|class-string $class + * @param string $name + * @param string $type + * @return void + * + * @throws \RuntimeException + */ + public function registerDoctrineType(Type|string $class, string $name, string $type): void + { + if (! $this->isDoctrineAvailable()) { + throw new RuntimeException( + 'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).' + ); + } + + if (! Type::hasType($name)) { + Type::getTypeRegistry() + ->register($name, is_string($class) ? new $class() : $class); + } + + $this->doctrineTypeMappings[$name] = $type; + } +} From e960492d513244f66f1bf2c1385b5121526bbd92 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 10:29:51 -0400 Subject: [PATCH 065/197] add abstract getDoctrineDriver() method --- phpstan-baseline.neon | 1 + src/Database/Connections/Connection.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e659a4ac..4f1ba304 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -124,3 +124,4 @@ parameters: paths: - src/Validation/Factory.php - src/Database/Connections/Connection.php + - src/Database/Traits/HasConnection.php diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php index 76022cb8..ad1adb05 100644 --- a/src/Database/Connections/Connection.php +++ b/src/Database/Connections/Connection.php @@ -2,7 +2,9 @@ use Winter\Storm\Database\Traits\HasConnection; -class Connection +abstract class Connection extends \Illuminate\Database\Connection { use HasConnection; + + abstract protected function getDoctrineDriver(); } From 0373d4598b277eb14635d7cee6a1845909e03799 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 10:34:58 -0400 Subject: [PATCH 066/197] mark Database/Connections/Connection class deprecated --- src/Database/Connections/Connection.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php index ad1adb05..1b4b9a62 100644 --- a/src/Database/Connections/Connection.php +++ b/src/Database/Connections/Connection.php @@ -1,8 +1,12 @@ Date: Mon, 15 Apr 2024 10:58:54 -0400 Subject: [PATCH 067/197] try to remove exclusions and disableSchemaScan config --- phpstan.neon | 3 --- 1 file changed, 3 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 291360af..54338d86 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -10,9 +10,6 @@ parameters: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php - src/Parse/PHP/ArrayPrinter.php - - src/Foundation/Console/KeyGenerateCommand.php - - src/Scaffold/GeneratorCommand.php - disableSchemaScan: true databaseMigrationsPath: - src/Auth/Migrations - src/Database/Migrations From 051946f01f128e7ab8123c7b641cc5e05bc7cf97 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 11:04:09 -0400 Subject: [PATCH 068/197] ignore undefined tags() method --- phpstan-baseline.neon | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index cb96c36d..825d6651 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,7 +1,12 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation\\:\\:getQualifiedFirstKeyName\\(\\)\\.$#" + message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Cache\\\\Repository::tags\\(\\)\\.$#" + count: 1 + path: src/Halcyon/Builder.php + + - + message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getQualifiedFirstKeyName\\(\\)\\.$#" count: 12 path: src/Database/Relations/Concerns/DefinedConstraints.php From 5da0cfd714c0c00f14338a33e36d7da909f4d855 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 11:23:22 -0400 Subject: [PATCH 069/197] disable schema scan in phpstan.neon --- phpstan-baseline.neon | 5 ----- phpstan.neon | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 825d6651..3a08494b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,10 +1,5 @@ parameters: ignoreErrors: - - - message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Cache\\\\Repository::tags\\(\\)\\.$#" - count: 1 - path: src/Halcyon/Builder.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::getQualifiedFirstKeyName\\(\\)\\.$#" count: 12 diff --git a/phpstan.neon b/phpstan.neon index 54338d86..371ede82 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -10,6 +10,7 @@ parameters: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php - src/Parse/PHP/ArrayPrinter.php + disableSchemaScan: true databaseMigrationsPath: - src/Auth/Migrations - src/Database/Migrations From dcf642d0c344498b9ea5468c7c73bf89412579c0 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 11:32:50 -0400 Subject: [PATCH 070/197] try another variation to appease phpstan --- phpstan.neon | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index 371ede82..0dbe2693 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -10,7 +10,8 @@ parameters: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php - src/Parse/PHP/ArrayPrinter.php - disableSchemaScan: true + - src/Foundation/Console/KeyGenerateCommand.php + - src/Scaffold/GeneratorCommand.php databaseMigrationsPath: - src/Auth/Migrations - src/Database/Migrations From 0653451eafa67741b9f2b613aeaf3d17d38e22eb Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 11:46:59 -0400 Subject: [PATCH 071/197] use $total value if provided --- src/Auth/Manager.php | 2 +- src/Database/Builder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Auth/Manager.php b/src/Auth/Manager.php index 57cecad1..419d4a19 100644 --- a/src/Auth/Manager.php +++ b/src/Auth/Manager.php @@ -171,7 +171,7 @@ public function hasUser() * Sets the user * @phpstan-param Models\User $user */ - public function setUser(Authenticatable $user) : self + public function setUser(Authenticatable $user): self { $this->user = $user; return $this; diff --git a/src/Database/Builder.php b/src/Database/Builder.php index cd7c632d..003f2b4a 100644 --- a/src/Database/Builder.php +++ b/src/Database/Builder.php @@ -153,7 +153,7 @@ public function paginate($perPage = null, $currentPage = null, $columns = ['*'], $perPage = $this->model->getPerPage(); } - $total = $this->toBase()->getCountForPagination(); + $total = value($total) ?? $this->toBase()->getCountForPagination(); $this->forPage((int) $currentPage, (int) $perPage); return $this->paginator($this->get($columns), $total, $perPage, $currentPage, [ From 8140a94fa30658c247f2f26b7c0d9a156471ddcc Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 13:22:18 -0400 Subject: [PATCH 072/197] restore formatting --- phpunit.xml.dist | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 504abbbd..3ec299c5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,13 @@ - + ./tests From 2eecd05cf23e69d9804e2fd9e7eb958c3cf396b6 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 13:36:40 -0400 Subject: [PATCH 073/197] remove unused overriden CacheManager --- src/Cache/CacheManager.php | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/Cache/CacheManager.php diff --git a/src/Cache/CacheManager.php b/src/Cache/CacheManager.php deleted file mode 100644 index b6b40d72..00000000 --- a/src/Cache/CacheManager.php +++ /dev/null @@ -1,9 +0,0 @@ - Date: Mon, 15 Apr 2024 15:31:45 -0400 Subject: [PATCH 074/197] default value for total should be null --- src/Database/Builder.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Database/Builder.php b/src/Database/Builder.php index 003f2b4a..345e1626 100644 --- a/src/Database/Builder.php +++ b/src/Database/Builder.php @@ -120,15 +120,16 @@ protected function searchWhereInternal($term, $columns, $mode, $boolean) * * This method also accepts the Laravel signature: * - * `paginate(int|null $perPage, array $columns, string $pageName, int|null $page)` + * `paginate(int|null $perPage, array $columns, string $pageName, int|null $page, \Closure|int|null $total)` * * @param int|null $perPage * @param array|int|null $currentPage * @param array|string $columns * @param string|int|null $pageName + * @param \Closure|int|null $total * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function paginate($perPage = null, $currentPage = null, $columns = ['*'], $pageName = 'page', $total = 0) + public function paginate($perPage = null, $currentPage = null, $columns = ['*'], $pageName = 'page', $total = null) { /* * Engage Laravel signature support From 08b9211179ee987558f1bc8c74b23b09c18df86d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 16:24:32 -0400 Subject: [PATCH 075/197] restore previous cache file name --- .gitignore | 1 - phpunit.xml.dist | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 06c24a82..baed57fc 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,5 @@ php_errors.log #phpunit tests/.phpunit.result.cache -.phpunit.result.cache tests/tmp tests/artifacts/* diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3ec299c5..78a1b927 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,7 +2,7 @@ Date: Mon, 15 Apr 2024 17:18:06 -0400 Subject: [PATCH 076/197] add missing name attribute --- src/Database/Schema/Grammars/SQLiteGrammar.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 8a3e22e7..8407d5d7 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -46,6 +46,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection $isGenerated = ! is_null($column['generation']); $column = new ColumnDefinition([ 'change' => true, + 'name' => $column['name'], 'type' => $column['type_name'], 'nullable' => $column['nullable'], 'default' => $column['default'] ? new Expression($column['default']) : null, From 31d2c27eb9ba902b10a9cac147ae8ea2ca09a199 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 15 Apr 2024 17:27:16 -0400 Subject: [PATCH 077/197] add missing typeVarChar() method --- src/Database/Schema/Grammars/SQLiteGrammar.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 8407d5d7..bbb6e7b4 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -157,4 +157,15 @@ public function getDefaultValue($value) return parent::getDefaultValue($value); } + + /** + * Create the column definition for a varchar type. + * + * @param \Illuminate\Support\Fluent $column + * @return string + */ + protected function typeVarChar(Fluent $column) + { + return 'varchar'; + } } From 72531cc4a83427e73ce14fea9fe1dc3653082ae5 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 16 Apr 2024 14:38:26 -0400 Subject: [PATCH 078/197] freeze twig to 3.8.x until we figure out the breakage in 3.9.x --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f8ac38f8..15307f63 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,7 @@ "scssphp/scssphp": "~1.0", "symfony/console": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", - "twig/twig": "~3.0", + "twig/twig": "~3.8.0", "wikimedia/less.php": "~3.0", "wikimedia/minify": "~2.2", "winter/laravel-config-writer": "^1.0.1" From 48883f6b5c4db5f154e48739a0bfa547c2b54b5b Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 16 Apr 2024 14:50:59 -0400 Subject: [PATCH 079/197] ignore twig for now --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 15307f63..3693ec13 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,6 @@ "scssphp/scssphp": "~1.0", "symfony/console": "^6.4|^7.0", "symfony/yaml": "^6.4|^7.0", - "twig/twig": "~3.8.0", "wikimedia/less.php": "~3.0", "wikimedia/minify": "~2.2", "winter/laravel-config-writer": "^1.0.1" From eacaea2f0cff4cf10dc5f437957d0e06d481e85f Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 19 Apr 2024 13:06:24 -0400 Subject: [PATCH 080/197] pass the relation instead of fetching it each time --- src/Database/Concerns/HasRelationships.php | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Database/Concerns/HasRelationships.php b/src/Database/Concerns/HasRelationships.php index 6eeb377a..d31c0e8b 100644 --- a/src/Database/Concerns/HasRelationships.php +++ b/src/Database/Concerns/HasRelationships.php @@ -308,17 +308,17 @@ protected function handleRelation($relationName) switch ($relationType) { case 'hasOne': case 'hasMany': - $relation = $this->validateRelationArgs($relationName, ['key', 'otherKey']); + $relation = $this->validateRelationArgs($relation, $relationName, ['key', 'otherKey']); $relationObj = $this->$relationType($relation[0], $relation['key'], $relation['otherKey'], $relationName); break; case 'belongsTo': - $relation = $this->validateRelationArgs($relationName, ['key', 'otherKey']); + $relation = $this->validateRelationArgs($relation, $relationName, ['key', 'otherKey']); $relationObj = $this->$relationType($relation[0], $relation['key'], $relation['otherKey'], $relationName); break; case 'belongsToMany': - $relation = $this->validateRelationArgs($relationName, ['table', 'key', 'otherKey', 'parentKey', 'relatedKey', 'pivot', 'timestamps']); + $relation = $this->validateRelationArgs($relation, $relationName, ['table', 'key', 'otherKey', 'parentKey', 'relatedKey', 'pivot', 'timestamps']); $relationObj = $this->$relationType($relation[0], $relation['table'], $relation['key'], $relation['otherKey'], $relation['parentKey'], $relation['relatedKey'], $relationName); if (isset($relation['pivotModel'])) { @@ -328,18 +328,18 @@ protected function handleRelation($relationName) break; case 'morphTo': - $relation = $this->validateRelationArgs($relationName, ['name', 'type', 'id']); + $relation = $this->validateRelationArgs($relation, $relationName, ['name', 'type', 'id']); $relationObj = $this->$relationType($relation['name'] ?: $relationName, $relation['type'], $relation['id']); break; case 'morphOne': case 'morphMany': - $relation = $this->validateRelationArgs($relationName, ['type', 'id', 'key'], ['name']); + $relation = $this->validateRelationArgs($relation, $relationName, ['type', 'id', 'key'], ['name']); $relationObj = $this->$relationType($relation[0], $relation['name'], $relation['type'], $relation['id'], $relation['key'], $relationName); break; case 'morphToMany': - $relation = $this->validateRelationArgs($relationName, ['table', 'key', 'otherKey', 'parentKey', 'relatedKey', 'pivot', 'timestamps'], ['name']); + $relation = $this->validateRelationArgs($relation, $relationName, ['table', 'key', 'otherKey', 'parentKey', 'relatedKey', 'pivot', 'timestamps'], ['name']); $relationObj = $this->$relationType($relation[0], $relation['name'], $relation['table'], $relation['key'], $relation['otherKey'], $relation['parentKey'], $relation['relatedKey'], $relationName, false); if (isset($relation['pivotModel'])) { @@ -349,19 +349,19 @@ protected function handleRelation($relationName) break; case 'morphedByMany': - $relation = $this->validateRelationArgs($relationName, ['table', 'key', 'otherKey', 'parentKey', 'relatedKey', 'pivot', 'timestamps'], ['name']); + $relation = $this->validateRelationArgs($relation, $relationName, ['table', 'key', 'otherKey', 'parentKey', 'relatedKey', 'pivot', 'timestamps'], ['name']); $relationObj = $this->$relationType($relation[0], $relation['name'], $relation['table'], $relation['key'], $relation['otherKey'], $relation['parentKey'], $relation['relatedKey'], $relationName); break; case 'attachOne': case 'attachMany': - $relation = $this->validateRelationArgs($relationName, ['public', 'key']); + $relation = $this->validateRelationArgs($relation, $relationName, ['public', 'key']); $relationObj = $this->$relationType($relation[0], $relation['public'], $relation['key'], $relationName); break; case 'hasOneThrough': case 'hasManyThrough': - $relation = $this->validateRelationArgs($relationName, ['key', 'throughKey', 'otherKey', 'secondOtherKey'], ['through']); + $relation = $this->validateRelationArgs($relation, $relationName, ['key', 'throughKey', 'otherKey', 'secondOtherKey'], ['through']); $relationObj = $this->$relationType($relation[0], $relation['through'], $relation['key'], $relation['throughKey'], $relation['otherKey'], $relation['secondOtherKey'], $relationName); break; @@ -375,10 +375,8 @@ protected function handleRelation($relationName) /** * Validate relation supplied arguments. */ - protected function validateRelationArgs($relationName, $optional, $required = []) + protected function validateRelationArgs($relation, $relationName, $optional, $required = []) { - $relation = $this->getRelationDefinition($relationName); - // Query filter arguments $filters = ['scope', 'conditions', 'order', 'pivot', 'timestamps', 'push', 'count', 'default']; From 36cae4877b2d2d0de9b1903a6d973dfe4516d628 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 13 May 2024 23:35:43 -0400 Subject: [PATCH 081/197] fix phpstan --- phpstan-baseline.neon | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3a08494b..9a6dff75 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -78,11 +78,14 @@ parameters: - messages: - - "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects array, int\\|null given\\.$#" + - "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::(simpleP|p)aginate\\(\\) expects array, int\\|null given\\.$#" - "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::paginate\\(\\)$#" - - "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::paginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::paginate\\(\\)$#" - - "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::paginate\\(\\) expects string, array given\\.$#" - - "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::paginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::paginate\\(\\)$#" + - "#^Parameter \\#2 \\$currentPage \\(int(\\|null)?\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::(simpleP|p)aginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::(simpleP|p)aginate\\(\\)$#" + - "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::(simpleP|p)aginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::(simpleP|p)aginate\\(\\)$#" + - "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::(simpleP|p)aginate\\(\\) expects string, array given\\.$#" + - "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::(simpleP|p)aginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::(simpleP|p)aginate\\(\\)$#" + - "#^Parameter \\#4 \\$page of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::(simpleP|p)aginate\\(\\) expects int\\|null, string given.$#" + paths: - src/Database/Relations/BelongsToMany.php - src/Database/Relations/MorphToMany.php From 83a5fbf8f6205c38d0a2c015c6f0dcea370ba45d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 17 May 2024 13:12:02 -0400 Subject: [PATCH 082/197] use latest symfony process --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index e6221b5d..562432f3 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,7 @@ "nikic/php-parser": "^4.10", "scssphp/scssphp": "~1.0", "symfony/console": "^6.4|^7.0", + "symfony/process": "^7.1", "symfony/yaml": "^6.4|^7.0", "twig/twig": "<3.9", "wikimedia/less.php": "~3.0", From d740fda506cd8a0fa6d912744051295a8aa36851 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 17 May 2024 16:31:05 -0400 Subject: [PATCH 083/197] .github/workflows/code-analysis.yaml --- .github/workflows/code-analysis.yaml | 2 +- phpstan-baseline.neon | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 0432f6f6..1674b671 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -49,4 +49,4 @@ jobs: run: composer install --no-interaction --no-progress --no-scripts - name: Analyse code - run: ./vendor/bin/phpstan analyse --memory-limit=2G --no-progress + run: ./vendor/bin/phpstan analyse --memory-limit=2G --no-progress -V diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9a6dff75..108a92c8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -133,3 +133,9 @@ parameters: - src/Validation/Factory.php - src/Database/Connections/Connection.php - src/Database/Traits/HasConnection.php + + - + message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Cache\\\\Repository::tags\\(\\)\\.$#" + count: 0 + path: src/Halcyon/Builder.php + From bbddad7dc4cfc23575767132377862ff9b893330 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 17 May 2024 16:37:59 -0400 Subject: [PATCH 084/197] Revert ".github/workflows/code-analysis.yaml" This reverts commit d740fda506cd8a0fa6d912744051295a8aa36851. --- .github/workflows/code-analysis.yaml | 2 +- phpstan-baseline.neon | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 1674b671..0432f6f6 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -49,4 +49,4 @@ jobs: run: composer install --no-interaction --no-progress --no-scripts - name: Analyse code - run: ./vendor/bin/phpstan analyse --memory-limit=2G --no-progress -V + run: ./vendor/bin/phpstan analyse --memory-limit=2G --no-progress diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 108a92c8..9a6dff75 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -133,9 +133,3 @@ parameters: - src/Validation/Factory.php - src/Database/Connections/Connection.php - src/Database/Traits/HasConnection.php - - - - message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Cache\\\\Repository::tags\\(\\)\\.$#" - count: 0 - path: src/Halcyon/Builder.php - From 41c8b9b829c6a91e235c2842fb2e6be4d1c072f0 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 17 May 2024 16:44:38 -0400 Subject: [PATCH 085/197] add ignore rule for tags method on Cache Repository --- phpstan-baseline.neon | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9a6dff75..6b014d6c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -133,3 +133,7 @@ parameters: - src/Validation/Factory.php - src/Database/Connections/Connection.php - src/Database/Traits/HasConnection.php + + - + message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Cache\\\\Repository::tags\\(\\)\\.$#" + path: src/Halcyon/Builder.php From f710dbac24bbf6a265d98ae481e50f1092cffb21 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 17 May 2024 16:56:13 -0400 Subject: [PATCH 086/197] try clearing phpstan cache in case it exists --- .github/workflows/code-analysis.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 0432f6f6..26b11246 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -48,5 +48,8 @@ jobs: - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts + - name: Clear phpstan cache + run: ./vendor/bin/phpstan clear-result-cache + - name: Analyse code run: ./vendor/bin/phpstan analyse --memory-limit=2G --no-progress From 65fe80de665d03d2a18670712eb89e3063ad1342 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 17 May 2024 17:00:42 -0400 Subject: [PATCH 087/197] test commit --- src/Halcyon/Builder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Halcyon/Builder.php b/src/Halcyon/Builder.php index de7313be..2fbb246d 100644 --- a/src/Halcyon/Builder.php +++ b/src/Halcyon/Builder.php @@ -703,6 +703,7 @@ protected function isCacheBusted($result) */ protected function getCache() { + $var = "value"; $cache = $this->model->getCacheManager()->driver($this->cacheDriver); return $this->cacheTags ? $cache->tags($this->cacheTags) : $cache; From d7c9f9a0fbc91493813b2f908868923e94857f18 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Fri, 17 May 2024 17:02:55 -0400 Subject: [PATCH 088/197] Revert "test commit" This reverts commit 65fe80de665d03d2a18670712eb89e3063ad1342. --- src/Halcyon/Builder.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Halcyon/Builder.php b/src/Halcyon/Builder.php index 2fbb246d..de7313be 100644 --- a/src/Halcyon/Builder.php +++ b/src/Halcyon/Builder.php @@ -703,7 +703,6 @@ protected function isCacheBusted($result) */ protected function getCache() { - $var = "value"; $cache = $this->model->getCacheManager()->driver($this->cacheDriver); return $this->cacheTags ? $cache->tags($this->cacheTags) : $cache; From a269f289011a2eb2784d78839815b7eb6576f44d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 18 Jun 2024 14:56:14 -0400 Subject: [PATCH 089/197] registerEloquentFactory() has been removed from Laravel --- src/Database/DatabaseServiceProvider.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php index abfd728f..f5872ee0 100644 --- a/src/Database/DatabaseServiceProvider.php +++ b/src/Database/DatabaseServiceProvider.php @@ -33,8 +33,6 @@ public function register() Model::flushDuplicateCache(); Model::flushEventListeners(); - $this->registerEloquentFactory(); - $this->registerQueueableEntityResolver(); // The connection factory is used to create the actual connection instances on From 0e4dc334bb098fb30386e7e4810630ddc59bdce4 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Jul 2024 14:54:24 -0400 Subject: [PATCH 090/197] remove extra phpstan ignore rules --- phpstan-baseline.neon | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 96ad8c65..485b5c52 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -127,17 +127,6 @@ parameters: - "#^Return type \\(void\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::send\\(\\) should be compatible with return type \\(Illuminate\\\\Mail\\\\SentMessage\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\Mailer::send\\(\\)$#" path: src/Support/Testing/Fakes/MailFake.php - - - message: "#^Call to function is_null\\(\\) with (Closure|Doctrine\\\\DBAL\\\\Connection) will always evaluate to false\\.$#" - paths: - - src/Validation/Factory.php - - src/Database/Connections/Connection.php - - src/Database/Traits/HasConnection.php - - - - message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Cache\\\\Repository::tags\\(\\)\\.$#" - path: src/Halcyon/Builder.php - - message: "#^Call to function is_null\\(\\) with Closure will always evaluate to false\\.$#" count: 1 From 6e9eaa3e764807a9a7a4fc5eb7f8035d7a74cd75 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Jul 2024 14:57:24 -0400 Subject: [PATCH 091/197] silence invalid phpstan warning --- phpstan.neon | 1 + 1 file changed, 1 insertion(+) diff --git a/phpstan.neon b/phpstan.neon index 0dbe2693..b77fcb7a 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -17,3 +17,4 @@ parameters: - src/Database/Migrations stubFiles: - tests/stubs/Facades.stub + treatPhpDocTypesAsCertain: false From 9bd20800d69ccc83bb4c1571df85c5346e25efd8 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 3 Jul 2024 14:59:48 -0400 Subject: [PATCH 092/197] remove other ignore rules --- phpstan-baseline.neon | 6 ------ 1 file changed, 6 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 485b5c52..451da8f0 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -73,7 +73,6 @@ parameters: - "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Attach|(Belongs|Morph)To)(One|Many)::(flushDuplicateCache|getRelationExistenceQueryForSelfJoin)\\(\\)\\.$#" - "#^Call to private method .+ of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Has|Morph)(One|Many)\\\\.$#" - - "#^If condition is always true\\.$#" path: src/Database/Relations/*.php - @@ -127,11 +126,6 @@ parameters: - "#^Return type \\(void\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::send\\(\\) should be compatible with return type \\(Illuminate\\\\Mail\\\\SentMessage\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\Mailer::send\\(\\)$#" path: src/Support/Testing/Fakes/MailFake.php - - - message: "#^Call to function is_null\\(\\) with Closure will always evaluate to false\\.$#" - count: 1 - path: src/Validation/Factory.php - - message: "#^Call to an undefined method Illuminate\\\\Routing\\\\Router\\:\\:before\\(\\)\\.$#" count: 1 From b3fabc6068f9d7c4afa510546832c9fb0adc305a Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 3 Sep 2024 10:36:30 -0400 Subject: [PATCH 093/197] follow Laravel method signature --- src/Translation/Translator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Translation/Translator.php b/src/Translation/Translator.php index aef6454f..3045d320 100644 --- a/src/Translation/Translator.php +++ b/src/Translation/Translator.php @@ -128,9 +128,9 @@ protected function getValidationSpecific($key, $replace, $locale) /** * @inheritDoc */ - protected function localeForChoice($locale) + protected function localeForChoice($key, $locale) { - $locale = parent::localeForChoice($locale); + $locale = parent::localeForChoice($key, locale); if (str_contains($locale, '-')) { $localeParts = explode('-', $locale, 2); From 3ca2c68afab3d82071152f471f8c6bb237481d61 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 3 Sep 2024 10:38:50 -0400 Subject: [PATCH 094/197] fix typo --- src/Translation/Translator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Translation/Translator.php b/src/Translation/Translator.php index 3045d320..a9609f35 100644 --- a/src/Translation/Translator.php +++ b/src/Translation/Translator.php @@ -130,7 +130,7 @@ protected function getValidationSpecific($key, $replace, $locale) */ protected function localeForChoice($key, $locale) { - $locale = parent::localeForChoice($key, locale); + $locale = parent::localeForChoice($key, $locale); if (str_contains($locale, '-')) { $localeParts = explode('-', $locale, 2); From 99ddfa2c9c2023e00166997d8fa425262611c2fc Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 15 Sep 2024 15:37:51 -0400 Subject: [PATCH 095/197] use newer twig --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 562432f3..b17348b8 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "symfony/console": "^6.4|^7.0", "symfony/process": "^7.1", "symfony/yaml": "^6.4|^7.0", - "twig/twig": "<3.9", + "twig/twig": "^3.9", "wikimedia/less.php": "~3.0", "wikimedia/minify": "~2.2", "winter/laravel-config-writer": "^1.0.1" From 659cdb5924296b63f31066546a1f1e1aa63f274f Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 15 Sep 2024 15:49:12 -0400 Subject: [PATCH 096/197] use latest 3.14+ twig, they fixed their shit --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b17348b8..21949f57 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "symfony/console": "^6.4|^7.0", "symfony/process": "^7.1", "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.9", + "twig/twig": "^3.14", "wikimedia/less.php": "~3.0", "wikimedia/minify": "~2.2", "winter/laravel-config-writer": "^1.0.1" From 91164e87705ff19d7fb6ec3690c93023f778e148 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 15 Sep 2024 16:12:20 -0400 Subject: [PATCH 097/197] force twig 3.x --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 21949f57..d8f293a7 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "symfony/console": "^6.4|^7.0", "symfony/process": "^7.1", "symfony/yaml": "^6.4|^7.0", - "twig/twig": "^3.14", + "twig/twig": "^3", "wikimedia/less.php": "~3.0", "wikimedia/minify": "~2.2", "winter/laravel-config-writer": "^1.0.1" From 3fff08ec4acc80bf63e0d5ba2aceb0cd5d8c1ecc Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 7 Oct 2024 10:37:36 -0400 Subject: [PATCH 098/197] use static return type to broaden the extension possibilities --- src/Auth/Manager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth/Manager.php b/src/Auth/Manager.php index 419d4a19..02935431 100644 --- a/src/Auth/Manager.php +++ b/src/Auth/Manager.php @@ -171,7 +171,7 @@ public function hasUser() * Sets the user * @phpstan-param Models\User $user */ - public function setUser(Authenticatable $user): self + public function setUser(Authenticatable $user): static { $this->user = $user; return $this; From 3ffb684f2bc9c34365fb39e1ea9595d158b60345 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Oct 2024 12:30:32 -0400 Subject: [PATCH 099/197] add GrammarTestCase --- tests/bootstrap.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index a89265b2..6a1b420f 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,3 +2,4 @@ class_alias('Winter\Storm\Tests\TestCase', 'TestCase'); class_alias('Winter\Storm\Tests\DbTestCase', 'DbTestCase'); +class_alias('Winter\Storm\Tests\GrammarTestCase', 'GrammarTestCase'); From c3aa137bdb3e9d6f19a735b8180b690c3fc7d0c8 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Oct 2024 13:23:51 -0400 Subject: [PATCH 100/197] fix phpstan --- phpstan-baseline.neon | 23 ++++++----------------- phpstan.neon | 4 ---- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e6b429da..daaaca3c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -7,11 +7,6 @@ parameters: - src/Auth/Models/Role.php - src/Auth/Models/User.php - - - messages: - - "# is not covariant with PHPDoc type TModel #" - path: src/Database/Builder.php - - messages: - "#^Parameter \\#1 \\$app of class Illuminate\\\\Database\\\\DatabaseManager constructor expects Illuminate\\\\Contracts\\\\Foundation\\\\Application, Illuminate\\\\Contracts\\\\Container\\\\Container given\\.$#" @@ -62,7 +57,6 @@ parameters: - messages: - "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface::getName\\(\\)\\.$#" - - "#^Property Illuminate\\\\Database\\\\Query\\\\Builder::\\$orders \\(array\\) does not accept null\\.$#" path: src/Database/QueryBuilder.php - @@ -71,23 +65,18 @@ parameters: - "#^Call to an undefined method (TDeclaringModel of )?Illuminate\\\\Database\\\\Eloquent\\\\Model::((un)?bind(Deferred|EventOnce)|fireEvent|getRelationDefinition|newRelationPivot|reloadRelations)\\(\\)\\.$#" - "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Attach|(Belongs|Morph)To)(One|Many)::(flushDuplicateCache|getRelationExistenceQueryForSelfJoin)\\(\\)\\.$#" - - "#^Call to private method .+ of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Has|Morph)(One|Many)\\\\.$#" + - "#^Call to private method .+ of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations#" paths: - src/Database/Relations/*.php - messages: - - "#^Parameter \\#2 \\$columns of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::(simpleP|p)aginate\\(\\) expects array, int\\|null given\\.$#" - - "#^Parameter \\#2 \\$currentPage \\(int\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::paginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::paginate\\(\\)$#" - - "#^Parameter \\#2 \\$currentPage \\(int(\\|null)?\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::(simpleP|p)aginate\\(\\) should be compatible with parameter \\$columns \\(array\\\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::(simpleP|p)aginate\\(\\)$#" - - "#^Parameter \\#3 \\$columns \\(array\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::(simpleP|p)aginate\\(\\) should be compatible with parameter \\$pageName \\(string\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::(simpleP|p)aginate\\(\\)$#" + - "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$(children|sessionKey)\\.$#" - "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::(simpleP|p)aginate\\(\\) expects string, array given\\.$#" - - "#^Parameter \\#4 \\$pageName \\(string\\) of method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Belongs|Morph)ToMany::(simpleP|p)aginate\\(\\) should be compatible with parameter \\$page \\(int\\|null\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\(Belongs|Morph)ToMany\\::(simpleP|p)aginate\\(\\)$#" - "#^Parameter \\#4 \\$page of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::(simpleP|p)aginate\\(\\) expects int\\|null, string given.$#" paths: - - src/Database/Relations/BelongsToMany.php - - src/Database/Relations/MorphToMany.php + - src/Database/Relations/* - messages: @@ -119,7 +108,7 @@ parameters: - messages: - - "#^Call to an undefined method Illuminate\\\\Routing\\\\Router\\:\\:(after|before)\\(\\)\\.$#" + - "#^Call to an undefined method Illuminate\\\\Routing\\\\Router::(after|before)\\(\\)\\.$#" path: src/Foundation/Application.php - @@ -137,9 +126,9 @@ parameters: - messages: - - "#^Access to an undefined property TDeclaringModel of Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$sessionKey\\.#" + - "#^Parameter .+ of method (Illuminate|Winter).+::(paginate|simplePaginate).+ (expects|should be compatible with)#" paths: - - src/Database/Relations/BelongsToMany.php + - src/Database/Relations/Concerns/BelongsOrMorphsToMany.php - messages: diff --git a/phpstan.neon b/phpstan.neon index c56c5ca5..423e6134 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -12,10 +12,6 @@ parameters: - src/Parse/PHP/ArrayPrinter.php - src/Foundation/Console/KeyGenerateCommand.php - src/Scaffold/GeneratorCommand.php - ignoreErrors: - - message: '#Call to private method select\(\)#' - paths: - - src/Database/Relations/Concerns/CanBeCounted.php disableSchemaScan: true databaseMigrationsPath: - src/Auth/Migrations From e06e4b49eae5138bf0a16eb06ab4df8d5cd918e5 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Oct 2024 14:53:08 -0400 Subject: [PATCH 101/197] fix translator tests --- tests/Translation/TranslatorTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Translation/TranslatorTest.php b/tests/Translation/TranslatorTest.php index 29b6f8cd..e26745f6 100644 --- a/tests/Translation/TranslatorTest.php +++ b/tests/Translation/TranslatorTest.php @@ -221,8 +221,9 @@ public function testSetMethodCanOverwriteAnEntireGroupForALocale() public function testChoiceMethodProperlyLoadsAndRetrievesItem() { - $t = $this->getMockBuilder(Translator::class)->onlyMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); + $t = $this->getMockBuilder(Translator::class)->onlyMethods(['get', 'localeForChoice'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo(['replace']), $this->equalTo('en'))->willReturn('line'); + $t->expects($this->once())->method('localeForChoice')->with($this->equalTo('foo'), $this->equalTo(null))->willReturn('en'); $t->setSelector($selector = m::mock(MessageSelector::class)); $selector->shouldReceive('choose')->once()->with('line', 10, 'en')->andReturn('choiced'); @@ -231,8 +232,9 @@ public function testChoiceMethodProperlyLoadsAndRetrievesItem() public function testChoiceMethodProperlyCountsCollectionsAndLoadsAndRetrievesItem() { - $t = $this->getMockBuilder(Translator::class)->onlyMethods(['get'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); + $t = $this->getMockBuilder(Translator::class)->onlyMethods(['get', 'localeForChoice'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); $t->expects($this->exactly(2))->method('get')->with($this->equalTo('foo'), $this->equalTo(['replace']), $this->equalTo('en'))->willReturn('line'); + $t->expects($this->exactly(2))->method('localeForChoice')->with($this->equalTo('foo'), $this->equalTo(null))->willReturn('en'); $t->setSelector($selector = m::mock(MessageSelector::class)); $selector->shouldReceive('choose')->twice()->with('line', 3, 'en')->andReturn('choiced'); From 545aaf2bb9da86cefc0592305d1d93d51cdf56d2 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Oct 2024 15:08:58 -0400 Subject: [PATCH 102/197] add method used in parent SQLiteGrammar --- tests/GrammarTestCase.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/GrammarTestCase.php b/tests/GrammarTestCase.php index cd195321..b95ffd25 100644 --- a/tests/GrammarTestCase.php +++ b/tests/GrammarTestCase.php @@ -20,6 +20,7 @@ protected function tearDown(): void protected function setupConnection(Blueprint $blueprint) { $connection = m::mock(Connection::class); + $connection->shouldReceive('getServerVersion')->andReturn('3.35'); $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); $connection->shouldReceive('scalar')->andReturn(''); $this->connection = $connection; From 549cf4ca9d01b99941b43ff47bb4abbe29954bea Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Oct 2024 23:46:35 -0400 Subject: [PATCH 103/197] fix logic bug in laravel sqlite grammar --- src/Database/Schema/Grammars/SQLiteGrammar.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index bbb6e7b4..33497cb9 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -13,6 +13,20 @@ class SQLiteGrammar extends SQLiteGrammarBase { + /** + * @inheritDoc + */ + public function getAlterCommands(Connection $connection) + { + $alterCommands = ['change', 'primary', 'dropPrimary', 'foreign', 'dropForeign']; + + if (version_compare($connection->getServerVersion(), '3.35', '>=')) { + $alterCommands[] = 'dropColumn'; + } + + return $alterCommands; + } + /** * Compile a change column command into a series of SQL statements. * From 9161946f7e71f2a1483dcc052e00c7720b21fd0d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Oct 2024 00:35:22 -0400 Subject: [PATCH 104/197] remove extra whitespaces --- src/Database/Schema/Grammars/SQLiteGrammar.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 33497cb9..af4c1472 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -17,7 +17,9 @@ class SQLiteGrammar extends SQLiteGrammarBase * @inheritDoc */ public function getAlterCommands(Connection $connection) - { + { + // Can be removed when the following PR gets merged: + // https://github.com/laravel/framework/pull/53262 $alterCommands = ['change', 'primary', 'dropPrimary', 'foreign', 'dropForeign']; if (version_compare($connection->getServerVersion(), '3.35', '>=')) { From 1c9acc9b4337fbab355dcf2ca833e22cb2b02f7a Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Oct 2024 22:52:30 -0400 Subject: [PATCH 105/197] Revert "remove extra whitespaces" This reverts commit 9161946f7e71f2a1483dcc052e00c7720b21fd0d. --- src/Database/Schema/Grammars/SQLiteGrammar.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index af4c1472..33497cb9 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -17,9 +17,7 @@ class SQLiteGrammar extends SQLiteGrammarBase * @inheritDoc */ public function getAlterCommands(Connection $connection) - { - // Can be removed when the following PR gets merged: - // https://github.com/laravel/framework/pull/53262 + { $alterCommands = ['change', 'primary', 'dropPrimary', 'foreign', 'dropForeign']; if (version_compare($connection->getServerVersion(), '3.35', '>=')) { From bcd83bb3e7b1d348310ab56563072508d6d88827 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Oct 2024 23:07:09 -0400 Subject: [PATCH 106/197] logic is good, revert the change --- src/Database/Schema/Grammars/SQLiteGrammar.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 33497cb9..bbb6e7b4 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -13,20 +13,6 @@ class SQLiteGrammar extends SQLiteGrammarBase { - /** - * @inheritDoc - */ - public function getAlterCommands(Connection $connection) - { - $alterCommands = ['change', 'primary', 'dropPrimary', 'foreign', 'dropForeign']; - - if (version_compare($connection->getServerVersion(), '3.35', '>=')) { - $alterCommands[] = 'dropColumn'; - } - - return $alterCommands; - } - /** * Compile a change column command into a series of SQL statements. * From 8627fab4421f6f00fca9d2f3df1a7091bbde7c32 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 17 Nov 2024 07:16:49 -0500 Subject: [PATCH 107/197] fix ignore pattern --- phpstan-baseline.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index daaaca3c..3171b036 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -92,7 +92,7 @@ parameters: - messages: - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" - - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::queue\\(\\)$#" + - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(BackedEnum\\|string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::queue\\(\\)$#" - "#^Parameter \\#2 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queueOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::queueOn\\(\\)$#" - "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::later\\(\\)$#" - "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::later\\(\\)$#" From 76c096ded468463582aba180474ae2598a16beae Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 17 Nov 2024 07:17:01 -0500 Subject: [PATCH 108/197] add namespace --- tests/GrammarTestCase.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/GrammarTestCase.php b/tests/GrammarTestCase.php index b95ffd25..c5418e2a 100644 --- a/tests/GrammarTestCase.php +++ b/tests/GrammarTestCase.php @@ -1,5 +1,7 @@ Date: Sun, 17 Nov 2024 09:15:32 -0500 Subject: [PATCH 109/197] update TranslatorTest after Laravel changes to choice() method --- tests/Translation/TranslatorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Translation/TranslatorTest.php b/tests/Translation/TranslatorTest.php index e26745f6..03ee5719 100644 --- a/tests/Translation/TranslatorTest.php +++ b/tests/Translation/TranslatorTest.php @@ -222,7 +222,7 @@ public function testSetMethodCanOverwriteAnEntireGroupForALocale() public function testChoiceMethodProperlyLoadsAndRetrievesItem() { $t = $this->getMockBuilder(Translator::class)->onlyMethods(['get', 'localeForChoice'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); - $t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo(['replace']), $this->equalTo('en'))->willReturn('line'); + $t->expects($this->once())->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('en'))->willReturn('line'); $t->expects($this->once())->method('localeForChoice')->with($this->equalTo('foo'), $this->equalTo(null))->willReturn('en'); $t->setSelector($selector = m::mock(MessageSelector::class)); $selector->shouldReceive('choose')->once()->with('line', 10, 'en')->andReturn('choiced'); @@ -233,7 +233,7 @@ public function testChoiceMethodProperlyLoadsAndRetrievesItem() public function testChoiceMethodProperlyCountsCollectionsAndLoadsAndRetrievesItem() { $t = $this->getMockBuilder(Translator::class)->onlyMethods(['get', 'localeForChoice'])->setConstructorArgs([$this->getLoader(), 'en'])->getMock(); - $t->expects($this->exactly(2))->method('get')->with($this->equalTo('foo'), $this->equalTo(['replace']), $this->equalTo('en'))->willReturn('line'); + $t->expects($this->exactly(2))->method('get')->with($this->equalTo('foo'), $this->equalTo([]), $this->equalTo('en'))->willReturn('line'); $t->expects($this->exactly(2))->method('localeForChoice')->with($this->equalTo('foo'), $this->equalTo(null))->willReturn('en'); $t->setSelector($selector = m::mock(MessageSelector::class)); $selector->shouldReceive('choose')->twice()->with('line', 3, 'en')->andReturn('choiced'); From 13d31845f41b752946881aa0fae1142f9b00f469 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 17 Nov 2024 09:21:02 -0500 Subject: [PATCH 110/197] update property signature to match Laravel --- src/Config/Repository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config/Repository.php b/src/Config/Repository.php index 719802df..04249f97 100644 --- a/src/Config/Repository.php +++ b/src/Config/Repository.php @@ -31,7 +31,7 @@ class Repository extends BaseRepository implements ArrayAccess, RepositoryContra /** * All of the configuration items. * - * @var array + * @var array */ protected $items = []; From 38364d170704fc343f77951369ccc420f22f2025 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 8 Feb 2025 13:28:39 -0500 Subject: [PATCH 111/197] Revert #191, this has been merged upstream. This reverts commit 752c16253ca7945e68423e91aa0e8ecbeb4fa064. --- src/Database/Connectors/ConnectionFactory.php | 17 -------- src/Database/Connectors/SQLiteConnector.php | 43 ------------------- 2 files changed, 60 deletions(-) delete mode 100644 src/Database/Connectors/SQLiteConnector.php diff --git a/src/Database/Connectors/ConnectionFactory.php b/src/Database/Connectors/ConnectionFactory.php index a9debce9..a193640f 100644 --- a/src/Database/Connectors/ConnectionFactory.php +++ b/src/Database/Connectors/ConnectionFactory.php @@ -39,23 +39,6 @@ protected function createPdoResolverWithHosts(array $config) }; } - /** - * Create a connector instance based on the configuration. - * - * @param array $config - * @return \Illuminate\Database\Connectors\ConnectorInterface - * - * @throws \InvalidArgumentException - */ - public function createConnector(array $config) - { - if (array_get($config, 'driver') === 'sqlite') { - return new SQLiteConnector; - } else { - return parent::createConnector($config); - } - } - /** * Create a new connection instance. * diff --git a/src/Database/Connectors/SQLiteConnector.php b/src/Database/Connectors/SQLiteConnector.php deleted file mode 100644 index d9209ca1..00000000 --- a/src/Database/Connectors/SQLiteConnector.php +++ /dev/null @@ -1,43 +0,0 @@ -getOptions($config); - - // SQLite supports "in-memory" databases that only last as long as the owning - // connection does. These are useful for tests or for short lifetime store - // querying. In-memory databases may only have a single open connection. - if ($config['database'] === ':memory:') { - return $this->createConnection('sqlite::memory:', $config, $options); - } - - $path = realpath($config['database']); - if (!file_exists($path)) { - $path = realpath(base_path($config['database'])); - } - - // Here we'll verify that the SQLite database exists before going any further - // as the developer probably wants to know if the database exists and this - // SQLite driver will not throw any exception if it does not by default. - if ($path === false) { - throw new SQLiteDatabaseDoesNotExistException($config['database']); - } - - return $this->createConnection("sqlite:{$path}", $config, $options); - } -} From 15253a0218439efcfa870fa7df7ebf61b9bdd25d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Wed, 19 Feb 2025 09:51:41 -0500 Subject: [PATCH 112/197] remove v6.3 upper limit for symfony console --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0e6c31cb..b01e58be 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "nesbot/carbon": "^2.0", "nikic/php-parser": "^4.10", "scssphp/scssphp": "~1.0", - "symfony/console": ">=6.0.9 <6.3.0", + "symfony/console": "^6.0", "symfony/yaml": "^6.0", "twig/twig": "^3.14", "wikimedia/less.php": "~3.0", From f220162c5278124063fac231d5f19ee1de68f40e Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 24 Feb 2025 23:25:29 -0600 Subject: [PATCH 113/197] Fix testing issue with orchestra/testbench-core orchestra/testbench-core v7.52 introduced a breaking change by first removing the getBasePath() method in https://github.com/orchestral/testbench-core/pull/307 and then re-adding it (except only to return null) in v7.53 (https://github.com/orchestral/testbench-core/commit/3f8eb2608ce8876252ae86f9b21da350f17f1b94). As it seems this API is now unstable, I'm locking our usage of testbench-core to 7.51 for now until the upstream issue is resolved. --- composer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index b01e58be..4019e067 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,8 @@ "meyfa/phpunit-assert-gd": "^3.0.0", "dms/phpunit-arraysubset-asserts": "^0.5.0", "larastan/larastan": "^2.8.1", - "orchestra/testbench": "^7.1.0" + "orchestra/testbench": "^7.1.0", + "orchestra/testbench-core": "~7.51.0" }, "suggest": { "ext-pdo_dblib": "Required to use MS SQL Server databases", @@ -88,7 +89,7 @@ } }, "scripts": { - "test": "phpunit ./tests", + "test": "./vendor/bin/phpunit ./tests", "lint": "parallel-lint --exclude vendor .", "sniff": "phpcs --colors -nq --report=\"full\" --extensions=\"php\"", "analyze": "phpstan analyse --memory-limit=2G src" From a7932d009b540d9779e8c000e2848c1ceab1fd5d Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 25 Feb 2025 00:10:19 -0600 Subject: [PATCH 114/197] Bump minimum required PHP to 8.1 and symfony/console to 6.4 --- composer.json | 4 ++-- src/Console/Traits/HandlesCleanup.php | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 4019e067..c01b3b1b 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } ], "require": { - "php": "^8.0.2", + "php": ">=8.1", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", @@ -44,7 +44,7 @@ "nesbot/carbon": "^2.0", "nikic/php-parser": "^4.10", "scssphp/scssphp": "~1.0", - "symfony/console": "^6.0", + "symfony/console": "^6.4", "symfony/yaml": "^6.0", "twig/twig": "^3.14", "wikimedia/less.php": "~3.0", diff --git a/src/Console/Traits/HandlesCleanup.php b/src/Console/Traits/HandlesCleanup.php index 22370543..d322f737 100644 --- a/src/Console/Traits/HandlesCleanup.php +++ b/src/Console/Traits/HandlesCleanup.php @@ -38,8 +38,12 @@ public function getSubscribedSignals(): array /** * Handle the provided Unix process signal + * + * @ param int|false $previousExitCode + * + * @return int|false The exit code to return or false to continue the normal execution */ - public function handleSignal(int $signal): void + public function handleSignal(int $signal, /* int|false $previousExitCode = 0 */) { // Handle the signal if (method_exists($this, 'handleCleanup')) { @@ -48,8 +52,10 @@ public function handleSignal(int $signal): void // Exit cleanly at this point if this was a user termination if (in_array($signal, [SIGINT, SIGQUIT])) { - exit(0); + return 0; } + + return false; } /** From b22ac12b7aa5842a0b9502e5cfda9da576cca097 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 25 Feb 2025 00:18:47 -0600 Subject: [PATCH 115/197] Drop 8.0 support --- .github/workflows/code-quality.yaml | 1 + .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-quality.yaml b/.github/workflows/code-quality.yaml index 065447d7..5a98cd76 100644 --- a/.github/workflows/code-quality.yaml +++ b/.github/workflows/code-quality.yaml @@ -6,6 +6,7 @@ on: branches: - 1.0 - 1.1 + - 1.2 - develop concurrency: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ebb1526e..9624293c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: max-parallel: 6 matrix: operatingSystem: [ubuntu-latest, windows-latest] - phpVersion: ['8.0', '8.1', '8.2', '8.3'] + phpVersion: ['8.1', '8.2', '8.3', '8.4'] fail-fast: false runs-on: ${{ matrix.operatingSystem }} name: ${{ matrix.operatingSystem }} / PHP ${{ matrix.phpVersion }} From 7de499245d772893c3d9f4edc00bfe4359144730 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 25 Feb 2025 00:20:50 -0600 Subject: [PATCH 116/197] Drop 8.0 for realzies --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1647511f..9624293c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: max-parallel: 6 matrix: operatingSystem: [ubuntu-latest, windows-latest] - phpVersion: ['8.0', '8.1', '8.2', '8.3', '8.4'] + phpVersion: ['8.1', '8.2', '8.3', '8.4'] fail-fast: false runs-on: ${{ matrix.operatingSystem }} name: ${{ matrix.operatingSystem }} / PHP ${{ matrix.phpVersion }} From 09f5425ea2310a367a538015003f6b5d92dacc07 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 25 Feb 2025 00:38:48 -0600 Subject: [PATCH 117/197] Bump to latest testbench. Related: https://github.com/wintercms/storm/pull/205/commits/f220162c5278124063fac231d5f19ee1de68f40e --- composer.json | 1 - tests/TestCase.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 05c7daf6..c009ec1b 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,6 @@ "mockery/mockery": "^1.6|^2.0", "dms/phpunit-arraysubset-asserts": "^0.5", "orchestra/testbench": "^9.0", - "orchestra/testbench-core": "~7.51.0", "larastan/larastan": "^2.8.1" }, "suggest": { diff --git a/tests/TestCase.php b/tests/TestCase.php index 53a58f40..56e7d6eb 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -17,7 +17,7 @@ class TestCase extends TestbenchTestCase */ protected function resolveApplication() { - return tap(new Application($this->getBasePath()), function ($app) { + return tap(new Application($this->applicationBasePath()), function ($app) { $app->bind( \Winter\Storm\Foundation\Bootstrap\LoadConfiguration::class, \Orchestra\Testbench\Bootstrap\LoadConfiguration::class From 19130ff1f64cea3f5415d8b0a5f7d664b133a058 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 25 Feb 2025 00:40:50 -0600 Subject: [PATCH 118/197] Fix PHPUnit deprecation notices --- tests/Filesystem/FilesystemTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Filesystem/FilesystemTest.php b/tests/Filesystem/FilesystemTest.php index 3a9ff6bc..af270d3c 100644 --- a/tests/Filesystem/FilesystemTest.php +++ b/tests/Filesystem/FilesystemTest.php @@ -44,7 +44,7 @@ public function testSizeToBytes($input, $expectedBytes) $this->assertEquals($expectedBytes, $result); } - public function provideSizesForSizeToBytes() + public static function provideSizesForSizeToBytes() { return [ ['1 byte', '1'], @@ -70,7 +70,7 @@ public function testSizeToString($bytes, $expectedString) $this->assertEquals($expectedString, $result); } - public function provideBytesForSizeToString() + public static function provideBytesForSizeToString() { return [ [1, '1 byte'], @@ -94,7 +94,7 @@ public function testSizeToBytesInvalidInput($input) $this->filesystem->sizeToBytes($input); } - public function provideInvalidSizesForSizeToBytes() + public static function provideInvalidSizesForSizeToBytes() { return [ ['-1G'], // Negative value From 3755793840d9c713b6216770027e6efc46cc55a7 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 25 Feb 2025 00:45:16 -0600 Subject: [PATCH 119/197] Fix spacing --- phpunit.xml.dist | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1a9bf1ea..801c16a6 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,14 +10,14 @@ xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" > - - ./tests - + + ./tests + - - ./src/ - + + ./src/ + From dd00f2c6ce951055e71c678f153b1605480e354e Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 25 Feb 2025 00:52:09 -0600 Subject: [PATCH 120/197] Move the HasConnection trait --- src/Database/Connections/Connection.php | 1 - src/Database/{Traits => Connections}/HasConnection.php | 2 +- src/Database/Connections/MariaDbConnection.php | 1 - src/Database/Connections/MySqlConnection.php | 1 - src/Database/Connections/PostgresConnection.php | 1 - src/Database/Connections/SQLiteConnection.php | 1 - src/Database/Connections/SqlServerConnection.php | 1 - 7 files changed, 1 insertion(+), 7 deletions(-) rename src/Database/{Traits => Connections}/HasConnection.php (99%) diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php index 1b4b9a62..a1df2f8b 100644 --- a/src/Database/Connections/Connection.php +++ b/src/Database/Connections/Connection.php @@ -1,7 +1,6 @@ Date: Tue, 25 Feb 2025 01:12:34 -0600 Subject: [PATCH 121/197] Remove unused seed argument in array_shuffle() Refs https://github.com/laravel/framework/commit/d8e5ec4909fb9bf963df5ea9759ab9b3760efc1f --- src/Support/helpers-array.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Support/helpers-array.php b/src/Support/helpers-array.php index f1105a82..b99d6fb4 100644 --- a/src/Support/helpers-array.php +++ b/src/Support/helpers-array.php @@ -328,10 +328,9 @@ function array_set(&$array, $key, $value) * Shuffle the given array and return the result. * * @param array $array - * @param int|null $seed * @return array */ - function array_shuffle($array, $seed = null) + function array_shuffle($array) { return Arr::shuffle($array); } From 9f32393a64937577ad12d6550135525641144908 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Sat, 19 Jul 2025 23:58:31 -0600 Subject: [PATCH 122/197] Remove broken extension cache step from workflow --- .github/workflows/code-analysis.yaml | 15 --------------- .gitignore | 1 + 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 26b11246..f0c6fa43 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -23,21 +23,6 @@ jobs: - name: Checkout changes uses: actions/checkout@v4 - - name: Setup extension cache - id: extcache - uses: shivammathur/cache-extensions@v1 - with: - php-version: '8.2' - extensions: ${{ env.extensions }} - key: ${{ env.key }} - - - name: Cache extensions - uses: actions/cache@v2 - with: - path: ${{ steps.extcache.outputs.dir }} - key: ${{ steps.extcache.outputs.key }} - restore-keys: ${{ steps.extcache.outputs.key }} - - name: Install PHP uses: shivammathur/setup-php@v2 with: diff --git a/.gitignore b/.gitignore index baed57fc..26b7105e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ php_errors.log /.settings/ #phpunit +.phpunit.result.cache tests/.phpunit.result.cache tests/tmp tests/artifacts/* From 7517cf0d4323c324f91dd3d9be9fc6987fcd96c3 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Sun, 20 Jul 2025 00:18:49 -0600 Subject: [PATCH 123/197] Require symfony/console v7.2 as a minimum for --silent support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a92b4922..08329e3e 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "nesbot/carbon": "^2.0", "nikic/php-parser": "^4.10", "scssphp/scssphp": "~1.0", - "symfony/console": "^7.0", + "symfony/console": "^7.2", "symfony/process": "^7.1", "symfony/yaml": "^6.4|^7.0", "twig/twig": "^3.14", From 5e4da25832361dbce5efc4ab5dcb42c189c671a0 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 21 Jul 2025 00:48:44 -0600 Subject: [PATCH 124/197] Target Laravel 12 --- composer.json | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 08329e3e..f63009f7 100644 --- a/composer.json +++ b/composer.json @@ -38,11 +38,10 @@ "assetic/framework": "^3.1", "doctrine/dbal": "^3.0", "enshrined/svg-sanitize": "~0.16", - "laravel/framework": "^11.0", + "laravel/framework": "^12.0", "laravel/tinker": "^2.8.1", "league/csv": "~9.1", - "nesbot/carbon": "^2.0", - "nikic/php-parser": "^4.10", + "nesbot/carbon": "^3.0", "scssphp/scssphp": "~1.0", "symfony/console": "^7.2", "symfony/process": "^7.1", @@ -50,18 +49,28 @@ "twig/twig": "^3.14", "wikimedia/less.php": "^5.0", "wikimedia/minify": "~2.2", - "winter/laravel-config-writer": "^1.0.1" + "winter/laravel-config-writer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^10.5|^11.0", + "phpunit/phpunit": "^11.0", "squizlabs/php_codesniffer": "^3.2", "php-parallel-lint/php-parallel-lint": "^1.0", - "meyfa/phpunit-assert-gd": "^4.0", + "meyfa/phpunit-assert-gd": "dev-main", "mockery/mockery": "^1.6|^2.0", - "dms/phpunit-arraysubset-asserts": "^0.5", - "orchestra/testbench": "^9.0", - "larastan/larastan": "^2.8.1" + "dms/phpunit-arraysubset-asserts": "dev-add-phpunit-11-support", + "orchestra/testbench": "^10.0", + "larastan/larastan": "^3.6.0" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/wintercms/phpunit-assert-gd" + }, + { + "type": "vcs", + "url": "https://github.com/pieterocp/phpunit-arraysubset-asserts" + } + ], "suggest": { "ext-pdo_dblib": "Required to use MS SQL Server databases", "ext-pdo_mysql": "Required to use MySQL databases", From f96a14b6681301c7316d179799e06d421d6a0f24 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Jul 2025 11:49:27 -0400 Subject: [PATCH 125/197] fix method signatures in laravel 12 latest changes --- src/Database/Connections/MariaDbConnection.php | 4 ++-- src/Database/Connections/MySqlConnection.php | 4 ++-- src/Database/Connections/PostgresConnection.php | 4 ++-- src/Database/Connections/SQLiteConnection.php | 4 ++-- src/Database/Connections/SqlServerConnection.php | 4 ++-- .../Grammars/Concerns/MySqlBasedGrammar.php | 9 +++++---- src/Database/Schema/Grammars/PostgresGrammar.php | 9 +++++---- src/Database/Schema/Grammars/SQLiteGrammar.php | 15 ++++++--------- src/Database/Schema/Grammars/SqlServerGrammar.php | 9 +++++---- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Database/Connections/MariaDbConnection.php b/src/Database/Connections/MariaDbConnection.php index f0ca5162..f2c11edd 100644 --- a/src/Database/Connections/MariaDbConnection.php +++ b/src/Database/Connections/MariaDbConnection.php @@ -22,7 +22,7 @@ class MariaDbConnection extends \Illuminate\Database\MariaDbConnection */ protected function getDefaultQueryGrammar() { - return $this->withTablePrefix(new QueryGrammar); + return new QueryGrammar($this); } /** @@ -46,7 +46,7 @@ public function getSchemaBuilder() */ protected function getDefaultSchemaGrammar() { - return $this->withTablePrefix(new SchemaGrammar); + return new SchemaGrammar($this); } /** diff --git a/src/Database/Connections/MySqlConnection.php b/src/Database/Connections/MySqlConnection.php index 630f5e74..fc7d998f 100644 --- a/src/Database/Connections/MySqlConnection.php +++ b/src/Database/Connections/MySqlConnection.php @@ -22,7 +22,7 @@ class MySqlConnection extends \Illuminate\Database\MySqlConnection */ protected function getDefaultQueryGrammar() { - return $this->withTablePrefix(new QueryGrammar); + return new QueryGrammar($this); } /** @@ -46,7 +46,7 @@ public function getSchemaBuilder() */ protected function getDefaultSchemaGrammar() { - return $this->withTablePrefix(new SchemaGrammar); + return new SchemaGrammar($this); } /** diff --git a/src/Database/Connections/PostgresConnection.php b/src/Database/Connections/PostgresConnection.php index 82d607fa..e4e7d866 100644 --- a/src/Database/Connections/PostgresConnection.php +++ b/src/Database/Connections/PostgresConnection.php @@ -21,7 +21,7 @@ class PostgresConnection extends \Illuminate\Database\PostgresConnection */ protected function getDefaultQueryGrammar() { - return $this->withTablePrefix(new QueryGrammar); + return new QueryGrammar($this); } /** @@ -45,7 +45,7 @@ public function getSchemaBuilder() */ protected function getDefaultSchemaGrammar() { - return $this->withTablePrefix(new SchemaGrammar); + return new SchemaGrammar($this); } /** diff --git a/src/Database/Connections/SQLiteConnection.php b/src/Database/Connections/SQLiteConnection.php index 33a2408b..2cae6b60 100644 --- a/src/Database/Connections/SQLiteConnection.php +++ b/src/Database/Connections/SQLiteConnection.php @@ -21,7 +21,7 @@ class SQLiteConnection extends \Illuminate\Database\SQLiteConnection */ protected function getDefaultQueryGrammar() { - return $this->withTablePrefix(new QueryGrammar); + return new QueryGrammar($this); } /** @@ -45,7 +45,7 @@ public function getSchemaBuilder() */ protected function getDefaultSchemaGrammar() { - return $this->withTablePrefix(new SchemaGrammar); + return new SchemaGrammar($this); } /** diff --git a/src/Database/Connections/SqlServerConnection.php b/src/Database/Connections/SqlServerConnection.php index 161b0417..525da572 100644 --- a/src/Database/Connections/SqlServerConnection.php +++ b/src/Database/Connections/SqlServerConnection.php @@ -67,7 +67,7 @@ public function transaction(Closure $callback, $attempts = 1) */ protected function getDefaultQueryGrammar() { - return $this->withTablePrefix(new QueryGrammar); + return new QueryGrammar($this); } /** @@ -91,7 +91,7 @@ public function getSchemaBuilder() */ protected function getDefaultSchemaGrammar() { - return $this->withTablePrefix(new SchemaGrammar); + return new SchemaGrammar($this); } /** diff --git a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php index 963acdce..bf16621e 100644 --- a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php +++ b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php @@ -2,7 +2,6 @@ namespace Winter\Storm\Database\Schema\Grammars\Concerns; -use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Support\Fluent; @@ -18,15 +17,17 @@ trait MySqlBasedGrammar * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command - * @param \Illuminate\Database\Connection $connection * @return array|string * * @throws \RuntimeException */ - public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) + public function compileChange(Blueprint $blueprint, Fluent $command) { $columns = []; - $oldColumns = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())); + $schema = $this->connection->getSchemaBuilder(); + $table = $blueprint->getTable(); + + $oldColumns = collect($schema->getColumns($table)); foreach ($blueprint->getChangedColumns() as $column) { $sql = sprintf( diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 3c61f036..2183425c 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -2,7 +2,6 @@ namespace Winter\Storm\Database\Schema\Grammars; -use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Database\Schema\Grammars\PostgresGrammar as PostgresGrammarBase; @@ -19,15 +18,17 @@ class PostgresGrammar extends PostgresGrammarBase * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command - * @param \Illuminate\Database\Connection $connection * @return array|string * * @throws \RuntimeException */ - public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) + public function compileChange(Blueprint $blueprint, Fluent $command) { $columns = []; - $oldColumns = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())); + $schema = $this->connection->getSchemaBuilder(); + $table = $blueprint->getTable(); + + $oldColumns = collect($schema->getColumns($table)); foreach ($blueprint->getChangedColumns() as $column) { $changes = ['type '.$this->getType($column).$this->modifyCollate($blueprint, $column)]; diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index bbb6e7b4..eda0ad24 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -2,7 +2,6 @@ namespace Winter\Storm\Database\Schema\Grammars; -use Illuminate\Database\Connection; use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\ColumnDefinition; @@ -22,23 +21,21 @@ class SQLiteGrammar extends SQLiteGrammarBase * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command - * @param \Illuminate\Database\Connection $connection * @return array|string * * @throws \RuntimeException */ - public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) + public function compileChange(Blueprint $blueprint, Fluent $command) { - $schema = $connection->getSchemaBuilder(); + $autoIncrementColumn = null; + $columnNames = []; + $schema = $this->connection->getSchemaBuilder(); $table = $blueprint->getTable(); $changedColumns = collect($blueprint->getChangedColumns()); - $columnNames = []; - $autoIncrementColumn = null; - - $oldColumns = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())); + $oldColumns = collect($schema->getColumns($table)); - $columns = collect($schema->getColumns($table)) + $columns = $oldColumns ->map(function ($column) use ($blueprint, $changedColumns, &$columnNames, &$autoIncrementColumn, $oldColumns) { $column = $changedColumns->first(fn ($col) => $col->name === $column['name'], $column); diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 770b71da..3d385081 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -2,7 +2,6 @@ namespace Winter\Storm\Database\Schema\Grammars; -use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Database\Schema\Grammars\SqlServerGrammar as SqlServerGrammarBase; @@ -19,15 +18,17 @@ class SqlServerGrammar extends SqlServerGrammarBase * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command - * @param \Illuminate\Database\Connection $connection * @return array|string * * @throws \RuntimeException */ - public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection) + public function compileChange(Blueprint $blueprint, Fluent $command) { $changes = [$this->compileDropDefaultConstraint($blueprint, $command)]; - $oldColumns = collect($connection->getSchemaBuilder()->getColumns($blueprint->getTable())); + $schema = $this->connection->getSchemaBuilder(); + $table = $blueprint->getTable(); + + $oldColumns = collect($schema->getColumns($table)); foreach ($blueprint->getChangedColumns() as $column) { $sql = sprintf( From 7b7689802a673c2a4e3ccb57bf9831c2f433b04c Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Jul 2025 12:14:34 -0400 Subject: [PATCH 126/197] fix query builder tests --- tests/Database/QueryBuilderTest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/Database/QueryBuilderTest.php b/tests/Database/QueryBuilderTest.php index 4018e584..518a64cb 100644 --- a/tests/Database/QueryBuilderTest.php +++ b/tests/Database/QueryBuilderTest.php @@ -1,5 +1,6 @@ createMock(Connection::class)); $processor = $this->createMock(Processor::class); return new QueryBuilder($this->getConnection(), $grammar, $processor); @@ -156,7 +157,7 @@ protected function getMySqlBuilder() protected function getPostgresBuilder() { - $grammar = new PostgresGrammar; + $grammar = new PostgresGrammar($this->createMock(Connection::class)); $processor = $this->createMock(Processor::class); return new QueryBuilder($this->getConnection(), $grammar, $processor); @@ -164,7 +165,7 @@ protected function getPostgresBuilder() protected function getSQLiteBuilder() { - $grammar = new SQLiteGrammar; + $grammar = new SQLiteGrammar($this->createMock(Connection::class)); $processor = $this->createMock(Processor::class); return new QueryBuilder($this->getConnection(), $grammar, $processor); @@ -172,7 +173,7 @@ protected function getSQLiteBuilder() protected function getSqlServerBuilder() { - $grammar = new SqlServerGrammar; + $grammar = new SqlServerGrammar($this->createMock(Connection::class)); $processor = $this->createMock(Processor::class); return new QueryBuilder($this->getConnection(), $grammar, $processor); From 880bb5c59599bb71590c55bee3b709ee255800d8 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Jul 2025 13:46:27 -0400 Subject: [PATCH 127/197] fix grammar tests --- .../Grammars/MySqlSchemaGrammarTest.php | 22 ++++----- .../Grammars/PostgresSchemaGrammarTest.php | 23 +++++----- .../Grammars/SQLiteSchemaGrammarTest.php | 22 ++++----- .../Grammars/SqlServerSchemaGrammarTest.php | 22 ++++----- tests/GrammarTestCase.php | 46 ++++++++++++++----- 5 files changed, 77 insertions(+), 58 deletions(-) diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index 38ed9281..bd0018f5 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -4,26 +4,28 @@ use Illuminate\Database\Schema\Blueprint; use Winter\Storm\Database\Schema\Grammars\MySqlGrammar; +use Illuminate\Database\Schema\MySqlBuilder; class MySqlSchemaGrammarTest extends \GrammarTestCase { public function setUp(): void { - parent::setUp(); + $this->grammarClass = MySqlGrammar::class; + $this->builderClass = MySqlBuilder::class; - $this->grammar = new MySqlGrammar; + parent::setUp(); } public function testNoInitialModifiersAddNullable() { - $initialBlueprint = new Blueprint('users'); + + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name'); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table `users` add `name` varchar(255) not null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->nullable()->change(); $statements = $this->runBlueprint($changedBlueprint); @@ -32,14 +34,13 @@ public function testNoInitialModifiersAddNullable() public function testNullableInitialModifierAddDefault() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name')->nullable(); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->default('admin')->change(); $statements = $this->runBlueprint($changedBlueprint); @@ -48,14 +49,13 @@ public function testNullableInitialModifierAddDefault() public function testNullableInitialModifierAddDefaultNotNullable() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name')->nullable(); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table `users` add `name` varchar(255) null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); $statements = $this->runBlueprint($changedBlueprint); diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index dbf13715..1e4edb6a 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -2,28 +2,28 @@ namespace Winter\Storm\Tests\Database\Schema\Grammars; -use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\PostgresBuilder; use Winter\Storm\Database\Schema\Grammars\PostgresGrammar; class PostgresSchemaGrammarTest extends \GrammarTestCase { public function setUp(): void { - parent::setUp(); + $this->grammarClass = PostgresGrammar::class; + $this->builderClass = PostgresBuilder::class; - $this->grammar = new PostgresGrammar; + parent::setUp(); } public function testNoInitialModifiersAddNullable() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name'); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar(255) not null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->nullable()->change(); $statements = $this->runBlueprint($changedBlueprint); @@ -32,16 +32,16 @@ public function testNoInitialModifiersAddNullable() $this->assertSame('alter column "name" drop not null', $parts[1]); $this->assertSame("alter column \"name\" drop default", $parts[2]); } + public function testNullableInitialModifierAddDefault() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name')->nullable(); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar(255) null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->default('admin')->change(); $statements = $this->runBlueprint($changedBlueprint); @@ -53,14 +53,13 @@ public function testNullableInitialModifierAddDefault() public function testNullableInitialModifierAddDefaultNotNullable() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name')->nullable(); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar(255) null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); $statements = $this->runBlueprint($changedBlueprint); diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index 8d6b8c97..a18f4d6a 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -2,28 +2,28 @@ namespace Winter\Storm\Tests\Database\Schema\Grammars; -use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\SQLiteBuilder; use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar; class SQLiteSchemaGrammarTest extends \GrammarTestCase { public function setUp(): void { - parent::setUp(); + $this->grammarClass = SQLiteGrammar::class; + $this->builderClass = SQLiteBuilder::class; - $this->grammar = new SQLiteGrammar; + parent::setUp(); } public function testNoInitialModifiersAddNullable() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name'); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar not null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->nullable()->change(); $statements = $this->runBlueprint($changedBlueprint); @@ -32,14 +32,13 @@ public function testNoInitialModifiersAddNullable() public function testNullableInitialModifierAddDefault() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name')->nullable(); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->default('admin')->change(); $statements = $this->runBlueprint($changedBlueprint); @@ -48,14 +47,13 @@ public function testNullableInitialModifierAddDefault() public function testNullableInitialModifierAddDefaultNotNullable() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name')->nullable(); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); $statements = $this->runBlueprint($changedBlueprint); diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index 60a42873..bbe8e13b 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -2,28 +2,28 @@ namespace Winter\Storm\Tests\Database\Schema\Grammars; -use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Schema\SqlServerBuilder; use Winter\Storm\Database\Schema\Grammars\SqlServerGrammar; class SqlServerSchemaGrammarTest extends \GrammarTestCase { public function setUp(): void { - parent::setUp(); + $this->grammarClass = SqlServerGrammar::class; + $this->builderClass = SqlServerBuilder::class; - $this->grammar = new SqlServerGrammar; + parent::setUp(); } public function testNoInitialModifiersAddNullable() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name'); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add "name" nvarchar(255) not null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->nullable()->change(); $statements = $this->runBlueprint($changedBlueprint); @@ -32,14 +32,13 @@ public function testNoInitialModifiersAddNullable() public function testNullableInitialModifierAddDefault() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name')->nullable(); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add "name" nvarchar(255) null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->default('admin')->change(); $statements = $this->runBlueprint($changedBlueprint); @@ -49,14 +48,13 @@ public function testNullableInitialModifierAddDefault() public function testNullableInitialModifierAddDefaultNotNullable() { - $initialBlueprint = new Blueprint('users'); + $initialBlueprint = $this->getBlueprint('users'); $initialBlueprint->string('name')->nullable(); - $this->setupConnection($initialBlueprint); $statements = $this->runBlueprint($initialBlueprint); $this->assertSame('alter table "users" add "name" nvarchar(255) null', $statements[0]); - $changedBlueprint = new Blueprint('users'); + $changedBlueprint = $this->getBlueprint('users'); $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); $statements = $this->runBlueprint($changedBlueprint); diff --git a/tests/GrammarTestCase.php b/tests/GrammarTestCase.php index c5418e2a..2ffcb92d 100644 --- a/tests/GrammarTestCase.php +++ b/tests/GrammarTestCase.php @@ -5,30 +5,54 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Builder; -use Illuminate\Database\Schema\Grammars\Grammar; use Mockery as m; use PHPUnit\Framework\TestCase; class GrammarTestCase extends TestCase { - protected Connection $connection; - protected Grammar $grammar; + protected $connection = null; + + protected function setUp(): void + { + parent::setUp(); + + $this->connection = m::mock(Connection::class) + ->shouldReceive('getTablePrefix')->andReturn('') + ->shouldReceive('getServerVersion')->andReturn('3.35') + ->shouldReceive('scalar')->andReturn('') + ->getMock(); + + $this->connection = $this->connection + ->shouldReceive('getSchemaGrammar')->andReturn($this->getSchemaGrammar()) + ->getMock(); + } protected function tearDown(): void { m::close(); } - protected function setupConnection(Blueprint $blueprint) + public function getConnection() + { + return $this->connection(); + } + + protected function getBlueprint(string $table) + { + $blueprint = new Blueprint($this->connection, $table); + $this->connection->shouldReceive('getSchemaBuilder')->andReturnUsing(function () use ($blueprint) { + return $this->getSchemaBuilder($blueprint); + }); + + return $blueprint; + } + + protected function getSchemaGrammar() { - $connection = m::mock(Connection::class); - $connection->shouldReceive('getServerVersion')->andReturn('3.35'); - $connection->shouldReceive('getSchemaBuilder')->andReturn($this->getSchemaBuilder($blueprint)); - $connection->shouldReceive('scalar')->andReturn(''); - $this->connection = $connection; + return new $this->grammarClass($this->connection); } - protected function getSchemaBuilder(Blueprint $blueprint) + protected function getSchemaBuilder($blueprint) { $schemaBuilder = m::mock(Builder::class); $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); @@ -40,6 +64,6 @@ protected function getSchemaBuilder(Blueprint $blueprint) protected function runBlueprint(Blueprint $blueprint) { - return $blueprint->toSql($this->connection, $this->grammar); + return $blueprint->toSql(); } } From 0c6afa51dabb71eed8628485da8471259fe77dc8 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Mon, 21 Jul 2025 16:40:55 -0400 Subject: [PATCH 128/197] a few tweaks to sqlite grammar --- src/Database/Schema/Grammars/SQLiteGrammar.php | 8 +++++--- tests/GrammarTestCase.php | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index eda0ad24..ef1eba9d 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -104,13 +104,13 @@ public function compileChange(Blueprint $blueprint, Fluent $command) fn ($index) => $this->{'compile'.ucfirst($index->name)}($blueprint, $index) )->all(); - $tempTable = $this->wrap('__temp__'.$blueprint->getPrefix().$table); + $tempTable = $this->wrapTable($blueprint, '__temp__'.$this->connection->getTablePrefix()); $table = $this->wrapTable($blueprint); $columnNames = implode(', ', $columnNames); - $foreignKeyConstraintsEnabled = $connection->scalar('pragma foreign_keys'); + $foreignKeyConstraintsEnabled = $this->connection->scalar($this->pragma('foreign_keys')); - return array_filter( + $sqlQuery = array_filter( array_merge( [ $foreignKeyConstraintsEnabled ? $this->compileDisableForeignKeyConstraints() : null, @@ -144,6 +144,8 @@ public function compileChange(Blueprint $blueprint, Fluent $command) ] ) ); + + return $sqlQuery; } public function getDefaultValue($value) diff --git a/tests/GrammarTestCase.php b/tests/GrammarTestCase.php index 2ffcb92d..6d52cc5b 100644 --- a/tests/GrammarTestCase.php +++ b/tests/GrammarTestCase.php @@ -12,7 +12,7 @@ class GrammarTestCase extends TestCase { protected $connection = null; - protected function setUp(): void + public function setUp(): void { parent::setUp(); @@ -27,7 +27,7 @@ protected function setUp(): void ->getMock(); } - protected function tearDown(): void + public function tearDown(): void { m::close(); } From 2d9a70a9c0f252859feca2da89b3138639b62f20 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 21 Jul 2025 22:46:31 -0700 Subject: [PATCH 129/197] Remove extra semicolon --- tests/Database/QueryBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Database/QueryBuilderTest.php b/tests/Database/QueryBuilderTest.php index 518a64cb..2f640992 100644 --- a/tests/Database/QueryBuilderTest.php +++ b/tests/Database/QueryBuilderTest.php @@ -1,6 +1,6 @@ Date: Tue, 22 Jul 2025 09:27:17 -0400 Subject: [PATCH 130/197] stay closer to Laravel for database service provider; add db.transaction singleton --- src/Database/DatabaseServiceProvider.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php index f5872ee0..698da21d 100644 --- a/src/Database/DatabaseServiceProvider.php +++ b/src/Database/DatabaseServiceProvider.php @@ -1,9 +1,11 @@ registerConnectionServices(); $this->registerQueueableEntityResolver(); + } + /** + * Register the primary database bindings. + * + * @return void + */ + protected function registerConnectionServices() + { // The connection factory is used to create the actual connection instances on // the database. We will inject the factory into the manager so that it may // make the connections while they are actually needed and not of before. @@ -61,6 +72,10 @@ public function register() return $builder; }); + $this->app->singleton('db.transactions', function ($app) { + return new DatabaseTransactionsManager; + }); + $this->app->singleton('db.dongle', function ($app) { return new Dongle($this->getDefaultDatabaseDriver(), $app['db']); }); From 490349445302442450b8ccad1fca266e81a0e072 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Jul 2025 09:37:35 -0400 Subject: [PATCH 131/197] consistency cleanup --- src/Database/Model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Model.php b/src/Database/Model.php index fdab01f6..fee8dded 100644 --- a/src/Database/Model.php +++ b/src/Database/Model.php @@ -697,10 +697,10 @@ public function newEloquentBuilder($query) protected function newBaseQueryBuilder() { $conn = $this->getConnection(); - $grammar = $conn->getQueryGrammar(); + $processor = $conn->getPostProcessor(); - $builder = new QueryBuilder($conn, $grammar, $conn->getPostProcessor()); + $builder = new QueryBuilder($conn, $grammar, $processor); if ($this->duplicateCache) { $builder->enableDuplicateCache(); From a6994f16e8df73dcc3183641f4a1172a97f47d4a Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Jul 2025 09:40:40 -0400 Subject: [PATCH 132/197] use better import naming --- src/Database/MigrationServiceProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/MigrationServiceProvider.php b/src/Database/MigrationServiceProvider.php index a1d81b32..086e1bce 100644 --- a/src/Database/MigrationServiceProvider.php +++ b/src/Database/MigrationServiceProvider.php @@ -1,8 +1,8 @@ Date: Tue, 22 Jul 2025 09:58:11 -0400 Subject: [PATCH 133/197] embrace laravel when we can --- src/Database/QueryBuilder.php | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index bad7d16f..aba632ad 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -313,35 +313,7 @@ public function upsert(array $values, $uniqueBy, $update = null) $this->clearDuplicateCache(); - if ($update === []) { - return (int) $this->insert($values); - } - - if (!is_array(reset($values))) { - $values = [$values]; - } else { - foreach ($values as $key => $value) { - ksort($value); - - $values[$key] = $value; - } - } - - if (is_null($update)) { - $update = array_keys(reset($values)); - } - - $bindings = $this->cleanBindings(array_merge( - Arr::flatten($values, 1), - collect($update)->reject(function ($value, $key) { - return is_int($key); - })->all() - )); - - return $this->connection->affectingStatement( - $this->grammar->compileUpsert($this, $values, (array) $uniqueBy, $update), - $bindings - ); + return parent::upsert($values, $uniqueBy, $update); } /** From db170de5622617ae01f8b0c1d753baa775aeb7e9 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Jul 2025 10:08:18 -0400 Subject: [PATCH 134/197] be consistent with aliases --- src/Database/Schema/Grammars/MariaDbGrammar.php | 4 ++-- src/Database/Schema/Grammars/MySqlGrammar.php | 4 ++-- src/Database/Schema/Grammars/PostgresGrammar.php | 4 ++-- src/Database/Schema/Grammars/SQLiteGrammar.php | 4 ++-- src/Database/Schema/Grammars/SqlServerGrammar.php | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Database/Schema/Grammars/MariaDbGrammar.php b/src/Database/Schema/Grammars/MariaDbGrammar.php index fbc4f6ca..c055e81e 100755 --- a/src/Database/Schema/Grammars/MariaDbGrammar.php +++ b/src/Database/Schema/Grammars/MariaDbGrammar.php @@ -2,9 +2,9 @@ namespace Winter\Storm\Database\Schema\Grammars; -use Illuminate\Database\Schema\Grammars\MariaDbGrammar as MariaDbGrammarBase; +use Illuminate\Database\Schema\Grammars\MariaDbGrammar as BaseMariaDbGrammar; -class MariaDbGrammar extends MariaDbGrammarBase +class MariaDbGrammar extends BaseMariaDbGrammar { use Concerns\MySqlBasedGrammar; } diff --git a/src/Database/Schema/Grammars/MySqlGrammar.php b/src/Database/Schema/Grammars/MySqlGrammar.php index 207f58a3..1230bd08 100755 --- a/src/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Database/Schema/Grammars/MySqlGrammar.php @@ -2,9 +2,9 @@ namespace Winter\Storm\Database\Schema\Grammars; -use Illuminate\Database\Schema\Grammars\MySqlGrammar as MySqlGrammarBase; +use Illuminate\Database\Schema\Grammars\MySqlGrammar as BaseMySqlGrammar; -class MySqlGrammar extends MySqlGrammarBase +class MySqlGrammar extends BaseMySqlGrammar { use Concerns\MySqlBasedGrammar; } diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 2183425c..2f6f4739 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -4,10 +4,10 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\ColumnDefinition; -use Illuminate\Database\Schema\Grammars\PostgresGrammar as PostgresGrammarBase; +use Illuminate\Database\Schema\Grammars\PostgresGrammar as BasePostgresGrammar; use Illuminate\Support\Fluent; -class PostgresGrammar extends PostgresGrammarBase +class PostgresGrammar extends BasePostgresGrammar { /** * Compile a change column command into a series of SQL statements. diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index ef1eba9d..4839732b 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -7,10 +7,10 @@ use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Database\Schema\ForeignKeyDefinition; use Illuminate\Database\Schema\IndexDefinition; -use Illuminate\Database\Schema\Grammars\SQLiteGrammar as SQLiteGrammarBase; +use Illuminate\Database\Schema\Grammars\SQLiteGrammar as BaseSQLiteGrammar; use Illuminate\Support\Fluent; -class SQLiteGrammar extends SQLiteGrammarBase +class SQLiteGrammar extends BaseSQLiteGrammar { /** * Compile a change column command into a series of SQL statements. diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 3d385081..613dc0a2 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -4,10 +4,10 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\ColumnDefinition; -use Illuminate\Database\Schema\Grammars\SqlServerGrammar as SqlServerGrammarBase; +use Illuminate\Database\Schema\Grammars\SqlServerGrammar as BaseSqlServerGrammar; use Illuminate\Support\Fluent; -class SqlServerGrammar extends SqlServerGrammarBase +class SqlServerGrammar extends BaseSqlServerGrammar { /** * Compile a change column command into a series of SQL statements. From 83159e4a9615b81606fe71ca46789d173979c0ea Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Jul 2025 10:08:47 -0400 Subject: [PATCH 135/197] fix typo in class aliases --- src/Database/Query/Grammars/MariaDbGrammar.php | 4 ++-- src/Database/Query/Grammars/MySqlGrammar.php | 4 ++-- src/Database/Query/Grammars/PostgresGrammar.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Database/Query/Grammars/MariaDbGrammar.php b/src/Database/Query/Grammars/MariaDbGrammar.php index 11eccd72..02051189 100644 --- a/src/Database/Query/Grammars/MariaDbGrammar.php +++ b/src/Database/Query/Grammars/MariaDbGrammar.php @@ -1,9 +1,9 @@ Date: Tue, 22 Jul 2025 10:16:27 -0400 Subject: [PATCH 136/197] use Base classes aliases in a consistent manner --- src/Database/Connections/Connection.php | 4 ++-- src/Database/Connections/MariaDbConnection.php | 3 ++- src/Database/Connections/MySqlConnection.php | 3 ++- src/Database/Connections/PostgresConnection.php | 3 ++- src/Database/Connections/SQLiteConnection.php | 3 ++- src/Database/Connections/SqlServerConnection.php | 4 +++- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Database/Connections/Connection.php b/src/Database/Connections/Connection.php index a1df2f8b..146a4545 100644 --- a/src/Database/Connections/Connection.php +++ b/src/Database/Connections/Connection.php @@ -1,11 +1,11 @@ Date: Tue, 22 Jul 2025 10:17:45 -0400 Subject: [PATCH 137/197] use Base classes aliases in a consistent manner --- src/Database/Connectors/ConnectionFactory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/Connectors/ConnectionFactory.php b/src/Database/Connectors/ConnectionFactory.php index a193640f..de1cb4c2 100644 --- a/src/Database/Connectors/ConnectionFactory.php +++ b/src/Database/Connectors/ConnectionFactory.php @@ -1,7 +1,7 @@ Date: Tue, 22 Jul 2025 12:26:11 -0400 Subject: [PATCH 138/197] fix bluePrint resolver signature --- src/Database/DatabaseServiceProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php index 698da21d..3d66719e 100644 --- a/src/Database/DatabaseServiceProvider.php +++ b/src/Database/DatabaseServiceProvider.php @@ -105,8 +105,8 @@ protected function getDefaultDatabaseDriver() protected function swapSchemaBuilderBlueprint() { $this->app['events']->listen('db.schema.getBuilder', function (\Illuminate\Database\Schema\Builder $builder) { - $builder->blueprintResolver(function ($table, $callback) { - return new Blueprint($table, $callback); + $builder->blueprintResolver(function ($connection, $table, $callback) { + return new Blueprint($connection, $table, $callback); }); }); } From 5359d0624e0c6b576c5035239df67f442e696783 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Jul 2025 12:40:08 -0400 Subject: [PATCH 139/197] configwriter fix from @jaxwilko --- src/Config/ConfigWriter.php | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/Config/ConfigWriter.php b/src/Config/ConfigWriter.php index a609a498..f701c7a2 100644 --- a/src/Config/ConfigWriter.php +++ b/src/Config/ConfigWriter.php @@ -1,8 +1,9 @@ [ - 'comments', - 'startTokenPos', - 'startLine', - 'endTokenPos', - 'endLine' - ] - ]); - $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7, $lexer); + $version = PhpVersion::getHostVersion(); + $lexer = new Lexer\Emulative($version); + $parser = new Php8($lexer, $version); try { - $ast = $parser->parse($contents); + $ast = $parser->parse($contents); } catch (Error $e) { - throw new SystemException($e); + throw new SystemException($e); } - return (new ArrayFile($ast, $lexer, null))->set($newValues)->render(); + return (new ArrayFile($ast, $parser))->set($newValues)->render(); } } From 6daadd3afc4961d383b7fe9bdabfc567d11bc890 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Jul 2025 12:46:31 -0400 Subject: [PATCH 140/197] fix remaining mock method for SQLiteGrammar --- tests/GrammarTestCase.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/GrammarTestCase.php b/tests/GrammarTestCase.php index 6d52cc5b..90e67ecd 100644 --- a/tests/GrammarTestCase.php +++ b/tests/GrammarTestCase.php @@ -54,12 +54,12 @@ protected function getSchemaGrammar() protected function getSchemaBuilder($blueprint) { - $schemaBuilder = m::mock(Builder::class); - $schemaBuilder->shouldReceive('getColumns')->andReturn($blueprint->getColumns()); - $schemaBuilder->shouldReceive('getForeignKeys')->andReturn([]); - $schemaBuilder->shouldReceive('getIndexes')->andReturn([]); - - return $schemaBuilder; + return m::mock(Builder::class) + ->shouldReceive('getColumns')->andReturn($blueprint->getColumns()) + ->shouldReceive('getForeignKeys')->andReturn([]) + ->shouldReceive('getIndexes')->andReturn([]) + ->shouldReceive('parseSchemaAndTable')->andReturnNull() + ->getMock(); } protected function runBlueprint(Blueprint $blueprint) From 3019c56603b90e418f0fac312cbfe89245981a7d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Jul 2025 12:58:50 -0400 Subject: [PATCH 141/197] fix MailFake tests --- tests/Support/MailFakeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Support/MailFakeTest.php b/tests/Support/MailFakeTest.php index b777d2e8..c6941239 100644 --- a/tests/Support/MailFakeTest.php +++ b/tests/Support/MailFakeTest.php @@ -17,7 +17,7 @@ public function setUp(): void parent::setUp(); App::shouldReceive('getLocale')->andReturn('en/US'); - $this->manager = m::mock(MailManager::class); + $this->manager = m::mock(MailManager::class)->shouldReceive('getDefaultDriver')->andReturn('smtp')->getMock(); Mail::swap(new MailFake($this->manager)); $this->recipient = 'fake@localhost'; From d5fd10ec72dc05eb16b836e096f324b35a61ac34 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 22 Jul 2025 13:02:42 -0400 Subject: [PATCH 142/197] remove non-existent path in phpstan.neon --- phpstan.neon | 1 - 1 file changed, 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index 423e6134..defc9bf2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,7 +9,6 @@ parameters: excludePaths: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php - - src/Parse/PHP/ArrayPrinter.php - src/Foundation/Console/KeyGenerateCommand.php - src/Scaffold/GeneratorCommand.php disableSchemaScan: true From 1d969aa9dcec6597e23e89808904defadd55b13a Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 14:07:11 -0600 Subject: [PATCH 143/197] Fix some PHPStan complaints --- src/Auth/Models/User.php | 2 +- src/Database/Attach/File.php | 6 +++--- src/Database/Connections/HasConnection.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Auth/Models/User.php b/src/Auth/Models/User.php index 64f6246e..81695a9a 100644 --- a/src/Auth/Models/User.php +++ b/src/Auth/Models/User.php @@ -54,7 +54,7 @@ class User extends Model implements \Illuminate\Contracts\Auth\Authenticatable protected $dates = ['activated_at', 'last_login']; /** - * @var array The attributes that should be hidden for arrays. + * @var array The attributes that should be hidden for arrays. */ protected $hidden = ['password', 'reset_password_code', 'activation_code', 'persist_code']; diff --git a/src/Database/Attach/File.php b/src/Database/Attach/File.php index f467efd2..a3a2940d 100644 --- a/src/Database/Attach/File.php +++ b/src/Database/Attach/File.php @@ -45,7 +45,7 @@ class File extends Model ]; /** - * @var array The attributes that are mass assignable. + * @var array The attributes that are mass assignable. */ protected $fillable = [ 'file_name', @@ -76,12 +76,12 @@ class File extends Model public static $imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif']; /** - * @var array Hidden fields from array/json access + * @var array Hidden fields from array/json access */ protected $hidden = ['attachment_type', 'attachment_id', 'is_public']; /** - * @var array Add fields to array/json access + * @var array Add fields to array/json access */ protected $appends = ['path', 'extension']; diff --git a/src/Database/Connections/HasConnection.php b/src/Database/Connections/HasConnection.php index 78c2e561..304f2529 100644 --- a/src/Database/Connections/HasConnection.php +++ b/src/Database/Connections/HasConnection.php @@ -70,7 +70,7 @@ protected function fireConnectionEvent($event) { $this->fireEvent('connection.'.$this->getName().'.'.$event, $this); - parent::fireConnectionEvent($event); + return parent::fireConnectionEvent($event); } /** From d8401507060e8b39fa778749defd021e72e27ede Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 15:21:07 -0600 Subject: [PATCH 144/197] Update the PHPStan baseline --- composer.json | 3 +- phpstan-baseline.neon | 1066 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 986 insertions(+), 83 deletions(-) diff --git a/composer.json b/composer.json index f63009f7..04871f1e 100644 --- a/composer.json +++ b/composer.json @@ -101,7 +101,8 @@ "test": "./vendor/bin/phpunit ./tests", "lint": "parallel-lint --exclude vendor .", "sniff": "phpcs --colors -nq --report=\"full\" --extensions=\"php\"", - "analyze": "phpstan analyse --memory-limit=2G src" + "analyze": "phpstan analyse --memory-limit=2G src", + "baseline": "phpstan analyse --memory-limit=2G src --generate-baseline" }, "extra": { "branch-alias": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3171b036..d896f05d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,137 +1,1039 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method \\$this\\(Winter\\\\Storm\\\\Auth\\\\Models\\\\(Group|Role|User)\\)::getOriginal(Encryptable|Hash)Values\\(\\)\\.$#" - paths: - - src/Auth/Models/Group.php - - src/Auth/Models/Role.php - - src/Auth/Models/User.php + message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Group\)\:\:getOriginalEncryptableValues\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Auth/Models/Group.php - - messages: - - "#^Parameter \\#1 \\$app of class Illuminate\\\\Database\\\\DatabaseManager constructor expects Illuminate\\\\Contracts\\\\Foundation\\\\Application, Illuminate\\\\Contracts\\\\Container\\\\Container given\\.$#" + message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Group\)\:\:getOriginalHashValues\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Auth/Models/Group.php + + - + message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Role\)\:\:getOriginalEncryptableValues\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Auth/Models/Role.php + + - + message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Role\)\:\:getOriginalHashValues\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Auth/Models/Role.php + + - + message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\User\)\:\:getOriginalEncryptableValues\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Auth/Models/User.php + + - + message: '#^Call to function method_exists\(\) with \$this\(Winter\\Storm\\Auth\\Models\\User\) and ''getHashableAttribut…'' will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: src/Auth/Models/User.php + + - + message: '#^Call to function property_exists\(\) with \$this\(Winter\\Storm\\Auth\\Models\\User\) and ''attributeNames'' will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: src/Auth/Models/User.php + + - + message: '#^Call to function property_exists\(\) with \$this\(Winter\\Storm\\Auth\\Models\\User\) and ''customMessages'' will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: src/Auth/Models/User.php + + - + message: '#^PHPDoc type array\ of property Winter\\Storm\\Auth\\Models\\User\:\:\$hidden is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$hidden\.$#' + identifier: property.phpDocType + count: 1 + path: src/Auth/Models/User.php + + - + message: '#^PHPDoc type array\ of property Winter\\Storm\\Database\\Attach\\File\:\:\$appends is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$appends\.$#' + identifier: property.phpDocType + count: 1 + path: src/Database/Attach/File.php + + - + message: '#^PHPDoc type array\ of property Winter\\Storm\\Database\\Attach\\File\:\:\$fillable is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$fillable\.$#' + identifier: property.phpDocType + count: 1 + path: src/Database/Attach/File.php + + - + message: '#^PHPDoc type array\ of property Winter\\Storm\\Database\\Attach\\File\:\:\$hidden is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$hidden\.$#' + identifier: property.phpDocType + count: 1 + path: src/Database/Attach/File.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Behaviors\\Encryptable\:\:propertyExists\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Behaviors/Encryptable.php + + - + message: '#^Variable \$model in PHPDoc tag @var does not exist\.$#' + identifier: varTag.variableNotFound + count: 1 + path: src/Database/Behaviors/Encryptable.php + + - + message: '#^Parameter \#1 \$app of class Illuminate\\Database\\DatabaseManager constructor expects Illuminate\\Contracts\\Foundation\\Application, Illuminate\\Contracts\\Container\\Container given\.$#' + identifier: argument.type + count: 1 path: src/Database/Capsule/Manager.php - - messages: - - "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface::getName\\(\\)\\.$#" + message: '#^Call to an undefined method Illuminate\\Database\\ConnectionInterface\:\:getName\(\)\.$#' + identifier: method.notFound + count: 1 path: src/Database/MemoryCache.php - - messages: - - "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$(is_bind|master_field|pivot_data|slave_(id|type))\\.$#" - - "#^Method Winter\\\\Storm\\\\Database\\\\Model::getDeferredBindingRecords\\(\\) should return Winter\\\\Storm\\\\Database\\\\Collection but returns Illuminate\\\\Database\\\\Eloquent\\\\Collection\\\\.$#" - - "#^Return type \\(Winter\\\\Storm\\\\Database\\\\Pivot\\) of method Winter\\\\Storm\\\\Database\\\\Model::newPivot\\(\\) should be compatible with return type \\(Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Pivot\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Model::newPivot\\(\\)$#" - - "#^Return type \\(Winter\\\\Storm\\\\Database\\\\Collection\\) of method Winter\\\\Storm\\\\Database\\\\Model::newCollection\\(\\) should be compatible with return type \\(Illuminate\\\\Database\\\\Eloquent\\\\Collection\\<\\(int\\|string\\), static\\(Illuminate\\\\Database\\\\Eloquent\\\\Model\\)\\>\\) of method Illuminate\\\\Database\\\\Eloquent\\\\Model::newCollection\\(\\)#" + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$is_bind\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Model.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$master_field\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Model.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$pivot_data\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Model.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$slave_id\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Model.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$slave_type\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:getDeferredBindingRecords\(\) should return Winter\\Storm\\Database\\Collection but returns Illuminate\\Database\\Eloquent\\Collection\\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newBelongsTo\(\) should return Illuminate\\Database\\Eloquent\\Relations\\BelongsTo\ but returns Winter\\Storm\\Database\\Relations\\BelongsTo\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newBelongsToMany\(\) should return Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\ but returns Winter\\Storm\\Database\\Relations\\BelongsToMany\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newHasMany\(\) should return Illuminate\\Database\\Eloquent\\Relations\\HasMany\ but returns Winter\\Storm\\Database\\Relations\\HasMany\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newHasManyThrough\(\) should return Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough\ but returns Winter\\Storm\\Database\\Relations\\HasManyThrough\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newHasOne\(\) should return Illuminate\\Database\\Eloquent\\Relations\\HasOne\ but returns Winter\\Storm\\Database\\Relations\\HasOne\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newHasOneThrough\(\) should return Illuminate\\Database\\Eloquent\\Relations\\HasOneThrough\ but returns Winter\\Storm\\Database\\Relations\\HasOneThrough\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newMorphMany\(\) should return Illuminate\\Database\\Eloquent\\Relations\\MorphMany\ but returns Winter\\Storm\\Database\\Relations\\MorphMany\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newMorphOne\(\) should return Illuminate\\Database\\Eloquent\\Relations\\MorphOne\ but returns Winter\\Storm\\Database\\Relations\\MorphOne\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newMorphTo\(\) should return Illuminate\\Database\\Eloquent\\Relations\\MorphTo\ but returns Winter\\Storm\\Database\\Relations\\MorphTo\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Method Winter\\Storm\\Database\\Model\:\:newMorphToMany\(\) should return Illuminate\\Database\\Eloquent\\Relations\\MorphToMany\ but returns Winter\\Storm\\Database\\Relations\\MorphToMany\.$#' + identifier: return.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Return type \(Winter\\Storm\\Database\\Collection\) of method Winter\\Storm\\Database\\Model\:\:newCollection\(\) should be compatible with return type \(Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\), static\(Illuminate\\Database\\Eloquent\\Model\)\>\) of method Illuminate\\Database\\Eloquent\\Model\:\:newCollection\(\)$#' + identifier: method.childReturnType + count: 1 path: src/Database/Model.php - - messages: - - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Model::errors\\(\\)\\.$#" + message: '#^Return type \(Winter\\Storm\\Database\\Pivot\) of method Winter\\Storm\\Database\\Model\:\:newPivot\(\) should be compatible with return type \(Illuminate\\Database\\Eloquent\\Relations\\Pivot\) of method Illuminate\\Database\\Eloquent\\Model\:\:newPivot\(\)$#' + identifier: method.childReturnType + count: 1 + path: src/Database/Model.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Model\:\:errors\(\)\.$#' + identifier: method.notFound + count: 1 path: src/Database/ModelException.php - - messages: - - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::deleteCancel\\(\\)\\.$#" + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:deleteCancel\(\)\.$#' + identifier: method.notFound + count: 2 path: src/Database/Models/DeferredBinding.php - - messages: - - "#^Parameter \\#1 \\$haystack of function str_contains expects string, int given\\.$#" - - "#^Parameter \\#1 \\$ids of method Winter\\\\Storm\\\\Database\\\\Pivot::newQueryForRestoration\\(\\) expects array\\\\|string, int given\\.$#" - - "#^Parameter \\#2 \\$string of function explode expects string, int given\\.$#" + message: '#^Parameter \#1 \$haystack of function str_contains expects string, int given\.$#' + identifier: argument.type + count: 1 + path: src/Database/MorphPivot.php + + - + message: '#^Parameter \#1 \$ids of method Winter\\Storm\\Database\\Pivot\:\:newQueryForRestoration\(\) expects array\\|string, int given\.$#' + identifier: argument.type + count: 1 path: src/Database/MorphPivot.php - - messages: - - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getLeftColumnName\\(\\)\\.$#" + message: '#^Parameter \#2 \$string of function explode expects string, int given\.$#' + identifier: argument.type + count: 1 + path: src/Database/MorphPivot.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getLeftColumnName\(\)\.$#' + identifier: method.notFound + count: 1 path: src/Database/NestedTreeScope.php - - messages: - - "#^Access to an undefined property Illuminate.+(Builder|(Column|Index)Definition|Fluent)::\\$(.+)\\.$#" - paths: - - src/Database/Schema/Grammars/*Grammar.php - - src/Database/Query/Grammars/*Grammar.php + message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' + identifier: property.notFound + count: 2 + path: src/Database/Query/Grammars/MariaDbGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' + identifier: property.notFound + count: 2 + path: src/Database/Query/Grammars/MySqlGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' + identifier: property.notFound + count: 2 + path: src/Database/Query/Grammars/PostgresGrammar.php - - messages: - - "#^Call to an undefined method Illuminate\\\\Database\\\\ConnectionInterface::getName\\(\\)\\.$#" + message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' + identifier: property.notFound + count: 2 + path: src/Database/Query/Grammars/SQLiteGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' + identifier: property.notFound + count: 2 + path: src/Database/Query/Grammars/SqlServerGrammar.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\ConnectionInterface\:\:getName\(\)\.$#' + identifier: method.notFound + count: 1 path: src/Database/QueryBuilder.php - - messages: - - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation::(getForeignKey|select|with(Default|Pivot|Timestamps))\\(\\)\\.$#" - - "#^Call to an undefined method (TDeclaringModel of )?Illuminate\\\\Database\\\\Eloquent\\\\Model::((un)?bind(Deferred|EventOnce)|fireEvent|getRelationDefinition|newRelationPivot|reloadRelations)\\(\\)\\.$#" - - "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder::lists\\(\\)\\.$#" - - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Attach|(Belongs|Morph)To)(One|Many)::(flushDuplicateCache|getRelationExistenceQueryForSelfJoin)\\(\\)\\.$#" - - "#^Call to private method .+ of parent class Illuminate\\\\Database\\\\Eloquent\\\\Relations#" - paths: - - src/Database/Relations/*.php + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/AttachMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/AttachMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/AttachMany.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\AttachMany\:\:getRelationExistenceQueryForSelfJoin\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/AttachMany.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/AttachMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/AttachOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/AttachOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/AttachOne.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\AttachOne\:\:getRelationExistenceQueryForSelfJoin\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/AttachOne.php + + - + message: '#^Call to private method delete\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphOne\\.$#' + identifier: method.private + count: 3 + path: src/Database/Relations/AttachOne.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphOne\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/AttachOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:bindEventOnce\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsTo.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsTo.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsTo.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsTo.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\BelongsTo\:\:getForeignKey\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsTo.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\BelongsTo\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/BelongsTo.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$sessionKey\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:bindDeferred\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:bindEventOnce\(\)\.$#' + identifier: method.notFound + count: 2 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:fireEvent\(\)\.$#' + identifier: method.notFound + count: 4 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getRelationDefinition\(\)\.$#' + identifier: method.notFound + count: 2 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:newRelationPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:reloadRelations\(\)\.$#' + identifier: method.notFound + count: 2 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:unbindDeferred\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Query\\Builder\:\:lists\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php - - messages: - - "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$(children|sessionKey)\\.$#" - - "#^Parameter \\#3 \\$pageName of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::(simpleP|p)aginate\\(\\) expects string, array given\\.$#" - - "#^Parameter \\#4 \\$page of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\::(simpleP|p)aginate\\(\\) expects int\\|null, string given.$#" + message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:flushDuplicateCache\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/BelongsToMany.php - paths: - - src/Database/Relations/* + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#2 \$columns of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects array\, int\|null given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#2 \$columns of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects array\, int\|null given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#2 \$currentPage \(int\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:paginate\(\) should be compatible with parameter \$columns \(array\\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#2 \$currentPage \(int\|null\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:simplePaginate\(\) should be compatible with parameter \$columns \(array\\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#3 \$columns \(array\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:paginate\(\) should be compatible with parameter \$pageName \(string\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#3 \$columns \(array\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:simplePaginate\(\) should be compatible with parameter \$pageName \(string\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#3 \$pageName of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects string, array given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#3 \$pageName of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects string, array given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#4 \$page of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects int\|null, string given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#4 \$page of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects int\|null, string given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/BelongsToMany.php - - messages: - - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getSortOrderColumn\\(\\)\\.$#" + message: '#^Parameter \#4 \$pageName \(string\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:paginate\(\) should be compatible with parameter \$page \(int\|null\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Parameter \#4 \$pageName \(string\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:simplePaginate\(\) should be compatible with parameter \$page \(int\|null\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/BelongsToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasMany.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/HasMany.php + + - + message: '#^Call to private method update\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/HasMany.php + + - + message: '#^Call to private method whereNotIn\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/HasMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasManyThrough.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasManyThrough.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasManyThrough.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/HasManyThrough.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasOne.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasOne\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/HasOne.php + + - + message: '#^Call to private method update\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasOne\\.$#' + identifier: method.private + count: 2 + path: src/Database/Relations/HasOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasOneThrough.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasOneThrough.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/HasOneThrough.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasOneThrough\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/HasOneThrough.php + + - + message: '#^Instanceof between \*NEVER\* and Winter\\Storm\\Database\\Relations\\HasManyThrough will always evaluate to false\.$#' + identifier: instanceof.alwaysFalse + count: 4 + path: src/Database/Relations/HasOneThrough.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphMany.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\MorphMany\:\:getForeignKey\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphMany.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/MorphMany.php + + - + message: '#^Call to private method update\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/MorphMany.php + + - + message: '#^Call to private method whereNotIn\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/MorphMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphOne.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\MorphOne\:\:getForeignKey\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphOne.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphOne\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/MorphOne.php + + - + message: '#^Call to private method update\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphOne\\.$#' + identifier: method.private + count: 2 + path: src/Database/Relations/MorphOne.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:bindEventOnce\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphTo.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphTo.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphTo.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphTo.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\MorphTo\:\:getForeignKey\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphTo.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphTo\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/MorphTo.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Query\\Builder\:\:lists\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:flushDuplicateCache\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphToMany\\.$#' + identifier: method.private + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#2 \$columns of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects array\, int\|null given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#2 \$columns of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects array\, int\|null given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#2 \$currentPage \(int\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:paginate\(\) should be compatible with parameter \$columns \(array\\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#2 \$currentPage \(int\|null\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:simplePaginate\(\) should be compatible with parameter \$columns \(array\\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#3 \$columns \(array\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:paginate\(\) should be compatible with parameter \$pageName \(string\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#3 \$columns \(array\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:simplePaginate\(\) should be compatible with parameter \$pageName \(string\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#3 \$pageName of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects string, array given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#3 \$pageName of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects string, array given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#4 \$page of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects int\|null, string given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#4 \$page of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects int\|null, string given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#4 \$pageName \(string\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:paginate\(\) should be compatible with parameter \$page \(int\|null\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Parameter \#4 \$pageName \(string\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:simplePaginate\(\) should be compatible with parameter \$page \(int\|null\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Database/Relations/MorphToMany.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/MariaDbGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$renameTo\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/MariaDbGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/MySqlGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$renameTo\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/MySqlGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/PostgresGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$autoIncrement\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/SQLiteGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$name\.$#' + identifier: property.notFound + count: 2 + path: src/Database/Schema/Grammars/SQLiteGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$storedAs\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/SQLiteGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$storedAsJson\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/SQLiteGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$virtualAs\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/SQLiteGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$virtualAsJson\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/SQLiteGrammar.php + + - + message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' + identifier: property.notFound + count: 1 + path: src/Database/Schema/Grammars/SqlServerGrammar.php + + - + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getSortOrderColumn\(\)\.$#' + identifier: method.notFound + count: 1 path: src/Database/SortableScope.php - - messages: - - "#^Access to an undefined property Illuminate\\\\Database\\\\Eloquent\\\\Model::\\$children\\.$#" - - "#^Call to an undefined method Illuminate\\\\Database\\\\Eloquent\\\\Model::getParentId\\(\\)\\.$#" + message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$children\.$#' + identifier: property.notFound + count: 1 path: src/Database/TreeCollection.php - - messages: - - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" - - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queue\\(\\) should be compatible with parameter \\$queue \\(BackedEnum\\|string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::queue\\(\\)$#" - - "#^Parameter \\#2 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::queueOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::queueOn\\(\\)$#" - - "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::later\\(\\)$#" - - "#^Parameter \\#3 \\$data \\(array\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::later\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Mail\\\\Mailer::later\\(\\)$#" - - "#^Parameter \\#3 \\$view \\(array\\|string\\) of method Winter\\\\Storm\\\\Mail\\\\Mailer::laterOn\\(\\) should be compatible with parameter \\$view \\(Illuminate\\\\Contracts\\\\Mail\\\\Mailable\\) of method Illuminate\\\\Mail\\\\Mailer::laterOn\\(\\)$#" - path: src/Mail/Mailer.php + message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getParentId\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Database/TreeCollection.php - - messages: - - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\MailQueue::queue\\(\\)$#" - - "#^Parameter \\#2 \\$data \\(array\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\) should be compatible with parameter \\$queue \\(string\\|null\\) of method Illuminate\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::queue\\(\\)$#" - - "#^Return type \\(void\\) of method Winter\\\\Storm\\\\Support\\\\Testing\\\\Fakes\\\\MailFake::send\\(\\) should be compatible with return type \\(Illuminate\\\\Mail\\\\SentMessage\\|null\\) of method Illuminate\\\\Contracts\\\\Mail\\\\Mailer::send\\(\\)$#" - path: src/Support/Testing/Fakes/MailFake.php + message: '#^Call to an undefined method Illuminate\\Routing\\Router\:\:after\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Foundation/Application.php - - messages: - - "#^Call to an undefined method Illuminate\\\\Routing\\\\Router::(after|before)\\(\\)\\.$#" + message: '#^Call to an undefined method Illuminate\\Routing\\Router\:\:before\(\)\.$#' + identifier: method.notFound + count: 1 path: src/Foundation/Application.php - - messages: - - "#^Instanceof between \\*NEVER\\* and Winter\\\\Storm\\\\Database\\\\Relations\\\\HasManyThrough will always evaluate to false\\.$#" - paths: - - src/Database/Relations/Concerns/DefinedConstraints.php - - src/Database/Relations/Concerns/CanBeCounted.php + message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Mail\\Mailer\:\:queue\(\) should be compatible with parameter \$queue \(BackedEnum\|string\|null\) of method Illuminate\\Mail\\Mailer\:\:queue\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Mail/Mailer.php - - messages: - - "#Method Winter\\\\Storm\\\\Database\\\\Model::new(Has(One|Many)(Through)?|BelongsTo(Many)?|Morph(One|To|Many|ToMany))\\(\\) should return Illuminate\\\\Database\\\\Eloquent\\\\Relations#" - paths: - - src/Database/Concerns/HasRelationships.php + message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Mail\\Mailer\:\:queue\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Contracts\\Mail\\MailQueue\:\:queue\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Mail/Mailer.php + + - + message: '#^Parameter \#2 \$view \(array\|string\) of method Winter\\Storm\\Mail\\Mailer\:\:queueOn\(\) should be compatible with parameter \$view \(Illuminate\\Contracts\\Mail\\Mailable\) of method Illuminate\\Mail\\Mailer\:\:queueOn\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Mail/Mailer.php + + - + message: '#^Parameter \#3 \$data \(array\) of method Winter\\Storm\\Mail\\Mailer\:\:later\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Contracts\\Mail\\MailQueue\:\:later\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Mail/Mailer.php + + - + message: '#^Parameter \#3 \$data \(array\) of method Winter\\Storm\\Mail\\Mailer\:\:later\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Mail\\Mailer\:\:later\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Mail/Mailer.php - - messages: - - "#^Parameter .+ of method (Illuminate|Winter).+::(paginate|simplePaginate).+ (expects|should be compatible with)#" - paths: - - src/Database/Relations/Concerns/BelongsOrMorphsToMany.php + message: '#^Parameter \#3 \$view \(array\|string\) of method Winter\\Storm\\Mail\\Mailer\:\:laterOn\(\) should be compatible with parameter \$view \(Illuminate\\Contracts\\Mail\\Mailable\) of method Illuminate\\Mail\\Mailer\:\:laterOn\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Mail/Mailer.php + + - + message: '#^PHPDoc tag @method for method Winter\\Storm\\Support\\Facades\\Url\:\:buildUrl\(\) parameter \#3 \$flags default value contains unresolvable type\.$#' + identifier: methodTag.unresolvableType + count: 1 + path: src/Support/Facades/Url.php + + - + message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Contracts\\Mail\\MailQueue\:\:queue\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Support/Testing/Fakes/MailFake.php - - messages: - - "#^Call to an undefined method Winter\\\\Storm\\\\Database\\\\Relations\\\\(Morph(One|To|Many)|BelongsTo|Has(One|Many)Through)::getForeignKey\\(\\)\\.$#" - paths: - - src/Database/Relations/Concerns/CanBeCounted.php + message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\)$#' + identifier: method.childParameterType + count: 1 + path: src/Support/Testing/Fakes/MailFake.php + + - + message: '#^Return type \(void\) of method Winter\\Storm\\Support\\Testing\\Fakes\\MailFake\:\:send\(\) should be compatible with return type \(Illuminate\\Mail\\SentMessage\|null\) of method Illuminate\\Contracts\\Mail\\Mailer\:\:send\(\)$#' + identifier: method.childReturnType + count: 1 + path: src/Support/Testing/Fakes/MailFake.php From 2f21b511b51445180f1622f26d69863d90901091 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 15:21:24 -0600 Subject: [PATCH 145/197] Ignore warning about unused traits, we provide traits to be consumed by users --- phpstan.neon | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index defc9bf2..4cf8b5d9 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,6 +6,8 @@ parameters: paths: - src level: 5 + ignoreErrors: + - identifier: trait.unused excludePaths: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php From cf55c872e770d42c3aa18ea1de177af8e0c0001a Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 15:23:14 -0600 Subject: [PATCH 146/197] Fix PHPStan errors --- phpstan-baseline.neon | 6 ------ src/Database/Behaviors/Encryptable.php | 6 ++++-- src/Database/DatabaseServiceProvider.php | 3 ++- src/Extension/ExtendableTrait.php | 1 + src/Foundation/Bootstrap/LoadConfiguration.php | 2 ++ src/Halcyon/Builder.php | 2 +- src/Halcyon/Datasource/DbDatasource.php | 5 ++--- src/Halcyon/Model.php | 2 ++ src/Halcyon/Processors/SectionParser.php | 2 +- src/Html/FormBuilder.php | 7 +++++-- src/Http/Middleware/TrustHosts.php | 1 + src/Mail/Mailer.php | 2 ++ src/Network/Http.php | 2 +- src/Support/Facades/DB.php | 2 +- src/Support/Facades/File.php | 1 + 15 files changed, 26 insertions(+), 18 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d896f05d..cc0b5b8c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -78,12 +78,6 @@ parameters: count: 1 path: src/Database/Behaviors/Encryptable.php - - - message: '#^Variable \$model in PHPDoc tag @var does not exist\.$#' - identifier: varTag.variableNotFound - count: 1 - path: src/Database/Behaviors/Encryptable.php - - message: '#^Parameter \#1 \$app of class Illuminate\\Database\\DatabaseManager constructor expects Illuminate\\Contracts\\Foundation\\Application, Illuminate\\Contracts\\Container\\Container given\.$#' identifier: argument.type diff --git a/src/Database/Behaviors/Encryptable.php b/src/Database/Behaviors/Encryptable.php index 9f525a23..d482ef2a 100644 --- a/src/Database/Behaviors/Encryptable.php +++ b/src/Database/Behaviors/Encryptable.php @@ -1,7 +1,9 @@ -app['events']->listen('db.schema.getBuilder', function (\Illuminate\Database\Schema\Builder $builder) { - $builder->blueprintResolver(function ($connection, $table, $callback) { + $builder->blueprintResolver(function (Connection $connection, string $table, \Closure $callback) { return new Blueprint($connection, $table, $callback); }); }); diff --git a/src/Extension/ExtendableTrait.php b/src/Extension/ExtendableTrait.php index 23c9a90e..8804459f 100644 --- a/src/Extension/ExtendableTrait.php +++ b/src/Extension/ExtendableTrait.php @@ -558,6 +558,7 @@ protected function extensionGetParentClass(?object $instance = null) if (!is_null($instance)) { $reflector = $instance; } else { + // @phpstan-ignore-next-line $reflector = new ReflectionClass($this); } $parent = $reflector->getParentClass(); diff --git a/src/Foundation/Bootstrap/LoadConfiguration.php b/src/Foundation/Bootstrap/LoadConfiguration.php index 3c26852f..69328bbb 100644 --- a/src/Foundation/Bootstrap/LoadConfiguration.php +++ b/src/Foundation/Bootstrap/LoadConfiguration.php @@ -42,6 +42,7 @@ protected function getEnvironmentFromHost(): string return $config['hosts'][$hostname]; } + // @phpstan-ignore-next-line return env('APP_ENV', array_get($config, 'default', 'production')); } @@ -52,6 +53,7 @@ protected function getEnvironmentConfiguration(): array { $config = []; + // @phpstan-ignore-next-line $environment = env('APP_ENV'); if ($environment && file_exists($configPath = base_path() . '/config/' . $environment . '/environment.php')) { diff --git a/src/Halcyon/Builder.php b/src/Halcyon/Builder.php index de7313be..834e4d7b 100644 --- a/src/Halcyon/Builder.php +++ b/src/Halcyon/Builder.php @@ -277,7 +277,7 @@ public function whereFileName($fileName) * Execute the query as a fresh "select" statement. * * @param array $columns - * @return \Winter\Storm\Halcyon\Collection|static[] + * @return array|null */ public function getFresh($columns = ['*']) { diff --git a/src/Halcyon/Datasource/DbDatasource.php b/src/Halcyon/Datasource/DbDatasource.php index 5d83c4b0..1f217b57 100644 --- a/src/Halcyon/Datasource/DbDatasource.php +++ b/src/Halcyon/Datasource/DbDatasource.php @@ -312,9 +312,9 @@ public function update(string $dirName, string $fileName, string $extension, str */ public function delete(string $dirName, string $fileName, string $extension): bool { + $path = $this->makeFilePath($dirName, $fileName, $extension); try { // Get the existing record - $path = $this->makeFilePath($dirName, $fileName, $extension); $recordQuery = $this->getQuery()->where('path', $path); // Attempt to delete the existing record @@ -330,8 +330,7 @@ public function delete(string $dirName, string $fileName, string $extension): bo } return true; - } - catch (Exception $ex) { + } catch (Exception $ex) { throw (new DeleteFileException)->setInvalidPath($path); } } diff --git a/src/Halcyon/Model.php b/src/Halcyon/Model.php index 5a9ff66d..abc209b1 100644 --- a/src/Halcyon/Model.php +++ b/src/Halcyon/Model.php @@ -976,6 +976,8 @@ public function delete() return true; } + + return null; } /** diff --git a/src/Halcyon/Processors/SectionParser.php b/src/Halcyon/Processors/SectionParser.php index e1ca58f3..cad193ba 100644 --- a/src/Halcyon/Processors/SectionParser.php +++ b/src/Halcyon/Processors/SectionParser.php @@ -21,7 +21,7 @@ class SectionParser */ protected static function parseIntoSections(string $content, int $limit = 3): array { - $sections = preg_split('/^'.preg_quote(self::SECTION_SEPARATOR).'\s*$/m', $content, -1); + $sections = preg_split('/^'.preg_quote(self::SECTION_SEPARATOR, '/').'\s*$/m', $content, -1); // If more than the limit sections found, merge the extra sections into the final section if ($limit >= 1 && count($sections) > $limit) { diff --git a/src/Html/FormBuilder.php b/src/Html/FormBuilder.php index af5acdb0..11de1192 100644 --- a/src/Html/FormBuilder.php +++ b/src/Html/FormBuilder.php @@ -865,6 +865,8 @@ public function getValueAttribute(?string $name = null, $value = null) if (isset($this->model)) { return $this->getModelValueAttribute($name); } + + return null; } /** @@ -877,10 +879,11 @@ protected function getModelValueAttribute($name) { if (is_object($this->model)) { return object_get($this->model, $this->transformKey($name)); - } - elseif (is_array($this->model)) { + } elseif (is_array($this->model)) { return array_get($this->model, $this->transformKey($name)); } + + return null; } /** diff --git a/src/Http/Middleware/TrustHosts.php b/src/Http/Middleware/TrustHosts.php index dd8a511b..bc94af21 100644 --- a/src/Http/Middleware/TrustHosts.php +++ b/src/Http/Middleware/TrustHosts.php @@ -67,5 +67,6 @@ protected function allSubdomainsOfApplicationUrl() if ($host = parse_url($this->app['config']->get('app.url'), PHP_URL_HOST)) { return '^(.+\.)?'.preg_quote($host).'$'; } + return null; } } diff --git a/src/Mail/Mailer.php b/src/Mail/Mailer.php index 4aec4a50..d226ed21 100644 --- a/src/Mail/Mailer.php +++ b/src/Mail/Mailer.php @@ -184,6 +184,8 @@ public function send($view, array $data = [], $callback = null) return $sentMessage; } } + + return null; } /** diff --git a/src/Network/Http.php b/src/Network/Http.php index cca7a97c..87b54dac 100644 --- a/src/Network/Http.php +++ b/src/Network/Http.php @@ -274,7 +274,7 @@ public function send() curl_setopt($curl, CURLOPT_HEADER, true); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($curl, CURLOPT_USERAGENT, 'Winter Storm'); if (defined('CURLOPT_FOLLOWLOCATION') && !ini_get('open_basedir')) { diff --git a/src/Support/Facades/DB.php b/src/Support/Facades/DB.php index 15bbc8c5..0d03619b 100644 --- a/src/Support/Facades/DB.php +++ b/src/Support/Facades/DB.php @@ -3,7 +3,7 @@ use Winter\Storm\Support\Facade; /** - * @method static \Doctrine\DBAL\Driver\PDOConnection getPdo() + * @method static \PDO getPdo() * @method static \Illuminate\Database\Connection connection(string $name = null) * @method static \Winter\Storm\Database\QueryBuilder table(\Closure|\Illuminate\Database\Query\Builder|\Illuminate\Contracts\Database\Query\Expression|string $table, string|null $as = null) * @method static \Illuminate\Database\Query\Expression raw($value) diff --git a/src/Support/Facades/File.php b/src/Support/Facades/File.php index df56aac4..052b0169 100644 --- a/src/Support/Facades/File.php +++ b/src/Support/Facades/File.php @@ -1,6 +1,7 @@ Date: Wed, 23 Jul 2025 15:28:09 -0600 Subject: [PATCH 147/197] Fix method signature --- src/Database/DatabaseServiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Database/DatabaseServiceProvider.php b/src/Database/DatabaseServiceProvider.php index b5f91cc8..b51ce187 100644 --- a/src/Database/DatabaseServiceProvider.php +++ b/src/Database/DatabaseServiceProvider.php @@ -106,7 +106,7 @@ protected function getDefaultDatabaseDriver() protected function swapSchemaBuilderBlueprint() { $this->app['events']->listen('db.schema.getBuilder', function (\Illuminate\Database\Schema\Builder $builder) { - $builder->blueprintResolver(function (Connection $connection, string $table, \Closure $callback) { + $builder->blueprintResolver(function (Connection $connection, string $table, \Closure|null $callback) { return new Blueprint($connection, $table, $callback); }); }); From fcda041e7eb43b80c83f56b3b37aa8ec289a2c98 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 15:28:22 -0600 Subject: [PATCH 148/197] Disable memory limit on PHPUnit --- phpunit.xml.dist | 1 + 1 file changed, 1 insertion(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 801c16a6..8b03793d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -22,5 +22,6 @@ + From aa937893a37f4ac70b1733f8d0d466742c7f5556 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 15:53:13 -0600 Subject: [PATCH 149/197] Add composer github token to tests action --- .github/workflows/tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b13a5cf0..2f35167a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,6 +34,9 @@ jobs: extensions: ${{ env.extensions }} coverage: none + - name: Configure Composer GitHub token + run: composer config --global --auth github-oauth.github.com ${{ secrets.COMPOSER_GITHUB_TOKEN }} + - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts From 202afc69d2e229c3846a40f79545fd202708c3fe Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 15:55:31 -0600 Subject: [PATCH 150/197] Update baseline to include error from Laravel Can remove once https://github.com/laravel/framework/pull/56392 is merged. --- phpstan-baseline.neon | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index cc0b5b8c..c31f014e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -73,8 +73,8 @@ parameters: path: src/Database/Attach/File.php - - message: '#^Call to an undefined method Winter\\Storm\\Database\\Behaviors\\Encryptable\:\:propertyExists\(\)\.$#' - identifier: method.notFound + message: '#^PHPDoc tag @var with type Winter\\Storm\\Database\\Model is not subtype of native type \$this\(Winter\\Storm\\Database\\Behaviors\\Encryptable\)\.$#' + identifier: varTag.nativeType count: 1 path: src/Database/Behaviors/Encryptable.php @@ -84,6 +84,12 @@ parameters: count: 1 path: src/Database/Capsule/Manager.php + - + message: '#^Parameter \#1 \$resolver of method Illuminate\\Database\\Schema\\Builder\:\:blueprintResolver\(\) expects Closure\(string, Closure, string\)\: Illuminate\\Database\\Schema\\Blueprint, Closure\(Illuminate\\Database\\Connection, string, Closure\|null\)\: Winter\\Storm\\Database\\Schema\\Blueprint given\.$#' + identifier: argument.type + count: 1 + path: src/Database/DatabaseServiceProvider.php + - message: '#^Call to an undefined method Illuminate\\Database\\ConnectionInterface\:\:getName\(\)\.$#' identifier: method.notFound From 2407b8980a4d617bd30bdba0eabdb53417266f15 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 16:00:31 -0600 Subject: [PATCH 151/197] Remove unused phpstan stub file & scan all files with composer analyze Related https://github.com/wintercms/storm/commit/6963a35392a930b6cd195c20cacb9752ce442224#diff-3a2d6cd3e517fc9e1588265307572f01a9cb367b21e8010dd3540c2e7697b727 --- .github/workflows/code-analysis.yaml | 3 +++ composer.json | 4 ++-- phpstan.neon | 2 -- tests/stubs/Facades.stub | 15 --------------- 4 files changed, 5 insertions(+), 19 deletions(-) delete mode 100644 tests/stubs/Facades.stub diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index f0c6fa43..2f945026 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -30,6 +30,9 @@ jobs: extensions: ${{ env.extensions }} coverage: none + - name: Configure Composer GitHub token + run: composer config --global --auth github-oauth.github.com ${{ secrets.COMPOSER_GITHUB_TOKEN }} + - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts diff --git a/composer.json b/composer.json index 04871f1e..00322f24 100644 --- a/composer.json +++ b/composer.json @@ -101,8 +101,8 @@ "test": "./vendor/bin/phpunit ./tests", "lint": "parallel-lint --exclude vendor .", "sniff": "phpcs --colors -nq --report=\"full\" --extensions=\"php\"", - "analyze": "phpstan analyse --memory-limit=2G src", - "baseline": "phpstan analyse --memory-limit=2G src --generate-baseline" + "analyze": "phpstan analyse --memory-limit=2G", + "baseline": "phpstan analyse --memory-limit=2G --generate-baseline" }, "extra": { "branch-alias": { diff --git a/phpstan.neon b/phpstan.neon index 4cf8b5d9..2e23a5b1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -17,6 +17,4 @@ parameters: databaseMigrationsPath: - src/Auth/Migrations - src/Database/Migrations - stubFiles: - - tests/stubs/Facades.stub treatPhpDocTypesAsCertain: false diff --git a/tests/stubs/Facades.stub b/tests/stubs/Facades.stub deleted file mode 100644 index 7567c11c..00000000 --- a/tests/stubs/Facades.stub +++ /dev/null @@ -1,15 +0,0 @@ - Date: Wed, 23 Jul 2025 16:06:56 -0600 Subject: [PATCH 152/197] Fix risky tests Related: https://github.com/laravel/framework/issues/49502 --- tests/Translation/FileLoaderTest.php | 2 ++ tests/Translation/TranslatorTest.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/Translation/FileLoaderTest.php b/tests/Translation/FileLoaderTest.php index 0f1848db..0f4287db 100644 --- a/tests/Translation/FileLoaderTest.php +++ b/tests/Translation/FileLoaderTest.php @@ -1,5 +1,6 @@ Date: Wed, 23 Jul 2025 21:11:17 -0600 Subject: [PATCH 153/197] Fix several PHP & PHPUnit deprecations --- composer.json | 2 +- .../Grammars/MySqlSchemaGrammarTest.php | 6 +- .../Grammars/PostgresSchemaGrammarTest.php | 3 +- .../Grammars/SQLiteSchemaGrammarTest.php | 3 +- .../Grammars/SqlServerSchemaGrammarTest.php | 3 +- tests/Events/DispatcherTest.php | 14 +- tests/Filesystem/FilesystemTest.php | 20 +-- tests/GrammarTestCase.php | 8 +- tests/Halcyon/ValidationTraitTest.php | 10 +- tests/Html/FormBuilderTest.php | 132 ++++-------------- tests/Support/EmitterTest.php | 6 +- tests/Translation/FileLoaderTest.php | 1 + 12 files changed, 70 insertions(+), 138 deletions(-) diff --git a/composer.json b/composer.json index 00322f24..82760c31 100644 --- a/composer.json +++ b/composer.json @@ -98,7 +98,7 @@ } }, "scripts": { - "test": "./vendor/bin/phpunit ./tests", + "test": "./vendor/bin/phpunit", "lint": "parallel-lint --exclude vendor .", "sniff": "phpcs --colors -nq --report=\"full\" --extensions=\"php\"", "analyze": "phpstan analyse --memory-limit=2G", diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index bd0018f5..617af08c 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -2,11 +2,11 @@ namespace Tests\Database\Schema\Grammars; -use Illuminate\Database\Schema\Blueprint; -use Winter\Storm\Database\Schema\Grammars\MySqlGrammar; use Illuminate\Database\Schema\MySqlBuilder; +use Winter\Storm\Database\Schema\Grammars\MySqlGrammar; +use Winter\Storm\Tests\GrammarTestCase; -class MySqlSchemaGrammarTest extends \GrammarTestCase +class MySqlSchemaGrammarTest extends GrammarTestCase { public function setUp(): void { diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index 1e4edb6a..d867e31b 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -4,8 +4,9 @@ use Illuminate\Database\Schema\PostgresBuilder; use Winter\Storm\Database\Schema\Grammars\PostgresGrammar; +use Winter\Storm\Tests\GrammarTestCase; -class PostgresSchemaGrammarTest extends \GrammarTestCase +class PostgresSchemaGrammarTest extends GrammarTestCase { public function setUp(): void { diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index a18f4d6a..29d99b0a 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -4,8 +4,9 @@ use Illuminate\Database\Schema\SQLiteBuilder; use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar; +use Winter\Storm\Tests\GrammarTestCase; -class SQLiteSchemaGrammarTest extends \GrammarTestCase +class SQLiteSchemaGrammarTest extends GrammarTestCase { public function setUp(): void { diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index bbe8e13b..ef9d85bb 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -4,8 +4,9 @@ use Illuminate\Database\Schema\SqlServerBuilder; use Winter\Storm\Database\Schema\Grammars\SqlServerGrammar; +use Winter\Storm\Tests\GrammarTestCase; -class SqlServerSchemaGrammarTest extends \GrammarTestCase +class SqlServerSchemaGrammarTest extends GrammarTestCase { public function setUp(): void { diff --git a/tests/Events/DispatcherTest.php b/tests/Events/DispatcherTest.php index 7884ff4d..99a1a9ff 100644 --- a/tests/Events/DispatcherTest.php +++ b/tests/Events/DispatcherTest.php @@ -1,21 +1,13 @@ filesystem = new Filesystem(); } - /** - * @dataProvider providePathsForIsAbsolutePath - * @see Symfony\Component\Filesystem\Tests\FilesystemTest::testIsAbsolutePath - */ + #[DataProvider('providePathsForIsAbsolutePath')] + /** @see Symfony\Component\Filesystem\Tests\FilesystemTest::testIsAbsolutePath */ public function testIsAbsolutePath($path, $expectedResult) { $result = $this->filesystem->isAbsolutePath($path); @@ -34,9 +34,7 @@ public static function providePathsForIsAbsolutePath() ]; } - /** - * @dataProvider provideSizesForSizeToBytes - */ + #[DataProvider('provideSizesForSizeToBytes')] public function testSizeToBytes($input, $expectedBytes) { $result = $this->filesystem->sizeToBytes($input); @@ -60,9 +58,7 @@ public static function provideSizesForSizeToBytes() ]; } - /** - * @dataProvider provideBytesForSizeToString - */ + #[DataProvider('provideBytesForSizeToString')] public function testSizeToString($bytes, $expectedString) { $result = $this->filesystem->sizeToString($bytes); @@ -84,9 +80,7 @@ public static function provideBytesForSizeToString() ]; } - /** - * @dataProvider provideInvalidSizesForSizeToBytes - */ + #[DataProvider('provideInvalidSizesForSizeToBytes')] public function testSizeToBytesInvalidInput($input) { $this->expectException(\InvalidArgumentException::class); diff --git a/tests/GrammarTestCase.php b/tests/GrammarTestCase.php index 90e67ecd..f3e91119 100644 --- a/tests/GrammarTestCase.php +++ b/tests/GrammarTestCase.php @@ -11,6 +11,8 @@ class GrammarTestCase extends TestCase { protected $connection = null; + protected $grammarClass = null; + protected $builderClass = null; public function setUp(): void { @@ -34,7 +36,7 @@ public function tearDown(): void public function getConnection() { - return $this->connection(); + return ($this->connection)(); } protected function getBlueprint(string $table) @@ -49,7 +51,7 @@ protected function getBlueprint(string $table) protected function getSchemaGrammar() { - return new $this->grammarClass($this->connection); + return new ($this->grammarClass)($this->connection); } protected function getSchemaBuilder($blueprint) @@ -58,7 +60,7 @@ protected function getSchemaBuilder($blueprint) ->shouldReceive('getColumns')->andReturn($blueprint->getColumns()) ->shouldReceive('getForeignKeys')->andReturn([]) ->shouldReceive('getIndexes')->andReturn([]) - ->shouldReceive('parseSchemaAndTable')->andReturnNull() + ->shouldReceive('parseSchemaAndTable')->andReturn([null, $blueprint->getTable()]) ->getMock(); } diff --git a/tests/Halcyon/ValidationTraitTest.php b/tests/Halcyon/ValidationTraitTest.php index 35b6db62..300f36da 100644 --- a/tests/Halcyon/ValidationTraitTest.php +++ b/tests/Halcyon/ValidationTraitTest.php @@ -1,10 +1,17 @@ getMockForTrait('Winter\Storm\Halcyon\Traits\Validation'); + $mock = new ValidationTraitHost(); $rules = [ 'field' => 'required', @@ -16,6 +23,7 @@ public function testArrayFieldNames() 'field][six' => 'required|string', 'field]seven' => 'required|string', ]; + $rules = self::callProtectedMethod($mock, 'processRuleFieldNames', [$rules]); $this->assertEquals([ diff --git a/tests/Html/FormBuilderTest.php b/tests/Html/FormBuilderTest.php index 2432b00a..feff3f6d 100644 --- a/tests/Html/FormBuilderTest.php +++ b/tests/Html/FormBuilderTest.php @@ -1,17 +1,18 @@ formBuilder = new FormBuilder($htmlBuilder, $generator); } - /** - * @testdox can generate a form open tag. - */ - public function testFormOpen() + public function test_it_can_generate_a_form_open_tag() { $result = $this->formBuilder->open(); @@ -46,10 +44,7 @@ public function testFormOpen() $this->assertElementDoesntHaveAttribute('enctype', $result); } - /** - * @testdox can generate a form open tag with method "GET". - */ - public function testFormOpenMethodGet() + public function test_it_can_generate_a_form_open_tag_with_method_GET() { $result = $this->formBuilder->open([ 'method' => 'GET' @@ -61,10 +56,7 @@ public function testFormOpenMethodGet() $this->assertElementDoesntHaveAttribute('enctype', $result); } - /** - * @testdox can generate a form open tag and accept file uploads. - */ - public function testFormOpenFiles() + public function test_it_can_generate_a_form_open_tag_and_accept_file_uploads() { $result = $this->formBuilder->open([ 'files' => true, @@ -76,10 +68,7 @@ public function testFormOpenFiles() $this->assertElementAttributeEquals('enctype', 'multipart/form-data', $result); } - /** - * @testdox can generate a form open tag and have custom attributes. - */ - public function testFormOpenCustomAttributes() + public function test_it_can_generate_a_form_open_tag_and_have_custom_attributes() { $result = $this->formBuilder->open([ 'data-my-attribute' => 'my-value', @@ -94,10 +83,7 @@ public function testFormOpenCustomAttributes() $this->assertElementAttributeEquals('class', 'boss-form', $result); } - /** - * @testdox can generate a form open tag with a data attribute AJAX request. - */ - public function testFormAjax() + public function test_it_can_generate_a_form_open_tag_with_a_data_attribute_AJAX_request() { $result = $this->formBuilder->ajax('onSave'); @@ -107,10 +93,7 @@ public function testFormAjax() $this->assertElementAttributeEquals('data-request', 'onSave', $result); } - /** - * @testdox can generate a form open tag with a data attribute AJAX request to a different target. - */ - public function testFormAjaxTarget() + public function test_it_can_generate_a_form_open_tag_with_a_data_attribute_ajax_request_to_a_different_target() { $result = $this->formBuilder->ajax(['myComponent', 'onSave']); @@ -120,10 +103,7 @@ public function testFormAjaxTarget() $this->assertElementAttributeEquals('data-request', 'myComponent::onSave', $result); } - /** - * @testdox can generate a form open tag with a data attribute AJAX request and accept files. - */ - public function testFormAjaxFiles() + public function test_it_can_generate_a_form_open_tag_with_a_data_attribute_ajax_request_and_accept_files() { $result = $this->formBuilder->ajax('onSave', [ 'files' => true, @@ -137,23 +117,15 @@ public function testFormAjaxFiles() $this->assertElementAttributeEquals('enctype', 'multipart/form-data', $result); } - /** - * @testdox can generate a form close tag. - */ - public function testClose() + public function test_it_can_generate_a_form_close_tag() { $result = $this->formBuilder->close(); - $this->assertEquals('', $result); } - /** - * @testdox can create a text input. The text input will not have an ID. - */ - public function testFormInputText() + public function test_it_can_create_a_text_input() { $result = $this->formBuilder->input(type: 'text', name: 'my-name', value: 'my value'); - $this->assertElementIs('input', $result); $this->assertElementDoesntHaveAttribute('id', $result); $this->assertElementAttributeEquals('name', 'my-name', $result); @@ -161,10 +133,7 @@ public function testFormInputText() $this->assertElementAttributeEquals('value', 'my value', $result); } - /** - * @testdox can create a text input with a corresponding label. The text input will have an ID. - */ - public function testFormInputTextWithLabel() + public function test_it_can_create_a_text_input_with_a_corresponding_label() { $result = $this->formBuilder->label(name: 'my-input', value: 'my input label'); $result = $this->formBuilder->input(type: 'text', name: 'my-input', value: 'my value'); @@ -176,13 +145,9 @@ public function testFormInputTextWithLabel() $this->assertElementAttributeEquals('value', 'my value', $result); } - /** - * @testdox accepts an empty ID and sets the ID attribute to empty. - */ - public function testFormInputTextIdEmpty() + public function test_it_accepts_an_empty_id_and_sets_the_id_attribute_to_empty() { $result = $this->formBuilder->input(type: 'text', name: 'my-name', value: 'my value', options: ['id' => '']); - $this->assertElementIs('input', $result); $this->assertElementAttributeEquals('id', '', $result); $this->assertElementAttributeEquals('name', 'my-name', $result); @@ -190,13 +155,9 @@ public function testFormInputTextIdEmpty() $this->assertElementAttributeEquals('value', 'my value', $result); } - /** - * @testdox ignores an ID that is "null". - */ - public function testFormInputTextNull() + public function test_it_ignores_an_id_that_is_null() { $result = $this->formBuilder->input(type: 'text', name: 'my-name', value: 'my value', options: ['id' => null]); - $this->assertElementIs('input', $result); $this->assertElementDoesntHaveAttribute('id', $result); $this->assertElementAttributeEquals('name', 'my-name', $result); @@ -204,13 +165,9 @@ public function testFormInputTextNull() $this->assertElementAttributeEquals('value', 'my value', $result); } - /** - * @testdox ignores an ID that is boolean "false". - */ - public function testFormInputTextFalse() + public function test_it_ignores_an_id_that_is_boolean_false() { $result = $this->formBuilder->input(type: 'text', name: 'my-name', value: 'my value', options: ['id' => false]); - $this->assertElementIs('input', $result); $this->assertElementDoesntHaveAttribute('id', $result); $this->assertElementAttributeEquals('name', 'my-name', $result); @@ -218,13 +175,9 @@ public function testFormInputTextFalse() $this->assertElementAttributeEquals('value', 'my value', $result); } - /** - * @testdox accepts an ID that is an integer of zero. - */ - public function testFormInputTextZero() + public function test_it_accepts_an_id_that_is_an_integer_of_zero() { $result = $this->formBuilder->input(type: 'text', name: 'my-name', value: 'my value', options: ['id' => 0]); - $this->assertElementIs('input', $result); $this->assertElementAttributeEquals('id', '0', $result); $this->assertElementAttributeEquals('name', 'my-name', $result); @@ -232,7 +185,7 @@ public function testFormInputTextZero() $this->assertElementAttributeEquals('value', 'my value', $result); } - public function testFormInputBooleanAttribute() + public function test_it_can_create_a_required_input() { $result = $this->formBuilder->input(type: 'text', name: 'my-name', value: 'my value', options: ['required']); @@ -242,13 +195,9 @@ public function testFormInputBooleanAttribute() $this->assertElementHasAttribute('required', $result); } - /** - * @testdox can create a text input of type "email". - */ - public function testFormInputEmail() + public function test_it_can_create_a_text_input_of_type_email() { $result = $this->formBuilder->input(type: 'email', name: 'my-input', value: 'my value'); - $this->assertElementIs('input', $result); $this->assertElementDoesntHaveAttribute('id', $result); $this->assertElementAttributeEquals('name', 'my-input', $result); @@ -257,7 +206,6 @@ public function testFormInputEmail() $result = $this->formBuilder->label(name: 'my-input', value: 'my input label'); $result = $this->formBuilder->email(name: 'my-input', value: 'my value'); - $this->assertElementIs('input', $result); $this->assertElementAttributeEquals('id', 'my-input', $result); $this->assertElementAttributeEquals('name', 'my-input', $result); @@ -265,56 +213,38 @@ public function testFormInputEmail() $this->assertElementAttributeEquals('value', 'my value', $result); } - /** - * @testdox can create a submit button. - * @see https://github.com/wintercms/winter/issues/864 - */ - public function testSubmit() + /** @see https://github.com/wintercms/winter/issues/864 */ + public function test_it_can_create_a_submit_button() { $result = $this->formBuilder->submit(value: 'Apply'); - $this->assertElementIs('input', $result); $this->assertElementAttributeEquals('type', 'submit', $result); $this->assertElementAttributeEquals('value', 'Apply', $result); } - /** - * @testdox can create a submit button with additional classes. - * @see https://github.com/wintercms/winter/issues/864 - */ - public function testSubmitWithClasses() + /** @see https://github.com/wintercms/winter/issues/864 */ + public function test_it_can_create_a_submit_button_with_additional_classes() { $result = $this->formBuilder->submit(value: 'Apply', options: ['class' => 'btn btn-primary']); - $this->assertElementIs('input', $result); $this->assertElementAttributeEquals('type', 'submit', $result); $this->assertElementAttributeEquals('class', 'btn btn-primary', $result); $this->assertElementAttributeEquals('value', 'Apply', $result); } - /** - * @testdox can create a standard button. - * @see https://github.com/wintercms/winter/issues/864 - */ - public function testButton() + /** @see https://github.com/wintercms/winter/issues/864 */ + public function test_it_can_create_a_standard_button() { $result = $this->formBuilder->button(value: 'Apply'); - $this->assertElementIs('button', $result); $this->assertElementAttributeEquals('type', 'button', $result); $this->assertElementContainsText('Apply', $result); } - /** - * @testdox can create a standard button that submits the form. - * @see https://github.com/wintercms/winter/issues/864 - */ - public function testButtonSubmitType() + /** @see https://github.com/wintercms/winter/issues/864 */ + public function test_it_can_create_a_standard_button_that_submits_the_form() { - $result = $this->formBuilder->button(value: 'Apply', options: [ - 'type' => 'submit', - ]); - + $result = $this->formBuilder->button(value: 'Apply', options: ['type' => 'submit']); $this->assertElementIs('button', $result); $this->assertElementAttributeEquals('type', 'submit', $result); $this->assertElementContainsText('Apply', $result); diff --git a/tests/Support/EmitterTest.php b/tests/Support/EmitterTest.php index a8a1c272..fab8f0e3 100644 --- a/tests/Support/EmitterTest.php +++ b/tests/Support/EmitterTest.php @@ -1,6 +1,7 @@ traitObject = $this->getObjectForTrait($traitName); + $this->traitObject = new class { + use \Winter\Storm\Support\Traits\Emitter; + }; } // diff --git a/tests/Translation/FileLoaderTest.php b/tests/Translation/FileLoaderTest.php index 0f4287db..c1383635 100644 --- a/tests/Translation/FileLoaderTest.php +++ b/tests/Translation/FileLoaderTest.php @@ -4,6 +4,7 @@ use Winter\Storm\Filesystem\Filesystem; use Winter\Storm\Translation\FileLoader; use Mockery as m; +use Winter\Storm\Tests\TestCase; /** * These tests were adapted from the Laravel 9.x test cases to ensure our functionality still From 4d1e9e0740eea385fda678f45611e01fd6231ef5 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Wed, 23 Jul 2025 21:18:18 -0600 Subject: [PATCH 154/197] Remove requirement for bootstrap file when testing --- phpunit.xml.dist | 5 ----- tests/Config/ConfigTest.php | 2 +- tests/Config/ConfigWriterTest.php | 2 +- tests/Database/Attach/ResizerTest.php | 2 +- tests/Database/Behaviors/EncryptableBehaviorTest.php | 2 +- tests/Database/Concerns/HasRelationshipsTest.php | 2 +- tests/Database/DongleTest.php | 2 +- tests/Database/Factories/FactoryTest.php | 2 +- tests/Database/ModelTest.php | 2 +- tests/Database/MorphPivotTest.php | 2 +- tests/Database/QueryBuilderTest.php | 2 +- tests/Database/Relations/AttachManyTest.php | 2 +- tests/Database/Relations/AttachOneTest.php | 2 +- tests/Database/Relations/BelongsToManyTest.php | 2 +- tests/Database/Relations/BelongsToTest.php | 2 +- tests/Database/Relations/DuplicateRelationTest.php | 2 +- tests/Database/Relations/DynamicRelationTest.php | 2 +- tests/Database/Relations/HasManyTest.php | 2 +- tests/Database/Relations/HasManyThroughTest.php | 2 +- tests/Database/Relations/HasOneTest.php | 2 +- tests/Database/Relations/HasOneThroughTest.php | 2 +- tests/Database/Relations/MorphManyTest.php | 2 +- tests/Database/Relations/MorphOneTest.php | 2 +- .../Schema/Grammars/MySqlSchemaGrammarTest.php | 2 +- .../Schema/Grammars/PostgresSchemaGrammarTest.php | 2 +- .../Schema/Grammars/SQLiteSchemaGrammarTest.php | 2 +- .../Schema/Grammars/SqlServerSchemaGrammarTest.php | 2 +- tests/Database/Traits/ArraySourceTest.php | 2 +- tests/Database/Traits/DeferredBindingTest.php | 2 +- tests/Database/Traits/EncryptableTest.php | 2 +- tests/Database/Traits/HasSortableRelationsTest.php | 2 +- tests/Database/Traits/NestedTreeTest.php | 2 +- tests/Database/Traits/NullableTest.php | 2 +- tests/Database/Traits/PurgeableTraitTest.php | 2 +- tests/Database/Traits/RevisionableTest.php | 2 +- tests/Database/Traits/SimpleTreeTest.php | 2 +- tests/Database/Traits/SluggableTest.php | 2 +- tests/Database/Traits/SoftDeleteTest.php | 2 +- tests/Database/Traits/SortableTest.php | 2 +- tests/Database/Traits/ValidationTest.php | 2 +- tests/Database/UpdaterTest.php | 2 +- tests/DbTestCase.php | 2 +- tests/Events/DispatcherTest.php | 3 +-- tests/Extension/ExtendableTest.php | 2 +- tests/Extension/ExtendableTraitTest.php | 10 +++++----- tests/Extension/ExtensionTest.php | 2 +- tests/Filesystem/FilesystemTest.php | 3 +-- tests/Filesystem/PathResolverTest.php | 2 +- tests/Foundation/ApplicationTest.php | 2 +- .../Http/Middleware/CheckForTrustedHostTest.php | 2 +- .../Http/Middleware/CheckForTrustedProxiesTest.php | 2 +- tests/Foundation/ProviderRepositoryTest.php | 2 +- tests/Halcyon/DatasourceResolverTest.php | 2 +- tests/Halcyon/HalcyonModelTest.php | 2 +- tests/Halcyon/MemoryRepositoryTest.php | 2 +- tests/Halcyon/SectionParserTest.php | 2 +- tests/Halcyon/ValidationTraitTest.php | 4 +--- tests/Html/BlockBuilderTest.php | 2 +- tests/Html/FormBuilderTest.php | 3 +-- tests/Html/HtmlBuilderTest.php | 2 +- tests/Html/HtmlHelperTest.php | 2 +- tests/Mail/MailerTest.php | 2 +- tests/Network/HttpTest.php | 2 +- tests/Parse/ArrayFileTest.php | 2 +- tests/Parse/BracketTest.php | 2 +- tests/Parse/EnvFileTest.php | 2 +- tests/Parse/IniTest.php | 2 +- tests/Parse/MarkdownTest.php | 2 +- tests/Parse/SyntaxFieldParserTest.php | 2 +- tests/Parse/SyntaxParserTest.php | 2 +- tests/Parse/YamlTest.php | 2 +- tests/Router/RouterHelperTest.php | 2 +- tests/Router/RouterTest.php | 2 +- tests/Router/RoutingUrlGeneratorTest.php | 2 +- tests/Router/UrlGeneratorTest.php | 2 +- tests/Scaffold/ScaffoldBaseTest.php | 2 +- tests/Support/ArrTest.php | 2 +- tests/Support/ClassLoaderTest.php | 2 +- tests/Support/CountableTest.php | 2 +- tests/Support/EmitterTest.php | 3 +-- tests/Support/EventFakeTest.php | 2 +- tests/Support/ExtensionAndEmitterSerializationTest.php | 2 +- tests/Support/HelpersTest.php | 2 +- tests/Support/MailFakeTest.php | 2 +- tests/Support/StrTest.php | 2 +- tests/Support/SvgTest.php | 2 +- tests/Translation/FileLoaderTest.php | 3 +-- tests/Translation/TranslatorTest.php | 2 +- tests/Validation/EmailValidationTest.php | 2 +- tests/Validation/RuleObjectTest.php | 2 +- tests/bootstrap.php | 5 ----- tests/fixtures/events/EventTest.php | 2 +- 92 files changed, 94 insertions(+), 111 deletions(-) delete mode 100644 tests/bootstrap.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 8b03793d..680a1778 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,12 +1,7 @@ diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php index 90e7e985..279e0010 100644 --- a/tests/Config/ConfigTest.php +++ b/tests/Config/ConfigTest.php @@ -4,7 +4,7 @@ use Winter\Storm\Config\FileLoader; use Illuminate\Filesystem\Filesystem; -class ConfigTest extends TestCase +class ConfigTest extends \Winter\Storm\Tests\TestCase { protected $config; diff --git a/tests/Config/ConfigWriterTest.php b/tests/Config/ConfigWriterTest.php index 16359bf8..0aff67de 100644 --- a/tests/Config/ConfigWriterTest.php +++ b/tests/Config/ConfigWriterTest.php @@ -2,7 +2,7 @@ use Winter\Storm\Config\ConfigWriter; -class ConfigWriterTest extends TestCase +class ConfigWriterTest extends \Winter\Storm\Tests\TestCase { public function testToFile() { diff --git a/tests/Database/Attach/ResizerTest.php b/tests/Database/Attach/ResizerTest.php index b5eaab0d..6680dd60 100644 --- a/tests/Database/Attach/ResizerTest.php +++ b/tests/Database/Attach/ResizerTest.php @@ -3,7 +3,7 @@ use AssertGD\DiffCalculator\ScaledRgbChannels; use Winter\Storm\Database\Attach\Resizer; -class ResizerTest extends TestCase +class ResizerTest extends \Winter\Storm\Tests\TestCase { use \AssertGD\GDAssertTrait; diff --git a/tests/Database/Behaviors/EncryptableBehaviorTest.php b/tests/Database/Behaviors/EncryptableBehaviorTest.php index 54dfd4a8..a0023583 100644 --- a/tests/Database/Behaviors/EncryptableBehaviorTest.php +++ b/tests/Database/Behaviors/EncryptableBehaviorTest.php @@ -2,7 +2,7 @@ use Illuminate\Encryption\Encrypter; -class EncryptableBehaviorTest extends DbTestCase +class EncryptableBehaviorTest extends \Winter\Storm\Tests\DbTestCase { const TEST_CRYPT_KEY = 'gBmM1S5bxZ5ePRj5'; diff --git a/tests/Database/Concerns/HasRelationshipsTest.php b/tests/Database/Concerns/HasRelationshipsTest.php index 941eefbd..6457b5b6 100644 --- a/tests/Database/Concerns/HasRelationshipsTest.php +++ b/tests/Database/Concerns/HasRelationshipsTest.php @@ -21,7 +21,7 @@ use Winter\Storm\Tests\Database\Fixtures\User; use Winter\Storm\Tests\DbTestCase; -class HasRelationshipsTest extends DbTestCase +class HasRelationshipsTest extends \Winter\Storm\Tests\DbTestCase { public function testHasRelation() { diff --git a/tests/Database/DongleTest.php b/tests/Database/DongleTest.php index a75a58d8..7965e4c3 100644 --- a/tests/Database/DongleTest.php +++ b/tests/Database/DongleTest.php @@ -2,7 +2,7 @@ use Winter\Storm\Database\Dongle; -class DongleTest extends TestCase +class DongleTest extends \Winter\Storm\Tests\TestCase { public function testSqliteParseConcat() { diff --git a/tests/Database/Factories/FactoryTest.php b/tests/Database/Factories/FactoryTest.php index 4c6b23f7..4ebf27e7 100644 --- a/tests/Database/Factories/FactoryTest.php +++ b/tests/Database/Factories/FactoryTest.php @@ -2,7 +2,7 @@ use Winter\Storm\Database\Factories\Factory; -class FactoryTest extends TestCase +class FactoryTest extends \Winter\Storm\Tests\TestCase { public function testResolveFactoryName() { diff --git a/tests/Database/ModelTest.php b/tests/Database/ModelTest.php index a5cb2021..94ed4064 100644 --- a/tests/Database/ModelTest.php +++ b/tests/Database/ModelTest.php @@ -7,7 +7,7 @@ use Winter\Storm\Database\Model; use Winter\Storm\Tests\DbTestCase; -class ModelTest extends DbTestCase +class ModelTest extends \Winter\Storm\Tests\DbTestCase { protected $seeded = []; diff --git a/tests/Database/MorphPivotTest.php b/tests/Database/MorphPivotTest.php index 2bd93377..121973f2 100644 --- a/tests/Database/MorphPivotTest.php +++ b/tests/Database/MorphPivotTest.php @@ -6,7 +6,7 @@ use Winter\Storm\Database\MorphPivot; use Winter\Storm\Tests\Database\Fixtures\CustomMorphPivot; -class MorphPivotTest extends \DbTestCase +class MorphPivotTest extends \Winter\Storm\Tests\DbTestCase { public function setUp(): void { diff --git a/tests/Database/QueryBuilderTest.php b/tests/Database/QueryBuilderTest.php index 2f640992..a916ce81 100644 --- a/tests/Database/QueryBuilderTest.php +++ b/tests/Database/QueryBuilderTest.php @@ -10,7 +10,7 @@ use Winter\Storm\Database\Query\Grammars\SqlServerGrammar; use Winter\Storm\Database\QueryBuilder; -class QueryBuilderTest extends TestCase +class QueryBuilderTest extends \Winter\Storm\Tests\TestCase { public function testSelectConcat() { diff --git a/tests/Database/Relations/AttachManyTest.php b/tests/Database/Relations/AttachManyTest.php index ae3e7592..ef283bbe 100644 --- a/tests/Database/Relations/AttachManyTest.php +++ b/tests/Database/Relations/AttachManyTest.php @@ -7,7 +7,7 @@ use Winter\Storm\Tests\Database\Fixtures\User; use Winter\Storm\Tests\DbTestCase; -class AttachManyTest extends DbTestCase +class AttachManyTest extends \Winter\Storm\Tests\DbTestCase { public function testDeleteFlagDestroyRelationship() { diff --git a/tests/Database/Relations/AttachOneTest.php b/tests/Database/Relations/AttachOneTest.php index 2ed5e47d..2f4bcc2f 100644 --- a/tests/Database/Relations/AttachOneTest.php +++ b/tests/Database/Relations/AttachOneTest.php @@ -9,7 +9,7 @@ use Winter\Storm\Tests\Database\Fixtures\User; use Winter\Storm\Tests\DbTestCase; -class AttachOneTest extends DbTestCase +class AttachOneTest extends \Winter\Storm\Tests\DbTestCase { public function testSetRelationValue() { diff --git a/tests/Database/Relations/BelongsToManyTest.php b/tests/Database/Relations/BelongsToManyTest.php index f149f54c..eecd174c 100644 --- a/tests/Database/Relations/BelongsToManyTest.php +++ b/tests/Database/Relations/BelongsToManyTest.php @@ -10,7 +10,7 @@ use Winter\Storm\Tests\Database\Fixtures\Author; use Winter\Storm\Tests\DbTestCase; -class BelongsToManyTest extends DbTestCase +class BelongsToManyTest extends \Winter\Storm\Tests\DbTestCase { public function testSetRelationValue() { diff --git a/tests/Database/Relations/BelongsToTest.php b/tests/Database/Relations/BelongsToTest.php index 614eaa0b..1aafd823 100644 --- a/tests/Database/Relations/BelongsToTest.php +++ b/tests/Database/Relations/BelongsToTest.php @@ -7,7 +7,7 @@ use Winter\Storm\Tests\Database\Fixtures\Post; use Winter\Storm\Tests\DbTestCase; -class BelongsToTest extends DbTestCase +class BelongsToTest extends \Winter\Storm\Tests\DbTestCase { public function testSetRelationValue() { diff --git a/tests/Database/Relations/DuplicateRelationTest.php b/tests/Database/Relations/DuplicateRelationTest.php index 106cddb5..815a10f9 100644 --- a/tests/Database/Relations/DuplicateRelationTest.php +++ b/tests/Database/Relations/DuplicateRelationTest.php @@ -8,7 +8,7 @@ use Winter\Storm\Tests\Database\Fixtures\DuplicateRelationNote; use Winter\Storm\Tests\DbTestCase; -class DuplicateRelationTest extends DbTestCase +class DuplicateRelationTest extends \Winter\Storm\Tests\DbTestCase { /** Note that we cannot stop people getting the method-style relation in this instance */ public function testMethodRelationWhenPropertyRelationExists() diff --git a/tests/Database/Relations/DynamicRelationTest.php b/tests/Database/Relations/DynamicRelationTest.php index 5989f3fb..6c1fb8fd 100644 --- a/tests/Database/Relations/DynamicRelationTest.php +++ b/tests/Database/Relations/DynamicRelationTest.php @@ -8,7 +8,7 @@ use Winter\Storm\Tests\Database\Fixtures\Post; use Winter\Storm\Tests\DbTestCase; -class DynamicRelationTest extends DbTestCase +class DynamicRelationTest extends \Winter\Storm\Tests\DbTestCase { public function testCreateDynamicRelation() { diff --git a/tests/Database/Relations/HasManyTest.php b/tests/Database/Relations/HasManyTest.php index 564684f2..e1ed57d9 100644 --- a/tests/Database/Relations/HasManyTest.php +++ b/tests/Database/Relations/HasManyTest.php @@ -8,7 +8,7 @@ use Winter\Storm\Tests\Database\Fixtures\Post; use Winter\Storm\Tests\DbTestCase; -class HasManyTest extends DbTestCase +class HasManyTest extends \Winter\Storm\Tests\DbTestCase { public function testSetRelationValue() { diff --git a/tests/Database/Relations/HasManyThroughTest.php b/tests/Database/Relations/HasManyThroughTest.php index ffaa5bf9..76806806 100644 --- a/tests/Database/Relations/HasManyThroughTest.php +++ b/tests/Database/Relations/HasManyThroughTest.php @@ -9,7 +9,7 @@ use Winter\Storm\Tests\Database\Fixtures\Post; use Winter\Storm\Tests\DbTestCase; -class HasManyThroughTest extends DbTestCase +class HasManyThroughTest extends \Winter\Storm\Tests\DbTestCase { public function testGet() { diff --git a/tests/Database/Relations/HasOneTest.php b/tests/Database/Relations/HasOneTest.php index fe76b67a..9127bdfb 100644 --- a/tests/Database/Relations/HasOneTest.php +++ b/tests/Database/Relations/HasOneTest.php @@ -7,7 +7,7 @@ use Winter\Storm\Tests\Database\Fixtures\Phone; use Winter\Storm\Tests\DbTestCase; -class HasOneTest extends DbTestCase +class HasOneTest extends \Winter\Storm\Tests\DbTestCase { public function testSetRelationValue() { diff --git a/tests/Database/Relations/HasOneThroughTest.php b/tests/Database/Relations/HasOneThroughTest.php index d27c680f..0e41d5f9 100644 --- a/tests/Database/Relations/HasOneThroughTest.php +++ b/tests/Database/Relations/HasOneThroughTest.php @@ -8,7 +8,7 @@ use Winter\Storm\Tests\Database\Fixtures\User; use Winter\Storm\Tests\DbTestCase; -class HasOneThroughTest extends DbTestCase +class HasOneThroughTest extends \Winter\Storm\Tests\DbTestCase { public function testGet() { diff --git a/tests/Database/Relations/MorphManyTest.php b/tests/Database/Relations/MorphManyTest.php index 9b3dbc2f..ebad3550 100644 --- a/tests/Database/Relations/MorphManyTest.php +++ b/tests/Database/Relations/MorphManyTest.php @@ -10,7 +10,7 @@ use Winter\Storm\Tests\Database\Fixtures\EventLog; use Winter\Storm\Tests\DbTestCase; -class MorphManyTest extends DbTestCase +class MorphManyTest extends \Winter\Storm\Tests\DbTestCase { public function testSetRelationValue() { diff --git a/tests/Database/Relations/MorphOneTest.php b/tests/Database/Relations/MorphOneTest.php index 005e334f..2d84ed11 100644 --- a/tests/Database/Relations/MorphOneTest.php +++ b/tests/Database/Relations/MorphOneTest.php @@ -8,7 +8,7 @@ use Winter\Storm\Tests\Database\Fixtures\Meta; use Winter\Storm\Tests\DbTestCase; -class MorphOneTest extends DbTestCase +class MorphOneTest extends \Winter\Storm\Tests\DbTestCase { public function testSetRelationValue() { diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index 617af08c..a9111971 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -6,7 +6,7 @@ use Winter\Storm\Database\Schema\Grammars\MySqlGrammar; use Winter\Storm\Tests\GrammarTestCase; -class MySqlSchemaGrammarTest extends GrammarTestCase +class MySqlSchemaGrammarTest extends \Winter\Storm\Tests\GrammarTestCase { public function setUp(): void { diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index d867e31b..1e312462 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -6,7 +6,7 @@ use Winter\Storm\Database\Schema\Grammars\PostgresGrammar; use Winter\Storm\Tests\GrammarTestCase; -class PostgresSchemaGrammarTest extends GrammarTestCase +class PostgresSchemaGrammarTest extends \Winter\Storm\Tests\GrammarTestCase { public function setUp(): void { diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index 29d99b0a..02d0252a 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -6,7 +6,7 @@ use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar; use Winter\Storm\Tests\GrammarTestCase; -class SQLiteSchemaGrammarTest extends GrammarTestCase +class SQLiteSchemaGrammarTest extends \Winter\Storm\Tests\GrammarTestCase { public function setUp(): void { diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index ef9d85bb..2bef14a3 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -6,7 +6,7 @@ use Winter\Storm\Database\Schema\Grammars\SqlServerGrammar; use Winter\Storm\Tests\GrammarTestCase; -class SqlServerSchemaGrammarTest extends GrammarTestCase +class SqlServerSchemaGrammarTest extends \Winter\Storm\Tests\GrammarTestCase { public function setUp(): void { diff --git a/tests/Database/Traits/ArraySourceTest.php b/tests/Database/Traits/ArraySourceTest.php index e808c405..c9445230 100644 --- a/tests/Database/Traits/ArraySourceTest.php +++ b/tests/Database/Traits/ArraySourceTest.php @@ -2,7 +2,7 @@ use Illuminate\Filesystem\Filesystem; -class ArraySourceTest extends DbTestCase +class ArraySourceTest extends \Winter\Storm\Tests\DbTestCase { public function setUp(): void { diff --git a/tests/Database/Traits/DeferredBindingTest.php b/tests/Database/Traits/DeferredBindingTest.php index cefe99d8..2019437c 100644 --- a/tests/Database/Traits/DeferredBindingTest.php +++ b/tests/Database/Traits/DeferredBindingTest.php @@ -8,7 +8,7 @@ use Winter\Storm\Tests\Database\Fixtures\Author; use Winter\Storm\Tests\DbTestCase; -class DeferredBindingTest extends DbTestCase +class DeferredBindingTest extends \Winter\Storm\Tests\DbTestCase { public function testNegatedBinding() { diff --git a/tests/Database/Traits/EncryptableTest.php b/tests/Database/Traits/EncryptableTest.php index 5d2de258..50b05eac 100644 --- a/tests/Database/Traits/EncryptableTest.php +++ b/tests/Database/Traits/EncryptableTest.php @@ -2,7 +2,7 @@ use Illuminate\Encryption\Encrypter; -class EncryptableTest extends DbTestCase +class EncryptableTest extends \Winter\Storm\Tests\DbTestCase { const TEST_CRYPT_KEY = 'gBmM1S5bxZ5ePRj5'; diff --git a/tests/Database/Traits/HasSortableRelationsTest.php b/tests/Database/Traits/HasSortableRelationsTest.php index b9cf350b..9a752d4c 100644 --- a/tests/Database/Traits/HasSortableRelationsTest.php +++ b/tests/Database/Traits/HasSortableRelationsTest.php @@ -1,6 +1,6 @@ Date: Wed, 23 Jul 2025 21:44:33 -0600 Subject: [PATCH 155/197] Fix more PHPUnit deprecations --- .github/workflows/tests.yml | 2 +- src/Support/Testing/MocksClassLoader.php | 2 +- tests/Database/QueryBuilderTest.php | 10 ++-------- tests/Database/Traits/ValidationTest.php | 4 +++- tests/Halcyon/HalcyonModelTest.php | 20 +++++++------------- tests/Router/RoutingUrlGeneratorTest.php | 2 +- 6 files changed, 15 insertions(+), 25 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2f35167a..c9ac21cb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,4 +45,4 @@ jobs: run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - name: Run tests - run: ./vendor/bin/phpunit ./tests + run: composer test diff --git a/src/Support/Testing/MocksClassLoader.php b/src/Support/Testing/MocksClassLoader.php index 46f756f8..7eade5bb 100644 --- a/src/Support/Testing/MocksClassLoader.php +++ b/src/Support/Testing/MocksClassLoader.php @@ -55,7 +55,7 @@ protected function mockClassLoader($class) $subject->expects($this->any()) ->method('extensionGetClassLoader') - ->will($this->returnValue($this->classLoader)); + ->willReturn($this->classLoader); // Run construction $subject->__construct(); diff --git a/tests/Database/QueryBuilderTest.php b/tests/Database/QueryBuilderTest.php index a916ce81..2f7efc22 100644 --- a/tests/Database/QueryBuilderTest.php +++ b/tests/Database/QueryBuilderTest.php @@ -1,7 +1,6 @@ getMockBuilder(ConnectionInterface::class) + $connection = $this->getMockBuilder(\Illuminate\Database\Connection::class) ->disableOriginalConstructor() - ->disableOriginalClone() - ->disableArgumentCloning() - ->disallowMockingUnknownTypes() ->onlyMethods([ 'table', 'raw', @@ -127,8 +123,6 @@ protected function getConnection($connection = null, $table = null) 'transactionLevel', 'pretend', 'getDatabaseName', - ]) - ->addMethods([ 'getConfig', ]) ->getMock(); @@ -141,7 +135,7 @@ protected function getConnection($connection = null, $table = null) protected function getBuilder() { - $grammar = new Grammar; + $grammar = new Grammar($this->getConnection()); $processor = $this->createMock(Processor::class); return new QueryBuilder($this->getConnection(), $grammar, $processor); diff --git a/tests/Database/Traits/ValidationTest.php b/tests/Database/Traits/ValidationTest.php index d857c2ca..7121420a 100644 --- a/tests/Database/Traits/ValidationTest.php +++ b/tests/Database/Traits/ValidationTest.php @@ -122,7 +122,9 @@ public function testUnique() public function testArrayFieldNames() { - $mock = $this->getMockForTrait('Winter\Storm\Database\Traits\Validation'); + $mock = new class { + use \Winter\Storm\Database\Traits\Validation; + }; $rules = [ 'field' => 'required', diff --git a/tests/Halcyon/HalcyonModelTest.php b/tests/Halcyon/HalcyonModelTest.php index d9e4739e..1ce20863 100644 --- a/tests/Halcyon/HalcyonModelTest.php +++ b/tests/Halcyon/HalcyonModelTest.php @@ -377,19 +377,13 @@ protected function setDatasourceResolver() protected function setValidatorOnModel() { - $translator = $this->getMockBuilder('Illuminate\Contracts\Translation\Translator') - ->onlyMethods([ - 'get', - 'choice', - 'setLocale', - 'getLocale' - ]) - ->addMethods([ - 'trans', - 'transChoice', - ])->getMock(); - - $translator->expects($this->any())->method('get')->will($this->returnArgument(0)); + $translator = $this->getMockBuilder(\Illuminate\Contracts\Translation\Translator::class) + ->onlyMethods(['get','choice','setLocale','getLocale']) // existing ones + ->getMock(); + + $translator->expects($this->any()) + ->method('get') + ->willReturnArgument(0); $factory = new \Winter\Storm\Validation\Factory($translator); diff --git a/tests/Router/RoutingUrlGeneratorTest.php b/tests/Router/RoutingUrlGeneratorTest.php index 3ac470e4..b28f926c 100644 --- a/tests/Router/RoutingUrlGeneratorTest.php +++ b/tests/Router/RoutingUrlGeneratorTest.php @@ -18,7 +18,7 @@ * @see https://github.com/laravel/framework/blob/6.x/tests/Routing/RoutingUrlGeneratorTest.php * Replicated here to verify Winter does not break compatibility with Laravel */ -class RoutingUrlGeneratorTest extends \Winter\Storm\Tests\TestCase +class RoutingUrlGeneratorTest extends TestCase { public function testBasicGeneration() { From 14a15704aa0929a739c4a5b4d64a2837e79b6081 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Fri, 25 Jul 2025 10:11:35 -0600 Subject: [PATCH 156/197] Add ContextServiceProvider from Laravel 11 https://github.com/laravel/framework/pull/49730 added the ContextServiceProvider --- src/Foundation/Application.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index b61989c4..aba5b55a 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -6,6 +6,7 @@ use Illuminate\Filesystem\Filesystem; use Illuminate\Foundation\Application as ApplicationBase; use Illuminate\Foundation\PackageManifest; +use Illuminate\Log\Context\ContextServiceProvider; use Symfony\Component\ErrorHandler\Error\FatalError; use Winter\Storm\Events\EventServiceProvider; use Winter\Storm\Filesystem\PathResolver; @@ -105,13 +106,11 @@ protected function registerBaseBindings() protected function registerBaseServiceProviders() { $this->register(new EventServiceProvider($this)); - $this->register(new LogServiceProvider($this)); - + $this->register(new ContextServiceProvider($this)); $this->register(new RoutingServiceProvider($this)); $this->register(new ExecutionContextProvider($this)); - $this->register(new CarbonServiceProvider($this)); } From 8a684bde17319b61b7a19869629c07ad61efeea2 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Fri, 25 Jul 2025 21:46:13 -0600 Subject: [PATCH 157/197] Properly tear down tests --- tests/Translation/FileLoaderTest.php | 3 +-- tests/Translation/TranslatorTest.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Translation/FileLoaderTest.php b/tests/Translation/FileLoaderTest.php index c9538727..fa28c279 100644 --- a/tests/Translation/FileLoaderTest.php +++ b/tests/Translation/FileLoaderTest.php @@ -1,6 +1,5 @@ Date: Mon, 28 Jul 2025 00:42:20 -0600 Subject: [PATCH 158/197] Use updated phpunit-assert-gd --- composer.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 82760c31..31137dcf 100644 --- a/composer.json +++ b/composer.json @@ -55,17 +55,13 @@ "phpunit/phpunit": "^11.0", "squizlabs/php_codesniffer": "^3.2", "php-parallel-lint/php-parallel-lint": "^1.0", - "meyfa/phpunit-assert-gd": "dev-main", + "meyfa/phpunit-assert-gd": "^4.2", "mockery/mockery": "^1.6|^2.0", "dms/phpunit-arraysubset-asserts": "dev-add-phpunit-11-support", "orchestra/testbench": "^10.0", "larastan/larastan": "^3.6.0" }, "repositories": [ - { - "type": "vcs", - "url": "https://github.com/wintercms/phpunit-assert-gd" - }, { "type": "vcs", "url": "https://github.com/pieterocp/phpunit-arraysubset-asserts" From ed064d762ff800158b1fb7981359b03c81bf2225 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 9 Sep 2025 14:00:00 -0400 Subject: [PATCH 159/197] remove array_first/array_last helpers as they now defined by symfony --- src/Support/helpers-array.php | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/src/Support/helpers-array.php b/src/Support/helpers-array.php index b99d6fb4..29bbccae 100644 --- a/src/Support/helpers-array.php +++ b/src/Support/helpers-array.php @@ -117,36 +117,6 @@ function array_except($array, $keys) } } -if (!function_exists('array_first')) { - /** - * Return the first element in an array passing a given truth test. - * - * @param array $array - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - function array_first($array, ?callable $callback = null, $default = null) - { - return Arr::first($array, $callback, $default); - } -} - -if (!function_exists('array_last')) { - /** - * Return the last element in an array passing a given truth test. - * - * @param array $array - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - function array_last($array, ?callable $callback = null, $default = null) - { - return Arr::last($array, $callback, $default); - } -} - if (!function_exists('array_flatten')) { /** * Flatten a multi-dimensional array into a single level. From 000af359d529f19905c2517aaefa022889292bf9 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Fri, 19 Sep 2025 07:43:26 -0700 Subject: [PATCH 160/197] Bump required Laravel version for array_first | array_last helpers that were removed See https://github.com/laravel/framework/commit/c6e9e71a1885aeee27da56d34a90fedc135822ef --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 31137dcf..77abb359 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "assetic/framework": "^3.1", "doctrine/dbal": "^3.0", "enshrined/svg-sanitize": "~0.16", - "laravel/framework": "^12.0", + "laravel/framework": "^12.30.1", "laravel/tinker": "^2.8.1", "league/csv": "~9.1", "nesbot/carbon": "^3.0", From 7897d0993917696d1ee3b00e9a75b58dc382da1c Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 30 Sep 2025 12:01:08 -0600 Subject: [PATCH 161/197] Handle Expression instances in query building Laravel 10 made it so that raw expression objects cannot be cast directly into strings anymore. There will be other places that will need to be updated. --- src/Database/Builder.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Database/Builder.php b/src/Database/Builder.php index 345e1626..3c4af0d1 100644 --- a/src/Database/Builder.php +++ b/src/Database/Builder.php @@ -1,5 +1,6 @@ getValue($query->getQuery()->getGrammar()); + } $fieldSql = $this->query->raw(sprintf("lower(%s)", DbDongle::cast($field, 'text'))); $wordSql = '%' . trim(mb_strtolower($word)) . '%'; $query->where($fieldSql, 'LIKE', $wordSql, $wordBoolean); From e002eaae4ba4039db79646491dc5e5f768b715c1 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Mon, 3 Nov 2025 11:16:03 +0800 Subject: [PATCH 162/197] Fix outstanding PHPStan and PHPUnit errors, minor code tidying --- .gitignore | 1 + phpstan-baseline.neon | 6 - phpunit.xml.dist | 2 +- src/Auth/Models/Group.php | 4 +- src/Auth/Models/Preferences.php | 10 +- src/Auth/Models/Role.php | 67 ++- src/Auth/Models/User.php | 68 +-- src/Database/Pivot.php | 2 +- .../ConsoleSupportServiceProvider.php | 2 +- src/Support/Traits/Emitter.php | 11 +- tests/Parse/ArrayFileTest.php | 396 +++++++++--------- 11 files changed, 299 insertions(+), 270 deletions(-) diff --git a/.gitignore b/.gitignore index 26b7105e..23df80e0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ composer.lock # Editor files .idea .vscode +.zed *.code-workspace # Other files diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index c31f014e..9f2e0407 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -84,12 +84,6 @@ parameters: count: 1 path: src/Database/Capsule/Manager.php - - - message: '#^Parameter \#1 \$resolver of method Illuminate\\Database\\Schema\\Builder\:\:blueprintResolver\(\) expects Closure\(string, Closure, string\)\: Illuminate\\Database\\Schema\\Blueprint, Closure\(Illuminate\\Database\\Connection, string, Closure\|null\)\: Winter\\Storm\\Database\\Schema\\Blueprint given\.$#' - identifier: argument.type - count: 1 - path: src/Database/DatabaseServiceProvider.php - - message: '#^Call to an undefined method Illuminate\\Database\\ConnectionInterface\:\:getName\(\)\.$#' identifier: method.notFound diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 680a1778..9f2fbbf4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,7 +2,7 @@ diff --git a/src/Auth/Models/Group.php b/src/Auth/Models/Group.php index 2c3da8fa..93c8c15c 100644 --- a/src/Auth/Models/Group.php +++ b/src/Auth/Models/Group.php @@ -27,11 +27,11 @@ class Group extends Model * @var array Relations */ public $belongsToMany = [ - 'users' => [User::class, 'table' => 'users_groups'] + 'users' => [User::class, 'table' => 'users_groups'], ]; /** - * @var string[]|bool The attributes that aren't mass assignable. + * @var string[] The attributes that aren't mass assignable. */ protected $guarded = []; diff --git a/src/Auth/Models/Preferences.php b/src/Auth/Models/Preferences.php index 8e21119e..79317d40 100644 --- a/src/Auth/Models/Preferences.php +++ b/src/Auth/Models/Preferences.php @@ -30,7 +30,7 @@ class Preferences extends Model protected static $cache = []; /** - * @var array List of attribute names which are json encoded and decoded from the database. + * @var string[] List of attribute names which are json encoded and decoded from the database. */ protected $jsonable = ['value']; @@ -60,7 +60,7 @@ public function resolveUser($user) */ public static function forUser($user = null) { - $self = new static; + $self = new static(); $self->userContext = $user ?: $self->resolveUser($user); return $self; } @@ -113,8 +113,8 @@ public function set($key, $value) $record = static::findRecord($key, $user); if (!$record) { - list($namespace, $group, $item) = $this->parseKey($key); - $record = new static; + [$namespace, $group, $item] = $this->parseKey($key); + $record = new static(); $record->namespace = $namespace; $record->group = $group; $record->item = $item; @@ -174,7 +174,7 @@ public static function findRecord($key, $user = null) */ public function scopeApplyKeyAndUser($query, $key, $user = null) { - list($namespace, $group, $item) = $this->parseKey($key); + [$namespace, $group, $item] = $this->parseKey($key); $query = $query ->where('namespace', $namespace) diff --git a/src/Auth/Models/Role.php b/src/Auth/Models/Role.php index efa8f335..4e4f5d38 100644 --- a/src/Auth/Models/Role.php +++ b/src/Auth/Models/Role.php @@ -28,11 +28,11 @@ class Role extends Model * @var array Relations */ public $hasMany = [ - 'users' => User::class + 'users' => User::class, ]; /** - * @var array List of attribute names which are json encoded and decoded from the database. + * @var string[] List of attribute names which are json encoded and decoded from the database. */ protected $jsonable = ['permissions']; @@ -46,7 +46,7 @@ class Role extends Model protected $allowedPermissionsValues = [0, 1]; /** - * @var string[]|bool The attributes that aren't mass assignable. + * @var string[] The attributes that aren't mass assignable. */ protected $guarded = []; @@ -77,7 +77,7 @@ public function hasAccess($permissions, $all = true) // Now, let's check if the permission ends in a wildcard "*" symbol. // If it does, we'll check through all the merged permissions to see // if a permission exists which matches the wildcard. - if ((strlen($permission) > 1) && ends_with($permission, '*')) { + if (strlen($permission) > 1 && ends_with($permission, '*')) { $matched = false; foreach ($rolePermissions as $rolePermission => $value) { @@ -86,7 +86,11 @@ public function hasAccess($permissions, $all = true) // We will make sure that the merged permission does not // exactly match our permission, but starts with it. - if ($checkPermission !== $rolePermission && starts_with($rolePermission, $checkPermission) && (int) $value === 1) { + if ( + $checkPermission !== $rolePermission && + starts_with($rolePermission, $checkPermission) && + (int) $value === 1 + ) { $matched = true; break; } @@ -95,7 +99,7 @@ public function hasAccess($permissions, $all = true) // Now, let's check if the permission starts in a wildcard "*" symbol. // If it does, we'll check through all the merged permissions to see // if a permission exists which matches the wildcard. - elseif ((strlen($permission) > 1) && starts_with($permission, '*')) { + elseif (strlen($permission) > 1 && starts_with($permission, '*')) { $matched = false; foreach ($rolePermissions as $rolePermission => $value) { @@ -104,18 +108,24 @@ public function hasAccess($permissions, $all = true) // We will make sure that the merged permission does not // exactly match our permission, but ends with it. - if ($checkPermission !== $rolePermission && ends_with($rolePermission, $checkPermission) && (int) $value === 1) { + if ( + $checkPermission !== $rolePermission && + ends_with($rolePermission, $checkPermission) && + (int) $value === 1 + ) { $matched = true; break; } } - } - else { + } else { $matched = false; foreach ($rolePermissions as $rolePermission => $value) { // This time check if the rolePermission ends in wildcard "*" symbol. - if ((strlen($rolePermission) > 1) && ends_with($rolePermission, '*')) { + if ( + strlen($rolePermission) > 1 && + ends_with($rolePermission, '*') + ) { $matched = false; // Strip the '*' off the end of the permission. @@ -123,14 +133,21 @@ public function hasAccess($permissions, $all = true) // We will make sure that the merged permission does not // exactly match our permission, but starts with it. - if ($checkGroupPermission !== $permission && starts_with($permission, $checkGroupPermission) && (int) $value === 1) { + if ( + $checkGroupPermission !== $permission && + starts_with($permission, $checkGroupPermission) && + (int) $value === 1 + ) { $matched = true; break; } } // Otherwise, we'll fallback to standard permissions checking where // we match that permissions explicitly exist. - elseif ($permission === $rolePermission && (int) $rolePermissions[$permission] === 1) { + elseif ( + $permission === $rolePermission && + (int) $rolePermissions[$permission] === 1 + ) { $matched = true; break; } @@ -142,8 +159,7 @@ public function hasAccess($permissions, $all = true) // accordingly. if ($all === true && $matched === false) { return false; - } - elseif ($all === false && $matched === true) { + } elseif ($all === false && $matched === true) { return true; } } @@ -171,12 +187,19 @@ public function setPermissionsAttribute($permissions) $permissions = json_decode($permissions, true); foreach ($permissions as $permission => $value) { - if (!in_array($value = (int) $value, $this->allowedPermissionsValues)) { - throw new InvalidArgumentException(sprintf( - 'Invalid value "%s" for permission "%s" given.', - $value, - $permission - )); + if ( + !in_array( + $value = (int) $value, + $this->allowedPermissionsValues, + ) + ) { + throw new InvalidArgumentException( + sprintf( + 'Invalid value "%s" for permission "%s" given.', + $value, + $permission, + ), + ); } if ($value === 0) { @@ -184,6 +207,8 @@ public function setPermissionsAttribute($permissions) } } - $this->attributes['permissions'] = !empty($permissions) ? json_encode($permissions) : ''; + $this->attributes['permissions'] = !empty($permissions) + ? json_encode($permissions) + : ''; } } diff --git a/src/Auth/Models/User.php b/src/Auth/Models/User.php index 81695a9a..582c79b8 100644 --- a/src/Auth/Models/User.php +++ b/src/Auth/Models/User.php @@ -32,7 +32,7 @@ class User extends Model implements \Illuminate\Contracts\Auth\Authenticatable public $rules = [ 'email' => 'required|between:3,255|email|unique:users', 'password' => 'required:create|min:4|confirmed', - 'password_confirmation' => 'required_with:password|min:4' + 'password_confirmation' => 'required_with:password|min:4', ]; /** @@ -43,48 +43,48 @@ class User extends Model implements \Illuminate\Contracts\Auth\Authenticatable ]; public $belongsTo = [ - 'role' => Role::class + 'role' => Role::class, ]; /** * The attributes that should be mutated to dates. * - * @var array + * @var string[] */ protected $dates = ['activated_at', 'last_login']; /** - * @var array The attributes that should be hidden for arrays. + * @var string[] The attributes that should be hidden for arrays. */ protected $hidden = ['password', 'reset_password_code', 'activation_code', 'persist_code']; /** - * @var string[]|bool The attributes that aren't mass assignable. + * @var string[] The attributes that aren't mass assignable. */ protected $guarded = ['is_superuser', 'reset_password_code', 'activation_code', 'persist_code', 'role_id']; /** - * @var array List of attribute names which should be hashed using the Bcrypt hashing algorithm. + * @var string[] List of attribute names which should be hashed using the Bcrypt hashing algorithm. */ protected $hashable = ['password', 'persist_code']; /** - * @var array List of attribute names which should not be saved to the database. + * @var string[] List of attribute names which should not be saved to the database. */ protected $purgeable = ['password_confirmation']; /** - * @var array The array of custom attribute names. + * @var array The array of custom attribute names. */ public $attributeNames = []; /** - * @var array The array of custom error messages. + * @var array The array of custom error messages. */ public $customMessages = []; /** - * @var array List of attribute names which are json encoded and decoded from the database. + * @var string[] List of attribute names which are json encoded and decoded from the database. */ protected $jsonable = ['permissions']; @@ -151,9 +151,7 @@ public function isSuperUser() // Events // - public function beforeLogin() - { - } + public function beforeLogin() {} public function afterLogin() { @@ -280,7 +278,7 @@ public function checkResetPasswordCode($resetCode) return false; } - return ($this->reset_password_code === $resetCode); + return $this->reset_password_code === $resetCode; } /** @@ -319,8 +317,7 @@ public function setPasswordAttribute($value) { if ($this->exists && empty($value)) { unset($this->attributes['password']); - } - else { + } else { $this->attributes['password'] = $value; // Password has changed, log out all users @@ -477,7 +474,7 @@ public function hasPermission($permissions, $all = true) // Now, let's check if the permission ends in a wildcard "*" symbol. // If it does, we'll check through all the merged permissions to see // if a permission exists which matches the wildcard. - if ((strlen($permission) > 1) && ends_with($permission, '*')) { + if (strlen($permission) > 1 && ends_with($permission, '*')) { $matched = false; foreach ($mergedPermissions as $mergedPermission => $value) { @@ -486,13 +483,16 @@ public function hasPermission($permissions, $all = true) // We will make sure that the merged permission does not // exactly match our permission, but starts with it. - if ($checkPermission !== $mergedPermission && starts_with($mergedPermission, $checkPermission) && (int) $value === 1) { + if ( + $checkPermission !== $mergedPermission && + starts_with($mergedPermission, $checkPermission) && + (int) $value === 1 + ) { $matched = true; break; } } - } - elseif ((strlen($permission) > 1) && starts_with($permission, '*')) { + } elseif (strlen($permission) > 1 && starts_with($permission, '*')) { $matched = false; foreach ($mergedPermissions as $mergedPermission => $value) { @@ -501,18 +501,21 @@ public function hasPermission($permissions, $all = true) // We will make sure that the merged permission does not // exactly match our permission, but ends with it. - if ($checkPermission !== $mergedPermission && ends_with($mergedPermission, $checkPermission) && (int) $value === 1) { + if ( + $checkPermission !== $mergedPermission && + ends_with($mergedPermission, $checkPermission) && + (int) $value === 1 + ) { $matched = true; break; } } - } - else { + } else { $matched = false; foreach ($mergedPermissions as $mergedPermission => $value) { // This time check if the mergedPermission ends in wildcard "*" symbol. - if ((strlen($mergedPermission) > 1) && ends_with($mergedPermission, '*')) { + if (strlen($mergedPermission) > 1 && ends_with($mergedPermission, '*')) { $matched = false; // Strip the '*' off the end of the permission. @@ -520,7 +523,11 @@ public function hasPermission($permissions, $all = true) // We will make sure that the merged permission does not // exactly match our permission, but starts with it. - if ($checkMergedPermission !== $permission && starts_with($permission, $checkMergedPermission) && (int) $value === 1) { + if ( + $checkMergedPermission !== $permission && + starts_with($permission, $checkMergedPermission) && + (int) $value === 1 + ) { $matched = true; break; } @@ -540,8 +547,7 @@ public function hasPermission($permissions, $all = true) // accordingly. if ($all === true && $matched === false) { return false; - } - elseif ($all === false && $matched === true) { + } elseif ($all === false && $matched === true) { return true; } } @@ -570,11 +576,9 @@ public function setPermissionsAttribute($permissions) foreach ($permissions as $permission => &$value) { if (!in_array($value = (int) $value, $this->allowedPermissionsValues)) { - throw new InvalidArgumentException(sprintf( - 'Invalid value "%s" for permission "%s" given.', - $value, - $permission - )); + throw new InvalidArgumentException( + sprintf('Invalid value "%s" for permission "%s" given.', $value, $permission), + ); } if ($value === 0) { diff --git a/src/Database/Pivot.php b/src/Database/Pivot.php index 01538273..8b39008b 100644 --- a/src/Database/Pivot.php +++ b/src/Database/Pivot.php @@ -9,7 +9,7 @@ class Pivot extends Model /** * The attributes that aren't mass assignable. * - * @var string[]|bool + * @var string[] */ protected $guarded = []; diff --git a/src/Foundation/Providers/ConsoleSupportServiceProvider.php b/src/Foundation/Providers/ConsoleSupportServiceProvider.php index 87dcd019..82db8c38 100644 --- a/src/Foundation/Providers/ConsoleSupportServiceProvider.php +++ b/src/Foundation/Providers/ConsoleSupportServiceProvider.php @@ -11,7 +11,7 @@ class ConsoleSupportServiceProvider extends AggregateServiceProvider implements /** * The provider class names. * - * @var array + * @var array> */ protected $providers = [ ArtisanServiceProvider::class, diff --git a/src/Support/Traits/Emitter.php b/src/Support/Traits/Emitter.php index a77562ff..01330b78 100644 --- a/src/Support/Traits/Emitter.php +++ b/src/Support/Traits/Emitter.php @@ -90,7 +90,10 @@ protected function emitterEventSortEvents($eventName) if (isset($this->emitterEventCollection[$eventName])) { krsort($this->emitterEventCollection[$eventName]); - $this->emitterEventSorted[$eventName] = call_user_func_array('array_merge', $this->emitterEventCollection[$eventName]); + $this->emitterEventSorted[$eventName] = call_user_func_array( + 'array_merge', + $this->emitterEventCollection[$eventName], + ); } } @@ -116,7 +119,9 @@ public function unbindEvent($event = null) } if ($event === null) { - unset($this->emitterSingleEventCollection, $this->emitterEventCollection, $this->emitterEventSorted); + $this->emitterSingleEventCollection = []; + $this->emitterEventCollection = []; + $this->emitterEventSorted = []; return $this; } @@ -148,7 +153,7 @@ public function fireEvent($event, $params = [], $halt = false) // When the given "event" is actually an object we will assume it is an event // object and use the class as the event name and this event itself as the // payload to the handler, which makes object based events quite simple. - list($event, $params) = $this->parseEventAndPayload($event, $params); + [$event, $params] = $this->parseEventAndPayload($event, $params); $result = []; diff --git a/tests/Parse/ArrayFileTest.php b/tests/Parse/ArrayFileTest.php index 200bcf1e..c431f054 100644 --- a/tests/Parse/ArrayFileTest.php +++ b/tests/Parse/ArrayFileTest.php @@ -174,8 +174,7 @@ public function testRender() * Test alternative quoting */ $arrayFile = ArrayFile::open(__DIR__ . '/../fixtures/parse/arrayfile/sample-array-file.php'); - $arrayFile->set('timezone', 'The Fifth Dimension') - ->set('timezoneAgain', 'The "Sixth" Dimension'); + $arrayFile->set('timezone', 'The Fifth Dimension')->set('timezoneAgain', 'The "Sixth" Dimension'); $result = eval('?>' . $arrayFile->render()); $this->assertArrayHasKey('timezone', $result); @@ -187,7 +186,8 @@ public function testRender() * Rewrite a boolean */ $arrayFile = ArrayFile::open(__DIR__ . '/../fixtures/parse/arrayfile/sample-array-file.php'); - $arrayFile->set('debug', false) + $arrayFile + ->set('debug', false) ->set('debugAgain', true) ->set('bullyIan', true) ->set('booLeeIan', false) @@ -239,15 +239,16 @@ public function testConfigImports() $arrayFile = ArrayFile::open(__DIR__ . '/../fixtures/parse/arrayfile/import.php'); $expected = << Response::HTTP_OK, - 'bar' => Response::HTTP_I_AM_A_TEAPOT, -]; + return [ + 'foo' => Response::HTTP_OK, + 'bar' => Response::HTTP_I_AM_A_TEAPOT, + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -258,15 +259,16 @@ public function testConfigImportsUpdating() $arrayFile->set('foo', $arrayFile->constant('Response::HTTP_CONFLICT')); $expected = << Response::HTTP_CONFLICT, - 'bar' => Response::HTTP_I_AM_A_TEAPOT, -]; + use Symfony\Component\HttpFoundation\Response; -PHP; + return [ + 'foo' => Response::HTTP_CONFLICT, + 'bar' => Response::HTTP_I_AM_A_TEAPOT, + ]; + + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -276,14 +278,15 @@ public function testConfigExpression() $arrayFile = ArrayFile::open(__DIR__ . '/../fixtures/parse/arrayfile/expression.php'); $expected = << \$bar, -]; + \$bar = nl2br("Hello\\nWorld"); -PHP; + return [ + 'foo' => \$bar, + ]; + + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -358,34 +361,34 @@ public function testWriteDotNotationMultiple() $contents = file_get_contents($file); $expected = << [ - 'i' => [ - 'n' => [ - 't' => [ - 'e' => [ - 'r' => 'Winter CMS', + return [ + 'w' => [ + 'i' => [ + 'n' => [ + 't' => [ + 'e' => [ + 'r' => 'Winter CMS', + ], + 'a' => 'very', + ], + 'b' => 'is', + 'c' => [ + 'l' => 'good', + 'e' => 'and', + 'f' => 'awesome', + ], + 'g' => 'for', + ], + 2 => [ + 'g' => 'development', ], - 'a' => 'very', - ], - 'b' => 'is', - 'c' => [ - 'l' => 'good', - 'e' => 'and', - 'f' => 'awesome', ], - 'g' => 'for', - ], - 2 => [ - 'g' => 'development', ], - ], - ], -]; + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $contents); @@ -407,26 +410,26 @@ public function testWriteDotDuplicateIntKeys() $contents = file_get_contents($file); $expected = << [ - 'i' => [ - 'n' => [ - 't' => [ - 'e' => [ - 'r' => 'Winter CMS', + return [ + 'w' => [ + 'i' => [ + 'n' => [ + 't' => [ + 'e' => [ + 'r' => 'Winter CMS', + ], + ], + ], + 2 => [ + 'g' => 'development', ], ], ], - 2 => [ - 'g' => 'development', - ], - ], - ], -]; + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $contents); @@ -463,27 +466,24 @@ public function testSetArray() $arrayFile->set([ 'w' => [ 'i' => 'n', - 't' => [ - 'e', - 'r' - ] - ] + 't' => ['e', 'r'], + ], ]); $expected = << [ - 'i' => 'n', - 't' => [ - 'e', - 'r', - ], - ], -]; + return [ + 'w' => [ + 'i' => 'n', + 't' => [ + 'e', + 'r', + ], + ], + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -500,25 +500,25 @@ public function testSetNumericArray() ], 'cms' => [ 0 => 'a', - 1 => 'b' - ] + 1 => 'b', + ], ]); $expected = << [ - 1 => 'a', - 2 => 'b', - ], - 'cms' => [ - 'a', - 'b', - ], -]; + return [ + 'winter' => [ + 1 => 'a', + 2 => 'b', + ], + 'cms' => [ + 'a', + 'b', + ], + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -529,22 +529,22 @@ public function testWriteConstCall() $arrayFile = ArrayFile::open($file); $arrayFile->set([ - 'curl_port' => $arrayFile->constant('CURLOPT_PORT') + 'curl_port' => $arrayFile->constant('CURLOPT_PORT'), ]); $arrayFile->set([ - 'curl_return' => new \Winter\Storm\Parse\PHP\PHPConstant('CURLOPT_RETURNTRANSFER') + 'curl_return' => new \Winter\Storm\Parse\PHP\PHPConstant('CURLOPT_RETURNTRANSFER'), ]); $expected = << CURLOPT_PORT, - 'curl_return' => CURLOPT_RETURNTRANSFER, -]; + return [ + 'curl_port' => CURLOPT_PORT, + 'curl_return' => CURLOPT_RETURNTRANSFER, + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -562,34 +562,34 @@ public function testWriteArrayFunctionsAndConstCall() 'test2', 'additional' => [ $arrayFile->constant('\Winter\Storm\Parse\PHP\ArrayFile::SORT_ASC'), - $arrayFile->constant('\Winter\Storm\Parse\PHP\ArrayFile::SORT_DESC') - ] - ] - ] + $arrayFile->constant('\Winter\Storm\Parse\PHP\ArrayFile::SORT_DESC'), + ], + ], + ], ]); $expected = << [ - 'to' => [ - 'config' => [ - 'test' => env('TEST_KEY', 'default'), - 'details' => [ - 'test1', - 'test2', - 'additional' => [ - \Winter\Storm\Parse\PHP\ArrayFile::SORT_ASC, - \Winter\Storm\Parse\PHP\ArrayFile::SORT_DESC, + [ + 'to' => [ + 'config' => [ + 'test' => env('TEST_KEY', 'default'), + 'details' => [ + 'test1', + 'test2', + 'additional' => [ + \Winter\Storm\Parse\PHP\ArrayFile::SORT_ASC, + \Winter\Storm\Parse\PHP\ArrayFile::SORT_DESC, + ], + ], ], ], ], - ], - ], -]; + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -600,22 +600,22 @@ public function testWriteFunctionCall() $arrayFile = ArrayFile::open($file); $arrayFile->set([ - 'key' => $arrayFile->function('env', ['KEY_A', true]) + 'key' => $arrayFile->function('env', ['KEY_A', true]), ]); $arrayFile->set([ - 'key2' => new \Winter\Storm\Parse\PHP\PHPFunction('nl2br', ['KEY_B', false]) + 'key2' => new \Winter\Storm\Parse\PHP\PHPFunction('nl2br', ['KEY_B', false]), ]); $expected = << env('KEY_A', true), - 'key2' => nl2br('KEY_B', false), -]; + return [ + 'key' => env('KEY_A', true), + 'key2' => nl2br('KEY_B', false), + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -626,21 +626,21 @@ public function testWriteFunctionCallOverwrite() $arrayFile = ArrayFile::open($file); $arrayFile->set([ - 'key' => $arrayFile->function('env', ['KEY_A', true]) + 'key' => $arrayFile->function('env', ['KEY_A', true]), ]); $arrayFile->set([ - 'key' => new \Winter\Storm\Parse\PHP\PHPFunction('nl2br', ['KEY_B', false]) + 'key' => new \Winter\Storm\Parse\PHP\PHPFunction('nl2br', ['KEY_B', false]), ]); $expected = << nl2br('KEY_B', false), -]; + return [ + 'key' => nl2br('KEY_B', false), + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -652,18 +652,18 @@ public function testInsertNull() $arrayFile->set([ 'key' => $arrayFile->function('env', ['KEY_A', null]), - 'key2' => null + 'key2' => null, ]); $expected = << env('KEY_A', null), - 'key2' => null, -]; + return [ + 'key' => env('KEY_A', null), + 'key2' => null, + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -685,29 +685,28 @@ public function testSortAsc() $arrayFile->sort(); $expected = << [ - 'a' => [ - 'a' => 'a', - 'b' => 'b', - ], - 'b' => 'b', - 'c' => 'c', - ], - 'b' => [ - 'a' => 'a', - 'b' => 'b', - ], -]; - -PHP; + return [ + 'a' => [ + 'a' => [ + 'a' => 'a', + 'b' => 'b', + ], + 'b' => 'b', + 'c' => 'c', + ], + 'b' => [ + 'a' => 'a', + 'b' => 'b', + ], + ]; + + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } - public function testSortDesc() { $file = __DIR__ . '/../fixtures/parse/arrayfile/empty.php'; @@ -725,24 +724,24 @@ public function testSortDesc() $arrayFile->sort(ArrayFile::SORT_DESC); $expected = << [ - 'b' => 'b', - 'a' => 'a', - ], - 'a' => [ - 'c' => 'c', - 'b' => 'b', - 'a' => [ - 'b' => 'b', - 'a' => 'a', - ], - ], -]; + return [ + 'b' => [ + 'b' => 'b', + 'a' => 'a', + ], + 'a' => [ + 'c' => 'c', + 'b' => 'b', + 'a' => [ + 'b' => 'b', + 'a' => 'a', + ], + ], + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -754,7 +753,7 @@ public function testSortUsort() $arrayFile->set([ 'a' => 'a', - 'b' => 'b' + 'b' => 'b', ]); $arrayFile->sort(function ($a, $b) { @@ -766,14 +765,14 @@ public function testSortUsort() }); $expected = << 'b', - 'a' => 'a', -]; + return [ + 'b' => 'b', + 'a' => 'a', + ]; -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -783,20 +782,21 @@ public function testIncludeFormatting() $arrayFile = ArrayFile::open($file); $expected = << array_merge(include(__DIR__ . '/sample-array-file.php'), [ + 'bar' => 'foo', + ]), + 'bar' => 'foo', + ]; -include(__DIR__ . '/sample-array-file.php'); -include_once(__DIR__ . '/sample-array-file.php'); -require(__DIR__ . '/sample-array-file.php'); -require_once(__DIR__ . '/sample-array-file.php'); -return [ - 'foo' => array_merge(include(__DIR__ . '/sample-array-file.php'), [ - 'bar' => 'foo', - ]), - 'bar' => 'foo', -]; - -PHP; + PHP; $this->assertEquals(str_replace("\r", '', $expected), $arrayFile->render()); } @@ -828,7 +828,7 @@ public function testSingleLineComment() $this->assertEquals( str_replace("\r", '', file_get_contents($file)), - str_replace("\r", '', $arrayFile->render()) + str_replace("\r", '', $arrayFile->render()), ); } @@ -839,7 +839,7 @@ public function testSingleLineCommentSubItem() $this->assertEquals( str_replace("\r", '', file_get_contents($file)), - str_replace("\r", '', $arrayFile->render()) + str_replace("\r", '', $arrayFile->render()), ); } } From 6786939af551952190dbe8339b0695e4cb08b0ec Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Mon, 3 Nov 2025 11:24:39 +0800 Subject: [PATCH 163/197] Minor code tidying, stubbing beforeLogin event for User model. --- src/Auth/Models/User.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Auth/Models/User.php b/src/Auth/Models/User.php index 582c79b8..1c1b1ed8 100644 --- a/src/Auth/Models/User.php +++ b/src/Auth/Models/User.php @@ -151,9 +151,18 @@ public function isSuperUser() // Events // - public function beforeLogin() {} + /** + * Event method called before a user is logged in. + */ + public function beforeLogin(): void + { + // Extend this method to define functionality before login + } - public function afterLogin() + /** + * Event method called after a user is logged in. + */ + public function afterLogin(): void { $this->last_login = $this->freshTimestamp(); $this->forceSave(); From 285d7de254841406410e027eaf87d5c3921cb4e8 Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Mon, 3 Nov 2025 11:27:37 +0800 Subject: [PATCH 164/197] Remove return type for user model callbacks Realised that this would be too much a BC break given that these methods are intended to be extended upon. --- src/Auth/Models/User.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Auth/Models/User.php b/src/Auth/Models/User.php index 1c1b1ed8..b3ad9ebc 100644 --- a/src/Auth/Models/User.php +++ b/src/Auth/Models/User.php @@ -153,16 +153,20 @@ public function isSuperUser() /** * Event method called before a user is logged in. + * + * @return void */ - public function beforeLogin(): void + public function beforeLogin() { // Extend this method to define functionality before login } /** * Event method called after a user is logged in. + * + * @return void */ - public function afterLogin(): void + public function afterLogin() { $this->last_login = $this->freshTimestamp(); $this->forceSave(); From 1994d5edec3ec66b96d65e146eb0b0a1191e4acb Mon Sep 17 00:00:00 2001 From: Mark Bingham Date: Wed, 27 May 2026 16:08:08 -0500 Subject: [PATCH 165/197] Implement typeTinyint for SQLite grammar (#226) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compileChange override in vendor/winter/storm/src/Database/Schema/Grammars/SQLiteGrammar.php reads SQLite column type names as tinyint (from SQLite metadata for boolean columns), then calls getType() which looks for ->typeTinyint(... — but only typeTinyInteger exists in the base grammar file Illuminate\Database\Schema\Grammars\SQLiteGrammar. This should resolve migration issues others might hit when upgrading to 1.3 --- .../Schema/Grammars/SQLiteGrammar.php | 15 +++++++++++ .../Grammars/SQLiteSchemaGrammarTest.php | 25 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 4839732b..65d4ceb3 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -157,6 +157,21 @@ public function getDefaultValue($value) return parent::getDefaultValue($value); } + /** + * Create the column definition for a tinyint type. + * + * SQLite's column introspection returns 'tinyint' as the type_name for boolean and tinyInteger + * columns. The base grammar only has typeTinyInteger, so we need this alias to avoid + * BadMethodCallException when compileChange rebuilds a table that contains boolean columns. + * + * @param \Illuminate\Support\Fluent $column + * @return string + */ + protected function typeTinyint(Fluent $column) + { + return 'integer'; + } + /** * Create the column definition for a varchar type. * diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index 02d0252a..2426caa6 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -2,11 +2,13 @@ namespace Winter\Storm\Tests\Database\Schema\Grammars; +use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\SQLiteBuilder; +use Illuminate\Database\SQLiteConnection; use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar; use Winter\Storm\Tests\GrammarTestCase; -class SQLiteSchemaGrammarTest extends \Winter\Storm\Tests\GrammarTestCase +class SQLiteSchemaGrammarTest extends GrammarTestCase { public function setUp(): void { @@ -60,4 +62,25 @@ public function testNullableInitialModifierAddDefaultNotNullable() $statements = $this->runBlueprint($changedBlueprint); $this->assertStringContainsString("\"name\" varchar not null default 'admin'", $statements[0]); } + + public function testTypeTinyintTypeIsValid(): void + { + $pdo = new \PDO('sqlite::memory:'); + $pdo->exec('CREATE TABLE users (is_active tinyint not null, name varchar not null)'); + + $connection = new SQLiteConnection($pdo, ':memory:', ''); + $grammar = new SQLiteGrammar($connection); + $connection->setSchemaGrammar($grammar); + + $blueprint = new Blueprint($connection, 'users'); + $blueprint->string('name')->nullable()->change(); + + // Prior to the typeTinyint fix, this would throw: + // BadMethodCallException: Method SQLiteGrammar::typeTinyint does not exist + $statements = $blueprint->toSql(); + + // compileChange maps 'tinyint' (SQLite's introspected type_name) to 'integer' + $this->assertNotEmpty($statements); + $this->assertStringContainsString('"is_active" integer', $statements[0]); + } } From 179ef50d02f61540b1c6360f4be760eebd04e101 Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Mon, 8 Jun 2026 23:49:28 -0500 Subject: [PATCH 166/197] Fix PHP 8.4 and 8.5 deprecations (#229) --- src/Config/Repository.php | 16 ++++++++-------- src/Console/Traits/ProcessesQuery.php | 2 +- src/Halcyon/Processors/Processor.php | 11 +++++++++++ src/Html/FormBuilder.php | 4 ++-- src/Support/Facades/Form.php | 4 ++-- tests/Scheduling/ScheduleListCommandTest.php | 4 ++-- 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/Config/Repository.php b/src/Config/Repository.php index 40a33437..58679ba7 100644 --- a/src/Config/Repository.php +++ b/src/Config/Repository.php @@ -174,11 +174,11 @@ public function set($key, $value = null) * Load the configuration group for the key. * * @param string $group - * @param string $namespace + * @param string|null $namespace * @param string $collection * @return void */ - protected function load($group, $namespace, $collection) + protected function load($group, ?string $namespace, $collection) { $env = $this->environment; @@ -191,10 +191,10 @@ protected function load($group, $namespace, $collection) $items = $this->loader->load($env, $group, $namespace); - // If we've already loaded this collection, we will just bail out since we do - // not want to load it again. Once items are loaded a first time they will - // stay kept in memory within this class and not loaded from disk again. - if (isset($this->afterLoad[$namespace])) { + // After load callbacks are only ever registered against a real namespace, so + // the global namespace (null) can never have one. Guarding here also avoids + // using null as an array offset, which is deprecated as of PHP 8.5. + if ($namespace !== null && isset($this->afterLoad[$namespace])) { $items = $this->callAfterLoad($namespace, $group, $items); } @@ -209,7 +209,7 @@ protected function load($group, $namespace, $collection) * @param array $items * @return array */ - protected function callAfterLoad($namespace, $group, $items) + protected function callAfterLoad(string $namespace, $group, $items) { $callback = $this->afterLoad[$namespace]; @@ -312,7 +312,7 @@ public function package($namespace, $hint) * @param \Closure $callback * @return void */ - public function afterLoading($namespace, Closure $callback) + public function afterLoading(string $namespace, Closure $callback) { $this->afterLoad[$namespace] = $callback; } diff --git a/src/Console/Traits/ProcessesQuery.php b/src/Console/Traits/ProcessesQuery.php index 19d88d75..06cbc1d2 100644 --- a/src/Console/Traits/ProcessesQuery.php +++ b/src/Console/Traits/ProcessesQuery.php @@ -16,7 +16,7 @@ trait ProcessesQuery * query by the provided chunkSize, running the callback on each record and * limiting number of records processed to the provided limit */ - public function processQuery(Builder $query, callable $callback, int $chunkSize = 100, int $limit = null): void + public function processQuery(Builder $query, callable $callback, int $chunkSize = 100, ?int $limit = null): void { $totalRecords = $query->count(); diff --git a/src/Halcyon/Processors/Processor.php b/src/Halcyon/Processors/Processor.php index 8a6b79df..4d6ba3bc 100644 --- a/src/Halcyon/Processors/Processor.php +++ b/src/Halcyon/Processors/Processor.php @@ -19,6 +19,11 @@ public function processSelectOne(Builder $query, $result) $fileName = array_get($result, 'fileName'); + // A record without a filename is not a valid template, so treat it as no result. + if ($fileName === null) { + return null; + } + return [$fileName => $this->parseTemplateContent($query, $result, $fileName)]; } @@ -39,6 +44,12 @@ public function processSelect(Builder $query, $results) foreach ($results as $result) { $fileName = array_get($result, 'fileName'); + + // Skip records without a filename so they cannot collide on an empty key. + if ($fileName === null) { + continue; + } + $items[$fileName] = $this->parseTemplateContent($query, $result, $fileName); } diff --git a/src/Html/FormBuilder.php b/src/Html/FormBuilder.php index 640d09c1..d59f49a0 100644 --- a/src/Html/FormBuilder.php +++ b/src/Html/FormBuilder.php @@ -478,12 +478,12 @@ public function selectYear(string $name, int $begin = 1900, ?int $end = null, st /** * Create a select month field. */ - public function selectMonth(string $name, string|array|null $selected = null, array $options = [], $format = '%B'): string + public function selectMonth(string $name, string|array|null $selected = null, array $options = [], $format = 'F'): string { $months = []; foreach (range(1, 12) as $month) { - $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1)); + $months[$month] = date($format, mktime(0, 0, 0, $month, 1)); } return $this->select($name, $months, $selected, $options); diff --git a/src/Support/Facades/Form.php b/src/Support/Facades/Form.php index 10edf557..098f68e0 100644 --- a/src/Support/Facades/Form.php +++ b/src/Support/Facades/Form.php @@ -19,9 +19,9 @@ * @method static string file(string $name, array $options = []) * @method static string textarea(string $name, string $value = null, array $options = []) * @method static string select(string $name, array $list = [], string $value = null, array $options = []) - * @method static string selectRange(string $name, string $begin, string $end, string $selected = null, array $options = []) + * @method static string selectRange(string $name, string|int|float $begin, string|int|float $end, string|array|null $selected = null, array $options = []) * @method static string selectYear() - * @method static string selectMonth(string $name, string $selected = null, array $options = [], string $format = '%B') + * @method static string selectMonth(string $name, string|array|null $selected = null, array $options = [], string $format = 'F') * @method static string getSelectOption(string|array $display, string $value, string $selected) * @method static string checkbox(string $name, $value = 1, bool $checked = null, array $options = []) * @method static string radio(string $name, $value = null, bool $checked = null, array $options = []) diff --git a/tests/Scheduling/ScheduleListCommandTest.php b/tests/Scheduling/ScheduleListCommandTest.php index 7e5e3b04..02f67883 100644 --- a/tests/Scheduling/ScheduleListCommandTest.php +++ b/tests/Scheduling/ScheduleListCommandTest.php @@ -54,7 +54,7 @@ public function testDisplaySchedule() ->expectsOutput(' * * * * * Winter\Storm\Tests\Scheduling\FooJob Next Due: 1 minute from now') ->expectsOutput(' 0 9,17 * * * php artisan inspire ......... Next Due: 9 hours from now') ->expectsOutput(' 0 10 * * * php artisan inspire ........ Next Due: 10 hours from now') - ->expectsOutput(' * * * * * Closure at: Winter\Storm\Tests\Scheduling\FooCall Next Due: 1 minute from now') + ->expectsOutput(' * * * * * Winter\Storm\Tests\Scheduling\FooCall Next Due: 1 minute from now') ->expectsOutput(' * * * * * Closure at: Winter\Storm\Tests\Scheduling\FooCall::fooFunction Next Due: 1 minute from now') ->expectsOutput(' * * * * * Closure at: '.$closureFilePath.':'.$closureLineNumber.' Next Due: 1 minute from now'); } @@ -78,7 +78,7 @@ public function testDisplayScheduleWithSort() ->assertSuccessful() ->expectsOutput(' * * * * * php artisan foobar a='.ProcessUtils::escapeArgument('b').' ... Next Due: 1 minute from now') ->expectsOutput(' * * * * * Winter\Storm\Tests\Scheduling\FooJob Next Due: 1 minute from now') - ->expectsOutput(' * * * * * Closure at: Winter\Storm\Tests\Scheduling\FooCall Next Due: 1 minute from now') + ->expectsOutput(' * * * * * Winter\Storm\Tests\Scheduling\FooCall Next Due: 1 minute from now') ->expectsOutput(' * * * * * Closure at: Winter\Storm\Tests\Scheduling\FooCall::fooFunction Next Due: 1 minute from now') ->expectsOutput(' * * * * * Closure at: '.$closureFilePath.':'.$closureLineNumber.' Next Due: 1 minute from now') ->expectsOutput(' 0 9,17 * * * php artisan inspire ......... Next Due: 9 hours from now') From 9e8f2a835c42354960db60d5a7b009a1d883dc6d Mon Sep 17 00:00:00 2001 From: Alex Orosciuc Date: Wed, 22 Jul 2026 22:35:58 +0100 Subject: [PATCH 167/197] Improve support for Expression field type (#236) Fixes https://github.com/wintercms/winter/issues/1500 Co-authored-by: Alex Orosciuc --- src/Database/Builder.php | 3 +++ tests/Database/ModelTest.php | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/Database/Builder.php b/src/Database/Builder.php index 3c4af0d1..9b64c0b2 100644 --- a/src/Database/Builder.php +++ b/src/Database/Builder.php @@ -88,6 +88,9 @@ protected function searchWhereInternal($term, $columns, $mode, $boolean) if (!strlen($term)) { continue; } + if ($field instanceof Expression) { + $field = $field->getValue($query->getQuery()->getGrammar()); + } $fieldSql = $this->query->raw(sprintf("lower(%s)", DbDongle::cast($field, 'text'))); $termSql = '%' . trim(mb_strtolower($term)) . '%'; $query->orWhere($fieldSql, 'LIKE', $termSql); diff --git a/tests/Database/ModelTest.php b/tests/Database/ModelTest.php index 94ed4064..625c5a68 100644 --- a/tests/Database/ModelTest.php +++ b/tests/Database/ModelTest.php @@ -133,6 +133,33 @@ protected function seedTables() $this->seeded['websites'][1]->users()->add($this->seeded['users'][1]); } + public function testSearchWhereExactHandlesExpressionColumns() + { + $grammar = Post::query()->getQuery()->getGrammar(); + $relationColumn = DB::raw(sprintf( + '(select %s from %s where %s = %s)', + $grammar->wrap('users.name'), + $grammar->wrapTable('users'), + $grammar->wrap('users.id'), + $grammar->wrap('posts.user_id') + )); + + $resultIds = Post::query() + ->searchWhere('User1', [$relationColumn], 'exact') + ->pluck('id') + ->all(); + + sort($resultIds); + + $expectedIds = [ + $this->seeded['posts'][0]->id, + $this->seeded['posts'][1]->id, + ]; + sort($expectedIds); + + $this->assertEquals($expectedIds, $resultIds); + } + // tests hasOneThrough & hasManyThrough public function testDeleteWithThroughRelations() { From 1f2ff06095664177fc3d64311d8f0c3a51490dfe Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Mon, 10 Aug 2026 11:28:48 -0600 Subject: [PATCH 168/197] Fix double-bound bindings in grouped pagination count query Remove the getCountForPagination/runPaginationCountQuery override, which was added in #39 to support having() in count queries -- Laravel's base builder now handles that natively. Since 0a6b24e1f ("Laravel 10 changes") the override passed the count subquery builder to from() (which registers its bindings) and also called mergeBindings() on it, binding the WHERE values twice and throwing SQLSTATE[HY093] "Invalid parameter number" on any grouped paginate() with bound conditions. Falling back to the base implementation fixes this and restores union support. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Database/QueryBuilder.php | 60 ---------------- tests/Database/QueryBuilderPaginateTest.php | 77 +++++++++++++++++++++ 2 files changed, 77 insertions(+), 60 deletions(-) create mode 100644 tests/Database/QueryBuilderPaginateTest.php diff --git a/src/Database/QueryBuilder.php b/src/Database/QueryBuilder.php index aba632ad..63dccec9 100644 --- a/src/Database/QueryBuilder.php +++ b/src/Database/QueryBuilder.php @@ -4,7 +4,6 @@ use Illuminate\Support\Collection as BaseCollection; use Illuminate\Support\Facades\App; use Illuminate\Database\Query\Builder as QueryBuilderBase; -use Illuminate\Database\Query\Expression; class QueryBuilder extends QueryBuilderBase { @@ -396,63 +395,4 @@ public function selectConcat(array $parts, string $as) return $this; } - - /** - * Get the count of the total records for the paginator. - * - * @param array $columns - * @return int - */ - public function getCountForPagination($columns = ['*']) - { - $results = $this->runPaginationCountQuery($columns); - - // Once we have run the pagination count query, we will get the resulting count and - // take into account what type of query it was. When there is a group by we will - // just return the count of the entire results set since that will be correct. - if (!isset($results[0])) { - return 0; - } elseif (is_object($results[0])) { - return (int) $results[0]->aggregate; - } - - return (int) array_change_key_case((array) $results[0])['aggregate']; - } - - /** - * Run a pagination count query. - * - * @param array $columns - * @return array - */ - protected function runPaginationCountQuery($columns = ['*']) - { - if ($this->groups || $this->havings) { - $clone = $this->cloneForPaginationCount(); - - if (empty($clone->columns) && !empty($this->joins)) { - $clone->select($this->from . '.*'); - } - - return $this->newQuery() - ->from($clone, $this->grammar->wrap('aggregate_table')) - ->mergeBindings($clone) - ->setAggregate('count', $this->withoutSelectAliases($columns)) - ->get() - ->all(); - } - - return parent::runPaginationCountQuery($columns); - } - - /** - * Clone the existing query instance for usage in a pagination subquery. - * - * @return self - */ - protected function cloneForPaginationCount() - { - return $this->cloneWithout(['orders', 'limit', 'offset']) - ->cloneWithoutBindings(['order']); - } } diff --git a/tests/Database/QueryBuilderPaginateTest.php b/tests/Database/QueryBuilderPaginateTest.php new file mode 100644 index 00000000..15c2030e --- /dev/null +++ b/tests/Database/QueryBuilderPaginateTest.php @@ -0,0 +1,77 @@ +getBuilder()->create('pagination_test', function ($table) { + $table->increments('id'); + $table->integer('group_id'); + $table->string('name'); + }); + + DB::table('pagination_test')->insert([ + ['group_id' => 1, 'name' => 'a'], + ['group_id' => 1, 'name' => 'b'], + ['group_id' => 2, 'name' => 'c'], + ['group_id' => 2, 'name' => 'd'], + ['group_id' => 2, 'name' => 'e'], + ['group_id' => 3, 'name' => 'f'], + ]); + } + + public function tearDown(): void + { + $this->getBuilder()->dropIfExists('pagination_test'); + parent::tearDown(); + } + + /** + * A grouped pagination whose WHERE clause carries bindings must not + * double-count those bindings when building the pagination count query. + * + * Regression test: the QueryBuilder::runPaginationCountQuery() override + * both passed the count subquery builder to from() (which registers its + * bindings) and called mergeBindings() on it, binding the WHERE values + * twice. This produced a SQLSTATE[HY093] "Invalid parameter number" error + * for any grouped paginate() with bound WHERE conditions. + */ + public function testGroupedPaginationWithBoundWhere() + { + $paginator = DB::table('pagination_test') + ->whereRaw('(group_id = ? OR group_id > ?)', [1, 0]) + ->groupBy('group_id') + ->orderBy('group_id') + ->paginate(2); + + // Three distinct groups => total of 3, spread over two pages of 2. + $this->assertEquals(3, $paginator->total()); + $this->assertEquals(2, $paginator->lastPage()); + $this->assertCount(2, $paginator->items()); + } + + /** + * The pagination count override originally existed to add having() support + * that Laravel's base builder now provides natively. Ensure grouped + * pagination filtered by a bound having() still returns the correct count. + */ + public function testGroupedPaginationWithBoundHaving() + { + $paginator = DB::table('pagination_test') + ->select('group_id', DB::raw('count(*) as total')) + ->groupBy('group_id') + ->havingRaw('count(*) > ?', [1]) + ->paginate(10); + + // Only groups 1 (2 rows) and 2 (3 rows) qualify; group 3 (1 row) does not. + $this->assertEquals(2, $paginator->total()); + $this->assertCount(2, $paginator->items()); + } +} From a69604ade2769cdfcccd5aaa70d0da800df0ad2d Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 11 Aug 2026 10:16:27 -0600 Subject: [PATCH 169/197] Fix CI: regenerate stale PHPStan baseline and code-quality drift (#240) Co-authored-by: Claude Opus 4.8 (1M context) --- phpstan-baseline.neon | 84 ++++++++++++++------ src/Filesystem/FilesystemServiceProvider.php | 2 - tests/Parse/Assetic/LessCompilerTest.php | 1 - 3 files changed, 60 insertions(+), 27 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9f2e0407..7b8caea0 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -12,6 +12,12 @@ parameters: count: 1 path: src/Auth/Models/Group.php + - + message: '#^Method Winter\\Storm\\Auth\\Models\\Preferences\:\:findRecord\(\) should return Winter\\Storm\\Auth\\Models\\Preferences\|null but returns stdClass\|null\.$#' + identifier: return.type + count: 1 + path: src/Auth/Models/Preferences.php + - message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Role\)\:\:getOriginalEncryptableValues\(\)\.$#' identifier: method.notFound @@ -30,24 +36,6 @@ parameters: count: 1 path: src/Auth/Models/User.php - - - message: '#^Call to function method_exists\(\) with \$this\(Winter\\Storm\\Auth\\Models\\User\) and ''getHashableAttribut…'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Auth/Models/User.php - - - - message: '#^Call to function property_exists\(\) with \$this\(Winter\\Storm\\Auth\\Models\\User\) and ''attributeNames'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Auth/Models/User.php - - - - message: '#^Call to function property_exists\(\) with \$this\(Winter\\Storm\\Auth\\Models\\User\) and ''customMessages'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Auth/Models/User.php - - message: '#^PHPDoc type array\ of property Winter\\Storm\\Auth\\Models\\User\:\:\$hidden is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$hidden\.$#' identifier: property.phpDocType @@ -186,6 +174,54 @@ parameters: count: 1 path: src/Database/Model.php + - + message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\BelongsTo constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\BelongsToMany constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\HasMany constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\HasManyThrough constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\HasOne constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\HasOneThrough constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\MorphTo constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Model.php + + - + message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\MorphToMany constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' + identifier: argument.type + count: 1 + path: src/Database/Model.php + - message: '#^Return type \(Winter\\Storm\\Database\\Collection\) of method Winter\\Storm\\Database\\Model\:\:newCollection\(\) should be compatible with return type \(Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\), static\(Illuminate\\Database\\Eloquent\\Model\)\>\) of method Illuminate\\Database\\Eloquent\\Model\:\:newCollection\(\)$#' identifier: method.childReturnType @@ -642,12 +678,6 @@ parameters: count: 1 path: src/Database/Relations/HasOneThrough.php - - - message: '#^Instanceof between \*NEVER\* and Winter\\Storm\\Database\\Relations\\HasManyThrough will always evaluate to false\.$#' - identifier: instanceof.alwaysFalse - count: 4 - path: src/Database/Relations/HasOneThrough.php - - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound @@ -960,6 +990,12 @@ parameters: count: 1 path: src/Database/TreeCollection.php + - + message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: src/Events/Dispatcher.php + - message: '#^Call to an undefined method Illuminate\\Routing\\Router\:\:after\(\)\.$#' identifier: method.notFound diff --git a/src/Filesystem/FilesystemServiceProvider.php b/src/Filesystem/FilesystemServiceProvider.php index 40a10328..5a238fa0 100644 --- a/src/Filesystem/FilesystemServiceProvider.php +++ b/src/Filesystem/FilesystemServiceProvider.php @@ -26,11 +26,9 @@ public function register() protected function extendFilesystemAdapter() { FilesystemAdapter::macro('getPathPrefix', function () { - /** @phpstan-ignore-next-line */ return $this->prefixer->prefixPath(''); }); FilesystemAdapter::macro('setPathPrefix', function (string $prefix) { - /** @phpstan-ignore-next-line */ $this->prefixer = new PathPrefixer($prefix, $this->config['directory_separator'] ?? DIRECTORY_SEPARATOR); }); } diff --git a/tests/Parse/Assetic/LessCompilerTest.php b/tests/Parse/Assetic/LessCompilerTest.php index 292ef3c9..14f0f6ff 100644 --- a/tests/Parse/Assetic/LessCompilerTest.php +++ b/tests/Parse/Assetic/LessCompilerTest.php @@ -110,5 +110,4 @@ protected function compile(string $sourceFile, ?LessCompiler $compiler = null): $compiler->filterLoad($asset); return $asset->getContent(); } - } From e842470549242260b4fe343eccf3a6299c282df4 Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Tue, 11 Aug 2026 10:31:03 -0600 Subject: [PATCH 170/197] Fix SQLite ->change(): restore attribute preservation without the crash-prone rebuild (#239) Co-authored-by: Claude Opus 4.8 (1M context) --- phpstan-baseline.neon | 36 ---- src/Database/Schema/Blueprint.php | 19 ++ src/Database/Schema/BlueprintState.php | 77 ++++++++ .../Schema/Grammars/SQLiteGrammar.php | 168 +----------------- .../Grammars/SQLiteSchemaGrammarTest.php | 142 ++++++++++----- 5 files changed, 199 insertions(+), 243 deletions(-) create mode 100644 src/Database/Schema/BlueprintState.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7b8caea0..37a96bd5 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -930,42 +930,6 @@ parameters: count: 1 path: src/Database/Schema/Grammars/PostgresGrammar.php - - - message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$autoIncrement\.$#' - identifier: property.notFound - count: 1 - path: src/Database/Schema/Grammars/SQLiteGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$name\.$#' - identifier: property.notFound - count: 2 - path: src/Database/Schema/Grammars/SQLiteGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$storedAs\.$#' - identifier: property.notFound - count: 1 - path: src/Database/Schema/Grammars/SQLiteGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$storedAsJson\.$#' - identifier: property.notFound - count: 1 - path: src/Database/Schema/Grammars/SQLiteGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$virtualAs\.$#' - identifier: property.notFound - count: 1 - path: src/Database/Schema/Grammars/SQLiteGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Support\\Fluent\:\:\$virtualAsJson\.$#' - identifier: property.notFound - count: 1 - path: src/Database/Schema/Grammars/SQLiteGrammar.php - - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' identifier: property.notFound diff --git a/src/Database/Schema/Blueprint.php b/src/Database/Schema/Blueprint.php index 53bd12ee..000b01f1 100644 --- a/src/Database/Schema/Blueprint.php +++ b/src/Database/Schema/Blueprint.php @@ -24,4 +24,23 @@ public function dropColumnIfExists($columns) return !empty($columns) ? $this->dropColumn($columns) : $this; } + + /** + * Add the commands that are implied by the blueprint's state. + * + * Swaps in Winter's BlueprintState so that a `->change()` preserves the existing column's + * attributes (see {@see \Winter\Storm\Database\Schema\BlueprintState}). Laravel hard-codes its + * own state class when an alter command is present, so we re-seed with ours once the base + * implied commands - and therefore the base state - have been created. + * + * @return void + */ + protected function addImpliedCommands() + { + parent::addImpliedCommands(); + + if (!is_null($this->state)) { + $this->state = new BlueprintState($this, $this->connection); + } + } } diff --git a/src/Database/Schema/BlueprintState.php b/src/Database/Schema/BlueprintState.php new file mode 100644 index 00000000..09b999c9 --- /dev/null +++ b/src/Database/Schema/BlueprintState.php @@ -0,0 +1,77 @@ +change()` unless the new column definition explicitly overrides them. + * + * Laravel 11 replaces the column definition wholesale when a column is changed, dropping any + * attribute (nullable, default, collation, generated expression, ...) the migration did not + * re-specify. On engines that rebuild the table to apply a change (SQLite) this state drives the + * rebuild, so merging the previous attributes here restores the expected behaviour through Laravel's + * own single rebuild - without Storm maintaining a second, hand-rolled rebuild in the grammar. + */ +class BlueprintState extends BaseBlueprintState +{ + /** + * Attributes carried over from the existing column definition when the changed definition does + * not set them explicitly. + * + * This mirrors the standard column modifiers Laravel's grammars emit. Should a future Laravel + * version introduce a new preservable modifier, add it here - omitting one is never a + * regression (Laravel already drops it on change), only a missed preservation opportunity. + * + * @var string[] + */ + protected array $preservedAttributes = [ + 'nullable', + 'default', + 'collation', + 'comment', + 'virtualAs', + 'virtualAsJson', + 'storedAs', + 'storedAsJson', + ]; + + /** + * Update the blueprint's state, preserving existing column attributes when a column is changed. + * + * @param \Illuminate\Support\Fluent $command + * @return void + */ + public function update(Fluent $command) + { + if ($command['name'] === 'change' && $command['column'] instanceof Fluent) { + $this->preserveExistingColumnAttributes($command['column']); + } + + parent::update($command); + } + + /** + * Copy any preserved attribute from the current (pre-change) column definition onto the changed + * column definition when the migration did not set it explicitly. + * + * @param \Illuminate\Support\Fluent $column + * @return void + */ + protected function preserveExistingColumnAttributes(Fluent $column): void + { + foreach ($this->getColumns() as $existing) { + if ($existing['name'] !== $column['name']) { + continue; + } + + foreach ($this->preservedAttributes as $attribute) { + if (!isset($column[$attribute]) && isset($existing[$attribute])) { + $column[$attribute] = $existing[$attribute]; + } + } + + return; + } + } +} diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index 65d4ceb3..bee7067d 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -2,152 +2,16 @@ namespace Winter\Storm\Database\Schema\Grammars; -use Illuminate\Database\Query\Expression; -use Illuminate\Database\Schema\Blueprint; -use Illuminate\Database\Schema\ColumnDefinition; -use Illuminate\Database\Schema\ForeignKeyDefinition; -use Illuminate\Database\Schema\IndexDefinition; use Illuminate\Database\Schema\Grammars\SQLiteGrammar as BaseSQLiteGrammar; -use Illuminate\Support\Fluent; class SQLiteGrammar extends BaseSQLiteGrammar { /** - * Compile a change column command into a series of SQL statements. + * Format a value so that it can be used in "default" clauses. * - * Starting with Laravel 11, previous column attributes do not persist when changing a column. - * This restores Laravel previous behavior where existing column attributes are kept - * unless they get changed by the new Blueprint. - * - * @param \Illuminate\Database\Schema\Blueprint $blueprint - * @param \Illuminate\Support\Fluent $command - * @return array|string - * - * @throws \RuntimeException + * @param mixed $value + * @return string */ - public function compileChange(Blueprint $blueprint, Fluent $command) - { - $autoIncrementColumn = null; - $columnNames = []; - $schema = $this->connection->getSchemaBuilder(); - $table = $blueprint->getTable(); - - $changedColumns = collect($blueprint->getChangedColumns()); - $oldColumns = collect($schema->getColumns($table)); - - $columns = $oldColumns - ->map(function ($column) use ($blueprint, $changedColumns, &$columnNames, &$autoIncrementColumn, $oldColumns) { - $column = $changedColumns->first(fn ($col) => $col->name === $column['name'], $column); - - if (! $column instanceof Fluent) { - $isGenerated = ! is_null($column['generation']); - $column = new ColumnDefinition([ - 'change' => true, - 'name' => $column['name'], - 'type' => $column['type_name'], - 'nullable' => $column['nullable'], - 'default' => $column['default'] ? new Expression($column['default']) : null, - 'autoIncrement' => $column['auto_increment'], - 'collation' => $column['collation'], - 'comment' => $column['comment'], - 'virtualAs' => $isGenerated && $column['generation']['type'] === 'virtual' - ? $column['generation']['expression'] : null, - 'storedAs' => $isGenerated && $column['generation']['type'] === 'stored' - ? $column['generation']['expression'] : null, - ]); - } - - $name = $this->wrap($column); - $autoIncrementColumn = $column->autoIncrement ? $column->name : $autoIncrementColumn; - - if (is_null($column->virtualAs) && is_null($column->virtualAsJson) && - is_null($column->storedAs) && is_null($column->storedAsJson) - ) { - $columnNames[] = $name; - } - - $oldColumn = $oldColumns->where('name', $column->name)->first(); - if (!$oldColumn instanceof ColumnDefinition) { - $oldColumn = new ColumnDefinition($oldColumn); - } - $sql = $name.' '.$this->getType($column); - - foreach ($this->modifiers as $modifier) { - if (method_exists($this, $method = "modify{$modifier}")) { - $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) && !isset($column->{$mod}) ? $oldColumn : $column; - $sql .= $this->{$method}($blueprint, $col); - } - } - return $sql; - })->all(); - - $foreignKeys = collect($schema->getForeignKeys($table))->map(fn ($foreignKey) => new ForeignKeyDefinition([ - 'columns' => $foreignKey['columns'], - 'on' => $foreignKey['foreign_table'], - 'references' => $foreignKey['foreign_columns'], - 'onUpdate' => $foreignKey['on_update'], - 'onDelete' => $foreignKey['on_delete'], - ]))->all(); - - [$primary, $indexes] = collect($schema->getIndexes($table))->map(fn ($index) => new IndexDefinition([ - 'name' => match (true) { - $index['primary'] => 'primary', - $index['unique'] => 'unique', - default => 'index', - }, - 'index' => $index['name'], - 'columns' => $index['columns'], - ]))->partition(fn ($index) => $index->name === 'primary'); - - $indexes = collect($indexes)->reject(fn ($index) => str_starts_with('sqlite_', $index->index))->map( - fn ($index) => $this->{'compile'.ucfirst($index->name)}($blueprint, $index) - )->all(); - - $tempTable = $this->wrapTable($blueprint, '__temp__'.$this->connection->getTablePrefix()); - $table = $this->wrapTable($blueprint); - $columnNames = implode(', ', $columnNames); - - $foreignKeyConstraintsEnabled = $this->connection->scalar($this->pragma('foreign_keys')); - - $sqlQuery = array_filter( - array_merge( - [ - $foreignKeyConstraintsEnabled ? $this->compileDisableForeignKeyConstraints() : null, - sprintf( - 'create table %s (%s%s%s)', - $tempTable, - implode(', ', $columns), - $this->addForeignKeys($foreignKeys), - $autoIncrementColumn ? '' : $this->addPrimaryKeys($primary->first()) - ), - sprintf( - 'insert into %s (%s) select %s from %s', - $tempTable, - $columnNames, - $columnNames, - $table - ), - sprintf( - 'drop table %s', - $table - ), - sprintf( - 'alter table %s rename to %s', - $tempTable, - $table - ), - ], - $indexes, - [ - $foreignKeyConstraintsEnabled ? $this->compileEnableForeignKeyConstraints() : null - ] - ) - ); - - return $sqlQuery; - } - public function getDefaultValue($value) { if (is_string($value)) { @@ -156,30 +20,4 @@ public function getDefaultValue($value) return parent::getDefaultValue($value); } - - /** - * Create the column definition for a tinyint type. - * - * SQLite's column introspection returns 'tinyint' as the type_name for boolean and tinyInteger - * columns. The base grammar only has typeTinyInteger, so we need this alias to avoid - * BadMethodCallException when compileChange rebuilds a table that contains boolean columns. - * - * @param \Illuminate\Support\Fluent $column - * @return string - */ - protected function typeTinyint(Fluent $column) - { - return 'integer'; - } - - /** - * Create the column definition for a varchar type. - * - * @param \Illuminate\Support\Fluent $column - * @return string - */ - protected function typeVarChar(Fluent $column) - { - return 'varchar'; - } } diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index 2426caa6..71a7b48f 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -2,9 +2,9 @@ namespace Winter\Storm\Tests\Database\Schema\Grammars; -use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\SQLiteBuilder; use Illuminate\Database\SQLiteConnection; +use Winter\Storm\Database\Schema\Blueprint; use Winter\Storm\Database\Schema\Grammars\SQLiteGrammar; use Winter\Storm\Tests\GrammarTestCase; @@ -18,69 +18,127 @@ public function setUp(): void parent::setUp(); } - public function testNoInitialModifiersAddNullable() + /** + * Boot a real in-memory SQLite connection wired with Winter's schema grammar and Blueprint, then + * seed it with the provided table definition. Column changes on SQLite force a full table + * rebuild, so a real connection (not a mocked one) is required to exercise the introspection that + * drives it. + * + * @return array{0: \PDO, 1: \Illuminate\Database\SQLiteConnection, 2: \Illuminate\Database\Schema\SQLiteBuilder} + */ + protected function bootSqlite(string $createTableSql): array { - $initialBlueprint = $this->getBlueprint('users'); - $initialBlueprint->string('name'); + $pdo = new \PDO('sqlite::memory:'); + $pdo->exec($createTableSql); - $statements = $this->runBlueprint($initialBlueprint); - $this->assertSame('alter table "users" add column "name" varchar not null', $statements[0]); + $connection = new SQLiteConnection($pdo, ':memory:', ''); + $connection->setSchemaGrammar(new SQLiteGrammar($connection)); - $changedBlueprint = $this->getBlueprint('users'); - $changedBlueprint->string('name')->nullable()->change(); + $builder = $connection->getSchemaBuilder(); + $builder->blueprintResolver(fn ($conn, $table, $callback) => new Blueprint($conn, $table, $callback)); - $statements = $this->runBlueprint($changedBlueprint); - $this->assertStringContainsString('"name" varchar', $statements[0]); + return [$pdo, $connection, $builder]; } - public function testNullableInitialModifierAddDefault() + /** + * Return the PRAGMA table_info row for a single column. + * + * @return array{name: string, type: string, notnull: string, dflt_value: ?string, pk: string} + */ + protected function columnInfo(\PDO $pdo, string $table, string $column): array { - $initialBlueprint = $this->getBlueprint('users'); - $initialBlueprint->string('name')->nullable(); + foreach ($pdo->query('PRAGMA table_info("' . $table . '")') as $info) { + if ($info['name'] === $column) { + return $info; + } + } - $statements = $this->runBlueprint($initialBlueprint); - $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); + $this->fail("Column [{$column}] was not found on table [{$table}]."); + } - $changedBlueprint = $this->getBlueprint('users'); - $changedBlueprint->string('name')->default('admin')->change(); + public function testChangeMakesColumnNullable(): void + { + [$pdo, , $builder] = $this->bootSqlite('CREATE TABLE users (id integer primary key, name varchar not null)'); - $statements = $this->runBlueprint($changedBlueprint); - $this->assertStringContainsString("varchar default 'admin'", $statements[0]); + $builder->table('users', fn (Blueprint $table) => $table->string('name')->nullable()->change()); + + $this->assertSame(0, (int) $this->columnInfo($pdo, 'users', 'name')['notnull']); } - public function testNullableInitialModifierAddDefaultNotNullable() + public function testChangeAddingDefaultPreservesExistingNullable(): void { - $initialBlueprint = $this->getBlueprint('users'); - $initialBlueprint->string('name')->nullable(); + [$pdo, , $builder] = $this->bootSqlite('CREATE TABLE users (id integer primary key, name varchar)'); - $statements = $this->runBlueprint($initialBlueprint); - $this->assertSame('alter table "users" add column "name" varchar', $statements[0]); + // Only a default is specified; the column's existing nullable state must be preserved. + $builder->table('users', fn (Blueprint $table) => $table->string('name')->default('admin')->change()); - $changedBlueprint = $this->getBlueprint('users'); - $changedBlueprint->string('name')->default('admin')->nullable(false)->change(); + $info = $this->columnInfo($pdo, 'users', 'name'); + $this->assertSame(0, (int) $info['notnull'], 'The column should remain nullable.'); + $this->assertSame("'admin'", $info['dflt_value']); + } - $statements = $this->runBlueprint($changedBlueprint); - $this->assertStringContainsString("\"name\" varchar not null default 'admin'", $statements[0]); + public function testChangeCanAddDefaultAndDropNullable(): void + { + [$pdo, , $builder] = $this->bootSqlite('CREATE TABLE users (id integer primary key, name varchar)'); + + $builder->table('users', fn (Blueprint $table) => $table->string('name')->default('admin')->nullable(false)->change()); + + $info = $this->columnInfo($pdo, 'users', 'name'); + $this->assertSame(1, (int) $info['notnull']); + $this->assertSame("'admin'", $info['dflt_value']); } - public function testTypeTinyintTypeIsValid(): void + public function testChangePreservesUnspecifiedAttributes(): void { - $pdo = new \PDO('sqlite::memory:'); - $pdo->exec('CREATE TABLE users (is_active tinyint not null, name varchar not null)'); + [$pdo, , $builder] = $this->bootSqlite("CREATE TABLE users (id integer primary key, name varchar default 'bob')"); - $connection = new SQLiteConnection($pdo, ':memory:', ''); - $grammar = new SQLiteGrammar($connection); - $connection->setSchemaGrammar($grammar); + // Change only the type; the existing default must survive (pre-Laravel 11 behaviour). + $builder->table('users', fn (Blueprint $table) => $table->text('name')->change()); + + $info = $this->columnInfo($pdo, 'users', 'name'); + $this->assertSame('text', strtolower($info['type'])); + $this->assertSame("'bob'", $info['dflt_value']); + } + + public function testChangePreservesTinyintTypeOfOtherColumns(): void + { + [$pdo, , $builder] = $this->bootSqlite('CREATE TABLE users (is_active tinyint not null, name varchar not null)'); + + // Changing an unrelated column rebuilds the whole table; is_active must keep its declared + // type verbatim rather than being re-derived to "integer". + $builder->table('users', fn (Blueprint $table) => $table->string('name')->nullable()->change()); + + $info = $this->columnInfo($pdo, 'users', 'is_active'); + $this->assertSame('tinyint', strtolower($info['type'])); + $this->assertSame(1, (int) $info['notnull']); + } + + public function testChangeOnTableWithDecimalAndBinaryColumnsIsFaithful(): void + { + [$pdo, , $builder] = $this->bootSqlite( + 'CREATE TABLE t (id integer primary key, amount numeric(9,2), payload blob, name varchar)' + ); + + // Prior to this fix the rebuild re-derived every column through the grammar, throwing + // "Method SQLiteGrammar::typeNumeric does not exist" for the decimal column. + $builder->table('t', fn (Blueprint $table) => $table->text('name')->change()); + + $this->assertSame('numeric(9,2)', strtolower($this->columnInfo($pdo, 't', 'amount')['type'])); + $this->assertSame('blob', strtolower($this->columnInfo($pdo, 't', 'payload')['type'])); + } + + public function testChangeRebuildsTableExactlyOnce(): void + { + [, $connection] = $this->bootSqlite('CREATE TABLE t (id integer primary key, name varchar)'); - $blueprint = new Blueprint($connection, 'users'); - $blueprint->string('name')->nullable()->change(); + $blueprint = new Blueprint($connection, 't'); + $blueprint->text('name')->change(); - // Prior to the typeTinyint fix, this would throw: - // BadMethodCallException: Method SQLiteGrammar::typeTinyint does not exist - $statements = $blueprint->toSql(); + $rebuilds = count(array_filter( + $blueprint->toSql(), + fn ($statement) => str_contains($statement, 'create table "__temp__') + )); - // compileChange maps 'tinyint' (SQLite's introspected type_name) to 'integer' - $this->assertNotEmpty($statements); - $this->assertStringContainsString('"is_active" integer', $statements[0]); + $this->assertSame(1, $rebuilds, 'A single ->change() must rebuild the table exactly once.'); } } From 0f69f95cf35b5cc59eee7dbe5acfa67c3c3f3eb7 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 10:32:32 -0400 Subject: [PATCH 171/197] prevent striping unique single quote in default value --- .../Schema/Grammars/Concerns/MySqlBasedGrammar.php | 10 ++++++++-- src/Database/Schema/Grammars/PostgresGrammar.php | 10 ++++++++-- src/Database/Schema/Grammars/SQLiteGrammar.php | 4 ++-- src/Database/Schema/Grammars/SqlServerGrammar.php | 10 ++++++++-- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php index bf16621e..17b992ec 100644 --- a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php +++ b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php @@ -56,10 +56,16 @@ public function compileChange(Blueprint $blueprint, Fluent $command) return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); } + /** + * Format a value so that it can be used in "default" clauses. + * + * @param mixed $value + * @return string + */ public function getDefaultValue($value) { - if (is_string($value)) { - $value = preg_replace('#\'#', '', $value); + if (is_string($value) && strlen($value) >= 2 && $value[0] === "'" && substr($value, -1) === "'") { + $value = substr($value, 1, -1); } return parent::getDefaultValue($value); diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 2f6f4739..84b2a844 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -60,10 +60,16 @@ public function compileChange(Blueprint $blueprint, Fluent $command) return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); } + /** + * Format a value so that it can be used in "default" clauses. + * + * @param mixed $value + * @return string + */ public function getDefaultValue($value) { - if (is_string($value)) { - $value = preg_replace('#\'#', '', $value); + if (is_string($value) && strlen($value) >= 2 && $value[0] === "'" && substr($value, -1) === "'") { + $value = substr($value, 1, -1); } return parent::getDefaultValue($value); diff --git a/src/Database/Schema/Grammars/SQLiteGrammar.php b/src/Database/Schema/Grammars/SQLiteGrammar.php index bee7067d..5107a1c7 100755 --- a/src/Database/Schema/Grammars/SQLiteGrammar.php +++ b/src/Database/Schema/Grammars/SQLiteGrammar.php @@ -14,8 +14,8 @@ class SQLiteGrammar extends BaseSQLiteGrammar */ public function getDefaultValue($value) { - if (is_string($value)) { - $value = preg_replace('#\'#', '', $value); + if (is_string($value) && strlen($value) >= 2 && $value[0] === "'" && substr($value, -1) === "'") { + $value = substr($value, 1, -1); } return parent::getDefaultValue($value); diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 613dc0a2..ac41addb 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -57,10 +57,16 @@ public function compileChange(Blueprint $blueprint, Fluent $command) return $changes; } + /** + * Format a value so that it can be used in "default" clauses. + * + * @param mixed $value + * @return string + */ public function getDefaultValue($value) { - if (is_string($value)) { - $value = preg_replace('#\'#', '', $value); + if (is_string($value) && strlen($value) >= 2 && $value[0] === "'" && substr($value, -1) === "'") { + $value = substr($value, 1, -1); } return parent::getDefaultValue($value); From 32b69ce8c98e6f192011cc143a23247a6c19a4e9 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 10:45:36 -0400 Subject: [PATCH 172/197] use column name, not column object for wrap method call --- src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php | 2 +- src/Database/Schema/Grammars/PostgresGrammar.php | 2 +- src/Database/Schema/Grammars/SqlServerGrammar.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php index 17b992ec..6f749de7 100644 --- a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php +++ b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php @@ -33,7 +33,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command) $sql = sprintf( '%s %s%s %s', is_null($column->renameTo) ? 'modify' : 'change', - $this->wrap($column), + $this->wrap($column->name), is_null($column->renameTo) ? '' : ' '.$this->wrap($column->renameTo), $this->getType($column) ); diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index 84b2a844..faf156f0 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -54,7 +54,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command) } } - $columns[] = implode(', ', $this->prefixArray('alter column '.$this->wrap($column), $changes)); + $columns[] = implode(', ', $this->prefixArray('alter column '.$this->wrap($column->name), $changes)); } return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index ac41addb..96337feb 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -34,7 +34,7 @@ public function compileChange(Blueprint $blueprint, Fluent $command) $sql = sprintf( 'alter table %s alter column %s %s', $this->wrapTable($blueprint), - $this->wrap($column), + $this->wrap($column->name), $this->getType($column) ); From 7cc37a05d35556132bf45209c27271e5b4f98c56 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 12:50:59 -0400 Subject: [PATCH 173/197] add some of CodeRabbit's suggested fix --- src/Config/ConfigWriter.php | 2 +- src/Database/Schema/Grammars/SqlServerGrammar.php | 2 +- tests/Database/QueryBuilderPaginateTest.php | 1 + tests/Support/EmitterTest.php | 2 ++ 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Config/ConfigWriter.php b/src/Config/ConfigWriter.php index 0bbf7265..5395c809 100644 --- a/src/Config/ConfigWriter.php +++ b/src/Config/ConfigWriter.php @@ -36,7 +36,7 @@ public function toContent(string $contents, $newValues): string try { $ast = $parser->parse($contents); } catch (Error $e) { - throw new SystemException($e); + throw new SystemException($e->getMessage(), $e->getCode(), $e); } return (new ArrayFile($ast, $parser))->set($newValues)->render(); diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 96337feb..46ea1604 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -24,7 +24,7 @@ class SqlServerGrammar extends BaseSqlServerGrammar */ public function compileChange(Blueprint $blueprint, Fluent $command) { - $changes = [$this->compileDropDefaultConstraint($blueprint, $command)]; + $changes = (array) $this->compileDropDefaultConstraint($blueprint, $command); $schema = $this->connection->getSchemaBuilder(); $table = $blueprint->getTable(); diff --git a/tests/Database/QueryBuilderPaginateTest.php b/tests/Database/QueryBuilderPaginateTest.php index 15c2030e..4da4c0a0 100644 --- a/tests/Database/QueryBuilderPaginateTest.php +++ b/tests/Database/QueryBuilderPaginateTest.php @@ -46,6 +46,7 @@ public function tearDown(): void public function testGroupedPaginationWithBoundWhere() { $paginator = DB::table('pagination_test') + ->select('group_id') ->whereRaw('(group_id = ? OR group_id > ?)', [1, 0]) ->groupBy('group_id') ->orderBy('group_id') diff --git a/tests/Support/EmitterTest.php b/tests/Support/EmitterTest.php index 6376acf1..07ff1659 100644 --- a/tests/Support/EmitterTest.php +++ b/tests/Support/EmitterTest.php @@ -22,6 +22,8 @@ class EmitterTest extends \Winter\Storm\Tests\TestCase */ public function setUp(): void { + parent::setUp(); + $this->traitObject = new class { use \Winter\Storm\Support\Traits\Emitter; }; From 5c4fdc0371de7d1f6afdcc87191ed9a247fe0427 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 13:09:50 -0400 Subject: [PATCH 174/197] league/commonmark is already included in Laravel 12.x --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 5fe1446f..fa17424c 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,6 @@ "enshrined/svg-sanitize": "~0.16", "laravel/framework": "^12.30.1", "laravel/tinker": "^2.8.1", - "league/commonmark": "^2.10.2", "league/csv": "~9.1", "nesbot/carbon": "^3.0", "scssphp/scssphp": "~1.0", From 9911ee7ebde3bd6339e914b3ed191d025405d16d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 6 Oct 2026 17:13:17 +0000 Subject: [PATCH 175/197] Use GitHub token for Composer auth Co-authored-by: mjauvin <2013630+mjauvin@users.noreply.github.com> --- .github/workflows/code-analysis.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index fde48bf5..5fbf6dea 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -16,6 +16,8 @@ jobs: codeAnalysis: runs-on: ubuntu-latest name: Code Analysis + permissions: + contents: read env: extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip key: winter-storm-cache-v1.2 @@ -31,7 +33,7 @@ jobs: coverage: none - name: Configure Composer GitHub token - run: composer config --global --auth github-oauth.github.com ${{ secrets.COMPOSER_GITHUB_TOKEN }} + run: composer config --global --auth github-oauth.github.com ${{ github.token }} - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts From fb218463bafd8018238af8666fe381b44e91852b Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 13:36:25 -0400 Subject: [PATCH 176/197] undo copilot changes --- .github/workflows/code-analysis.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 5fbf6dea..8f17cbf0 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -16,8 +16,6 @@ jobs: codeAnalysis: runs-on: ubuntu-latest name: Code Analysis - permissions: - contents: read env: extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip key: winter-storm-cache-v1.2 @@ -32,9 +30,6 @@ jobs: extensions: ${{ env.extensions }} coverage: none - - name: Configure Composer GitHub token - run: composer config --global --auth github-oauth.github.com ${{ github.token }} - - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts From ffb08e5840ff98ba1082a756bd6a1eb2d8d5f5d6 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 13:43:19 -0400 Subject: [PATCH 177/197] add GITHUB_TOKEN to env --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1ee4b8c9..7828a495 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,6 +23,7 @@ jobs: env: extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip key: winter-storm-cache-v1.2 + GITHUB_TOKEN: ${{ secrets.COMPOSER_GITHUB_TOKEN }} steps: - name: Checkout changes uses: actions/checkout@v4 From c485ab6cec8d3e037ce65ef3f8205048309a953d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 13:48:44 -0400 Subject: [PATCH 178/197] remove composer config --- .github/workflows/tests.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7828a495..aa94c0f4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,9 +35,6 @@ jobs: extensions: ${{ env.extensions }} coverage: none - - name: Configure Composer GitHub token - run: composer config --global --auth github-oauth.github.com ${{ secrets.COMPOSER_GITHUB_TOKEN }} - - name: Install Composer dependencies run: composer install --no-interaction --no-progress --no-scripts From d333d2a4a95d9cb60d602aaf06d408935330b754 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 14:46:35 -0400 Subject: [PATCH 179/197] try fixing github auth --- .github/workflows/code-analysis.yaml | 1 + .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 8f17cbf0..71dde4b6 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -19,6 +19,7 @@ jobs: env: extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip key: winter-storm-cache-v1.2 + COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}' steps: - name: Checkout changes uses: actions/checkout@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aa94c0f4..53d530b4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: env: extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip key: winter-storm-cache-v1.2 - GITHUB_TOKEN: ${{ secrets.COMPOSER_GITHUB_TOKEN }} + COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}' steps: - name: Checkout changes uses: actions/checkout@v4 From 289a62db2b8ae12ee991ba60d32ff2373b2e6a5d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 15:13:07 -0400 Subject: [PATCH 180/197] add missing phpstan rules --- phpstan.neon | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index a7583c8f..779754b5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,14 +7,19 @@ parameters: - src level: 5 ignoreErrors: - - identifier: trait.unused - - message: '#Anonymous function should return static\(.*\) but returns static\(.*\)#' - paths: - - src/Support/Traits/Singleton.php - reportUnmatched: false + - + message: '#Call to private method select\(\)#' + path: src/Database/Relations/Concerns/CanBeCounted.php + + - + message: '#Anonymous function should return static\(.*\) but returns static\(.*\)#' + identifier: trait.unused + path: src/Support/Traits/Singleton.php + reportUnmatched: false excludePaths: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php + - src/Parse/PHP/ArrayPrinter.php - src/Foundation/Console/KeyGenerateCommand.php - src/Scaffold/GeneratorCommand.php disableSchemaScan: true From 4f6e53ff7201ff55e59c962799b1856eb7da103e Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 15:14:59 -0400 Subject: [PATCH 181/197] fix indentation --- phpstan.neon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 779754b5..a8d49526 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,11 +7,11 @@ parameters: - src level: 5 ignoreErrors: - - + - message: '#Call to private method select\(\)#' path: src/Database/Relations/Concerns/CanBeCounted.php - - + - message: '#Anonymous function should return static\(.*\) but returns static\(.*\)#' identifier: trait.unused path: src/Support/Traits/Singleton.php From 1f00f955f170a92bcb5e36573e8bc6a6f63ea743 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 15:17:47 -0400 Subject: [PATCH 182/197] use full path to TestCase class --- tests/Parse/Assetic/ScssCompilerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Parse/Assetic/ScssCompilerTest.php b/tests/Parse/Assetic/ScssCompilerTest.php index e6652d00..ae7e9a12 100644 --- a/tests/Parse/Assetic/ScssCompilerTest.php +++ b/tests/Parse/Assetic/ScssCompilerTest.php @@ -12,7 +12,7 @@ * * Mirrors LessCompilerTest, which covers the equivalent LESS wiring. */ -class ScssCompilerTest extends TestCase +class ScssCompilerTest extends \Winter\Storm\Tests\TestCase { /** @var string */ protected $tmpRoot; From 6d85b1fbd1f2156cabf2f2335c8c402068380b2a Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 15:20:14 -0400 Subject: [PATCH 183/197] remove old exclude rule --- phpstan.neon | 1 - 1 file changed, 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index a8d49526..159f9a8d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -19,7 +19,6 @@ parameters: excludePaths: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php - - src/Parse/PHP/ArrayPrinter.php - src/Foundation/Console/KeyGenerateCommand.php - src/Scaffold/GeneratorCommand.php disableSchemaScan: true From 67f5877dc99c07c94d48b859ab7ed51d81a20a2d Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 15:59:33 -0400 Subject: [PATCH 184/197] fix phpstan --- phpstan-baseline.neon | 181 +----------------------------------------- phpstan.neon | 9 +-- 2 files changed, 8 insertions(+), 182 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 37a96bd5..15e13044 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3,1031 +3,858 @@ parameters: - message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Group\)\:\:getOriginalEncryptableValues\(\)\.$#' identifier: method.notFound - count: 1 path: src/Auth/Models/Group.php - message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Group\)\:\:getOriginalHashValues\(\)\.$#' identifier: method.notFound - count: 1 path: src/Auth/Models/Group.php - message: '#^Method Winter\\Storm\\Auth\\Models\\Preferences\:\:findRecord\(\) should return Winter\\Storm\\Auth\\Models\\Preferences\|null but returns stdClass\|null\.$#' identifier: return.type - count: 1 path: src/Auth/Models/Preferences.php - message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Role\)\:\:getOriginalEncryptableValues\(\)\.$#' identifier: method.notFound - count: 1 path: src/Auth/Models/Role.php - message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\Role\)\:\:getOriginalHashValues\(\)\.$#' identifier: method.notFound - count: 1 path: src/Auth/Models/Role.php - message: '#^Call to an undefined method \$this\(Winter\\Storm\\Auth\\Models\\User\)\:\:getOriginalEncryptableValues\(\)\.$#' identifier: method.notFound - count: 1 path: src/Auth/Models/User.php - message: '#^PHPDoc type array\ of property Winter\\Storm\\Auth\\Models\\User\:\:\$hidden is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$hidden\.$#' identifier: property.phpDocType - count: 1 path: src/Auth/Models/User.php - message: '#^PHPDoc type array\ of property Winter\\Storm\\Database\\Attach\\File\:\:\$appends is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$appends\.$#' identifier: property.phpDocType - count: 1 path: src/Database/Attach/File.php - message: '#^PHPDoc type array\ of property Winter\\Storm\\Database\\Attach\\File\:\:\$fillable is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$fillable\.$#' identifier: property.phpDocType - count: 1 path: src/Database/Attach/File.php - message: '#^PHPDoc type array\ of property Winter\\Storm\\Database\\Attach\\File\:\:\$hidden is not covariant with PHPDoc type list\ of overridden property Illuminate\\Database\\Eloquent\\Model\:\:\$hidden\.$#' identifier: property.phpDocType - count: 1 path: src/Database/Attach/File.php - message: '#^PHPDoc tag @var with type Winter\\Storm\\Database\\Model is not subtype of native type \$this\(Winter\\Storm\\Database\\Behaviors\\Encryptable\)\.$#' identifier: varTag.nativeType - count: 1 path: src/Database/Behaviors/Encryptable.php - message: '#^Parameter \#1 \$app of class Illuminate\\Database\\DatabaseManager constructor expects Illuminate\\Contracts\\Foundation\\Application, Illuminate\\Contracts\\Container\\Container given\.$#' identifier: argument.type - count: 1 path: src/Database/Capsule/Manager.php - message: '#^Call to an undefined method Illuminate\\Database\\ConnectionInterface\:\:getName\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/MemoryCache.php - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$is_bind\.$#' identifier: property.notFound - count: 1 path: src/Database/Model.php - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$master_field\.$#' identifier: property.notFound - count: 1 path: src/Database/Model.php - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$pivot_data\.$#' identifier: property.notFound - count: 1 path: src/Database/Model.php - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$slave_id\.$#' identifier: property.notFound - count: 1 path: src/Database/Model.php - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$slave_type\.$#' identifier: property.notFound - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:getDeferredBindingRecords\(\) should return Winter\\Storm\\Database\\Collection but returns Illuminate\\Database\\Eloquent\\Collection\\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newBelongsTo\(\) should return Illuminate\\Database\\Eloquent\\Relations\\BelongsTo\ but returns Winter\\Storm\\Database\\Relations\\BelongsTo\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newBelongsToMany\(\) should return Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\ but returns Winter\\Storm\\Database\\Relations\\BelongsToMany\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newHasMany\(\) should return Illuminate\\Database\\Eloquent\\Relations\\HasMany\ but returns Winter\\Storm\\Database\\Relations\\HasMany\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newHasManyThrough\(\) should return Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough\ but returns Winter\\Storm\\Database\\Relations\\HasManyThrough\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newHasOne\(\) should return Illuminate\\Database\\Eloquent\\Relations\\HasOne\ but returns Winter\\Storm\\Database\\Relations\\HasOne\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newHasOneThrough\(\) should return Illuminate\\Database\\Eloquent\\Relations\\HasOneThrough\ but returns Winter\\Storm\\Database\\Relations\\HasOneThrough\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newMorphMany\(\) should return Illuminate\\Database\\Eloquent\\Relations\\MorphMany\ but returns Winter\\Storm\\Database\\Relations\\MorphMany\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newMorphOne\(\) should return Illuminate\\Database\\Eloquent\\Relations\\MorphOne\ but returns Winter\\Storm\\Database\\Relations\\MorphOne\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newMorphTo\(\) should return Illuminate\\Database\\Eloquent\\Relations\\MorphTo\ but returns Winter\\Storm\\Database\\Relations\\MorphTo\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Method Winter\\Storm\\Database\\Model\:\:newMorphToMany\(\) should return Illuminate\\Database\\Eloquent\\Relations\\MorphToMany\ but returns Winter\\Storm\\Database\\Relations\\MorphToMany\.$#' identifier: return.type - count: 1 path: src/Database/Model.php - message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\BelongsTo constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' identifier: argument.type - count: 1 path: src/Database/Model.php - message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\BelongsToMany constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' identifier: argument.type - count: 1 path: src/Database/Model.php - message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\HasMany constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' identifier: argument.type - count: 1 path: src/Database/Model.php - message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\HasManyThrough constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' identifier: argument.type - count: 1 path: src/Database/Model.php - message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\HasOne constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' identifier: argument.type - count: 1 path: src/Database/Model.php - message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\HasOneThrough constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' identifier: argument.type - count: 1 path: src/Database/Model.php - message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\MorphTo constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' identifier: argument.type - count: 1 path: src/Database/Model.php - message: '#^Parameter \#1 \$query of class Winter\\Storm\\Database\\Relations\\MorphToMany constructor expects Illuminate\\Database\\Eloquent\\Builder\, Illuminate\\Database\\Eloquent\\Builder\ given\.$#' identifier: argument.type - count: 1 + path: src/Database/Model.php + + - + message: '#^Parameter \#2 \$mutatedAttributes of method Illuminate\\Database\\Eloquent\\Model\:\:add(Cast|Mutated)AttributesToArray\(\) expects array\, list\ given.$#' path: src/Database/Model.php - message: '#^Return type \(Winter\\Storm\\Database\\Collection\) of method Winter\\Storm\\Database\\Model\:\:newCollection\(\) should be compatible with return type \(Illuminate\\Database\\Eloquent\\Collection\<\(int\|string\), static\(Illuminate\\Database\\Eloquent\\Model\)\>\) of method Illuminate\\Database\\Eloquent\\Model\:\:newCollection\(\)$#' identifier: method.childReturnType - count: 1 path: src/Database/Model.php - message: '#^Return type \(Winter\\Storm\\Database\\Pivot\) of method Winter\\Storm\\Database\\Model\:\:newPivot\(\) should be compatible with return type \(Illuminate\\Database\\Eloquent\\Relations\\Pivot\) of method Illuminate\\Database\\Eloquent\\Model\:\:newPivot\(\)$#' identifier: method.childReturnType - count: 1 path: src/Database/Model.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Model\:\:errors\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/ModelException.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:deleteCancel\(\)\.$#' identifier: method.notFound - count: 2 path: src/Database/Models/DeferredBinding.php - message: '#^Parameter \#1 \$haystack of function str_contains expects string, int given\.$#' identifier: argument.type - count: 1 path: src/Database/MorphPivot.php - message: '#^Parameter \#1 \$ids of method Winter\\Storm\\Database\\Pivot\:\:newQueryForRestoration\(\) expects array\\|string, int given\.$#' identifier: argument.type - count: 1 path: src/Database/MorphPivot.php - message: '#^Parameter \#2 \$string of function explode expects string, int given\.$#' identifier: argument.type - count: 1 path: src/Database/MorphPivot.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getLeftColumnName\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/NestedTreeScope.php - message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' identifier: property.notFound - count: 2 path: src/Database/Query/Grammars/MariaDbGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' identifier: property.notFound - count: 2 path: src/Database/Query/Grammars/MySqlGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' identifier: property.notFound - count: 2 path: src/Database/Query/Grammars/PostgresGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' identifier: property.notFound - count: 2 path: src/Database/Query/Grammars/SQLiteGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Query\\Builder\:\:\$concats\.$#' identifier: property.notFound - count: 2 path: src/Database/Query/Grammars/SqlServerGrammar.php - message: '#^Call to an undefined method Illuminate\\Database\\ConnectionInterface\:\:getName\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/QueryBuilder.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/AttachMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/AttachMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/AttachMany.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\AttachMany\:\:getRelationExistenceQueryForSelfJoin\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/AttachMany.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/AttachMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/AttachOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/AttachOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/AttachOne.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\AttachOne\:\:getRelationExistenceQueryForSelfJoin\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/AttachOne.php - message: '#^Call to private method delete\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphOne\\.$#' identifier: method.private - count: 3 path: src/Database/Relations/AttachOne.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphOne\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/AttachOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:bindEventOnce\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsTo.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsTo.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsTo.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsTo.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\BelongsTo\:\:getForeignKey\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsTo.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\BelongsTo\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/BelongsTo.php - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$sessionKey\.$#' identifier: property.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:bindDeferred\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:bindEventOnce\(\)\.$#' identifier: method.notFound - count: 2 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:fireEvent\(\)\.$#' identifier: method.notFound - count: 4 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getRelationDefinition\(\)\.$#' identifier: method.notFound - count: 2 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:newRelationPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:reloadRelations\(\)\.$#' identifier: method.notFound - count: 2 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:unbindDeferred\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Query\\Builder\:\:lists\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:flushDuplicateCache\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#2 \$columns of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects array\, int\|null given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#2 \$columns of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects array\, int\|null given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#2 \$currentPage \(int\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:paginate\(\) should be compatible with parameter \$columns \(array\\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#2 \$currentPage \(int\|null\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:simplePaginate\(\) should be compatible with parameter \$columns \(array\\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#3 \$columns \(array\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:paginate\(\) should be compatible with parameter \$pageName \(string\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#3 \$columns \(array\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:simplePaginate\(\) should be compatible with parameter \$pageName \(string\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#3 \$pageName of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects string, array given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#3 \$pageName of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects string, array given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#4 \$page of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects int\|null, string given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#4 \$page of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects int\|null, string given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#4 \$pageName \(string\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:paginate\(\) should be compatible with parameter \$page \(int\|null\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Parameter \#4 \$pageName \(string\) of method Winter\\Storm\\Database\\Relations\\BelongsToMany\:\:simplePaginate\(\) should be compatible with parameter \$page \(int\|null\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/BelongsToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasMany.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/HasMany.php - message: '#^Call to private method update\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/HasMany.php - message: '#^Call to private method whereNotIn\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/HasMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasManyThrough.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasManyThrough.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasManyThrough.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasManyThrough\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/HasManyThrough.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasOne.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasOne\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/HasOne.php - message: '#^Call to private method update\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasOne\\.$#' identifier: method.private - count: 2 path: src/Database/Relations/HasOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasOneThrough.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasOneThrough.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/HasOneThrough.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\HasOneThrough\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/HasOneThrough.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphMany.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\MorphMany\:\:getForeignKey\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphMany.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/MorphMany.php - message: '#^Call to private method update\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/MorphMany.php - message: '#^Call to private method whereNotIn\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/MorphMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphOne.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\MorphOne\:\:getForeignKey\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphOne.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphOne\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/MorphOne.php - message: '#^Call to private method update\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphOne\\.$#' identifier: method.private - count: 2 path: src/Database/Relations/MorphOne.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:bindEventOnce\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphTo.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphTo.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphTo.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphTo.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\MorphTo\:\:getForeignKey\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphTo.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphTo\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/MorphTo.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withDefault\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withPivot\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\Relation\:\:withTimestamps\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Call to an undefined method Illuminate\\Database\\Query\\Builder\:\:lists\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Call to an undefined method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:flushDuplicateCache\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Call to private method select\(\) of parent class Illuminate\\Database\\Eloquent\\Relations\\MorphToMany\\.$#' identifier: method.private - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#2 \$columns of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects array\, int\|null given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#2 \$columns of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects array\, int\|null given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#2 \$currentPage \(int\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:paginate\(\) should be compatible with parameter \$columns \(array\\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#2 \$currentPage \(int\|null\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:simplePaginate\(\) should be compatible with parameter \$columns \(array\\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#3 \$columns \(array\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:paginate\(\) should be compatible with parameter \$pageName \(string\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#3 \$columns \(array\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:simplePaginate\(\) should be compatible with parameter \$pageName \(string\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#3 \$pageName of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects string, array given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#3 \$pageName of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects string, array given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#4 \$page of method Illuminate\\Database\\Eloquent\\Builder\\:\:paginate\(\) expects int\|null, string given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#4 \$page of method Illuminate\\Database\\Eloquent\\Builder\\:\:simplePaginate\(\) expects int\|null, string given\.$#' identifier: argument.type - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#4 \$pageName \(string\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:paginate\(\) should be compatible with parameter \$page \(int\|null\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:paginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Parameter \#4 \$pageName \(string\) of method Winter\\Storm\\Database\\Relations\\MorphToMany\:\:simplePaginate\(\) should be compatible with parameter \$page \(int\|null\) of method Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany\\:\:simplePaginate\(\)$#' identifier: method.childParameterType - count: 1 path: src/Database/Relations/MorphToMany.php - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' identifier: property.notFound - count: 1 path: src/Database/Schema/Grammars/MariaDbGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$renameTo\.$#' identifier: property.notFound - count: 1 path: src/Database/Schema/Grammars/MariaDbGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' identifier: property.notFound - count: 1 path: src/Database/Schema/Grammars/MySqlGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$renameTo\.$#' identifier: property.notFound - count: 1 path: src/Database/Schema/Grammars/MySqlGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' identifier: property.notFound - count: 1 path: src/Database/Schema/Grammars/PostgresGrammar.php - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' identifier: property.notFound - count: 1 path: src/Database/Schema/Grammars/SqlServerGrammar.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getSortOrderColumn\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/SortableScope.php - message: '#^Access to an undefined property Illuminate\\Database\\Eloquent\\Model\:\:\$children\.$#' identifier: property.notFound - count: 1 path: src/Database/TreeCollection.php - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getParentId\(\)\.$#' identifier: method.notFound - count: 1 path: src/Database/TreeCollection.php - - - message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: src/Events/Dispatcher.php - - message: '#^Call to an undefined method Illuminate\\Routing\\Router\:\:after\(\)\.$#' identifier: method.notFound - count: 1 path: src/Foundation/Application.php - message: '#^Call to an undefined method Illuminate\\Routing\\Router\:\:before\(\)\.$#' identifier: method.notFound - count: 1 path: src/Foundation/Application.php - message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Mail\\Mailer\:\:queue\(\) should be compatible with parameter \$queue \(BackedEnum\|string\|null\) of method Illuminate\\Mail\\Mailer\:\:queue\(\)$#' identifier: method.childParameterType - count: 1 path: src/Mail/Mailer.php - message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Mail\\Mailer\:\:queue\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Contracts\\Mail\\MailQueue\:\:queue\(\)$#' identifier: method.childParameterType - count: 1 path: src/Mail/Mailer.php - message: '#^Parameter \#2 \$view \(array\|string\) of method Winter\\Storm\\Mail\\Mailer\:\:queueOn\(\) should be compatible with parameter \$view \(Illuminate\\Contracts\\Mail\\Mailable\) of method Illuminate\\Mail\\Mailer\:\:queueOn\(\)$#' identifier: method.childParameterType - count: 1 path: src/Mail/Mailer.php - message: '#^Parameter \#3 \$data \(array\) of method Winter\\Storm\\Mail\\Mailer\:\:later\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Contracts\\Mail\\MailQueue\:\:later\(\)$#' identifier: method.childParameterType - count: 1 path: src/Mail/Mailer.php - message: '#^Parameter \#3 \$data \(array\) of method Winter\\Storm\\Mail\\Mailer\:\:later\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Mail\\Mailer\:\:later\(\)$#' identifier: method.childParameterType - count: 1 path: src/Mail/Mailer.php - message: '#^Parameter \#3 \$view \(array\|string\) of method Winter\\Storm\\Mail\\Mailer\:\:laterOn\(\) should be compatible with parameter \$view \(Illuminate\\Contracts\\Mail\\Mailable\) of method Illuminate\\Mail\\Mailer\:\:laterOn\(\)$#' identifier: method.childParameterType - count: 1 path: src/Mail/Mailer.php - message: '#^PHPDoc tag @method for method Winter\\Storm\\Support\\Facades\\Url\:\:buildUrl\(\) parameter \#3 \$flags default value contains unresolvable type\.$#' identifier: methodTag.unresolvableType - count: 1 path: src/Support/Facades/Url.php - message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Contracts\\Mail\\MailQueue\:\:queue\(\)$#' identifier: method.childParameterType - count: 1 path: src/Support/Testing/Fakes/MailFake.php - message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\)$#' identifier: method.childParameterType - count: 1 path: src/Support/Testing/Fakes/MailFake.php - message: '#^Return type \(void\) of method Winter\\Storm\\Support\\Testing\\Fakes\\MailFake\:\:send\(\) should be compatible with return type \(Illuminate\\Mail\\SentMessage\|null\) of method Illuminate\\Contracts\\Mail\\Mailer\:\:send\(\)$#' identifier: method.childReturnType - count: 1 path: src/Support/Testing/Fakes/MailFake.php diff --git a/phpstan.neon b/phpstan.neon index 159f9a8d..0c5cdc27 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,15 +7,14 @@ parameters: - src level: 5 ignoreErrors: - - - message: '#Call to private method select\(\)#' - path: src/Database/Relations/Concerns/CanBeCounted.php - - message: '#Anonymous function should return static\(.*\) but returns static\(.*\)#' - identifier: trait.unused path: src/Support/Traits/Singleton.php reportUnmatched: false + + - + identifier: trait.unused + excludePaths: # Exclude PHP Parser files - src/Parse/PHP/ArrayFile.php From 16bdbbe678e326a43389c20cd90d5bc00a61485f Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Tue, 6 Oct 2026 16:29:53 -0400 Subject: [PATCH 185/197] add unit tests for the quote management in default values --- .../Schema/Grammars/MySqlSchemaGrammarTest.php | 9 +++++++++ .../Schema/Grammars/PostgresSchemaGrammarTest.php | 10 ++++++++++ .../Schema/Grammars/SQLiteSchemaGrammarTest.php | 11 +++++++++++ .../Schema/Grammars/SqlServerSchemaGrammarTest.php | 9 +++++++++ 4 files changed, 39 insertions(+) diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index a9111971..7ee243c6 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -61,4 +61,13 @@ public function testNullableInitialModifierAddDefaultNotNullable() $statements = $this->runBlueprint($changedBlueprint); $this->assertSame("alter table `users` modify `name` varchar(255) not null default 'admin'", $statements[0]); } + + public function testQuoteInDefaultValue() + { + $initialBlueprint = $this->getBlueprint('users'); + $initialBlueprint->string('name')->default("'O'Brian'"); + + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame("alter table `users` add `name` varchar(255) not null default 'O''Brian'", $statements[0]); + } } diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index 1e312462..e4227811 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -69,4 +69,14 @@ public function testNullableInitialModifierAddDefaultNotNullable() $this->assertSame('alter column "name" set not null', $parts[1]); $this->assertSame("alter column \"name\" set default 'admin'", $parts[2]); } + + public function testQuoteInDefaultValue() + { + $initialBlueprint = $this->getBlueprint('users'); + $initialBlueprint->string('name')->default("'O'Brian'"); + + $statements = $this->runBlueprint($initialBlueprint); + $parts = explode(', ', $statements[0]); + $this->assertSame("alter table \"users\" add column \"name\" varchar(255) not null default 'O''Brian'", $parts[0]); + } } diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index 71a7b48f..9fa7be9f 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -88,6 +88,17 @@ public function testChangeCanAddDefaultAndDropNullable(): void $this->assertSame("'admin'", $info['dflt_value']); } + public function testQuoteInDefaultValue() + { + [$pdo, , $builder] = $this->bootSqlite('CREATE TABLE users (id integer primary key, name varchar)'); + + // Add single quoted default value with a single quote by itself in the middle + $builder->table('users', fn (Blueprint $table) => $table->string('name')->default("'O'Brian'")->change()); + + $info = $this->columnInfo($pdo, 'users', 'name'); + $this->assertSame("'O''Brian'", $info['dflt_value']); + } + public function testChangePreservesUnspecifiedAttributes(): void { [$pdo, , $builder] = $this->bootSqlite("CREATE TABLE users (id integer primary key, name varchar default 'bob')"); diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index 2bef14a3..b7ef82ea 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -62,4 +62,13 @@ public function testNullableInitialModifierAddDefaultNotNullable() $this->assertSame('alter table "users" alter column "name" nvarchar(255) not null', $statements[1]); $this->assertSame('alter table "users" add default \'admin\' for "name"', $statements[2]); } + + public function testQuoteInDefaultValue() + { + $initialBlueprint = $this->getBlueprint('users'); + $initialBlueprint->string('name')->default("'O'Brian'"); + + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame("alter table \"users\" add \"name\" nvarchar(255) not null default 'O''Brian'", $statements[0]); + } } From 4dda14558a6be72069f2c6fa479354a3baf0cbeb Mon Sep 17 00:00:00 2001 From: Jonas Pardon Date: Thu, 8 Oct 2026 08:49:16 +0200 Subject: [PATCH 186/197] Let Mail::fake() resolve the mail manager itself (#256) Co-authored-by: Claude Opus 5.5 --- src/Support/Facades/Mail.php | 8 ++++-- tests/Support/MailFacadeTest.php | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 tests/Support/MailFacadeTest.php diff --git a/src/Support/Facades/Mail.php b/src/Support/Facades/Mail.php index b1556b51..87bd8c7e 100755 --- a/src/Support/Facades/Mail.php +++ b/src/Support/Facades/Mail.php @@ -32,11 +32,15 @@ class Mail extends Facade /** * Replace the bound instance with a fake. * - * @param \Winter\Storm\Mail\MailManager $manager + * @param \Winter\Storm\Mail\MailManager|null $manager Defaults to the application's mail manager * @return \Winter\Storm\Support\Testing\Fakes\MailFake */ - public static function fake(MailManager $manager) + public static function fake(?MailManager $manager = null) { + $manager ??= static::isFake() + ? static::getFacadeRoot()->manager + : static::$app->make('mail.manager'); + static::swap($fake = new MailFake($manager)); return $fake; diff --git a/tests/Support/MailFacadeTest.php b/tests/Support/MailFacadeTest.php new file mode 100644 index 00000000..446e9a85 --- /dev/null +++ b/tests/Support/MailFacadeTest.php @@ -0,0 +1,46 @@ +mockManager(); + $this->app->instance('mail.manager', $manager); + + $fake = Mail::fake(); + + $this->assertInstanceOf(MailFake::class, $fake); + $this->assertSame($manager, $fake->manager); + $this->assertSame($fake, Mail::getFacadeRoot()); + } + + public function testFakeKeepsTheRealMailManagerWhenCalledAgain() + { + $manager = $this->mockManager(); + $this->app->instance('mail.manager', $manager); + + Mail::fake(); + $fake = Mail::fake(); + + $this->assertSame($manager, $fake->manager); + } + + public function testFakeAcceptsAMailManager() + { + $manager = $this->mockManager(); + + $fake = Mail::fake($manager); + + $this->assertSame($manager, $fake->manager); + } + + protected function mockManager(): MailManager + { + return m::mock(MailManager::class)->shouldReceive('getDefaultDriver')->andReturn('smtp')->getMock(); + } +} From 74f80e22b011511ca3e0162609c06da21f46257d Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Sat, 10 Oct 2026 12:15:51 -0500 Subject: [PATCH 187/197] Run deferred callbacks in HTTP requests Add Laravel's InvokeDeferredCallbacks middleware to the HTTP kernel so callbacks registered with defer() run after the response is sent. --- src/Foundation/Http/Kernel.php | 1 + tests/Foundation/Http/KernelTest.php | 31 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/Foundation/Http/KernelTest.php diff --git a/src/Foundation/Http/Kernel.php b/src/Foundation/Http/Kernel.php index 9ca5b711..4189bf00 100644 --- a/src/Foundation/Http/Kernel.php +++ b/src/Foundation/Http/Kernel.php @@ -23,6 +23,7 @@ class Kernel extends HttpKernel * {@inheritDoc} */ protected $middleware = [ + \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class, \Winter\Storm\Foundation\Http\Middleware\CheckForTrustedHost::class, \Winter\Storm\Foundation\Http\Middleware\CheckForTrustedProxies::class, \Illuminate\Http\Middleware\HandleCors::class, diff --git a/tests/Foundation/Http/KernelTest.php b/tests/Foundation/Http/KernelTest.php new file mode 100644 index 00000000..18aacaa0 --- /dev/null +++ b/tests/Foundation/Http/KernelTest.php @@ -0,0 +1,31 @@ +app['router']->get('/deferred', function () use (&$ran) { + defer(function () use (&$ran) { + $ran = true; + }); + + return 'OK'; + }); + + $kernel = new Kernel($this->app, $this->app['router']); + $request = Request::create('/deferred'); + $response = $kernel->handle($request); + + $this->assertSame('OK', $response->getContent()); + $this->assertFalse($ran); + + $kernel->terminate($request, $response); + + $this->assertTrue($ran); + } +} From 12c5cb9d8986a13c157720747d3cb8097cecd05d Mon Sep 17 00:00:00 2001 From: Luke Towers Date: Fri, 9 Oct 2026 10:26:28 -0600 Subject: [PATCH 188/197] Update enshrined/svg-sanitize version to ~1.0 Fix https://github.com/wintercms/winter/issues/1568 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fa17424c..71e41d11 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "ext-zip": "*", "assetic/framework": "^3.2.3", "doctrine/dbal": "^3.0", - "enshrined/svg-sanitize": "~0.16", + "enshrined/svg-sanitize": "~1.0", "laravel/framework": "^12.30.1", "laravel/tinker": "^2.8.1", "league/csv": "~9.1", From 10c8e6d56379b0491648d6f7432be2c7c86a71bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20pavez?= Date: Sat, 10 Oct 2026 18:40:44 -0300 Subject: [PATCH 189/197] Declare league/commonmark 2.10.2 or higher as a direct dependency (#247) (#253) --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 71e41d11..da9c04f7 100644 --- a/composer.json +++ b/composer.json @@ -39,6 +39,7 @@ "enshrined/svg-sanitize": "~1.0", "laravel/framework": "^12.30.1", "laravel/tinker": "^2.8.1", + "league/commonmark": "^2.10.2", "league/csv": "~9.1", "nesbot/carbon": "^3.0", "scssphp/scssphp": "~1.0", From c7990f38076254dd876571b7677b2eabe8c91e8b Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Thu, 15 Oct 2026 20:34:57 -0500 Subject: [PATCH 190/197] Fix dispatching events that have listeners registered against an interface (#241) --- src/Events/Dispatcher.php | 24 ++++++ tests/Events/InterfaceListenerTest.php | 110 +++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 tests/Events/InterfaceListenerTest.php diff --git a/src/Events/Dispatcher.php b/src/Events/Dispatcher.php index c9314139..500d9af8 100644 --- a/src/Events/Dispatcher.php +++ b/src/Events/Dispatcher.php @@ -212,6 +212,30 @@ public function getListeners($eventName) : $listeners; } + /** + * Add the listeners for the event's interfaces to the given array. + * + * @param string $eventName + * @param array $listeners + * @return array + */ + protected function addInterfaceListeners($eventName, array $listeners = []) + { + foreach (class_implements($eventName) as $interface) { + if (!isset($this->listeners[$interface])) { + continue; + } + + if (!isset($this->sorted[$interface])) { + $this->sortListeners($interface); + } + + $listeners = array_merge($listeners, $this->sorted[$interface]); + } + + return $listeners; + } + /** * Sort the listeners for a given event by priority. * diff --git a/tests/Events/InterfaceListenerTest.php b/tests/Events/InterfaceListenerTest.php new file mode 100644 index 00000000..6a86d920 --- /dev/null +++ b/tests/Events/InterfaceListenerTest.php @@ -0,0 +1,110 @@ +listen(InterfaceListenerTestContract::class, function ($event) use (&$received) { + $received[] = get_class($event); + }); + + $dispatcher->dispatch(new InterfaceListenerTestEvent('payload')); + + $this->assertSame([InterfaceListenerTestEvent::class], $received); + } + + public function testInterfaceAndClassListenersBothFireInPriorityOrder() + { + $order = []; + + $dispatcher = new Dispatcher(); + $dispatcher->listen(InterfaceListenerTestEvent::class, function () use (&$order) { + $order[] = 'class-default'; + }); + $dispatcher->listen(InterfaceListenerTestEvent::class, function () use (&$order) { + $order[] = 'class-high'; + }, 10); + $dispatcher->listen(InterfaceListenerTestContract::class, function () use (&$order) { + $order[] = 'interface'; + }); + + $dispatcher->dispatch(new InterfaceListenerTestEvent('payload')); + + // Class listeners sort by descending priority; interface listeners are appended after. + $this->assertSame(['class-high', 'class-default', 'interface'], $order); + } + + public function testGetListenersIncludesInterfaceListenersOnce() + { + $dispatcher = new Dispatcher(); + $dispatcher->listen(InterfaceListenerTestContract::class, fn () => null); + $dispatcher->listen(InterfaceListenerTestEvent::class, fn () => null); + + $this->assertCount(2, $dispatcher->getListeners(InterfaceListenerTestEvent::class)); + $this->assertCount(1, $dispatcher->getListeners(InterfaceListenerTestContract::class)); + } + + public function testAnEventWithNoInterfaceListenersStillDispatches() + { + $hit = false; + + $dispatcher = new Dispatcher(); + $dispatcher->listen(InterfaceListenerTestEvent::class, function () use (&$hit) { + $hit = true; + }); + + $dispatcher->dispatch(new InterfaceListenerTestEvent('payload')); + + $this->assertTrue($hit); + } + + /** + * Class-string listeners are how Laravel Octane registers its post-operation cleanup against + * the OperationTerminated contract, so they must resolve through the container as well. + */ + public function testClassStringListenerRegisteredOnAnInterfaceIsResolved() + { + InterfaceListenerTestHandler::$calls = []; + + $dispatcher = new Dispatcher(); + $dispatcher->listen(InterfaceListenerTestContract::class, InterfaceListenerTestHandler::class); + + $dispatcher->dispatch(new InterfaceListenerTestEvent('from-contract')); + + $this->assertSame(['from-contract'], InterfaceListenerTestHandler::$calls); + } +} + +interface InterfaceListenerTestContract +{ +} + +class InterfaceListenerTestEvent implements InterfaceListenerTestContract +{ + public function __construct(public string $value) + { + } +} + +class InterfaceListenerTestHandler +{ + public static array $calls = []; + + public function handle(InterfaceListenerTestEvent $event): void + { + static::$calls[] = $event->value; + } +} From 084e10c26b764a1fd687b623b9446fc022284118 Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Thu, 15 Oct 2026 20:50:29 -0500 Subject: [PATCH 191/197] Clear the static called class when a static extension method throws (#262) --- src/Extension/ExtendableTrait.php | 10 ++++--- tests/Extension/ExtendableTraitTest.php | 35 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Extension/ExtendableTrait.php b/src/Extension/ExtendableTrait.php index d7d65a5d..08b2134f 100644 --- a/src/Extension/ExtendableTrait.php +++ b/src/Extension/ExtendableTrait.php @@ -508,9 +508,13 @@ public static function extendableCallStatic($name, $params = null) if (method_exists($extension, $name) && is_callable([$extension, $name])) { $extension::$extendableStaticCalledClass = $className; - $result = forward_static_call_array(array($extension, $name), $params); - $extension::$extendableStaticCalledClass = null; - return $result; + + try { + return forward_static_call_array(array($extension, $name), $params); + } + finally { + $extension::$extendableStaticCalledClass = null; + } } } diff --git a/tests/Extension/ExtendableTraitTest.php b/tests/Extension/ExtendableTraitTest.php index 91d21752..78640ffd 100644 --- a/tests/Extension/ExtendableTraitTest.php +++ b/tests/Extension/ExtendableTraitTest.php @@ -42,6 +42,41 @@ public function testGetExtensionParentClassOnExtendableTrait() $this->assertEquals(Level1NonExtendable::class, $this->callProtectedMethod($level2NonExtendable, 'extensionGetParentClass')->getName()); $this->assertEquals(Level1NonExtendable::class, $this->callProtectedMethod($level3NonExtendable, 'extensionGetParentClass')->getName()); } + + public function testStaticCalledClassIsClearedWhenAStaticExtensionMethodThrows() + { + try { + StaticThrowingHost::explode(); + $this->fail('The exception from the extension should propagate.'); + } + catch (\RuntimeException $ex) { + // Expected. + } + + $this->assertNull(StaticThrowingExtension::$extendableStaticCalledClass); + } +} + +class StaticThrowingHost +{ + use ExtendableTrait; + + public $implement = [StaticThrowingExtension::class]; + + public static function __callStatic($name, $params) + { + return self::extendableCallStatic($name, $params); + } +} + +class StaticThrowingExtension +{ + use \Winter\Storm\Extension\ExtensionTrait; + + public static function explode() + { + throw new \RuntimeException('static extension failed'); + } } class ExtendableRoot extends Extendable From f2149d6018594dfa67595f9316b4c93ac329c6f3 Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Thu, 22 Oct 2026 12:28:32 -0500 Subject: [PATCH 192/197] Alias the log service to LogManager so deprecations reach the log (#263) --- src/Foundation/Application.php | 2 +- tests/Foundation/CoreContainerAliasesTest.php | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/Foundation/CoreContainerAliasesTest.php diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index aba5b55a..9655ab4a 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -468,7 +468,7 @@ public function registerCoreContainerAliases() 'filesystem.cloud' => [\Illuminate\Contracts\Filesystem\Cloud::class], 'hash' => [\Illuminate\Contracts\Hashing\Hasher::class], 'translator' => [\Illuminate\Translation\Translator::class, \Illuminate\Contracts\Translation\Translator::class], - 'log' => [\Illuminate\Log\Logger::class, \Psr\Log\LoggerInterface::class], + 'log' => [\Illuminate\Log\LogManager::class, \Psr\Log\LoggerInterface::class], 'mail.manager' => [\Illuminate\Mail\MailManager::class, \Illuminate\Contracts\Mail\Factory::class], 'mailer' => [\Illuminate\Mail\Mailer::class, \Illuminate\Contracts\Mail\Mailer::class, \Illuminate\Contracts\Mail\MailQueue::class], 'queue' => [\Illuminate\Queue\QueueManager::class, \Illuminate\Contracts\Queue\Factory::class, \Illuminate\Contracts\Queue\Monitor::class], diff --git a/tests/Foundation/CoreContainerAliasesTest.php b/tests/Foundation/CoreContainerAliasesTest.php new file mode 100644 index 00000000..51940d40 --- /dev/null +++ b/tests/Foundation/CoreContainerAliasesTest.php @@ -0,0 +1,11 @@ +assertSame($this->app->make('log'), $this->app->make(LogManager::class)); + } +} From 33254283f36df63d2d6dced91f2fffa75eea12b5 Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Thu, 22 Oct 2026 12:29:38 -0500 Subject: [PATCH 193/197] Improved Laravel 13 Support [Minimal PR] (#246) --- composer.json | 9 +++++---- phpstan-baseline.neon | 2 +- src/Database/Traits/ArraySource.php | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index da9c04f7..305be29f 100644 --- a/composer.json +++ b/composer.json @@ -37,8 +37,8 @@ "assetic/framework": "^3.2.3", "doctrine/dbal": "^3.0", "enshrined/svg-sanitize": "~1.0", - "laravel/framework": "^12.30.1", - "laravel/tinker": "^2.8.1", + "laravel/framework": "^12.30.1|^13.0", + "laravel/tinker": "^2.8.1|^3.0", "league/commonmark": "^2.10.2", "league/csv": "~9.1", "nesbot/carbon": "^3.0", @@ -58,13 +58,14 @@ "meyfa/phpunit-assert-gd": "^4.2", "mockery/mockery": "^1.6|^2.0", "dms/phpunit-arraysubset-asserts": "dev-add-phpunit-11-support", - "orchestra/testbench": "^10.0", + "orchestra/testbench": "^10.0|^11.0", "larastan/larastan": "^3.6.0" }, "repositories": [ { "type": "vcs", - "url": "https://github.com/pieterocp/phpunit-arraysubset-asserts" + "url": "https://github.com/pieterocp/phpunit-arraysubset-asserts", + "no-api": true } ], "suggest": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 15e13044..a53ba4eb 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -850,7 +850,7 @@ parameters: path: src/Support/Testing/Fakes/MailFake.php - - message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\) should be compatible with parameter \$queue \(string\|null\) of method Illuminate\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\)$#' + message: '#^Parameter \#2 \$data \(array\) of method Winter\\Storm\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\) should be compatible with parameter \$queue \((BackedEnum\|)?string\|null\) of method Illuminate\\Support\\Testing\\Fakes\\MailFake\:\:queue\(\)$#' identifier: method.childParameterType path: src/Support/Testing/Fakes/MailFake.php diff --git a/src/Database/Traits/ArraySource.php b/src/Database/Traits/ArraySource.php index 1c9e09fe..690e1c6e 100644 --- a/src/Database/Traits/ArraySource.php +++ b/src/Database/Traits/ArraySource.php @@ -29,7 +29,7 @@ trait ArraySource /** * Connection. to the SQLite datasource. */ - protected static \Illuminate\Database\Connection $arraySourceConnection; + protected static ?\Illuminate\Database\Connection $arraySourceConnection = null; /** * Boots the ArraySource trait. @@ -40,6 +40,17 @@ public static function bootArraySource(): void throw new ApplicationException('You must enable the SQLite PDO driver to use the ArraySource trait'); } + static::$arraySourceConnection = null; + } + + /** + * Creates the temporary SQLite datasource for this model. + * + * Laravel 13 throws a LogicException when a model is instantiated while it is booting, so this runs the first + * time the connection is resolved instead of in `bootArraySource()`. + */ + protected static function arraySourceBootConnection(): void + { $instance = new static; static::arraySourceSetDbConnection( @@ -79,6 +90,10 @@ public function getRecords() */ public static function resolveConnection($connection = null) { + if (static::$arraySourceConnection === null) { + static::arraySourceBootConnection(); + } + return static::$arraySourceConnection; } From 3c149ba9c225c46402f04903de8cdfbd021fd0a0 Mon Sep 17 00:00:00 2001 From: Derrick Austin Date: Thu, 22 Oct 2026 12:30:39 -0500 Subject: [PATCH 194/197] Restore session use in Auth\Manager::once() when authentication throws (#261) --- src/Auth/Manager.php | 11 ++++-- tests/Auth/ManagerOnceTest.php | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/Auth/ManagerOnceTest.php diff --git a/src/Auth/Manager.php b/src/Auth/Manager.php index 02935431..bc031037 100644 --- a/src/Auth/Manager.php +++ b/src/Auth/Manager.php @@ -622,11 +622,16 @@ public function id() */ public function once(array $credentials = []) { - $this->useSession = false; + $previousUseSession = $this->useSession; - $user = $this->authenticate($credentials); + $this->useSession = false; - $this->useSession = true; + try { + $user = $this->authenticate($credentials); + } + finally { + $this->useSession = $previousUseSession; + } return !!$user; } diff --git a/tests/Auth/ManagerOnceTest.php b/tests/Auth/ManagerOnceTest.php new file mode 100644 index 00000000..a31c922e --- /dev/null +++ b/tests/Auth/ManagerOnceTest.php @@ -0,0 +1,62 @@ +assertTrue($this->getUseSession($manager)); + + try { + $manager->once(['login' => 'someone', 'password' => 'secret']); + $this->fail('The exception from authenticate() should propagate.'); + } + catch (RuntimeException $ex) { + // Expected. + } + + $this->assertTrue($this->getUseSession($manager)); + } + + public function testOnceRestoresThePreviousValueRatherThanTrue() + { + $manager = ManagerOnceTestThrowingManager::instance(); + (new ReflectionProperty($manager, 'useSession'))->setValue($manager, false); + + try { + $manager->once(['login' => 'someone', 'password' => 'secret']); + } + catch (RuntimeException $ex) { + // Expected. + } + + $this->assertFalse($this->getUseSession($manager)); + } + + protected function getUseSession(AuthManager $manager): bool + { + return (new ReflectionProperty($manager, 'useSession'))->getValue($manager); + } +} + +class ManagerOnceTestThrowingManager extends AuthManager +{ + public function authenticate(array $credentials, $remember = true) + { + throw new RuntimeException('authentication failed'); + } +} From eff4c75efdacb2d517919aa71658ace23b1c2b56 Mon Sep 17 00:00:00 2001 From: Jonas Pardon Date: Thu, 22 Oct 2026 20:22:35 +0200 Subject: [PATCH 195/197] Fix changing a column on MySQL, MariaDB, PostgreSQL and SQL Server (#258) Co-authored-by: Claude Opus 5.5 Co-authored-by: Luke Towers Co-authored-by: Luke Towers --- phpstan-baseline.neon | 30 ----- src/Database/Schema/BlueprintState.php | 4 +- .../KeepsExistingColumnAttributes.php | 60 +++++++++ .../Grammars/Concerns/MySqlBasedGrammar.php | 76 +++++++----- .../Schema/Grammars/PostgresGrammar.php | 54 ++++---- .../Schema/Grammars/SqlServerGrammar.php | 50 ++++---- .../Grammars/MariaDbSchemaGrammarTest.php | 63 ++++++++++ .../Grammars/MySqlSchemaGrammarTest.php | 115 ++++++++++++++++++ .../Grammars/PostgresSchemaGrammarTest.php | 94 +++++++++++++- .../Grammars/SQLiteSchemaGrammarTest.php | 12 ++ .../Grammars/SqlServerSchemaGrammarTest.php | 81 ++++++++++++ tests/GrammarTestCase.php | 17 ++- 12 files changed, 536 insertions(+), 120 deletions(-) create mode 100644 src/Database/Schema/Grammars/Concerns/KeepsExistingColumnAttributes.php create mode 100644 tests/Database/Schema/Grammars/MariaDbSchemaGrammarTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a53ba4eb..43d42405 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -754,36 +754,6 @@ parameters: identifier: method.childParameterType path: src/Database/Relations/MorphToMany.php - - - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' - identifier: property.notFound - path: src/Database/Schema/Grammars/MariaDbGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$renameTo\.$#' - identifier: property.notFound - path: src/Database/Schema/Grammars/MariaDbGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' - identifier: property.notFound - path: src/Database/Schema/Grammars/MySqlGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$renameTo\.$#' - identifier: property.notFound - path: src/Database/Schema/Grammars/MySqlGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' - identifier: property.notFound - path: src/Database/Schema/Grammars/PostgresGrammar.php - - - - message: '#^Access to an undefined property Illuminate\\Database\\Schema\\ColumnDefinition\:\:\$name\.$#' - identifier: property.notFound - path: src/Database/Schema/Grammars/SqlServerGrammar.php - - message: '#^Call to an undefined method Illuminate\\Database\\Eloquent\\Model\:\:getSortOrderColumn\(\)\.$#' identifier: method.notFound diff --git a/src/Database/Schema/BlueprintState.php b/src/Database/Schema/BlueprintState.php index 09b999c9..275d3d4d 100644 --- a/src/Database/Schema/BlueprintState.php +++ b/src/Database/Schema/BlueprintState.php @@ -65,8 +65,10 @@ protected function preserveExistingColumnAttributes(Fluent $column): void continue; } + $attributes = $column->getAttributes(); + foreach ($this->preservedAttributes as $attribute) { - if (!isset($column[$attribute]) && isset($existing[$attribute])) { + if (!array_key_exists($attribute, $attributes) && isset($existing[$attribute])) { $column[$attribute] = $existing[$attribute]; } } diff --git a/src/Database/Schema/Grammars/Concerns/KeepsExistingColumnAttributes.php b/src/Database/Schema/Grammars/Concerns/KeepsExistingColumnAttributes.php new file mode 100644 index 00000000..877c9464 --- /dev/null +++ b/src/Database/Schema/Grammars/Concerns/KeepsExistingColumnAttributes.php @@ -0,0 +1,60 @@ +connection->getSchemaBuilder()->getColumns($blueprint->getTable())) + ->firstWhere('name', $column->get('name')); + + if (!$existing) { + return; + } + + $attributes = $column->getAttributes(); + + foreach ($this->getKeptColumnAttributes($existing, $column) as $attribute => $value) { + if (!is_null($value) && !array_key_exists($attribute, $attributes)) { + $column[$attribute] = $value; + } + } + } + + /** + * Whether the column is being changed to a type that accepts a collation. + * + * Like Laravel 9, the collation is only kept while the column stays a text column. + */ + protected function changesToATextColumn(Fluent $column): bool + { + return in_array($column->get('type'), [ + 'char', 'string', 'tinyText', 'text', 'mediumText', 'longText', 'enum', 'set', + ]); + } +} diff --git a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php index 6f749de7..201b96c3 100644 --- a/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php +++ b/src/Database/Schema/Grammars/Concerns/MySqlBasedGrammar.php @@ -2,12 +2,16 @@ namespace Winter\Storm\Database\Schema\Grammars\Concerns; +use Illuminate\Database\MySqlConnection; +use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; -use Illuminate\Database\Schema\ColumnDefinition; +use Illuminate\Database\Schema\Grammars\MariaDbGrammar; use Illuminate\Support\Fluent; trait MySqlBasedGrammar { + use KeepsExistingColumnAttributes; + /** * Compile a change column command into a series of SQL statements. * @@ -18,42 +22,58 @@ trait MySqlBasedGrammar * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command * @return array|string - * - * @throws \RuntimeException */ public function compileChange(Blueprint $blueprint, Fluent $command) { - $columns = []; - $schema = $this->connection->getSchemaBuilder(); - $table = $blueprint->getTable(); + /** @var \Illuminate\Database\Schema\ColumnDefinition $column */ + $column = $command->get('column'); - $oldColumns = collect($schema->getColumns($table)); + $this->keepExistingColumnAttributes($blueprint, $column); - foreach ($blueprint->getChangedColumns() as $column) { - $sql = sprintf( - '%s %s%s %s', - is_null($column->renameTo) ? 'modify' : 'change', - $this->wrap($column->name), - is_null($column->renameTo) ? '' : ' '.$this->wrap($column->renameTo), - $this->getType($column) - ); + return parent::compileChange($blueprint, $command); + } + + /** @inheritDoc */ + protected function getKeptColumnAttributes($existing, Fluent $column): array + { + $attributes = [ + 'nullable' => (bool) $existing['nullable'], + 'default' => $this->getKeptColumnDefault($existing['default']), + 'comment' => $existing['comment'], + ]; + + // Only keep the unsigned attribute while the column stays a numeric column, as no other type accepts it + if (in_array($column->get('type'), ['boolean', 'tinyInteger', 'smallInteger', 'mediumInteger', 'integer', 'bigInteger', 'decimal', 'float', 'double'])) { + $attributes['unsigned'] = str_contains(strtolower((string) $existing['type']), 'unsigned') ?: null; + } + + if ($this->changesToATextColumn($column)) { + $attributes['collation'] = $existing['collation']; + } - $oldColumn = $oldColumns->where('name', $column->name)->first(); - if (!$oldColumn instanceof ColumnDefinition) { - $oldColumn = new ColumnDefinition($oldColumn); - } + return $attributes; + } + + /** + * Turn the default of an existing column, as reported by the schema builder, back into a definition. + * + * MariaDB reports every default as an SQL expression ('text', NULL, current_timestamp()). MySQL reports + * a literal default as its raw value, and an expression such as CURRENT_TIMESTAMP as is. + * + * @param mixed $default + * @return mixed + */ + protected function getKeptColumnDefault($default) + { + if (!is_string($default)) { + return $default; + } - foreach ($this->modifiers as $modifier) { - if (method_exists($this, $method = "modify{$modifier}")) { - $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) && !isset($column->{$mod}) ? $oldColumn : $column; - $sql .= $this->{$method}($blueprint, $col); - } - } - $columns[] = $sql; + if ($this instanceof MariaDbGrammar || ($this->connection instanceof MySqlConnection && $this->connection->isMaria())) { + return $default === 'NULL' ? null : new Expression($default); } - return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); + return str_starts_with(strtolower($default), 'current_timestamp') ? new Expression($default) : $default; } /** diff --git a/src/Database/Schema/Grammars/PostgresGrammar.php b/src/Database/Schema/Grammars/PostgresGrammar.php index faf156f0..532095fa 100755 --- a/src/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Database/Schema/Grammars/PostgresGrammar.php @@ -2,13 +2,15 @@ namespace Winter\Storm\Database\Schema\Grammars; +use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; -use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Database\Schema\Grammars\PostgresGrammar as BasePostgresGrammar; use Illuminate\Support\Fluent; class PostgresGrammar extends BasePostgresGrammar { + use Concerns\KeepsExistingColumnAttributes; + /** * Compile a change column command into a series of SQL statements. * @@ -16,48 +18,38 @@ class PostgresGrammar extends BasePostgresGrammar * This restores Laravel previous behavior where existing column attributes are kept * unless they get changed by the new Blueprint. * + * The kept attributes are set on the column itself, so that the comment command that runs + * after the change keeps the existing comment as well. + * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command * @return array|string - * - * @throws \RuntimeException */ public function compileChange(Blueprint $blueprint, Fluent $command) { - $columns = []; - $schema = $this->connection->getSchemaBuilder(); - $table = $blueprint->getTable(); - - $oldColumns = collect($schema->getColumns($table)); + /** @var \Illuminate\Database\Schema\ColumnDefinition $column */ + $column = $command->get('column'); - foreach ($blueprint->getChangedColumns() as $column) { - $changes = ['type '.$this->getType($column).$this->modifyCollate($blueprint, $column)]; + $this->keepExistingColumnAttributes($blueprint, $column); - $oldColumn = $oldColumns->where('name', $column->name)->first(); - if (!$oldColumn instanceof ColumnDefinition) { - $oldColumn = new ColumnDefinition($oldColumn); - } - - foreach ($this->modifiers as $modifier) { - if ($modifier === 'Collate') { - continue; - } - - if (method_exists($this, $method = "modify{$modifier}")) { - $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) && ! isset($column->{$mod}) ? $oldColumn : $column; - $constraints = (array) $this->{$method}($blueprint, $col); + return parent::compileChange($blueprint, $command); + } - foreach ($constraints as $constraint) { - $changes[] = $constraint; - } - } - } + /** @inheritDoc */ + protected function getKeptColumnAttributes($existing, Fluent $column): array + { + $attributes = [ + 'nullable' => (bool) $existing['nullable'], + // PostgreSQL reports every default as an SQL expression, such as 'text'::character varying + 'default' => is_null($existing['default']) ? null : new Expression($existing['default']), + 'comment' => $existing['comment'], + ]; - $columns[] = implode(', ', $this->prefixArray('alter column '.$this->wrap($column->name), $changes)); + if ($this->changesToATextColumn($column)) { + $attributes['collation'] = $existing['collation']; } - return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns); + return $attributes; } /** diff --git a/src/Database/Schema/Grammars/SqlServerGrammar.php b/src/Database/Schema/Grammars/SqlServerGrammar.php index 46ea1604..50f92f88 100755 --- a/src/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Database/Schema/Grammars/SqlServerGrammar.php @@ -2,13 +2,15 @@ namespace Winter\Storm\Database\Schema\Grammars; +use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; -use Illuminate\Database\Schema\ColumnDefinition; use Illuminate\Database\Schema\Grammars\SqlServerGrammar as BaseSqlServerGrammar; use Illuminate\Support\Fluent; class SqlServerGrammar extends BaseSqlServerGrammar { + use Concerns\KeepsExistingColumnAttributes; + /** * Compile a change column command into a series of SQL statements. * @@ -16,45 +18,37 @@ class SqlServerGrammar extends BaseSqlServerGrammar * This restores Laravel previous behavior where existing column attributes are kept * unless they get changed by the new Blueprint. * + * The kept attributes are set on the column itself, so that the default command that runs + * after the change adds the existing default back once the change has dropped its constraint. + * * @param \Illuminate\Database\Schema\Blueprint $blueprint * @param \Illuminate\Support\Fluent $command * @return array|string - * - * @throws \RuntimeException */ public function compileChange(Blueprint $blueprint, Fluent $command) { - $changes = (array) $this->compileDropDefaultConstraint($blueprint, $command); - $schema = $this->connection->getSchemaBuilder(); - $table = $blueprint->getTable(); + /** @var \Illuminate\Database\Schema\ColumnDefinition $column */ + $column = $command->get('column'); - $oldColumns = collect($schema->getColumns($table)); + $this->keepExistingColumnAttributes($blueprint, $column); - foreach ($blueprint->getChangedColumns() as $column) { - $sql = sprintf( - 'alter table %s alter column %s %s', - $this->wrapTable($blueprint), - $this->wrap($column->name), - $this->getType($column) - ); - - $oldColumn = $oldColumns->where('name', $column->name)->first(); - if (!$oldColumn instanceof ColumnDefinition) { - $oldColumn = new ColumnDefinition($oldColumn); - } + return parent::compileChange($blueprint, $command); + } - foreach ($this->modifiers as $modifier) { - if (method_exists($this, $method = "modify{$modifier}")) { - $mod = strtolower($modifier); - $col = isset($oldColumn->{$mod}) && !isset($column->{$mod}) ? $oldColumn : $column; - $sql .= $this->{$method}($blueprint, $col); - } - } + /** @inheritDoc */ + protected function getKeptColumnAttributes($existing, Fluent $column): array + { + $attributes = [ + 'nullable' => (bool) $existing['nullable'], + // SQL Server reports a default as the expression of its constraint, such as ('text') + 'default' => is_null($existing['default']) ? null : new Expression($existing['default']), + ]; - $changes[] = $sql; + if ($this->changesToATextColumn($column)) { + $attributes['collation'] = $existing['collation']; } - return $changes; + return $attributes; } /** diff --git a/tests/Database/Schema/Grammars/MariaDbSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MariaDbSchemaGrammarTest.php new file mode 100644 index 00000000..0f44d971 --- /dev/null +++ b/tests/Database/Schema/Grammars/MariaDbSchemaGrammarTest.php @@ -0,0 +1,63 @@ +grammarClass = MariaDbGrammar::class; + $this->builderClass = MariaDbBuilder::class; + + parent::setUp(); + } + + public function testChangeKeepsANullableColumnWithoutADefault() + { + // MariaDB reports a nullable column without a default as the expression NULL + $this->existingColumns([$this->mariaDbColumn('name', 'varchar(255)', nullable: true, default: 'NULL')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('name', 100)->change(); + + $this->assertSame('alter table `users` modify `name` varchar(100) null', $this->runBlueprint($blueprint)[0]); + } + + public function testChangeKeepsAQuotedDefault() + { + $this->existingColumns([$this->mariaDbColumn('name', 'varchar(255)', default: "'O''Brien'")]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('name', 100)->change(); + + $this->assertSame("alter table `users` modify `name` varchar(100) not null default 'O''Brien'", $this->runBlueprint($blueprint)[0]); + } + + public function testChangeKeepsACurrentTimestampDefault() + { + $this->existingColumns([$this->mariaDbColumn('created_at', 'timestamp', default: 'current_timestamp()')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->timestamp('created_at')->nullable()->change(); + + $this->assertSame('alter table `users` modify `created_at` timestamp null default current_timestamp()', $this->runBlueprint($blueprint)[0]); + } + + /** + * A column as MariaDB's getColumns() reports it: every default as an SQL expression. + */ + protected function mariaDbColumn(string $name, string $type, bool $nullable = false, ?string $default = null): array + { + return compact('name', 'type', 'nullable', 'default') + [ + 'type_name' => strtok($type, '('), + 'collation' => null, + 'auto_increment' => false, + 'comment' => null, + 'generation' => null, + ]; + } +} diff --git a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php index 7ee243c6..ef518c69 100644 --- a/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/MySqlSchemaGrammarTest.php @@ -2,7 +2,9 @@ namespace Tests\Database\Schema\Grammars; +use Illuminate\Database\MySqlConnection; use Illuminate\Database\Schema\MySqlBuilder; +use Mockery as m; use Winter\Storm\Database\Schema\Grammars\MySqlGrammar; use Winter\Storm\Tests\GrammarTestCase; @@ -70,4 +72,117 @@ public function testQuoteInDefaultValue() $statements = $this->runBlueprint($initialBlueprint); $this->assertSame("alter table `users` add `name` varchar(255) not null default 'O''Brian'", $statements[0]); } + + public function testChangeCanRemoveDefaultWithNull() + { + $initialBlueprint = $this->getBlueprint('users'); + $initialBlueprint->string('name')->default('admin'); + + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame("alter table `users` add `name` varchar(255) not null default 'admin'", $statements[0]); + + $changedBlueprint = $this->getBlueprint('users'); + $changedBlueprint->string('name')->nullable()->default(null)->change(); + + $statements = $this->runBlueprint($changedBlueprint); + $this->assertSame('alter table `users` modify `name` varchar(255) null', $statements[0]); + } + + public function testChangeKeepsTheCollation() + { + $this->existingColumns([$this->mysqlColumn('name', 'varchar(255)', collation: 'utf8mb4_bin')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('name', 100)->change(); + + $this->assertSame("alter table `users` modify `name` varchar(100) collate 'utf8mb4_bin' not null", $this->runBlueprint($blueprint)[0]); + } + + public function testChangeToANonTextTypeDropsTheCollation() + { + $this->existingColumns([$this->mysqlColumn('code', 'varchar(255)', collation: 'utf8mb4_bin')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->integer('code')->change(); + + $this->assertSame('alter table `users` modify `code` int not null', $this->runBlueprint($blueprint)[0]); + } + + public function testChangeKeepsACurrentTimestampDefault() + { + $this->existingColumns([$this->mysqlColumn('created_at', 'timestamp', default: 'CURRENT_TIMESTAMP')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->timestamp('created_at')->nullable()->change(); + + $this->assertSame('alter table `users` modify `created_at` timestamp null default CURRENT_TIMESTAMP', $this->runBlueprint($blueprint)[0]); + } + + public function testChangeKeepsTheUnsignedAttribute() + { + $this->existingColumns([$this->mysqlColumn('amount', 'decimal(8,2) unsigned')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->decimal('amount', 10, 2)->change(); + + $this->assertSame('alter table `users` modify `amount` decimal(10, 2) unsigned not null', $this->runBlueprint($blueprint)[0]); + } + + public function testChangeToANonNumericTypeDropsTheUnsignedAttribute() + { + // Only a numeric column accepts the unsigned attribute, so keeping it here would be a syntax error + $this->existingColumns([$this->mysqlColumn('code', 'int unsigned')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('code', 50)->change(); + + $this->assertSame('alter table `users` modify `code` varchar(50) not null', $this->runBlueprint($blueprint)[0]); + } + + public function testChangeOnAMariaDbServerTreatsTheReportedDefaultAsAnExpression() + { + // The mysql driver can point at a MariaDB server, which reports no default as the expression NULL + $this->connection = m::mock(MySqlConnection::class) + ->shouldReceive('getTablePrefix')->andReturn('') + ->shouldReceive('getServerVersion')->andReturn('11.4.13') + ->shouldReceive('isMaria')->andReturn(true) + ->shouldReceive('scalar')->andReturn('') + ->getMock(); + + $this->connection->shouldReceive('getSchemaGrammar')->andReturn($this->getSchemaGrammar()); + + $this->existingColumns([$this->mysqlColumn('name', 'varchar(255)', nullable: true, default: 'NULL')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('name', 100)->change(); + + $this->assertSame('alter table `users` modify `name` varchar(100) null', $this->runBlueprint($blueprint)[0]); + } + + public function testChangingTwoColumnsCompilesOneStatementPerColumn() + { + $this->existingColumns([$this->mysqlColumn('first', 'varchar(255)'), $this->mysqlColumn('last', 'varchar(255)')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('first', 100)->change(); + $blueprint->string('last', 100)->change(); + + $this->assertSame([ + 'alter table `users` modify `first` varchar(100) not null', + 'alter table `users` modify `last` varchar(100) not null', + ], $this->runBlueprint($blueprint)); + } + + /** + * A column as MySQL's getColumns() reports it: literal defaults unquoted, expressions as is. + */ + protected function mysqlColumn(string $name, string $type, bool $nullable = false, ?string $default = null, ?string $collation = null): array + { + return compact('name', 'type', 'nullable', 'default', 'collation') + [ + 'type_name' => strtok($type, '('), + 'auto_increment' => false, + 'comment' => null, + 'generation' => null, + ]; + } } diff --git a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php index e4227811..ba7857a2 100644 --- a/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/PostgresSchemaGrammarTest.php @@ -48,7 +48,7 @@ public function testNullableInitialModifierAddDefault() $statements = $this->runBlueprint($changedBlueprint); $parts = explode(', ', $statements[0]); $this->assertSame('alter table "users" alter column "name" type varchar(255)', $parts[0]); - $this->assertSame('alter column "name" null', $parts[1]); + $this->assertSame('alter column "name" drop not null', $parts[1]); $this->assertSame("alter column \"name\" set default 'admin'", $parts[2]); } @@ -79,4 +79,96 @@ public function testQuoteInDefaultValue() $parts = explode(', ', $statements[0]); $this->assertSame("alter table \"users\" add column \"name\" varchar(255) not null default 'O''Brian'", $parts[0]); } + + public function testChangeCanRemoveDefaultWithNull() + { + $initialBlueprint = $this->getBlueprint('users'); + $initialBlueprint->string('name')->default('admin'); + + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame("alter table \"users\" add column \"name\" varchar(255) not null default 'admin'", $statements[0]); + + $changedBlueprint = $this->getBlueprint('users'); + $changedBlueprint->string('name')->nullable()->default(null)->change(); + + $statements = $this->runBlueprint($changedBlueprint); + $this->assertSame('alter table "users" alter column "name" type varchar(255), alter column "name" drop not null, alter column "name" drop default, alter column "name" drop identity if exists', $statements[0]); + } + + public function testChangeKeepsADefault() + { + $this->existingColumns([$this->postgresColumn('role', 'character varying(255)', default: "'O''Brien'::character varying")]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('role', 100)->change(); + + $this->assertSame( + 'alter table "users" alter column "role" type varchar(100), alter column "role" set not null, alter column "role" set default \'O\'\'Brien\'::character varying, alter column "role" drop identity if exists', + $this->runBlueprint($blueprint)[0] + ); + } + + public function testChangeKeepsACurrentTimestampDefault() + { + $this->existingColumns([$this->postgresColumn('created_at', 'timestamp(0) without time zone', default: 'CURRENT_TIMESTAMP')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->timestamp('created_at')->nullable()->change(); + + $this->assertSame( + 'alter table "users" alter column "created_at" type timestamp(0) without time zone, alter column "created_at" drop not null, alter column "created_at" set default CURRENT_TIMESTAMP, alter column "created_at" drop identity if exists', + $this->runBlueprint($blueprint)[0] + ); + } + + public function testChangeKeepsTheCollation() + { + $this->existingColumns([$this->postgresColumn('code', 'character varying(255)', collation: 'C')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('code', 100)->change(); + + $this->assertSame( + 'alter table "users" alter column "code" type varchar(100) collate "C", alter column "code" set not null, alter column "code" drop default, alter column "code" drop identity if exists', + $this->runBlueprint($blueprint)[0] + ); + } + + public function testChangeKeepsTheComment() + { + $this->existingColumns([$this->postgresColumn('note', 'character varying(255)', comment: 'a note')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('note', 100)->change(); + + $this->assertSame('comment on column "users"."note" is \'a note\'', $this->runBlueprint($blueprint)[1]); + } + + public function testChangingTwoColumnsCompilesOneStatementPerColumn() + { + $this->existingColumns([$this->postgresColumn('first', 'character varying(255)'), $this->postgresColumn('last', 'character varying(255)')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('first', 100)->change(); + $blueprint->string('last', 100)->change(); + + $this->assertSame([ + 'alter table "users" alter column "first" type varchar(100), alter column "first" set not null, alter column "first" drop default, alter column "first" drop identity if exists', + 'alter table "users" alter column "last" type varchar(100), alter column "last" set not null, alter column "last" drop default, alter column "last" drop identity if exists', + 'comment on column "users"."first" is NULL', + 'comment on column "users"."last" is NULL', + ], $this->runBlueprint($blueprint)); + } + + /** + * A column as PostgreSQL's getColumns() reports it: every default as an SQL expression. + */ + protected function postgresColumn(string $name, string $type, bool $nullable = false, ?string $default = null, ?string $collation = null, ?string $comment = null): array + { + return compact('name', 'type', 'nullable', 'default', 'collation', 'comment') + [ + 'type_name' => strtok($type, '('), + 'auto_increment' => false, + 'generation' => null, + ]; + } } diff --git a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php index 9fa7be9f..87df2f6a 100644 --- a/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SQLiteSchemaGrammarTest.php @@ -152,4 +152,16 @@ public function testChangeRebuildsTableExactlyOnce(): void $this->assertSame(1, $rebuilds, 'A single ->change() must rebuild the table exactly once.'); } + + public function testChangeCanRemoveDefaultWithNull(): void + { + [$pdo, , $builder] = $this->bootSqlite("CREATE TABLE users (id integer primary key, name varchar not null default 'bob')"); + + // An explicit null default must remove the existing default, not keep it. + $builder->table('users', fn (Blueprint $table) => $table->string('name')->nullable()->default(null)->change()); + + $info = $this->columnInfo($pdo, 'users', 'name'); + $this->assertSame(0, (int) $info['notnull']); + $this->assertNull($info['dflt_value']); + } } diff --git a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php index b7ef82ea..7d1f682e 100644 --- a/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php +++ b/tests/Database/Schema/Grammars/SqlServerSchemaGrammarTest.php @@ -71,4 +71,85 @@ public function testQuoteInDefaultValue() $statements = $this->runBlueprint($initialBlueprint); $this->assertSame("alter table \"users\" add \"name\" nvarchar(255) not null default 'O''Brian'", $statements[0]); } + + public function testChangeCanRemoveDefaultWithNull() + { + $initialBlueprint = $this->getBlueprint('users'); + $initialBlueprint->string('name')->default('admin'); + + $statements = $this->runBlueprint($initialBlueprint); + $this->assertSame('alter table "users" add "name" nvarchar(255) not null default \'admin\'', $statements[0]); + + $changedBlueprint = $this->getBlueprint('users'); + $changedBlueprint->string('name')->nullable()->default(null)->change(); + + $statements = $this->runBlueprint($changedBlueprint); + $this->assertCount(2, $statements); + $this->assertSame('alter table "users" alter column "name" nvarchar(255) null', $statements[1]); + } + + public function testChangeKeepsADefault() + { + $this->existingColumns([$this->sqlServerColumn('role', 'nvarchar(510)', default: "('O''Brien')", collation: 'SQL_Latin1_General_CP1_CI_AS')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('role', 100)->change(); + + $statements = $this->runBlueprint($blueprint); + $this->assertSame('alter table "users" alter column "role" nvarchar(100) collate SQL_Latin1_General_CP1_CI_AS not null', $statements[1]); + $this->assertSame('alter table "users" add default (\'O\'\'Brien\') for "role"', $statements[2]); + } + + public function testChangeKeepsACurrentTimestampDefault() + { + $this->existingColumns([$this->sqlServerColumn('created_at', 'datetime', default: '(getdate())')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->timestamp('created_at')->nullable()->change(); + + $statements = $this->runBlueprint($blueprint); + $this->assertSame('alter table "users" alter column "created_at" datetime null', $statements[1]); + $this->assertSame('alter table "users" add default (getdate()) for "created_at"', $statements[2]); + } + + public function testChangeKeepsTheCollation() + { + $this->existingColumns([$this->sqlServerColumn('code', 'nvarchar(510)', collation: 'Latin1_General_BIN')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('code', 100)->change(); + + $statements = $this->runBlueprint($blueprint); + $this->assertCount(2, $statements); + $this->assertSame('alter table "users" alter column "code" nvarchar(100) collate Latin1_General_BIN not null', $statements[1]); + } + + public function testChangingTwoColumnsCompilesOneStatementPerColumn() + { + $this->existingColumns([$this->sqlServerColumn('first', 'nvarchar(510)'), $this->sqlServerColumn('last', 'nvarchar(510)')]); + + $blueprint = $this->getBlueprint('users'); + $blueprint->string('first', 100)->change(); + $blueprint->string('last', 100)->change(); + + $statements = $this->runBlueprint($blueprint); + $this->assertCount(4, $statements); + $this->assertStringContainsString("[name] in ('first')", $statements[0]); + $this->assertSame('alter table "users" alter column "first" nvarchar(100) not null', $statements[1]); + $this->assertStringContainsString("[name] in ('last')", $statements[2]); + $this->assertSame('alter table "users" alter column "last" nvarchar(100) not null', $statements[3]); + } + + /** + * A column as SQL Server's getColumns() reports it: a default as the expression of its constraint. + */ + protected function sqlServerColumn(string $name, string $type, bool $nullable = false, ?string $default = null, ?string $collation = null): array + { + return compact('name', 'type', 'nullable', 'default', 'collation') + [ + 'type_name' => strtok($type, '('), + 'auto_increment' => false, + 'comment' => null, + 'generation' => null, + ]; + } } diff --git a/tests/GrammarTestCase.php b/tests/GrammarTestCase.php index f3e91119..d34b3c55 100644 --- a/tests/GrammarTestCase.php +++ b/tests/GrammarTestCase.php @@ -14,6 +14,13 @@ class GrammarTestCase extends TestCase protected $grammarClass = null; protected $builderClass = null; + /** + * The rows existingColumns() reports as the table's existing columns, if any. + * + * @var array>|null + */ + protected $reportedColumns = null; + public function setUp(): void { parent::setUp(); @@ -57,13 +64,21 @@ protected function getSchemaGrammar() protected function getSchemaBuilder($blueprint) { return m::mock(Builder::class) - ->shouldReceive('getColumns')->andReturn($blueprint->getColumns()) + ->shouldReceive('getColumns')->andReturn($this->reportedColumns ?? $blueprint->getColumns()) ->shouldReceive('getForeignKeys')->andReturn([]) ->shouldReceive('getIndexes')->andReturn([]) ->shouldReceive('parseSchemaAndTable')->andReturn([null, $blueprint->getTable()]) ->getMock(); } + /** + * Report the given rows, in the format of Builder::getColumns(), as the table's existing columns. + */ + protected function existingColumns(array $columns): void + { + $this->reportedColumns = $columns; + } + protected function runBlueprint(Blueprint $blueprint) { return $blueprint->toSql(); From 28f5bbe01a0503b9c566d1289d68bb7c206e306a Mon Sep 17 00:00:00 2001 From: Jonas Pardon Date: Thu, 22 Oct 2026 22:19:44 +0200 Subject: [PATCH 196/197] Read UNIX timestamps in the application timezone (#257) Co-authored-by: Claude Opus 5.5 With a non-UTC app.timezone, a UNIX timestamp assigned to a date attribute is stored shifted by the UTC offset on wip/1.3: // app.timezone = 'Europe/Brussels' $model->published_at = 1700000000; // 2023-11-14 22:13:20 UTC // 1.2: stored as '2023-11-14 23:13:20' // wip/1.3: stored as '2023-11-14 22:13:20', which reads back an hour earlier Model::asDateTime() calls Argon::createFromTimestamp($value) without a timezone. Carbon 2 used PHP's default timezone for that, which Storm sets from app.timezone. Carbon 3 uses UTC instead. Laravel hit the same regression and fixed it the same way in laravel/framework@8c61f50 (#50751), which is still how HasAttributes::asDateTime() reads on 12.x. Projects on the default UTC timezone are not affected. Pass date_default_timezone_get(), like Laravel. Add a test that assigns a timestamp with Europe/Brussels as the default timezone and checks that the stored value and the timestamp read back are correct. It fails without the change. asDateTime() is the only numeric-timestamp cast in Storm. Laravel's commit also covered its other createFromTimestamp() call sites; the equivalent ones in Winter core are fixed in wintercms/winter#1579. --- src/Database/Model.php | 2 +- tests/Database/ModelTest.php | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Database/Model.php b/src/Database/Model.php index 414c1268..f8ebbc34 100644 --- a/src/Database/Model.php +++ b/src/Database/Model.php @@ -638,7 +638,7 @@ protected function asDateTime($value) // and format a Carbon object from this timestamp. This allows flexibility // when defining your date fields as they might be UNIX timestamps here. if (is_numeric($value)) { - return Argon::createFromTimestamp($value); + return Argon::createFromTimestamp($value, date_default_timezone_get()); } // If the value is in simply year, month, day format, we will instantiate the diff --git a/tests/Database/ModelTest.php b/tests/Database/ModelTest.php index 71c7788f..bdddd907 100644 --- a/tests/Database/ModelTest.php +++ b/tests/Database/ModelTest.php @@ -526,6 +526,26 @@ public function testAddDatesCasts() ); } + public function testNumericDatesUseTheDefaultTimezone() + { + $timezone = date_default_timezone_get(); + date_default_timezone_set('Europe/Brussels'); + + try { + $model = new TestModelGuarded(); + $model->timestamps = false; + $model->addCasts(['deleted_at' => 'datetime']); + + // 2023-11-14 22:13:20 UTC + $model->deleted_at = 1700000000; + + $this->assertSame('2023-11-14 23:13:20', $model->getAttributes()['deleted_at']); + $this->assertSame(1700000000, $model->deleted_at->getTimestamp()); + } finally { + date_default_timezone_set($timezone); + } + } + public function testStringIsTrimmed() { $name = "Name"; From 792beea29794920194200157ea87bdf38efef437 Mon Sep 17 00:00:00 2001 From: Jonas Pardon Date: Fri, 23 Oct 2026 09:17:49 +0200 Subject: [PATCH 197/197] Register discovered package providers again (#255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claude Opus 5.5 Co-authored-by: Luke Towers Co-authored-by: Luke Towers With app.loadDiscoveredPackages enabled, no discovered package provider gets registered on wip/1.3. 0a6b24e ("Laravel 10 changes") dropped the brackets around the manifest's providers in Application::registerConfiguredProviders(): $providers->splice(1, 0, $this->make(PackageManifest::class)->providers()); That splices each provider class in as a separate string, between the Illuminate\ group and the other providers. collapse() only flattens arrays and skips anything else, so those strings never reach ProviderRepository::load(). Laravel 12 still passes them as one group ([…->providers()]), which is what this restores. The default (loadDiscoveredPackages => false) is unaffected. Restore the brackets. Add a test that boots an Application with a discovered provider and checks that it gets registered. It fails without the change. --- src/Foundation/Application.php | 2 +- tests/Foundation/ApplicationTest.php | 54 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index 8aa33968..3962374a 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -401,7 +401,7 @@ public function registerConfiguredProviders($isRetry = false) }); if (Config::get('app.loadDiscoveredPackages', false)) { - $providers->splice(1, 0, $this->make(PackageManifest::class)->providers()); + $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]); } $filesystem = new Filesystem; diff --git a/tests/Foundation/ApplicationTest.php b/tests/Foundation/ApplicationTest.php index d114f04b..338ce2f8 100644 --- a/tests/Foundation/ApplicationTest.php +++ b/tests/Foundation/ApplicationTest.php @@ -1,5 +1,10 @@ app = new Application($this->basePath); } + protected function tearDown(): void + { + // These tests build their own throwaway applications instead of booting one through Testbench, so there is nothing for Testbench to tear down. + } + public function testPathMethods() { $this->assertEquals(PathResolver::join($this->basePath, '/plugins'), $this->app->pluginsPath()); @@ -45,4 +55,48 @@ public function testSetPathMethods() $this->assertEquals($expected, $this->app->{$getter}()); } } + + public function testRegistersDiscoveredPackageProviders() + { + $basePath = sys_get_temp_dir() . '/winter-storm-' . uniqid(); + mkdir($basePath . '/storage/framework', 0777, true); + + $this->app = new Application($basePath); + $this->app->instance('config', new Repository([ + 'app' => ['providers' => [], 'loadDiscoveredPackages' => true], + ])); + $this->app->instance(PackageManifest::class, new class extends PackageManifest { + public function __construct() + { + } + + public function providers() + { + return [DiscoveredPackageServiceProvider::class]; + } + }); + $facadeApp = Facade::getFacadeApplication(); + Facade::clearResolvedInstances(); + Facade::setFacadeApplication($this->app); + + try { + $this->app->registerConfiguredProviders(); + + $this->assertNotNull($this->app->getProvider(DiscoveredPackageServiceProvider::class)); + $this->assertTrue($this->app->bound('discovered-package')); + } finally { + Facade::clearResolvedInstances(); + Facade::setFacadeApplication($facadeApp); + + (new Filesystem)->deleteDirectory($basePath); + } + } +} + +class DiscoveredPackageServiceProvider extends ServiceProvider +{ + public function register() + { + $this->app->instance('discovered-package', true); + } }