606 lines
17 KiB
PHP
606 lines
17 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Core PHP Framework
|
||
|
|
*
|
||
|
|
* Licensed under the European Union Public Licence (EUPL) v1.2.
|
||
|
|
* See LICENSE file for details.
|
||
|
|
*/
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Core\Console\Commands;
|
||
|
|
|
||
|
|
use Illuminate\Console\Command;
|
||
|
|
use Illuminate\Support\Facades\File;
|
||
|
|
use Illuminate\Support\Str;
|
||
|
|
use Symfony\Component\Console\Completion\CompletionInput;
|
||
|
|
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate a new Website scaffold.
|
||
|
|
*
|
||
|
|
* Creates a domain-isolated website in the Website namespace
|
||
|
|
* that is loaded based on incoming HTTP host.
|
||
|
|
*
|
||
|
|
* Usage: php artisan make:website MarketingSite --domain=marketing.test
|
||
|
|
*/
|
||
|
|
class MakeWebsiteCommand extends Command
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* The name and signature of the console command.
|
||
|
|
*/
|
||
|
|
protected $signature = 'make:website
|
||
|
|
{name : The name of the website (e.g., MarketingSite, Blog)}
|
||
|
|
{--domain= : Primary domain pattern (e.g., example.test, example.com)}
|
||
|
|
{--web : Include web routes}
|
||
|
|
{--admin : Include admin routes}
|
||
|
|
{--api : Include API routes}
|
||
|
|
{--all : Include all route types}
|
||
|
|
{--force : Overwrite existing website}';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The console command description.
|
||
|
|
*/
|
||
|
|
protected $description = 'Create a new domain-isolated website';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Files created during generation for summary table.
|
||
|
|
*
|
||
|
|
* @var array<array{file: string, description: string}>
|
||
|
|
*/
|
||
|
|
protected array $createdFiles = [];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Execute the console command.
|
||
|
|
*/
|
||
|
|
public function handle(): int
|
||
|
|
{
|
||
|
|
$name = Str::studly($this->argument('name'));
|
||
|
|
$domain = $this->option('domain') ?: Str::snake($name, '-').'.test';
|
||
|
|
$websitePath = $this->getWebsitePath($name);
|
||
|
|
|
||
|
|
if (File::isDirectory($websitePath) && ! $this->option('force')) {
|
||
|
|
$this->newLine();
|
||
|
|
$this->components->error("Website [{$name}] already exists!");
|
||
|
|
$this->newLine();
|
||
|
|
$this->components->warn('Use --force to overwrite the existing website.');
|
||
|
|
$this->newLine();
|
||
|
|
|
||
|
|
return self::FAILURE;
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->newLine();
|
||
|
|
$this->components->info("Creating website: <comment>{$name}</comment>");
|
||
|
|
$this->components->twoColumnDetail('Domain', "<fg=yellow>{$domain}</>");
|
||
|
|
$this->newLine();
|
||
|
|
|
||
|
|
// Create directory structure
|
||
|
|
$this->createDirectoryStructure($websitePath);
|
||
|
|
|
||
|
|
// Create Boot.php
|
||
|
|
$this->createBootFile($websitePath, $name, $domain);
|
||
|
|
|
||
|
|
// Create optional route files
|
||
|
|
$this->createOptionalFiles($websitePath, $name);
|
||
|
|
|
||
|
|
// Show summary table of created files
|
||
|
|
$this->newLine();
|
||
|
|
$this->components->twoColumnDetail('<fg=green;options=bold>Created Files</>', '<fg=gray>Description</>');
|
||
|
|
foreach ($this->createdFiles as $file) {
|
||
|
|
$this->components->twoColumnDetail(
|
||
|
|
"<fg=cyan>{$file['file']}</>",
|
||
|
|
"<fg=gray>{$file['description']}</>"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->newLine();
|
||
|
|
$this->components->info("Website [{$name}] created successfully!");
|
||
|
|
$this->newLine();
|
||
|
|
$this->components->twoColumnDetail('Location', "<fg=yellow>{$websitePath}</>");
|
||
|
|
$this->newLine();
|
||
|
|
|
||
|
|
$this->components->info('Next steps:');
|
||
|
|
$this->line(" <fg=gray>1.</> Configure your local dev server to serve <fg=yellow>{$domain}</>");
|
||
|
|
$this->line(' <fg=gray>(e.g.,</> valet link '.Str::snake($name, '-').'<fg=gray>)</>');
|
||
|
|
$this->line(" <fg=gray>2.</> Visit <fg=cyan>http://{$domain}</> to see your website");
|
||
|
|
$this->line(' <fg=gray>3.</> Add routes, views, and controllers as needed');
|
||
|
|
$this->newLine();
|
||
|
|
|
||
|
|
return self::SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the path for the website.
|
||
|
|
*/
|
||
|
|
protected function getWebsitePath(string $name): string
|
||
|
|
{
|
||
|
|
// Websites go in app/Website for consuming applications
|
||
|
|
return base_path("app/Website/{$name}");
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create the directory structure for the website.
|
||
|
|
*/
|
||
|
|
protected function createDirectoryStructure(string $websitePath): void
|
||
|
|
{
|
||
|
|
$directories = [
|
||
|
|
$websitePath,
|
||
|
|
"{$websitePath}/View",
|
||
|
|
"{$websitePath}/View/Blade",
|
||
|
|
"{$websitePath}/View/Blade/layouts",
|
||
|
|
];
|
||
|
|
|
||
|
|
if ($this->hasRoutes()) {
|
||
|
|
$directories[] = "{$websitePath}/Routes";
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach ($directories as $directory) {
|
||
|
|
File::ensureDirectoryExists($directory);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->components->task('Creating directory structure', fn () => true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if any route handlers are requested.
|
||
|
|
*/
|
||
|
|
protected function hasRoutes(): bool
|
||
|
|
{
|
||
|
|
return $this->option('web')
|
||
|
|
|| $this->option('admin')
|
||
|
|
|| $this->option('api')
|
||
|
|
|| $this->option('all')
|
||
|
|
|| ! $this->hasAnyOption();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if any specific option was provided.
|
||
|
|
*/
|
||
|
|
protected function hasAnyOption(): bool
|
||
|
|
{
|
||
|
|
return $this->option('web')
|
||
|
|
|| $this->option('admin')
|
||
|
|
|| $this->option('api')
|
||
|
|
|| $this->option('all');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create the Boot.php file.
|
||
|
|
*/
|
||
|
|
protected function createBootFile(string $websitePath, string $name, string $domain): void
|
||
|
|
{
|
||
|
|
$namespace = "Website\\{$name}";
|
||
|
|
$domainPattern = $this->buildDomainPattern($domain);
|
||
|
|
$listeners = $this->buildListenersArray();
|
||
|
|
$handlers = $this->buildHandlerMethods($name);
|
||
|
|
|
||
|
|
$content = <<<PHP
|
||
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace {$namespace};
|
||
|
|
|
||
|
|
use Core\Events\DomainResolving;
|
||
|
|
{$this->buildUseStatements()}
|
||
|
|
use Illuminate\Support\ServiceProvider;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* {$name} Website - Domain-isolated website provider.
|
||
|
|
*
|
||
|
|
* This website is loaded when the incoming HTTP host matches
|
||
|
|
* the domain pattern defined in \$domains.
|
||
|
|
*/
|
||
|
|
class Boot extends ServiceProvider
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* Domain patterns this website responds to.
|
||
|
|
*
|
||
|
|
* Uses regex patterns. Common examples:
|
||
|
|
* - '/^example\\.test\$/' - exact match
|
||
|
|
* - '/^example\\.(com|test)\$/' - multiple TLDs
|
||
|
|
* - '/^(www\\.)?example\\.com\$/' - optional www
|
||
|
|
*
|
||
|
|
* @var array<string>
|
||
|
|
*/
|
||
|
|
public static array \$domains = [
|
||
|
|
'{$domainPattern}',
|
||
|
|
];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Events this module listens to for lazy loading.
|
||
|
|
*
|
||
|
|
* @var array<class-string, string>
|
||
|
|
*/
|
||
|
|
public static array \$listens = [
|
||
|
|
DomainResolving::class => 'onDomainResolving',
|
||
|
|
{$listeners}
|
||
|
|
];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Register any application services.
|
||
|
|
*/
|
||
|
|
public function register(): void
|
||
|
|
{
|
||
|
|
//
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Bootstrap any application services.
|
||
|
|
*/
|
||
|
|
public function boot(): void
|
||
|
|
{
|
||
|
|
//
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handle domain resolution - register if domain matches.
|
||
|
|
*/
|
||
|
|
public function onDomainResolving(DomainResolving \$event): void
|
||
|
|
{
|
||
|
|
foreach (self::\$domains as \$pattern) {
|
||
|
|
if (\$event->matches(\$pattern)) {
|
||
|
|
\$event->register(self::class);
|
||
|
|
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
{$handlers}
|
||
|
|
}
|
||
|
|
|
||
|
|
PHP;
|
||
|
|
|
||
|
|
File::put("{$websitePath}/Boot.php", $content);
|
||
|
|
$this->createdFiles[] = ['file' => 'Boot.php', 'description' => 'Domain-isolated website provider'];
|
||
|
|
$this->components->task('Creating Boot.php', fn () => true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Build the domain regex pattern.
|
||
|
|
*/
|
||
|
|
protected function buildDomainPattern(string $domain): string
|
||
|
|
{
|
||
|
|
// Escape dots and create a regex pattern
|
||
|
|
$escaped = preg_quote($domain, '/');
|
||
|
|
|
||
|
|
return '/^'.$escaped.'$/';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Build the use statements for the Boot file.
|
||
|
|
*/
|
||
|
|
protected function buildUseStatements(): string
|
||
|
|
{
|
||
|
|
$statements = [];
|
||
|
|
|
||
|
|
if ($this->option('web') || $this->option('all') || ! $this->hasAnyOption()) {
|
||
|
|
$statements[] = 'use Core\Events\WebRoutesRegistering;';
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->option('admin') || $this->option('all')) {
|
||
|
|
$statements[] = 'use Core\Events\AdminPanelBooting;';
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->option('api') || $this->option('all')) {
|
||
|
|
$statements[] = 'use Core\Events\ApiRoutesRegistering;';
|
||
|
|
}
|
||
|
|
|
||
|
|
return implode("\n", $statements);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Build the listeners array content (excluding DomainResolving).
|
||
|
|
*/
|
||
|
|
protected function buildListenersArray(): string
|
||
|
|
{
|
||
|
|
$listeners = [];
|
||
|
|
|
||
|
|
if ($this->option('web') || $this->option('all') || ! $this->hasAnyOption()) {
|
||
|
|
$listeners[] = " WebRoutesRegistering::class => 'onWebRoutes',";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->option('admin') || $this->option('all')) {
|
||
|
|
$listeners[] = " AdminPanelBooting::class => 'onAdminPanel',";
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->option('api') || $this->option('all')) {
|
||
|
|
$listeners[] = " ApiRoutesRegistering::class => 'onApiRoutes',";
|
||
|
|
}
|
||
|
|
|
||
|
|
return implode("\n", $listeners);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Build the handler methods.
|
||
|
|
*/
|
||
|
|
protected function buildHandlerMethods(string $name): string
|
||
|
|
{
|
||
|
|
$methods = [];
|
||
|
|
$websiteName = Str::snake($name);
|
||
|
|
|
||
|
|
if ($this->option('web') || $this->option('all') || ! $this->hasAnyOption()) {
|
||
|
|
$methods[] = <<<PHP
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Register web routes and views.
|
||
|
|
*/
|
||
|
|
public function onWebRoutes(WebRoutesRegistering \$event): void
|
||
|
|
{
|
||
|
|
\$event->views('{$websiteName}', __DIR__.'/View/Blade');
|
||
|
|
|
||
|
|
if (file_exists(__DIR__.'/Routes/web.php')) {
|
||
|
|
\$event->routes(fn () => require __DIR__.'/Routes/web.php');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
PHP;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->option('admin') || $this->option('all')) {
|
||
|
|
$methods[] = <<<'PHP'
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Register admin panel routes.
|
||
|
|
*/
|
||
|
|
public function onAdminPanel(AdminPanelBooting $event): void
|
||
|
|
{
|
||
|
|
if (file_exists(__DIR__.'/Routes/admin.php')) {
|
||
|
|
$event->routes(fn () => require __DIR__.'/Routes/admin.php');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
PHP;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->option('api') || $this->option('all')) {
|
||
|
|
$methods[] = <<<'PHP'
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Register API routes.
|
||
|
|
*/
|
||
|
|
public function onApiRoutes(ApiRoutesRegistering $event): void
|
||
|
|
{
|
||
|
|
if (file_exists(__DIR__.'/Routes/api.php')) {
|
||
|
|
$event->routes(fn () => require __DIR__.'/Routes/api.php');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
PHP;
|
||
|
|
}
|
||
|
|
|
||
|
|
return implode("\n", $methods);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create optional files based on flags.
|
||
|
|
*/
|
||
|
|
protected function createOptionalFiles(string $websitePath, string $name): void
|
||
|
|
{
|
||
|
|
$websiteName = Str::snake($name);
|
||
|
|
|
||
|
|
if ($this->option('web') || $this->option('all') || ! $this->hasAnyOption()) {
|
||
|
|
$this->createWebRoutes($websitePath, $websiteName);
|
||
|
|
$this->createLayout($websitePath, $name);
|
||
|
|
$this->createHomepage($websitePath, $websiteName);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->option('admin') || $this->option('all')) {
|
||
|
|
$this->createAdminRoutes($websitePath, $websiteName);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($this->option('api') || $this->option('all')) {
|
||
|
|
$this->createApiRoutes($websitePath, $websiteName);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create web routes file.
|
||
|
|
*/
|
||
|
|
protected function createWebRoutes(string $websitePath, string $websiteName): void
|
||
|
|
{
|
||
|
|
$content = <<<PHP
|
||
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
use Illuminate\Support\Facades\Route;
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| {$websiteName} Web Routes
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
|
|
||
|
|
| Public web routes for this website.
|
||
|
|
|
|
||
|
|
*/
|
||
|
|
|
||
|
|
Route::get('/', function () {
|
||
|
|
return view('{$websiteName}::home');
|
||
|
|
})->name('{$websiteName}.home');
|
||
|
|
|
||
|
|
PHP;
|
||
|
|
|
||
|
|
File::put("{$websitePath}/Routes/web.php", $content);
|
||
|
|
$this->createdFiles[] = ['file' => 'Routes/web.php', 'description' => 'Public web routes'];
|
||
|
|
$this->components->task('Creating Routes/web.php', fn () => true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create admin routes file.
|
||
|
|
*/
|
||
|
|
protected function createAdminRoutes(string $websitePath, string $websiteName): void
|
||
|
|
{
|
||
|
|
$content = <<<PHP
|
||
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
use Illuminate\Support\Facades\Route;
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| {$websiteName} Admin Routes
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
|
|
||
|
|
| Admin routes for this website.
|
||
|
|
|
|
||
|
|
*/
|
||
|
|
|
||
|
|
Route::prefix('admin/{$websiteName}')->name('{$websiteName}.admin.')->group(function () {
|
||
|
|
Route::get('/', function () {
|
||
|
|
return 'Admin dashboard for {$websiteName}';
|
||
|
|
})->name('index');
|
||
|
|
});
|
||
|
|
|
||
|
|
PHP;
|
||
|
|
|
||
|
|
File::put("{$websitePath}/Routes/admin.php", $content);
|
||
|
|
$this->createdFiles[] = ['file' => 'Routes/admin.php', 'description' => 'Admin panel routes'];
|
||
|
|
$this->components->task('Creating Routes/admin.php', fn () => true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create API routes file.
|
||
|
|
*/
|
||
|
|
protected function createApiRoutes(string $websitePath, string $websiteName): void
|
||
|
|
{
|
||
|
|
$content = <<<PHP
|
||
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
use Illuminate\Support\Facades\Route;
|
||
|
|
|
||
|
|
/*
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
| {$websiteName} API Routes
|
||
|
|
|--------------------------------------------------------------------------
|
||
|
|
|
|
||
|
|
| API routes for this website.
|
||
|
|
|
|
||
|
|
*/
|
||
|
|
|
||
|
|
Route::prefix('{$websiteName}')->name('api.{$websiteName}.')->group(function () {
|
||
|
|
Route::get('/health', function () {
|
||
|
|
return response()->json(['status' => 'ok', 'website' => '{$websiteName}']);
|
||
|
|
})->name('health');
|
||
|
|
});
|
||
|
|
|
||
|
|
PHP;
|
||
|
|
|
||
|
|
File::put("{$websitePath}/Routes/api.php", $content);
|
||
|
|
$this->createdFiles[] = ['file' => 'Routes/api.php', 'description' => 'REST API routes'];
|
||
|
|
$this->components->task('Creating Routes/api.php', fn () => true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create a base layout file.
|
||
|
|
*/
|
||
|
|
protected function createLayout(string $websitePath, string $name): void
|
||
|
|
{
|
||
|
|
$content = <<<BLADE
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||
|
|
|
||
|
|
<title>{{ \$title ?? '{$name}' }}</title>
|
||
|
|
|
||
|
|
<!-- Styles -->
|
||
|
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||
|
|
</head>
|
||
|
|
<body class="font-sans antialiased">
|
||
|
|
<div class="min-h-screen bg-gray-100">
|
||
|
|
<!-- Navigation -->
|
||
|
|
<nav class="bg-white border-b border-gray-200">
|
||
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||
|
|
<div class="flex justify-between h-16">
|
||
|
|
<div class="flex items-center">
|
||
|
|
<a href="/" class="text-xl font-bold text-gray-800">
|
||
|
|
{$name}
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</nav>
|
||
|
|
|
||
|
|
<!-- Page Content -->
|
||
|
|
<main>
|
||
|
|
{{ \$slot }}
|
||
|
|
</main>
|
||
|
|
</div>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
|
||
|
|
BLADE;
|
||
|
|
|
||
|
|
File::put("{$websitePath}/View/Blade/layouts/app.blade.php", $content);
|
||
|
|
$this->createdFiles[] = ['file' => 'View/Blade/layouts/app.blade.php', 'description' => 'Base layout template'];
|
||
|
|
$this->components->task('Creating View/Blade/layouts/app.blade.php', fn () => true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create a homepage view.
|
||
|
|
*/
|
||
|
|
protected function createHomepage(string $websitePath, string $websiteName): void
|
||
|
|
{
|
||
|
|
$name = Str::studly($websiteName);
|
||
|
|
|
||
|
|
$content = <<<BLADE
|
||
|
|
<x-{$websiteName}::layouts.app>
|
||
|
|
<x-slot name="title">Welcome - {$name}</x-slot>
|
||
|
|
|
||
|
|
<div class="py-12">
|
||
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||
|
|
<div class="bg-white overflow-hidden shadow-sm rounded-lg">
|
||
|
|
<div class="p-6 text-gray-900">
|
||
|
|
<h1 class="text-3xl font-bold mb-4">Welcome to {$name}</h1>
|
||
|
|
<p class="text-gray-600">
|
||
|
|
This is your new website. Start building something amazing!
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</x-{$websiteName}::layouts.app>
|
||
|
|
|
||
|
|
BLADE;
|
||
|
|
|
||
|
|
File::put("{$websitePath}/View/Blade/home.blade.php", $content);
|
||
|
|
$this->createdFiles[] = ['file' => 'View/Blade/home.blade.php', 'description' => 'Homepage view'];
|
||
|
|
$this->components->task('Creating View/Blade/home.blade.php', fn () => true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get shell completion suggestions for arguments and options.
|
||
|
|
*/
|
||
|
|
public function complete(
|
||
|
|
CompletionInput $input,
|
||
|
|
CompletionSuggestions $suggestions
|
||
|
|
): void {
|
||
|
|
if ($input->mustSuggestArgumentValuesFor('name')) {
|
||
|
|
// Suggest common website naming patterns
|
||
|
|
$suggestions->suggestValues([
|
||
|
|
'MarketingSite',
|
||
|
|
'Blog',
|
||
|
|
'Documentation',
|
||
|
|
'LandingPage',
|
||
|
|
'Portal',
|
||
|
|
'Dashboard',
|
||
|
|
'Support',
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($input->mustSuggestOptionValuesFor('domain')) {
|
||
|
|
// Suggest common development domains
|
||
|
|
$suggestions->suggestValues([
|
||
|
|
'example.test',
|
||
|
|
'app.test',
|
||
|
|
'site.test',
|
||
|
|
'dev.test',
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|