From a21fc3154757d63ab22121150ac033ee1bc444c8 Mon Sep 17 00:00:00 2001 From: Rias Date: Thu, 5 Feb 2026 09:47:21 +0100 Subject: [PATCH 1/8] Install workbench --- composer.json | 19 +- src/Http/Middleware/HandleInertiaRequests.php | 14 +- testbench.yaml | 11 + workbench/.gitignore | 2 + workbench/app/Models/.gitkeep | 0 .../Providers/WorkbenchServiceProvider.php | 27 +++ workbench/bootstrap/app.php | 17 ++ workbench/bootstrap/providers.php | 5 + workbench/config/app.php | 190 ++++++++++++++++++ workbench/database/factories/.gitkeep | 0 workbench/database/factories/UserFactory.php | 56 ++++++ workbench/database/migrations/.gitkeep | 0 workbench/database/seeders/DatabaseSeeder.php | 27 +++ workbench/resources/views/.gitkeep | 0 workbench/routes/console.php | 8 + workbench/routes/web.php | 5 + yii2-adapter/src/Web/Request.php | 2 +- 17 files changed, 376 insertions(+), 7 deletions(-) create mode 100644 workbench/.gitignore create mode 100644 workbench/app/Models/.gitkeep create mode 100644 workbench/app/Providers/WorkbenchServiceProvider.php create mode 100644 workbench/bootstrap/app.php create mode 100644 workbench/bootstrap/providers.php create mode 100644 workbench/config/app.php create mode 100644 workbench/database/factories/.gitkeep create mode 100644 workbench/database/factories/UserFactory.php create mode 100644 workbench/database/migrations/.gitkeep create mode 100644 workbench/database/seeders/DatabaseSeeder.php create mode 100644 workbench/resources/views/.gitkeep create mode 100644 workbench/routes/console.php create mode 100644 workbench/routes/web.php diff --git a/composer.json b/composer.json index e554f26ac79..c89253974e3 100644 --- a/composer.json +++ b/composer.json @@ -109,7 +109,10 @@ "psr-4": { "CraftCms\\Cms\\Tests\\": "tests/", "CraftCms\\Cms\\Database\\": "database/", - "CraftCms\\Yii2Adapter\\Tests\\": "yii2-adapter/tests-laravel/" + "CraftCms\\Yii2Adapter\\Tests\\": "yii2-adapter/tests-laravel/", + "Workbench\\App\\": "workbench/app/", + "Workbench\\Database\\Factories\\": "workbench/database/factories/", + "Workbench\\Database\\Seeders\\": "workbench/database/seeders/" } }, "scripts": { @@ -118,7 +121,19 @@ "phpstan": "phpstan --memory-limit=1G", "rector": "rector", "tests": "./vendor/bin/pest --compact", - "tests-adapter": "./vendor/bin/pest --configuration ./yii2-adapter/phpunit.xml.dist --test-directory ./yii2-adapter/tests-laravel" + "tests-adapter": "./vendor/bin/pest --configuration ./yii2-adapter/phpunit.xml.dist --test-directory ./yii2-adapter/tests-laravel", + "post-autoload-dump": [ + "@clear", + "@prepare" + ], + "clear": "@php vendor/bin/testbench package:purge-skeleton --ansi", + "prepare": "@php vendor/bin/testbench package:discover --ansi", + "build": "@php vendor/bin/testbench workbench:build --ansi", + "serve": [ + "Composer\\Config::disableProcessTimeout", + "@build", + "@php vendor/bin/testbench serve --ansi" + ] }, "config": { "sort-packages": true, diff --git a/src/Http/Middleware/HandleInertiaRequests.php b/src/Http/Middleware/HandleInertiaRequests.php index cca6c5366b6..2728c5785e3 100644 --- a/src/Http/Middleware/HandleInertiaRequests.php +++ b/src/Http/Middleware/HandleInertiaRequests.php @@ -16,6 +16,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Inertia\Middleware; +use Override; class HandleInertiaRequests extends Middleware { @@ -33,7 +34,7 @@ class HandleInertiaRequests extends Middleware * * @see https://inertiajs.com/asset-versioning */ - #[\Override] + #[Override] public function version(Request $request): ?string { return parent::version($request); @@ -46,15 +47,20 @@ public function version(Request $request): ?string * * @return array */ - #[\Override] + #[Override] public function share(Request $request): array { - $currentSite = Sites::getCurrentSite(); $isInstalled = Cms::isInstalled(); + + if (! $isInstalled) { + return parent::share($request); + } + + $currentSite = Sites::getCurrentSite(); $updates = app(Updates::class); $nav = app(Navigation::class); - if ($isInstalled && ! $updates->isCraftUpdatePending()) { + if (! $updates->isCraftUpdatePending()) { $currentUser = Craft::$app->getUser()->getIdentity(); if (! $currentUser) { diff --git a/testbench.yaml b/testbench.yaml index 6c968b2d7a0..e365eb93f2f 100644 --- a/testbench.yaml +++ b/testbench.yaml @@ -7,3 +7,14 @@ providers: - CraftCms\Cms\Providers\CraftServiceProvider - CraftCms\Yii2Adapter\Yii2ServiceProvider - Laravel\Wayfinder\WayfinderServiceProvider + +workbench: + start: / + install: true + sync: + - from: resources + to: public/vendor/craft + discovers: + web: true + commands: true + config: true diff --git a/workbench/.gitignore b/workbench/.gitignore new file mode 100644 index 00000000000..72603218f78 --- /dev/null +++ b/workbench/.gitignore @@ -0,0 +1,2 @@ +.env +.env.dusk diff --git a/workbench/app/Models/.gitkeep b/workbench/app/Models/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/workbench/app/Providers/WorkbenchServiceProvider.php b/workbench/app/Providers/WorkbenchServiceProvider.php new file mode 100644 index 00000000000..85478feecb3 --- /dev/null +++ b/workbench/app/Providers/WorkbenchServiceProvider.php @@ -0,0 +1,27 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + ) + ->withMiddleware(function (Middleware $middleware): void {}) + ->withExceptions(function (Exceptions $exceptions): void { + // + })->create(); diff --git a/workbench/bootstrap/providers.php b/workbench/bootstrap/providers.php new file mode 100644 index 00000000000..3ac44ad1013 --- /dev/null +++ b/workbench/bootstrap/providers.php @@ -0,0 +1,5 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + 'frontend_url' => env('FRONTEND_URL', 'http://localhost:3000'), + + 'asset_url' => env('ASSET_URL'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + /* + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the default one + | is not available. You may change the value to correspond to any of + | the languages which are currently supported by your application. + | + */ + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + /* + |-------------------------------------------------------------------------- + | Faker Locale + |-------------------------------------------------------------------------- + | + | This locale will be used by the Faker PHP library when generating fake + | data for your database seeds. For example, this will be used to get + | localized telephone numbers, street address information and more. + | + */ + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on any + | requests to your application. You may add your own services to the + | arrays below to provide additional features to this application. + | + */ + + 'providers' => ServiceProvider::defaultProviders()->merge([ + // Package Service Providers... + ])->merge([ + // Application Service Providers... + // App\Providers\AppServiceProvider::class, + ])->merge([ + // Added Service Providers (Do not remove this line)... + ])->toArray(), + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. You may add any additional class aliases which should + | be loaded to the array. For speed, all aliases are lazy loaded. + | + */ + + 'aliases' => Facade::defaultAliases()->merge([ + // 'Example' => App\Facades\Example::class, + ])->toArray(), + +]; diff --git a/workbench/database/factories/.gitkeep b/workbench/database/factories/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/workbench/database/factories/UserFactory.php b/workbench/database/factories/UserFactory.php new file mode 100644 index 00000000000..103e89d13dc --- /dev/null +++ b/workbench/database/factories/UserFactory.php @@ -0,0 +1,56 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * The name of the factory's corresponding model. + * + * @var class-string + */ + protected $model = User::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/workbench/database/migrations/.gitkeep b/workbench/database/migrations/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/workbench/database/seeders/DatabaseSeeder.php b/workbench/database/seeders/DatabaseSeeder.php new file mode 100644 index 00000000000..4105579bb0d --- /dev/null +++ b/workbench/database/seeders/DatabaseSeeder.php @@ -0,0 +1,27 @@ +times(10)->create(); + + UserFactory::new()->create([ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + } +} diff --git a/workbench/resources/views/.gitkeep b/workbench/resources/views/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/workbench/routes/console.php b/workbench/routes/console.php new file mode 100644 index 00000000000..79297257142 --- /dev/null +++ b/workbench/routes/console.php @@ -0,0 +1,8 @@ +comment(Inspiring::quote()); +// })->purpose('Display an inspiring quote'); diff --git a/workbench/routes/web.php b/workbench/routes/web.php new file mode 100644 index 00000000000..9d02afde1e0 --- /dev/null +++ b/workbench/routes/web.php @@ -0,0 +1,5 @@ + view('welcome')); diff --git a/yii2-adapter/src/Web/Request.php b/yii2-adapter/src/Web/Request.php index 8e2b1f037ac..87608d8dabe 100644 --- a/yii2-adapter/src/Web/Request.php +++ b/yii2-adapter/src/Web/Request.php @@ -62,7 +62,7 @@ class Request extends \yii\web\Request public function getIlluminateRequest(): IlluminateRequest { /** @var IlluminateRequest $request */ - $request = $this->_illuminateRequest ??= app('request'); + $request = $this->_illuminateRequest ??= IlluminateRequest::capture(); $request->setLaravelSession(session()->driver()); From 1b1dd57851b920e4c79c20416073d8570d870d0f Mon Sep 17 00:00:00 2001 From: Rias Date: Thu, 5 Feb 2026 10:07:03 +0100 Subject: [PATCH 2/8] Cleanup --- workbench/database/factories/UserFactory.php | 56 ------------------- workbench/database/seeders/DatabaseSeeder.php | 6 -- 2 files changed, 62 deletions(-) delete mode 100644 workbench/database/factories/UserFactory.php diff --git a/workbench/database/factories/UserFactory.php b/workbench/database/factories/UserFactory.php deleted file mode 100644 index 103e89d13dc..00000000000 --- a/workbench/database/factories/UserFactory.php +++ /dev/null @@ -1,56 +0,0 @@ - - */ -class UserFactory extends Factory -{ - /** - * The current password being used by the factory. - */ - protected static ?string $password; - - /** - * The name of the factory's corresponding model. - * - * @var class-string - */ - protected $model = User::class; - - /** - * Define the model's default state. - * - * @return array - */ - public function definition(): array - { - return [ - 'name' => fake()->name(), - 'email' => fake()->unique()->safeEmail(), - 'email_verified_at' => now(), - 'password' => static::$password ??= Hash::make('password'), - 'remember_token' => Str::random(10), - ]; - } - - /** - * Indicate that the model's email address should be unverified. - */ - public function unverified(): static - { - return $this->state(fn (array $attributes) => [ - 'email_verified_at' => null, - ]); - } -} diff --git a/workbench/database/seeders/DatabaseSeeder.php b/workbench/database/seeders/DatabaseSeeder.php index 4105579bb0d..54cd82025e3 100644 --- a/workbench/database/seeders/DatabaseSeeder.php +++ b/workbench/database/seeders/DatabaseSeeder.php @@ -6,7 +6,6 @@ use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; -use Workbench\Database\Factories\UserFactory; class DatabaseSeeder extends Seeder { @@ -18,10 +17,5 @@ class DatabaseSeeder extends Seeder public function run(): void { // UserFactory::new()->times(10)->create(); - - UserFactory::new()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', - ]); } } From 1ab13fff37fb7782c1b7417b764d8f0e5d010b7d Mon Sep 17 00:00:00 2001 From: Rias Date: Thu, 5 Feb 2026 10:07:23 +0100 Subject: [PATCH 3/8] Move Rector to the Laravel GitHub workflows --- .github/workflows/ci.yml | 2 +- .github/workflows/laravel-ci.yml | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5d31433307..e0c2ad4a0b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: php_version: '8.4' craft_version: '6' node_version: '22' - jobs: '["ecs", "prettier", "phpstan", "tests", "rector"]' + jobs: '["ecs", "prettier", "phpstan", "tests"]' working_directory: 'yii2-adapter' notify_slack: true slack_subteam: diff --git a/.github/workflows/laravel-ci.yml b/.github/workflows/laravel-ci.yml index 8c032095049..457a32c4ecc 100644 --- a/.github/workflows/laravel-ci.yml +++ b/.github/workflows/laravel-ci.yml @@ -34,6 +34,41 @@ jobs: - name: Run Pint run: pint --parallel --test --verbose + rector: + name: 'Code Quality / Rector' + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: none + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMPOSER_AUTH_JSON: | + { + "http-basic": { + "repo.packagist.com": { + "username": "${{ secrets.packagist_username }}", + "password": "${{ secrets.packagist_token }}" + } + } + } + + - name: Install dependencies + uses: ramsey/composer-install@v3 + + - name: Run Rector + run: vendor/bin/rector process --dry-run --ansi + phpstan: name: 'Code Quality / Phpstan' runs-on: ubuntu-latest From d9bbca2d7cf39cd994b3491c537336f22fe37620 Mon Sep 17 00:00:00 2001 From: Rias Date: Thu, 5 Feb 2026 10:12:05 +0100 Subject: [PATCH 4/8] Need to set version --- .github/workflows/laravel-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/laravel-ci.yml b/.github/workflows/laravel-ci.yml index 457a32c4ecc..9c2b7d74e7d 100644 --- a/.github/workflows/laravel-ci.yml +++ b/.github/workflows/laravel-ci.yml @@ -63,6 +63,9 @@ jobs: } } + - name: Set version + run: composer config version "6.x-dev" + - name: Install dependencies uses: ramsey/composer-install@v3 From ecef8e9749663e18fac3c82bcce107ea614a721f Mon Sep 17 00:00:00 2001 From: Rias Date: Thu, 5 Feb 2026 10:26:42 +0100 Subject: [PATCH 5/8] Fix test --- yii2-adapter/tests/unit/helpers/UrlHelperTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yii2-adapter/tests/unit/helpers/UrlHelperTest.php b/yii2-adapter/tests/unit/helpers/UrlHelperTest.php index 676a7a184e1..ba5d22f4cd1 100644 --- a/yii2-adapter/tests/unit/helpers/UrlHelperTest.php +++ b/yii2-adapter/tests/unit/helpers/UrlHelperTest.php @@ -101,6 +101,8 @@ public function testIsFullUrl(bool $expected, string $url): void */ public function testCpUrlCreation(string $expected, string $path, array $params, string $scheme = 'https'): void { + Aliases::set('@web', 'http://localhost'); + $this->tester->mockCraftMethods('request', [ 'getIsSecureConnection' => false, 'getIsCpRequest' => true, From 3e838423272e946912833d1fd916b5a31e3711a5 Mon Sep 17 00:00:00 2001 From: Rias Date: Thu, 5 Feb 2026 10:36:12 +0100 Subject: [PATCH 6/8] Fix another test --- yii2-adapter/tests/unit/web/ControllerTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yii2-adapter/tests/unit/web/ControllerTest.php b/yii2-adapter/tests/unit/web/ControllerTest.php index 83ad1eb2728..73a406bb998 100644 --- a/yii2-adapter/tests/unit/web/ControllerTest.php +++ b/yii2-adapter/tests/unit/web/ControllerTest.php @@ -16,6 +16,7 @@ use craft\web\TemplateResponseFormatter; use craft\web\View; use CraftCms\Cms\Cms; +use CraftCms\Cms\Support\Str; use Illuminate\Support\Facades\Crypt; use UnitTester; use yii\base\Action; @@ -113,7 +114,7 @@ public function testRedirectToPostedUrl(): void $default = $this->controller->redirectToPostedUrl(); // Test that with nothing passed in. It defaults to the base. See self::getBaseUrlForRedirect() for more info. - self::assertSame(TestSetup::SITE_URL, $default->headers->get('Location')); + self::assertSame(rtrim(TestSetup::SITE_URL, '/'), Str::before($default->headers->get('Location'), ':80')); // What happens when we pass in a param. Craft::$app->getRequest()->setBodyParams(['redirect' => $redirect]); @@ -168,7 +169,7 @@ public function testRedirect(): void self::assertSame(TestSetup::SITE_URL . 'do/stuff', $this->controller->redirect('do/stuff')->headers->get('Location')); // We dont use _getBaseUrlForRedirect because the :port80 wont work with urlWithScheme. - self::assertSame(TestSetup::SITE_URL, $this->controller->redirect(null)->headers->get('Location')); + self::assertSame(rtrim(TestSetup::SITE_URL, '/'), Str::before($this->controller->redirect(null)->headers->get('Location'), ':80')); // Absolute url self::assertSame( From 8cac2010d0047dc6a2e204570be41743b77f0c18 Mon Sep 17 00:00:00 2001 From: Rias Date: Thu, 5 Feb 2026 11:31:30 +0100 Subject: [PATCH 7/8] Run install automatically, log in automatically --- testbench.yaml | 17 +++++++-- workbench/.gitignore | 1 + workbench/database/seeders/DatabaseSeeder.php | 36 ++++++++++++++++++- workbench/routes/web.php | 4 --- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/testbench.yaml b/testbench.yaml index e365eb93f2f..2a25bb456a6 100644 --- a/testbench.yaml +++ b/testbench.yaml @@ -8,13 +8,26 @@ providers: - CraftCms\Yii2Adapter\Yii2ServiceProvider - Laravel\Wayfinder\WayfinderServiceProvider +seeders: + - Workbench\Database\Seeders\DatabaseSeeder + workbench: - start: / - install: true + welcome: true + start: /admin + install: false # We run the Craft install in DatabaseSeeder + user: 1 + guard: craft sync: - from: resources to: public/vendor/craft + - from: storage + to: workbench/storage + reverse: true + build: + - migrate-fresh discovers: web: true commands: true config: true + factories: true + seeders: true diff --git a/workbench/.gitignore b/workbench/.gitignore index 72603218f78..3badc3a0461 100644 --- a/workbench/.gitignore +++ b/workbench/.gitignore @@ -1,2 +1,3 @@ .env .env.dusk +storage diff --git a/workbench/database/seeders/DatabaseSeeder.php b/workbench/database/seeders/DatabaseSeeder.php index 54cd82025e3..1d703150cbb 100644 --- a/workbench/database/seeders/DatabaseSeeder.php +++ b/workbench/database/seeders/DatabaseSeeder.php @@ -4,8 +4,16 @@ namespace Workbench\Database\Seeders; +use CraftCms\Cms\Database\Migrations\Install; +use CraftCms\Cms\Database\Table; +use CraftCms\Cms\ProjectConfig\ProjectConfig; +use CraftCms\Cms\Site\Data\Site; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Context; +use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Schema; class DatabaseSeeder extends Seeder { @@ -16,6 +24,32 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - // UserFactory::new()->times(10)->create(); + if (! Schema::hasTable(Table::ELEMENTS)) { + Context::forgetHidden('craft.info'); + Context::forgetHidden('craft.isInstalled'); + + File::cleanDirectory(config_path('craft/project')); + File::cleanDirectory(storage_path('runtime/compiled_classes')); + + Cache::lock(ProjectConfig::MUTEX_NAME)->forceRelease(); + + $site = new Site( + name: 'Craft test site', + handle: 'defaultSite', + language: 'en-US', + baseUrl: config('app.url'), + primary: true, + hasUrls: true, + ); + + $migration = new Install( + username: 'craftcms', + password: 'craftcms2018!!', + email: 'support@craftcms.com', + site: $site, + ); + + $migration->up(); + } } } diff --git a/workbench/routes/web.php b/workbench/routes/web.php index 9d02afde1e0..b3d9bbc7f37 100644 --- a/workbench/routes/web.php +++ b/workbench/routes/web.php @@ -1,5 +1 @@ view('welcome')); From ad28d38e5e02d621276239c8ea2472558b98e82d Mon Sep 17 00:00:00 2001 From: Rias Date: Thu, 5 Feb 2026 12:33:48 +0100 Subject: [PATCH 8/8] Create a basic seeder --- src/Database/Migrations/Install.php | 1 + src/Translation/I18N.php | 9 +- workbench/database/seeders/DatabaseSeeder.php | 142 ++++++++++++++---- 3 files changed, 122 insertions(+), 30 deletions(-) diff --git a/src/Database/Migrations/Install.php b/src/Database/Migrations/Install.php index 0770a9fdca3..ac7a7e93d3e 100644 --- a/src/Database/Migrations/Install.php +++ b/src/Database/Migrations/Install.php @@ -488,6 +488,7 @@ public function createTables(): void $table->char('uid', 36)->default('0'); }); + Schema::dropIfExists(Table::MIGRATIONS); app(Migrator::class) ->getRepository() ->createRepository(); diff --git a/src/Translation/I18N.php b/src/Translation/I18N.php index ed5699d96d6..9a7ae35287e 100644 --- a/src/Translation/I18N.php +++ b/src/Translation/I18N.php @@ -295,7 +295,14 @@ public function translate(string|Stringable $message, array $parameters = [], ?s * Translate it using Laravel's translations. */ if ($translation === (string) $message) { - return __($message, $parameters, $locale); + $result = __($message, $parameters, $locale); + + // We're dealing with a message that's equal to a translation file (for example 'site') + if (is_array($result)) { + return $translation; + } + + return $result; } return $translation; diff --git a/workbench/database/seeders/DatabaseSeeder.php b/workbench/database/seeders/DatabaseSeeder.php index 1d703150cbb..23c42a0763d 100644 --- a/workbench/database/seeders/DatabaseSeeder.php +++ b/workbench/database/seeders/DatabaseSeeder.php @@ -4,52 +4,136 @@ namespace Workbench\Database\Seeders; +use craft\fieldlayoutelements\entries\EntryTitleField; use CraftCms\Cms\Database\Migrations\Install; -use CraftCms\Cms\Database\Table; +use CraftCms\Cms\Entry\Data\EntryType; +use CraftCms\Cms\Entry\Elements\Entry; +use CraftCms\Cms\FieldLayout\Models\FieldLayout; use CraftCms\Cms\ProjectConfig\ProjectConfig; +use CraftCms\Cms\Section\Data\Section; +use CraftCms\Cms\Section\Data\SectionSiteSettings; +use CraftCms\Cms\Section\Enums\SectionType; use CraftCms\Cms\Site\Data\Site; +use CraftCms\Cms\Support\Facades\EntryTypes; +use CraftCms\Cms\Support\Facades\Fields; +use CraftCms\Cms\Support\Facades\Sections; +use CraftCms\Cms\Support\Facades\Sites; +use CraftCms\Cms\Support\Str; +use Illuminate\Console\Concerns\InteractsWithIO; +use Illuminate\Console\OutputStyle; +use Illuminate\Console\View\Components\Factory; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Context; use Illuminate\Support\Facades\File; -use Illuminate\Support\Facades\Schema; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\ConsoleOutput; class DatabaseSeeder extends Seeder { + use InteractsWithIO; use WithoutModelEvents; + public function __construct() + { + $this->input = new ArrayInput([]); + $this->output = new OutputStyle($this->input, new ConsoleOutput); + $this->components = new Factory($this->output); + } + /** * Seed the application's database. */ public function run(): void { - if (! Schema::hasTable(Table::ELEMENTS)) { - Context::forgetHidden('craft.info'); - Context::forgetHidden('craft.isInstalled'); - - File::cleanDirectory(config_path('craft/project')); - File::cleanDirectory(storage_path('runtime/compiled_classes')); - - Cache::lock(ProjectConfig::MUTEX_NAME)->forceRelease(); - - $site = new Site( - name: 'Craft test site', - handle: 'defaultSite', - language: 'en-US', - baseUrl: config('app.url'), - primary: true, - hasUrls: true, - ); - - $migration = new Install( - username: 'craftcms', - password: 'craftcms2018!!', - email: 'support@craftcms.com', - site: $site, - ); - - $migration->up(); - } + Context::forgetHidden('craft.info'); + Context::forgetHidden('craft.isInstalled'); + + File::cleanDirectory(config_path('craft/project')); + File::cleanDirectory(storage_path('runtime/compiled_classes')); + + Cache::lock(ProjectConfig::MUTEX_NAME)->forceRelease(); + + $site = new Site( + name: 'Craft test site', + handle: 'defaultSite', + language: 'en-US', + baseUrl: config('app.url'), + primary: true, + hasUrls: true, + ); + + new Install( + username: 'craftcms', + password: 'craftcms2018!!', + email: 'support@craftcms.com', + site: $site, + )->up(); + + $site = Sites::getCurrentSite(); + + $this->components->info('Creating default entry types & sections...'); + + $fieldLayout = null; + $this->components->task('Creating field layout', function () use (&$fieldLayout) { + $fieldLayout = FieldLayout::create([ + 'uid' => Str::uuid()->toString(), + 'type' => Entry::class, + 'config' => [ + 'tabs' => [ + [ + 'uid' => Str::uuid()->toString(), + 'name' => 'Content', + 'elements' => [ + [ + 'uid' => Str::uuid()->toString(), + 'type' => EntryTitleField::class, + 'required' => true, + ], + ], + ], + ], + ], + ]); + }); + + Fields::refreshFields(); + + $pageType = null; + $this->components->task('Page entry type', function () use ($fieldLayout, &$pageType) { + EntryTypes::saveEntryType($pageType = new EntryType( + fieldLayoutId: $fieldLayout->id, + name: 'Page', + handle: 'page', + )); + }); + + $this->createSection($site, 'Home', SectionType::Single, '__HOME__', [$pageType]); + $this->createSection($site, 'Pages', SectionType::Structure, '{parent.uri}/{slug}', [$pageType]); + $this->createSection($site, 'Posts', SectionType::Channel, 'blog/{slug}', [$pageType]); + } + + public function createSection(Site $site, string $title, SectionType $sectionType = SectionType::Channel, ?string $uriFormat = null, array $entryTypes = []): ?Section + { + $section = null; + + $this->components->task("{$title} section ({$sectionType->label()})", function () use ($uriFormat, $entryTypes, $sectionType, $title, $site, &$section) { + Sections::saveSection($section = new Section( + name: $title, + handle: Str::slug($title), + type: $sectionType, + siteSettings: [ + $site->id => new SectionSiteSettings( + siteId: $site->id, + hasUrls: ! is_null($uriFormat), + uriFormat: $uriFormat, + ), + ], + entryTypes: $entryTypes, + )); + }); + + return $section; } }