orchestral / tenanti

[Package] Multi-tenant Database Schema Manager for Laravel
MIT License
587 stars 53 forks source link

Dynamic drivers #35

Closed fernandobandeira closed 7 years ago

fernandobandeira commented 7 years ago

I'm using this package with Multi Database Connection version 3.3.

I currently need to separate migrations by groups, so I thought that maybe using the driver's would help doing this since each driver has it's own migration folder,

However when running the migrations i'm seeing that the migrations are running for all drivers. I'm using a single User model, the driver is changed by a column, right now i'm checking inside the migrations if the injected model should run the migration, however, it still runs the migration and creates a register on the tenants_migrations table (it just doesn't update/create the tabe since im checking this).

Ideally it should just run the migration if the model belongs to the specific driver that I passed when calling the console command avoiding creating these records.

Could you share some info about how the package checks which connection should run the migration?

To give you more info here's some code:

middleware

$user = User::whereSubdomain($request->route()->parameter('subdomain'))->first();       
Tenanti::driver($user->driver)->asDefaultConnection($user, $user->subdomain);

provider

Tenanti::connection('tenants', function (User $entity, array $config) {
      $connection = config('orchestra.connections.'.$entity->subdomain);
      $config['database'] = $entity->subdomain;
      if($connection !== null) {
          $config = $connection + $config;
      }

       return $config;
});

config

   'drivers' => [
        'website' => [
            'model'     => 'App\User',
            'migration' => 'tenant_migrations',
            'path'      => database_path('tenant/website'),
            'shared'    => false,
        ],
        'ecommerce' => [
            'model'     => 'App\User',
            'migration' => 'tenant_migrations',
            'path'      => database_path('tenant/ecommerce'),
            'shared'    => false,
        ],
    ],

Also since the observer doesn't inject the model instance for the getDriverName and getConnectionName how can I dynamic change those?

Ps: Probably this would work with two different models however I think it would be nice to also support this using a single model, i gave an example with 2 drivers but i'll be using this with way more drivers...

Also this package is awesome, everything else is working great 😄

fernandobandeira commented 7 years ago

For anyone looking into this I fixed it this way:

My user class:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
    protected $table = 'users';
    public $timestamps = false;    

    public static function boot()
    {
        parent::boot();      

        $class = explode('\\', get_called_class());
        $driver = strtolower(end($class));

        if($driver !== 'user') {
          self::addGlobalScope('driver', function (Builder $builder) use($driver) {
              $builder->where('driver', '=', $driver);
          });
        }
    }
}

Each driver has it's own class:

<?php

namespace App\Entities;

use App\User;

class Website extends User { }

I modified for each driver to use a specific class, also reading the updated docs it seems that the observer is not required when using multiple databases so I removed it.