thedevdojo / voyager

Voyager - The Missing Laravel Admin
https://voyager.devdojo.com
MIT License
11.81k stars 2.67k forks source link

Multiple Database #1703

Open alexsalzer opened 7 years ago

alexsalzer commented 7 years ago

Description:

I do have multiple databases which works quite well within a Laravel App.

How can I configure Voyager to use them?

Is it possible to switch between connections?

Kind Regards

Steps To Reproduce:

lancepioch commented 7 years ago

Labels: question, feature

marktopper commented 7 years ago

This is not yet supported, we are open for pull requests.

DimaTiunov commented 4 years ago

Greetings, this feature, as I see it, has not yet been implemented. Is it planned for the next releases?

shota commented 4 years ago

If you have interested multiple connection implement, I solved this problem by adding a connection parameter to SchemeManager. Though the connections parameters is hard coded but it might help some guys.

https://github.com/shota/voyager/blob/1.4/src/Database/Schema/SchemaManager.php

<?php

namespace TCG\Voyager\Database\Schema;

use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Table as DoctrineTable;
use Illuminate\Support\Facades\DB;
use TCG\Voyager\Database\Types\Type;

abstract class SchemaManager
{
    // todo: trim parameters
    static $connections = ['mysql','mysql_backend'];

    public static function __callStatic($method, $args)
    {
        return static::manager()->$method(...$args);
    }

    public static function manager($connection = null)
    {
        return DB::connection($connection)->getDoctrineSchemaManager();
    }

    public static function getDatabaseConnection($connection = null)
    {
        return DB::connection($connection)->getDoctrineConnection();
    }

    public static function tableExists($table)
    {
        if (!is_array($table)) {
            $table = [$table];
        }

        $matches = false;
        foreach(static::$connections as $connection) {
            $matches = $matches || static::manager($connection)->tablesExist($table);
        }

        // return static::tablesExist($table);
        return $matches;
    }

    public static function listTables()
    {
        $tables = [];

        foreach (static::listTableNames() as $tableName) {
            $tables[$tableName] = static::listTableDetails($tableName);
        }

        return $tables;
    }

    public static function listTableNames() {
        $tableNames = [];
        foreach(static::$connections as $connection) {
            $tableNames = array_merge(
                $tableNames,
                static::manager($connection)->listTableNames()
            );
        }
        return $tableNames;
    }

    /**
     * @param string $tableName
     *
     * @return \TCG\Voyager\Database\Schema\Table
     */
    public static function listTableDetails($tableName)
    {
        $tableConnection = null;
        foreach(static::$connections as $connection) {
            if (static::manager($connection)->tablesExist([$tableName])) {
                $tableConnection = $connection;
            }
        }
        if (!$connection) {
            throw new \Exception("No acceptable connection for table $tableName");
        }

        $columns = static::manager($connection)->listTableColumns($tableName);

        $foreignKeys = [];
        if (static::manager($connection)->getDatabasePlatform()->supportsForeignKeyConstraints()) {
            $foreignKeys = static::manager($connection)->listTableForeignKeys($tableName);
        }

        $indexes = static::manager($connection)->listTableIndexes($tableName);

        return new Table($tableName, $columns, $indexes, $foreignKeys, false, []);
    }

    /**
     * Describes given table.
     *
     * @param string $tableName
     *
     * @return \Illuminate\Support\Collection
     */
    public static function describeTable($tableName)
    {
        Type::registerCustomPlatformTypes();

        $table = static::listTableDetails($tableName);

        return collect($table->columns)->map(function ($column) use ($table) {
            $columnArr = Column::toArray($column);

            $columnArr['field'] = $columnArr['name'];
            $columnArr['type'] = $columnArr['type']['name'];

            // Set the indexes and key
            $columnArr['indexes'] = [];
            $columnArr['key'] = null;
            if ($columnArr['indexes'] = $table->getColumnsIndexes($columnArr['name'], true)) {
                // Convert indexes to Array
                foreach ($columnArr['indexes'] as $name => $index) {
                    $columnArr['indexes'][$name] = Index::toArray($index);
                }

                // If there are multiple indexes for the column
                // the Key will be one with highest priority
                $indexType = array_values($columnArr['indexes'])[0]['type'];
                $columnArr['key'] = substr($indexType, 0, 3);
            }

            return $columnArr;
        });
    }

