first commit
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Config\Factories;
|
||||
use CodeIgniter\Database\SQLite3\Connection;
|
||||
use Config\Database;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Creates a new database.
|
||||
*/
|
||||
class CreateDatabase extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'db:create';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new database schema.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'db:create <db_name> [options]';
|
||||
|
||||
/**
|
||||
* The Command's arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'db_name' => 'The database name to use',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--ext' => 'File extension of the database file for SQLite3. Can be `db` or `sqlite`. Defaults to `db`.',
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a new database.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$name = array_shift($params);
|
||||
|
||||
if (empty($name)) {
|
||||
$name = CLI::prompt('Database name', null, 'required'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
try {
|
||||
$config = config(Database::class);
|
||||
|
||||
// Set to an empty database to prevent connection errors.
|
||||
$group = ENVIRONMENT === 'testing' ? 'tests' : $config->defaultGroup;
|
||||
|
||||
$config->{$group}['database'] = '';
|
||||
|
||||
$db = Database::connect();
|
||||
|
||||
// Special SQLite3 handling
|
||||
if ($db instanceof Connection) {
|
||||
$ext = $params['ext'] ?? CLI::getOption('ext') ?? 'db';
|
||||
|
||||
if (! in_array($ext, ['db', 'sqlite'], true)) {
|
||||
$ext = CLI::prompt('Please choose a valid file extension', ['db', 'sqlite']); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if ($name !== ':memory:') {
|
||||
$name = str_replace(['.db', '.sqlite'], '', $name) . ".{$ext}";
|
||||
}
|
||||
|
||||
$config->{$group}['DBDriver'] = 'SQLite3';
|
||||
$config->{$group}['database'] = $name;
|
||||
|
||||
if ($name !== ':memory:') {
|
||||
$dbName = ! str_contains($name, DIRECTORY_SEPARATOR) ? WRITEPATH . $name : $name;
|
||||
|
||||
if (is_file($dbName)) {
|
||||
CLI::error("Database \"{$dbName}\" already exists.", 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unset($dbName);
|
||||
}
|
||||
|
||||
// Connect to new SQLite3 to create new database
|
||||
$db = Database::connect(null, false);
|
||||
$db->connect();
|
||||
|
||||
if (! is_file($db->getDatabase()) && $name !== ':memory:') {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error('Database creation failed.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
} elseif (! Database::forge()->createDatabase($name)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error('Database creation failed.', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
CLI::write("Database \"{$name}\" successfully created.", 'green');
|
||||
CLI::newLine();
|
||||
} catch (Throwable $e) {
|
||||
$this->showError($e);
|
||||
} finally {
|
||||
Factories::reset('config');
|
||||
Database::connect(null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Runs all new migrations.
|
||||
*/
|
||||
class Migrate extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Locates and runs all new migrations against the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate [options]';
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-n' => 'Set migration namespace',
|
||||
'-g' => 'Set database group',
|
||||
'--all' => 'Set for all namespaces, will ignore (-n) option',
|
||||
];
|
||||
|
||||
/**
|
||||
* Ensures that all migrations have been run.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$runner = service('migrations');
|
||||
$runner->clearCliMessages();
|
||||
|
||||
CLI::write(lang('Migrations.latest'), 'yellow');
|
||||
|
||||
$namespace = $params['n'] ?? CLI::getOption('n');
|
||||
$group = $params['g'] ?? CLI::getOption('g');
|
||||
|
||||
try {
|
||||
if (array_key_exists('all', $params) || CLI::getOption('all')) {
|
||||
$runner->setNamespace(null);
|
||||
} elseif ($namespace) {
|
||||
$runner->setNamespace($namespace);
|
||||
}
|
||||
|
||||
if (! $runner->latest($group)) {
|
||||
CLI::error(lang('Migrations.generalFault'), 'light_gray', 'red'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$messages = $runner->getCliMessages();
|
||||
|
||||
foreach ($messages as $message) {
|
||||
CLI::write($message);
|
||||
}
|
||||
|
||||
CLI::write(lang('Migrations.migrated'), 'green');
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Throwable $e) {
|
||||
$this->showError($e);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Does a rollback followed by a latest to refresh the current state
|
||||
* of the database.
|
||||
*/
|
||||
class MigrateRefresh extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:refresh';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Does a rollback followed by a latest to refresh the current state of the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:refresh [options]';
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-n' => 'Set migration namespace',
|
||||
'-g' => 'Set database group',
|
||||
'--all' => 'Set latest for all namespace, will ignore (-n) option',
|
||||
'-f' => 'Force command - this option allows you to bypass the confirmation question when running this command in a production environment',
|
||||
];
|
||||
|
||||
/**
|
||||
* Does a rollback followed by a latest to refresh the current state
|
||||
* of the database.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$params['b'] = 0;
|
||||
|
||||
if (ENVIRONMENT === 'production') {
|
||||
// @codeCoverageIgnoreStart
|
||||
$force = array_key_exists('f', $params) || CLI::getOption('f');
|
||||
|
||||
if (! $force && CLI::prompt(lang('Migrations.refreshConfirm'), ['y', 'n']) === 'n') {
|
||||
return;
|
||||
}
|
||||
|
||||
$params['f'] = null;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$this->call('migrate:rollback', $params);
|
||||
$this->call('migrate', $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Database\MigrationRunner;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Runs all of the migrations in reverse order, until they have
|
||||
* all been unapplied.
|
||||
*/
|
||||
class MigrateRollback extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:rollback';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Runs the "down" method for all migrations in the last batch.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:rollback [options]';
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-b' => 'Specify a batch to roll back to; e.g. "3" to return to batch #3',
|
||||
'-f' => 'Force command - this option allows you to bypass the confirmation question when running this command in a production environment',
|
||||
];
|
||||
|
||||
/**
|
||||
* Runs all of the migrations in reverse order, until they have
|
||||
* all been unapplied.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
if (ENVIRONMENT === 'production') {
|
||||
// @codeCoverageIgnoreStart
|
||||
$force = array_key_exists('f', $params) || CLI::getOption('f');
|
||||
|
||||
if (! $force && CLI::prompt(lang('Migrations.rollBackConfirm'), ['y', 'n']) === 'n') {
|
||||
return;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/** @var MigrationRunner $runner */
|
||||
$runner = service('migrations');
|
||||
|
||||
try {
|
||||
$batch = $params['b'] ?? CLI::getOption('b') ?? $runner->getLastBatch() - 1;
|
||||
|
||||
if (is_string($batch)) {
|
||||
if (! ctype_digit($batch)) {
|
||||
CLI::error('Invalid batch number: ' . $batch, 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$batch = (int) $batch;
|
||||
}
|
||||
|
||||
CLI::write(lang('Migrations.rollingBack') . ' ' . $batch, 'yellow');
|
||||
|
||||
if (! $runner->regress($batch)) {
|
||||
CLI::error(lang('Migrations.generalFault'), 'light_gray', 'red'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$messages = $runner->getCliMessages();
|
||||
|
||||
foreach ($messages as $message) {
|
||||
CLI::write($message);
|
||||
}
|
||||
|
||||
CLI::write('Done rolling back migrations.', 'green');
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Throwable $e) {
|
||||
$this->showError($e);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Displays a list of all migrations and whether they've been run or not.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Database\MigrateStatusTest
|
||||
*/
|
||||
class MigrateStatus extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'migrate:status';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Displays a list of all migrations and whether they\'ve been run or not.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'migrate:status [options]';
|
||||
|
||||
/**
|
||||
* the Command's Options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'-g' => 'Set database group',
|
||||
];
|
||||
|
||||
/**
|
||||
* Namespaces to ignore when looking for migrations.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $ignoredNamespaces = [
|
||||
'CodeIgniter',
|
||||
'Config',
|
||||
'Kint',
|
||||
'Laminas\ZendFrameworkBridge',
|
||||
'Laminas\Escaper',
|
||||
'Psr\Log',
|
||||
];
|
||||
|
||||
/**
|
||||
* Displays a list of all migrations and whether they've been run or not.
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$runner = service('migrations');
|
||||
$paramGroup = $params['g'] ?? CLI::getOption('g');
|
||||
|
||||
// Get all namespaces
|
||||
$namespaces = service('autoloader')->getNamespace();
|
||||
|
||||
// Collection of migration status
|
||||
$status = [];
|
||||
|
||||
foreach (array_keys($namespaces) as $namespace) {
|
||||
if (ENVIRONMENT !== 'testing') {
|
||||
// Make Tests\\Support discoverable for testing
|
||||
$this->ignoredNamespaces[] = 'Tests\Support'; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if (in_array($namespace, $this->ignoredNamespaces, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (APP_NAMESPACE !== 'App' && $namespace === 'App') {
|
||||
continue; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$migrations = $runner->findNamespaceMigrations($namespace);
|
||||
|
||||
if (empty($migrations)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$runner->setNamespace($namespace);
|
||||
$history = $runner->getHistory((string) $paramGroup);
|
||||
ksort($migrations);
|
||||
|
||||
foreach ($migrations as $uid => $migration) {
|
||||
$migrations[$uid]->name = mb_substr($migration->name, (int) mb_strpos($migration->name, $uid . '_'));
|
||||
|
||||
$date = '---';
|
||||
$group = '---';
|
||||
$batch = '---';
|
||||
|
||||
foreach ($history as $row) {
|
||||
// @codeCoverageIgnoreStart
|
||||
if ($runner->getObjectUid($row) !== $migration->uid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$date = date('Y-m-d H:i:s', (int) $row->time);
|
||||
$group = $row->group;
|
||||
$batch = $row->batch;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$status[] = [
|
||||
$namespace,
|
||||
$migration->version,
|
||||
$migration->name,
|
||||
$group,
|
||||
$date,
|
||||
$batch,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($status === []) {
|
||||
// @codeCoverageIgnoreStart
|
||||
CLI::error(lang('Migrations.noneFound'), 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$headers = [
|
||||
CLI::color(lang('Migrations.namespace'), 'yellow'),
|
||||
CLI::color(lang('Migrations.version'), 'yellow'),
|
||||
CLI::color(lang('Migrations.filename'), 'yellow'),
|
||||
CLI::color(lang('Migrations.group'), 'yellow'),
|
||||
CLI::color(str_replace(': ', '', lang('Migrations.on')), 'yellow'),
|
||||
CLI::color(lang('Migrations.batch'), 'yellow'),
|
||||
];
|
||||
|
||||
CLI::table($status, $headers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Database\Seeder;
|
||||
use Config\Database;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Runs the specified Seeder file to populate the database
|
||||
* with some data.
|
||||
*/
|
||||
class Seed extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'db:seed';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Runs the specified seeder to populate known data into the database.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = 'db:seed <seeder_name>';
|
||||
|
||||
/**
|
||||
* the Command's Arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'seeder_name' => 'The seeder name to run',
|
||||
];
|
||||
|
||||
/**
|
||||
* Passes to Seeder to populate the database.
|
||||
*/
|
||||
public function run(array $params)
|
||||
{
|
||||
$seeder = new Seeder(new Database());
|
||||
$seedName = array_shift($params);
|
||||
|
||||
if (empty($seedName)) {
|
||||
$seedName = CLI::prompt(lang('Migrations.migSeeder'), null, 'required'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
try {
|
||||
$seeder->call($seedName);
|
||||
} catch (Throwable $e) {
|
||||
$this->showError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Commands\Database;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use CodeIgniter\Database\BaseConnection;
|
||||
use Config\Database;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Get table data if it exists in the database.
|
||||
*
|
||||
* @see \CodeIgniter\Commands\Database\ShowTableInfoTest
|
||||
*/
|
||||
class ShowTableInfo extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The group the command is lumped under
|
||||
* when listing commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $group = 'Database';
|
||||
|
||||
/**
|
||||
* The Command's name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'db:table';
|
||||
|
||||
/**
|
||||
* the Command's short description
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Retrieves information on the selected table.';
|
||||
|
||||
/**
|
||||
* the Command's usage
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $usage = <<<'EOL'
|
||||
db:table [<table_name>] [options]
|
||||
|
||||
Examples:
|
||||
db:table --show
|
||||
db:table --metadata
|
||||
db:table my_table --metadata
|
||||
db:table my_table
|
||||
db:table my_table --limit-rows 5 --limit-field-value 10 --desc
|
||||
EOL;
|
||||
|
||||
/**
|
||||
* The Command's arguments
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $arguments = [
|
||||
'table_name' => 'The table name to show info',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Command's options
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $options = [
|
||||
'--show' => 'Lists the names of all database tables.',
|
||||
'--metadata' => 'Retrieves list containing field information.',
|
||||
'--desc' => 'Sorts the table rows in DESC order.',
|
||||
'--limit-rows' => 'Limits the number of rows. Default: 10.',
|
||||
'--limit-field-value' => 'Limits the length of field values. Default: 15.',
|
||||
'--dbgroup' => 'Database group to show.',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var list<list<int|string>> Table Data.
|
||||
*/
|
||||
private array $tbody;
|
||||
|
||||
private ?BaseConnection $db = null;
|
||||
|
||||
/**
|
||||
* @var bool Sort the table rows in DESC order or not.
|
||||
*/
|
||||
private bool $sortDesc = false;
|
||||
|
||||
private string $DBPrefix;
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
$dbGroup = $params['dbgroup'] ?? CLI::getOption('dbgroup');
|
||||
|
||||
try {
|
||||
$this->db = Database::connect($dbGroup);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
CLI::error($e->getMessage());
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$this->DBPrefix = $this->db->getPrefix();
|
||||
|
||||
$this->showDBConfig();
|
||||
|
||||
$tables = $this->db->listTables();
|
||||
|
||||
if (array_key_exists('desc', $params)) {
|
||||
$this->sortDesc = true;
|
||||
}
|
||||
|
||||
if ($tables === []) {
|
||||
CLI::error('Database has no tables!', 'light_gray', 'red');
|
||||
CLI::newLine();
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
if (array_key_exists('show', $params)) {
|
||||
$this->showAllTables($tables);
|
||||
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$tableName = $params[0] ?? null;
|
||||
$limitRows = (int) ($params['limit-rows'] ?? 10);
|
||||
$limitFieldValue = (int) ($params['limit-field-value'] ?? 15);
|
||||
|
||||
while (! in_array($tableName, $tables, true)) {
|
||||
$tableNameNo = CLI::promptByKey(
|
||||
['Here is the list of your database tables:', 'Which table do you want to see?'],
|
||||
$tables,
|
||||
'required'
|
||||
);
|
||||
CLI::newLine();
|
||||
|
||||
$tableName = $tables[$tableNameNo] ?? null;
|
||||
}
|
||||
|
||||
if (array_key_exists('metadata', $params)) {
|
||||
$this->showFieldMetaData($tableName);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
$this->showDataOfTable($tableName, $limitRows, $limitFieldValue);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
private function showDBConfig(): void
|
||||
{
|
||||
$data = [[
|
||||
'hostname' => $this->db->hostname,
|
||||
'database' => $this->db->getDatabase(),
|
||||
'username' => $this->db->username,
|
||||
'DBDriver' => $this->db->getPlatform(),
|
||||
'DBPrefix' => $this->DBPrefix,
|
||||
'port' => $this->db->port,
|
||||
]];
|
||||
CLI::table(
|
||||
$data,
|
||||
['hostname', 'database', 'username', 'DBDriver', 'DBPrefix', 'port']
|
||||
);
|
||||
}
|
||||
|
||||
private function removeDBPrefix(): void
|
||||
{
|
||||
$this->db->setPrefix('');
|
||||
}
|
||||
|
||||
private function restoreDBPrefix(): void
|
||||
{
|
||||
$this->db->setPrefix($this->DBPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Data of Table
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function showDataOfTable(string $tableName, int $limitRows, int $limitFieldValue)
|
||||
{
|
||||
CLI::write("Data of Table \"{$tableName}\":", 'black', 'yellow');
|
||||
CLI::newLine();
|
||||
|
||||
$this->removeDBPrefix();
|
||||
$thead = $this->db->getFieldNames($tableName);
|
||||
$this->restoreDBPrefix();
|
||||
|
||||
// If there is a field named `id`, sort by it.
|
||||
$sortField = null;
|
||||
if (in_array('id', $thead, true)) {
|
||||
$sortField = 'id';
|
||||
}
|
||||
|
||||
$this->tbody = $this->makeTableRows($tableName, $limitRows, $limitFieldValue, $sortField);
|
||||
CLI::table($this->tbody, $thead);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show All Tables
|
||||
*
|
||||
* @param list<string> $tables
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function showAllTables(array $tables)
|
||||
{
|
||||
CLI::write('The following is a list of the names of all database tables:', 'black', 'yellow');
|
||||
CLI::newLine();
|
||||
|
||||
$thead = ['ID', 'Table Name', 'Num of Rows', 'Num of Fields'];
|
||||
$this->tbody = $this->makeTbodyForShowAllTables($tables);
|
||||
|
||||
CLI::table($this->tbody, $thead);
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make body for table
|
||||
*
|
||||
* @param list<string> $tables
|
||||
*
|
||||
* @return list<list<int|string>>
|
||||
*/
|
||||
private function makeTbodyForShowAllTables(array $tables): array
|
||||
{
|
||||
$this->removeDBPrefix();
|
||||
|
||||
foreach ($tables as $id => $tableName) {
|
||||
$table = $this->db->protectIdentifiers($tableName);
|
||||
$db = $this->db->query("SELECT * FROM {$table}");
|
||||
|
||||
$this->tbody[] = [
|
||||
$id + 1,
|
||||
$tableName,
|
||||
$db->getNumRows(),
|
||||
$db->getFieldCount(),
|
||||
];
|
||||
}
|
||||
|
||||
$this->restoreDBPrefix();
|
||||
|
||||
if ($this->sortDesc) {
|
||||
krsort($this->tbody);
|
||||
}
|
||||
|
||||
return $this->tbody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make table rows
|
||||
*
|
||||
* @return list<list<int|string>>
|
||||
*/
|
||||
private function makeTableRows(
|
||||
string $tableName,
|
||||
int $limitRows,
|
||||
int $limitFieldValue,
|
||||
?string $sortField = null
|
||||
): array {
|
||||
$this->tbody = [];
|
||||
|
||||
$this->removeDBPrefix();
|
||||
$builder = $this->db->table($tableName);
|
||||
$builder->limit($limitRows);
|
||||
if ($sortField !== null) {
|
||||
$builder->orderBy($sortField, $this->sortDesc ? 'DESC' : 'ASC');
|
||||
}
|
||||
$rows = $builder->get()->getResultArray();
|
||||
$this->restoreDBPrefix();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$row = array_map(
|
||||
static fn ($item): string => mb_strlen((string) $item) > $limitFieldValue
|
||||
? mb_substr((string) $item, 0, $limitFieldValue) . '...'
|
||||
: (string) $item,
|
||||
$row
|
||||
);
|
||||
$this->tbody[] = $row;
|
||||
}
|
||||
|
||||
if ($sortField === null && $this->sortDesc) {
|
||||
krsort($this->tbody);
|
||||
}
|
||||
|
||||
return $this->tbody;
|
||||
}
|
||||
|
||||
private function showFieldMetaData(string $tableName): void
|
||||
{
|
||||
CLI::write("List of Metadata Information in Table \"{$tableName}\":", 'black', 'yellow');
|
||||
CLI::newLine();
|
||||
|
||||
$thead = ['Field Name', 'Type', 'Max Length', 'Nullable', 'Default', 'Primary Key'];
|
||||
|
||||
$this->removeDBPrefix();
|
||||
$fields = $this->db->getFieldData($tableName);
|
||||
$this->restoreDBPrefix();
|
||||
|
||||
foreach ($fields as $row) {
|
||||
$this->tbody[] = [
|
||||
$row->name,
|
||||
$row->type,
|
||||
$row->max_length,
|
||||
isset($row->nullable) ? $this->setYesOrNo($row->nullable) : 'n/a',
|
||||
$row->default,
|
||||
isset($row->primary_key) ? $this->setYesOrNo($row->primary_key) : 'n/a',
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->sortDesc) {
|
||||
krsort($this->tbody);
|
||||
}
|
||||
|
||||
CLI::table($this->tbody, $thead);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|int|string|null $fieldValue
|
||||
*/
|
||||
private function setYesOrNo($fieldValue): string
|
||||
{
|
||||
if ((bool) $fieldValue) {
|
||||
return CLI::color('Yes', 'green');
|
||||
}
|
||||
|
||||
return CLI::color('No', 'red');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user