Skip to content

135 support dbtable #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,22 @@
*/
public function up()
{
Schema::create('characters', function (Blueprint $collection) {});
Schema::create(
'characters',
function (Blueprint $collection) {},
[
'computedValues' => [
[
'name' => 'full_name',
'expression' => "RETURN CONCAT_SEPARATOR(' ', @doc.name, @doc.surname)",
'overwrite' => true,
'computeOn' => ["insert"],
'failOnWarning' => false,
'keepNull' => true,
],
],
],
);
}

/**
Expand Down
5 changes: 5 additions & 0 deletions docs/console-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ In addition to the default Laravel options, you have the following:
* --graphs: show a list of available named graphs
* --system: include system tables in the table list

## db:table
db:table gives you an overview of the selected table. With ArangoDB specific information.

The new --system option allows you to select a system table as well.

## Migrations
_**Migrations for ArangoDB use a different Schema blueprint. Therefore, you either need to run the convert:migrations
command first, or convert them manually**_
Expand Down
214 changes: 214 additions & 0 deletions src/Console/TableCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<?php

namespace LaravelFreelancerNL\Aranguent\Console;

use Illuminate\Database\ConnectionResolverInterface;
use Illuminate\Database\Console\TableCommand as IlluminateTableCommand;
use Illuminate\Database\Schema\Builder;
use Illuminate\Support\Number;
use LaravelFreelancerNL\Aranguent\Connection;

use function Laravel\Prompts\select;

class TableCommand extends IlluminateTableCommand
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'db:table
{table? : The name of the table}
{--database= : The database connection}
{--system= : Include system tables (ArangoDB)}
{--json : Output the table information as JSON}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Display information about the given database table';

/**
* Execute the console command.
*
* @return int
*/
public function handle(ConnectionResolverInterface $connections)
{
$connection = $connections->connection($this->input->getOption('database'));

if (! $connection instanceof Connection) {
return parent::handle($connections);
}

$schema = $connection->getSchemaBuilder();

$tables = collect(
($this->input->getOption('system'))
? $schema->getAllTables()
: $schema->getTables(),
)
->keyBy(fn($table) => (string) $table->name)
->all();

$tableName = (string) $this->argument('table') ?: select(
'Which table would you like to inspect?',
array_keys($tables),
);

$table = $schema->getTable((string) $tableName);

if (! $table) {
$this->components->warn("Table [{$tableName}] doesn't exist.");

return 1;
}

$tableName = $this->withoutTablePrefix($connection, $table['name']);

$columns = $this->columns($schema, $tableName);
$indexes = $this->indexes($schema, $tableName);

$data = [
'table' => $table,
'columns' => $columns,
'indexes' => $indexes,
];

$this->display($data);

return 0;
}

/**
* Get the information regarding the table's columns.
*
* @param \Illuminate\Database\Schema\Builder $schema
* @param string $table
* @return \Illuminate\Support\Collection
*/
protected function columns(Builder $schema, string $table)
{
return collect($schema->getColumns($table));
}

/**
* Get the information regarding the table's indexes.
*
* @param \Illuminate\Database\Schema\Builder $schema
* @param string $table
* @return \Illuminate\Support\Collection
*/
protected function indexes(Builder $schema, string $table)
{
return collect($schema->getIndexes($table))->map(fn($index) => [
'name' => (string) $index['name'],
'columns' => collect((array) $index['fields']),
'attributes' => $this->getAttributesForIndex((array) $index),
]);
}

/**
* Get the attributes for a table index.
*
* @param array<mixed> $index
* @return \Illuminate\Support\Collection
*/
protected function getAttributesForIndex($index)
{
return collect(
array_filter([
'sparse' => $index['sparse'] ? 'sparse' : null,
'unique' => $index['unique'] ? 'unique' : null,
'type' => $index['type'],
]),
)->filter();
}

/**
* Render the table information.
*
* @param mixed[] $data
* @return void
*/
protected function display(array $data)
{
$this->option('json') ? $this->displayJson($data) : $this->displayForCli($data);
}

protected function displayLongStringValue(string $value): string
{
if (strlen($value) < 136) {
return $value;
}
return substr($value, 0, 133) . '...';
}