    public static function listTableColumnNames($tableName)
    {
        Type::registerCustomPlatformTypes();

        $columnNames = [];

        foreach (static::manager()->listTableColumns($tableName) as $column) {
            $columnNames[] = $column->getName();
        }

        return $columnNames;
    }

    public static function createTable($table)
    {
        if (!($table instanceof DoctrineTable)) {
            $table = Table::make($table);
        }

        static::manager()->createTable($table);
    }

    public static function getDoctrineTable($table)
    {
        $table = trim($table);

        if (!static::tableExists($table)) {
            throw SchemaException::tableDoesNotExist($table);
        }

        return static::manager()->listTableDetails($table);
    }

    public static function getDoctrineColumn($table, $column)
    {
        return static::getDoctrineTable($table)->getColumn($column);
    }
}
hubgituser1 commented 2 years ago

@shota can you please guide where we should place it and where it should be included?

vu-cv commented 2 years ago

@shota function listTableDetails($tableName) always take the last connection

vu-cv commented 2 years ago

I fixed it


public static function listTableDetails($tableName) {
        $tableConnection = null;
        foreach(static::$connections as $connection) {
            if (static::manager($connection)->tablesExist([$tableName])) {
                $tableConnection = $connection;
            }
        }
        if (!$tableConnection) {
            throw new \Exception("No acceptable connection for table $tableName");
        }

        $columns = static::manager($tableConnection)->listTableColumns($tableName);

        $foreignKeys = [];
        if (static::manager($tableConnection)->getDatabasePlatform()->supportsForeignKeyConstraints()) {
            $foreignKeys = static::manager($tableConnection)->listTableForeignKeys($tableName);
        }

        $indexes = static::manager($tableConnection)->listTableIndexes($tableName);

        return new Table($tableName, $columns, $indexes, $foreignKeys, false, []);
}
Kushelbek commented 1 year ago

If after creating a new BREAD for the table you don't have any data, change the listTableColumnNames(TCG\Voyager\Database\Schema) method like this:

` public static function listTableColumnNames($tableName) { Type::registerCustomPlatformTypes();

    $columnNames = [];

    $tableConnection = null;
    foreach(static::$connections as $connection) {
        if (static::manager($connection)->tablesExist([$tableName])) {
            $tableConnection = $connection;
        }
    }

    foreach (static::manager($tableConnection)->listTableColumns($tableName) as $column) {
        $columnNames[] = $column->getName();
    }
    return $columnNames;
}`
FardinSaadati commented 1 year ago

how to override this class. i do not want change class into vendor folder laravel

tonycesar commented 1 year ago

how to override this class. i do not want change class into vendor folder laravel

For override withou change class into vendor folder you can use AppServiceProvider:register function. Edit app\Providers\AppServiceProvider.php. The first argument of the alias() method must be a vendor class, and after is the overriding class (ref link):

<?php
namespace App\Providers;

use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\ServiceProvider;
use TCG\Voyager\Database\Schema\SchemaManager;
use TCG\Voyager\Database\Schema\Index;
use TCG\Voyager\Database\Schema\Table;
use TCG\Voyager\Database\Schema\Column;

class AppServiceProvider extends ServiceProvider
{
    /**
    * Register any application services.
    *
    * @return void
    */
    public function register()
    {
        $loader = AliasLoader::getInstance();
        $loader->alias(SchemaManager::class, \App\Voyager\SchemaManager::class);
    }

    /**
    * Bootstrap any application services.
    *
    * @return void
    */
    public function boot()
    {
    }
}

And into app\Voyager\SchemaManager.php create and paste this

<?php

namespace App\Voyager;

use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Table as DoctrineTable;
use Illuminate\Support\Facades\DB;
use TCG\Voyager\Database\Types\Type;

abstract class SchemaManager
{
    // todo: trim parameters
    static $connections = ['mysql', 'mysql_backend'];

    public static function __callStatic($method, $args)
    {
        return static::manager()->$method(...$args);
    }

