Skip to content

Commit b860085

Browse files
duncanmccleanjackmcdadejesseleitejasonvarga
authored
Add "Flat Files or Database" prompt (#79)
Co-authored-by: Jack McDade <[email protected]> Co-authored-by: Jesse Leite <[email protected]> Co-authored-by: Jason Varga <[email protected]>
1 parent 466230c commit b860085

File tree

2 files changed

+275
-17
lines changed

2 files changed

+275
-17
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?php
2+
3+
namespace Statamic\Cli\Concerns;
4+
5+
use function Laravel\Prompts\select;
6+
7+
/**
8+
* The code in this trait is a slightly modified version of the code in Laravel's Installer CLI.
9+
* See: https://github.com/laravel/installer/blob/master/src/NewCommand.php
10+
*/
11+
trait ConfiguresDatabase
12+
{
13+
/**
14+
* Determine the default database connection.
15+
*/
16+
protected function promptForDatabaseOptions(): string
17+
{
18+
$defaultDatabase = collect(
19+
$databaseOptions = $this->databaseOptions()
20+
)->keys()->first();
21+
22+
if ($this->input->isInteractive()) {
23+
$database = select(
24+
label: 'Which database will your application use?',
25+
options: $databaseOptions,
26+
default: $defaultDatabase,
27+
);
28+
}
29+
30+
return $database ?? $defaultDatabase;
31+
}
32+
33+
/**
34+
* Get the available database options.
35+
*/
36+
protected function databaseOptions(): array
37+
{
38+
return collect([
39+
'sqlite' => ['SQLite', extension_loaded('pdo_sqlite')],
40+
'mysql' => ['MySQL', extension_loaded('pdo_mysql')],
41+
'mariadb' => ['MariaDB', extension_loaded('pdo_mysql')],
42+
'pgsql' => ['PostgreSQL', extension_loaded('pdo_pgsql')],
43+
'sqlsrv' => ['SQL Server', extension_loaded('pdo_sqlsrv')],
44+
])
45+
->sortBy(fn ($database) => $database[1] ? 0 : 1)
46+
->map(fn ($database) => $database[0].($database[1] ? '' : ' (Missing PDO extension)'))
47+
->all();
48+
}
49+
50+
/**
51+
* Configure the default database connection.
52+
*/
53+
protected function configureDefaultDatabaseConnection(string $database, string $name): void
54+
{
55+
$this->pregReplaceInFile(
56+
'/DB_CONNECTION=.*/',
57+
'DB_CONNECTION='.$database,
58+
$this->absolutePath.'/.env'
59+
);
60+
61+
$this->pregReplaceInFile(
62+
'/DB_CONNECTION=.*/',
63+
'DB_CONNECTION='.$database,
64+
$this->absolutePath.'/.env.example'
65+
);
66+
67+
if ($database === 'sqlite') {
68+
$environment = file_get_contents($this->absolutePath.'/.env');
69+
70+
// If database options aren't commented, comment them for SQLite...
71+
if (! str_contains($environment, '# DB_HOST=127.0.0.1')) {
72+
$this->commentDatabaseConfigurationForSqlite($this->absolutePath);
73+
74+
return;
75+
}
76+
77+
return;
78+
}
79+
80+
// Any commented database configuration options should be uncommented when not on SQLite...
81+
$this->uncommentDatabaseConfiguration($this->absolutePath);
82+
83+
$defaultPorts = [
84+
'pgsql' => '5432',
85+
'sqlsrv' => '1433',
86+
];
87+
88+
if (isset($defaultPorts[$database])) {
89+
$this->replaceInFile(
90+
'DB_PORT=3306',
91+
'DB_PORT='.$defaultPorts[$database],
92+
$this->absolutePath.'/.env'
93+
);
94+
95+
$this->replaceInFile(
96+
'DB_PORT=3306',
97+
'DB_PORT='.$defaultPorts[$database],
98+
$this->absolutePath.'/.env.example'
99+
);
100+
}
101+
102+
$this->replaceInFile(
103+
'DB_DATABASE=laravel',
104+
'DB_DATABASE='.str_replace('-', '_', strtolower($name)),
105+
$this->absolutePath.'/.env'
106+
);
107+
108+
$this->replaceInFile(
109+
'DB_DATABASE=laravel',
110+
'DB_DATABASE='.str_replace('-', '_', strtolower($name)),
111+
$this->absolutePath.'/.env.example'
112+
);
113+
}
114+
115+
/**
116+
* Comment the irrelevant database configuration entries for SQLite applications.
117+
*/
118+
protected function commentDatabaseConfigurationForSqlite(string $directory): void
119+
{
120+
$defaults = [
121+
'DB_HOST=127.0.0.1',
122+
'DB_PORT=3306',
123+
'DB_DATABASE=laravel',
124+
'DB_USERNAME=root',
125+
'DB_PASSWORD=',
126+
];
127+
128+
$this->replaceInFile(
129+
$defaults,
130+
collect($defaults)->map(fn ($default) => "# {$default}")->all(),
131+
$directory.'/.env'
132+
);
133+
134+
$this->replaceInFile(
135+
$defaults,
136+
collect($defaults)->map(fn ($default) => "# {$default}")->all(),
137+
$directory.'/.env.example'
138+
);
139+
}
140+
141+
/**
142+
* Uncomment the relevant database configuration entries for non SQLite applications.
143+
*/
144+
protected function uncommentDatabaseConfiguration(string $directory): void
145+
{
146+
$defaults = [
147+
'# DB_HOST=127.0.0.1',
148+
'# DB_PORT=3306',
149+
'# DB_DATABASE=laravel',
150+
'# DB_USERNAME=root',
151+
'# DB_PASSWORD=',
152+
];
153+
154+
$this->replaceInFile(
155+
$defaults,
156+
collect($defaults)->map(fn ($default) => substr($default, 2))->all(),
157+
$directory.'/.env'
158+
);
159+
160+
$this->replaceInFile(
161+
$defaults,
162+
collect($defaults)->map(fn ($default) => substr($default, 2))->all(),
163+
$directory.'/.env.example'
164+
);
165+
}
166+
}

src/NewCommand.php

Lines changed: 109 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
class NewCommand extends Command
3434
{
35-
use Concerns\ConfiguresPrompts, Concerns\RunsCommands;
35+
use Concerns\ConfiguresDatabase, Concerns\ConfiguresPrompts, Concerns\RunsCommands;
3636

3737
const BASE_REPO = 'statamic/statamic';
3838
const OUTPOST_ENDPOINT = 'https://outpost.statamic.com/v3/starter-kits/';
@@ -55,6 +55,7 @@ class NewCommand extends Command
5555
public $local;
5656
public $withConfig;
5757
public $withoutDependencies;
58+
public $shouldConfigureDatabase = false;
5859
public $ssg;
5960
public $force;
6061
public $baseInstallSuccessful;
@@ -133,6 +134,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
133134
->validateArguments()
134135
->askForRepo()
135136
->validateStarterKitLicense()
137+
->askToInstallEloquentDriver()
136138
->askToEnableStatamicPro()
137139
->askToInstallSsg()
138140
->askToMakeSuperUser()
@@ -143,6 +145,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
143145
->installStarterKit()
144146
->enableStatamicPro()
145147
->makeSuperUser()
148+
->configureDatabaseConnection()
149+
->installEloquentDriver()
146150
->installSsg()
147151
->initializeGitRepository()
148152
->pushToGithub()
@@ -505,7 +509,11 @@ protected function installBaseProject()
505509
throw new RuntimeException('There was a problem installing Statamic!');
506510
}
507511

508-
$this->updateEnvVars();
512+
$this->replaceInFile(
513+
'APP_URL=http://localhost',
514+
'APP_URL=http://'.$this->name.'.test',
515+
$this->absolutePath.'/.env'
516+
);
509517

510518
$this->baseInstallSuccessful = true;
511519

@@ -562,6 +570,97 @@ protected function installStarterKit()
562570
return $this;
563571
}
564572