/**
* Render the table information formatted for the CLI.
*
* @param mixed[] $data
* @return void
*/
protected function displayForCli(array $data)
{
[$table, $columns, $indexes ] = [
$data['table'], $data['columns'], $data['indexes'],
];

$this->newLine();

$this->components->twoColumnDetail('<fg=green;options=bold>Table</>', '<fg=green;options=bold>' . $table['name'] . '</>');
$this->components->twoColumnDetail('Type', ($table['type'] == 2) ? 'Vertex' : 'Edge');
$this->components->twoColumnDetail('Status', $table['statusString']);
$this->components->twoColumnDetail('User Keys Allowed', ($table['keyOptions']->allowUserKeys) ? 'Yes' : 'No');
$this->components->twoColumnDetail('Key Type', $table['keyOptions']->type);
$this->components->twoColumnDetail('Last Used Key', $table['keyOptions']->lastValue);
$this->components->twoColumnDetail('Wait For Sync', ($table['waitForSync']) ? 'Yes' : 'No');
$this->components->twoColumnDetail('Columns', $table['count']);
$this->components->twoColumnDetail('Size Estimate', Number::fileSize($table['figures']->documentsSize, 2));

$this->newLine();

if ($columns->isNotEmpty()) {
$this->components->twoColumnDetail('<fg=green;options=bold>Column</>', '<fg=green;options=bold>Type</>');

$columns->each(function ($column) {
$this->components->twoColumnDetail(
$column['name'],
implode(', ', $column['types']),
);
});
$this->components->info('ArangoDB is schemaless by default. Hence, the column & types are a representation of current data within the table.');
}

$computedValues = collect((array) $table['computedValues']);
if ($computedValues->isNotEmpty()) {
$this->components->twoColumnDetail('<fg=green;options=bold>Computed Value</>', '<fg=green;options=bold>Expression</>');

$computedValues->each(function ($value) {
$this->components->twoColumnDetail(
$value->name,
$this->displayLongStringValue($value->expression),
);
});

$this->newLine();
}

if ($indexes->isNotEmpty()) {
$this->components->twoColumnDetail('<fg=green;options=bold>Index</>');

$indexes->each(function ($index) {
$this->components->twoColumnDetail(
$index['name'] . ' <fg=gray>' . $index['columns']->implode(', ') . '</>',
$index['attributes']->implode(', '),
);
});

$this->newLine();
}
}
}
2 changes: 2 additions & 0 deletions src/Providers/CommandServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace LaravelFreelancerNL\Aranguent\Providers;

use LaravelFreelancerNL\Aranguent\Console\ShowCommand;
use LaravelFreelancerNL\Aranguent\Console\TableCommand;
use LaravelFreelancerNL\Aranguent\Console\WipeCommand;
use LaravelFreelancerNL\Aranguent\Console\DbCommand;
use Illuminate\Database\Console\DbCommand as IlluminateDbCommand;
Expand All @@ -25,6 +26,7 @@ class CommandServiceProvider extends ServiceProvider
'Db' => DbCommand::class,
'DbWipe' => WipeCommand::class,
'DbShow' => ShowCommand::class,
'DbTable' => TableCommand::class,
];


Expand Down
57 changes: 34 additions & 23 deletions src/Schema/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use LaravelFreelancerNL\Aranguent\Connection;
use LaravelFreelancerNL\Aranguent\Exceptions\QueryException;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\HandlesAnalyzers;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\HandlesIndexes;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\HandlesIndexNaming;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\HandlesGraphs;
use LaravelFreelancerNL\Aranguent\Schema\Concerns\HandlesViews;
Expand All @@ -22,6 +23,7 @@ class Builder extends \Illuminate\Database\Schema\Builder
use HandlesAnalyzers;
use HandlesIndexNaming;
use HandlesGraphs;
use HandlesIndexes;
use HandlesViews;
use UsesBlueprints;

Expand Down Expand Up @@ -187,46 +189,46 @@ public function hasColumn($table, $column)
*
* @param string $table
* @param string|string[] $columns
* @return bool
* @return array<mixed>
*/
public function hasColumns($table, $columns)
public function getColumns($table)
{
if (is_string($columns)) {
$columns = [$columns];
}

$parameters = [];
$parameters['name'] = 'hasColumn';
$parameters['name'] = 'columns';
$parameters['handler'] = 'aql';
$parameters['columns'] = $columns;
$parameters['table'] = $table;

$command = new Fluent($parameters);

$compilation = $this->grammar->compileHasColumn($table, $command);
return $this->connection->select($compilation['aqb'])[0];
$compilation = $this->grammar->compileColumns($table, $command);

$rawColumns = $this->connection->select($compilation['aqb'], $compilation['bindings']);

return $this->mapResultsToArray($rawColumns);
}

/**
* Determine if the given table has a given index.
* Determine if the given table has given columns.
*
* @param string $table
* @param string|array<string> $index
* @param string|null $type
* @param string $table
* @param string|string[] $columns
* @return bool
*/
public function hasIndex($table, $index, $type = null, array $options = [])
public function hasColumns($table, $columns)
{
$name = $index;

if ($type === null) {
$type = 'persistent';
if (is_string($columns)) {
$columns = [$columns];
}

if (is_array($index)) {
$name = $this->createIndexName($type, $index, $options, $table);
}
$parameters = [];
$parameters['name'] = 'hasColumn';
$parameters['handler'] = 'aql';
$parameters['columns'] = $columns;

return !!$this->schemaManager->getIndexByName($table, $name);
$command = new Fluent($parameters);

$compilation = $this->grammar->compileHasColumn($table, $command);
return $this->connection->select($compilation['aqb'])[0];
}

/**
Expand Down Expand Up @@ -295,6 +297,15 @@ public function withoutForeignKeyConstraints(Closure $callback)
return $callback();
}

/**
* @param mixed[] $results
* @return mixed[]
*/
protected function mapResultsToArray($results)
{
return array_map(function ($result) { return (array) $result; }, $results);
}

/**
* Silently catch the use of unsupported builder methods.
*/
Expand Down
Loading
Loading