    public static function manager($connection = null)
    {
        return DB::connection($connection)->getDoctrineSchemaManager();
    }

    public static function getDatabaseConnection($connection = null)
    {
        return DB::connection($connection)->getDoctrineConnection();
    }

    public static function tableExists($table)
    {
        if (!is_array($table)) {
            $table = [$table];
        }

        $matches = false;
        foreach (static::$connections as $connection) {
            $matches = $matches || static::manager($connection)->tablesExist($table);
        }

        // return static::tablesExist($table);
        return $matches;
    }

    public static function listTables()
    {
        $tables = [];

        foreach (static::listTableNames() as $tableName) {
            $tables[$tableName] = static::listTableDetails($tableName);
        }

        return $tables;
    }

    public static function listTableNames()
    {
        $tableNames = [];
        foreach (static::$connections as $connection) {
            $tableNames = array_merge(
                $tableNames,
                static::manager($connection)->listTableNames()
            );
        }
        return $tableNames;
    }

    /**
     * @param string $tableName
     *
     * @return \TCG\Voyager\Database\Schema\Table
     */
    public static function listTableDetails($tableName)
    {
        $tableConnection = null;
        foreach (static::$connections as $connection) {
            if (static::manager($connection)->tablesExist([$tableName])) {
                $tableConnection = $connection;
            }
        }
        if (!$tableConnection) {
            throw new \Exception("No acceptable connection for table $tableName");
        }

        $columns = static::manager($tableConnection)->listTableColumns($tableName);

        $foreignKeys = [];
        if (static::manager($tableConnection)->getDatabasePlatform()->supportsForeignKeyConstraints()) {
            $foreignKeys = static::manager($tableConnection)->listTableForeignKeys($tableName);
        }

        $indexes = static::manager($tableConnection)->listTableIndexes($tableName);

        return new Table($tableName, $columns, $indexes, $foreignKeys, false, []);
    }

    /**
     * Describes given table.
     *
     * @param string $tableName
     *
     * @return \Illuminate\Support\Collection
     */
    public static function describeTable($tableName)
    {
        Type::registerCustomPlatformTypes();

        $table = static::listTableDetails($tableName);

        return collect($table->columns)->map(function ($column) use ($table) {
            $columnArr = Column::toArray($column);

            $columnArr['field'] = $columnArr['name'];
            $columnArr['type'] = $columnArr['type']['name'];

            // Set the indexes and key
            $columnArr['indexes'] = [];
            $columnArr['key'] = null;
            if ($columnArr['indexes'] = $table->getColumnsIndexes($columnArr['name'], true)) {
                // Convert indexes to Array
                foreach ($columnArr['indexes'] as $name => $index) {
                    $columnArr['indexes'][$name] = Index::toArray($index);
                }

                // If there are multiple indexes for the column
                // the Key will be one with highest priority
                $indexType = array_values($columnArr['indexes'])[0]['type'];
                $columnArr['key'] = substr($indexType, 0, 3);
            }

            return $columnArr;
        });
    }

    public static function listTableColumnNames($tableName)
    {
        Type::registerCustomPlatformTypes();

        $tableConnection = null;
        foreach (static::$connections as $connection) {
            if (static::manager($connection)->tablesExist([$tableName])) {
                $tableConnection = $connection;
            }
        }

        foreach (static::manager($tableConnection)->listTableColumns($tableName) as $column) {
            $columnNames[] = $column->getName();
        }
        return $columnNames;
    }

    public static function createTable($table)
    {
        if (!($table instanceof DoctrineTable)) {
            $table = Table::make($table);
        }

        static::manager()->createTable($table);
    }

    public static function getDoctrineTable($table)
    {
        $table = trim($table);

        if (!static::tableExists($table)) {
            throw SchemaException::tableDoesNotExist($table);
        }

        return static::manager()->listTableDetails($table);
    }

    public static function getDoctrineColumn($table, $column)
    {
        return static::getDoctrineTable($table)->getColumn($column);
    }
}

Don't forget duplicate the config of mysql into config\database.php with the key ´mysql_backend´ or your database name connection.

Thanks @shota, @vu-cv, @Kushelbek