573+
protected function askToInstallEloquentDriver()
574+
{
575+
if (! $this->input->isInteractive()) {
576+
return $this;
577+
}
578+
579+
$choice = select(
580+
label: 'Where do you want to store your content and data?',
581+
options: [
582+
'flat-file' => 'Flat Files',
583+
'database' => 'Database',
584+
],
585+
default : 'flat-file',
586+
hint: 'When in doubt, choose Flat Files. You can always change this later.'
587+
);
588+
589+
$this->shouldConfigureDatabase = $choice === 'database';
590+
591+
return $this;
592+
}
593+
594+
protected function configureDatabaseConnection()
595+
{
596+
if (! $this->shouldConfigureDatabase) {
597+
return $this;
598+
}
599+
600+
$database = $this->promptForDatabaseOptions();
601+
602+
$this->configureDefaultDatabaseConnection($database, $this->name);
603+
604+
if ($database === 'sqlite') {
605+
touch($this->absolutePath.'/database/database.sqlite');
606+
}
607+
608+
$command = ['migrate'];
609+
610+
if (! $this->input->isInteractive()) {
611+
$command[] = '--no-interaction';
612+
}
613+
614+
$migrate = (new Please($this->output))
615+
->cwd($this->absolutePath)
616+
->run(...$command);
617+
618+
// When there's an issue running the migrations, it's likely because of connection issues.
619+
// Let's let the user know and continue with the install process.
620+
if ($migrate !== 0) {
621+
$this->shouldConfigureDatabase = false;
622+
623+
$this->output->write(' <bg=red;options=bold> There was a problem connecting to the database. </>'.PHP_EOL);
624+
$this->output->write(PHP_EOL);
625+
$this->output->write(' Once the install process is complete, please run <info>php please install:eloquent-driver</info> to finish setting up the database.'.PHP_EOL);
626+
}
627+
628+
return $this;
629+
}
630+
631+
protected function installEloquentDriver()
632+
{
633+
if (! $this->shouldConfigureDatabase) {
634+
return $this;
635+
}
636+
637+
$options = [
638+
'--import',
639+
'--without-messages',
640+
];
641+
642+
$whichRepositories = select(
643+
label: 'Do you want to store everything in the database, or just some things?',
644+
options: [
645+
'everything' => 'Everything',
646+
'custom' => 'Let me choose',
647+
],
648+
default: 'everything'
649+
);
650+
651+
if ($whichRepositories === 'everything') {
652+
$options[] = '--all';
653+
}
654+
655+
(new Please($this->output))
656+
->cwd($this->absolutePath)
657+
->run('install:eloquent-driver', ...$options);
658+
659+
$this->output->write(' <info>[✔] Database setup complete!</info>', PHP_EOL);
660+
661+
return $this;
662+
}
663+
565664
protected function askToInstallSsg()
566665
{
567666
if ($this->ssg || ! $this->input->isInteractive()) {
@@ -1075,31 +1174,24 @@ protected function findComposer()
10751174
}
10761175

10771176
/**
1078-
* Update application env vars.
1177+
* Replace the given string in the given file.
10791178
*/
1080-
protected function updateEnvVars()
1179+
protected function replaceInFile(string|array $search, string|array $replace, string $file): void
10811180
{
1082-
$this->replaceInFile(
1083-
'APP_URL=http://localhost',
1084-
'APP_URL=http://'.$this->name.'.test',
1085-
$this->absolutePath.'/.env'
1086-
);
1087-
1088-
$this->replaceInFile(
1089-
'DB_DATABASE=laravel',
1090-
'DB_DATABASE='.str_replace('-', '_', strtolower($this->name)),
1091-
$this->absolutePath.'/.env'
1181+
file_put_contents(
1182+
$file,
1183+
str_replace($search, $replace, file_get_contents($file))
10921184
);
10931185
}
10941186

10951187
/**
1096-
* Replace the given string in the given file.
1188+
* Replace the given string in the given file using regular expressions.
10971189
*/
1098-
protected function replaceInFile(string $search, string $replace, string $file)
1190+
protected function pregReplaceInFile(string $pattern, string $replace, string $file): void
10991191
{
11001192
file_put_contents(
11011193
$file,
1102-
str_replace($search, $replace, file_get_contents($file))
1194+
preg_replace($pattern, $replace, file_get_contents($file))
11031195
);
11041196
}
11051197

0 commit comments

Comments
 (0)