2016-07-05 23 views
1

私はマルチテナントアプリケーションを開発しています。私がやりたいことの1つは、組織/テナントが訪問するときにDB接続をオンザフライで切り替えることができることですそのサブドメインを使用してテナントを識別することによってアプリケーションを実行します。私は、単一DBアプローチとは対照的に、複数DBアプローチを使用しています。これまでの実装では、サインアップして必要なすべての移行を実行したときに、組織のデータベースを作成して環境の設定を自動化しています。Laravelのnullのメンバー関数connection()への呼び出し5

問題は、IoCコンテナからtenantを解決し、そのデータベースをデフォルトとして使用するようにDB接続を設定して、組織/テナントがそのデータにアクセスできるようにすることに関連しています。これは私のAppServiceProvider.phpクラスのregister()方法は次のようになります。私は明示的にmysql接続を使用するようにそれを言っていたが、私はそれをコメントアウトしていたOrganization.phpモデルについては、

/** 
* Register any application services. 
* 
* @return void 
*/ 
public function register() 
{ 
    App::bind('setDbConnection', function($app, $db) { 
     Config::set("database.connections.{$db[0]}", [ 
      'driver' => env('DB_CONNECTION'), 
      'host'  => env('DB_HOST'), 
      'port'  => env('DB_PORT'), 
      'database' => "{$db[0]}", 
      'username' => env('DB_USERNAME'), 
      'password' => env('DB_PASSWORD'), 
      'charset' => 'utf8', 
      'collation' => 'utf8_unicode_ci', 
      'prefix' => '', 
      'strict' => false, 
      'engine' => null, 
     ]); 
    }); 

    App::singleton('tenant', function() { 
     $server = explode('.', Request::getHost()); 

     if (count($server) === 3 && $server !== 'www') { 
      return Organization::where('slug', $server[0])->firstOrFail(); 
     } 
    }); 

    // dd(App::make('tenant')->slug); 

    if (! App::runningInConsole()) { 
     App::make('setDbConnection', [App::make('tenant')->slug]); 
     Config::set('database.default', App::make('tenant')->slug); 
    } 
} 

。組織がアプリに来る場合にどうするか

namespace App; 

use Illuminate\Database\Eloquent\Model; 

class Organization extends Model 
{ 
    // protected $connection = 'mysql'; 

    protected $fillable = ['name', 'slug']; 
} 

は、例えば、samplecompany.example.com私は接続を切り替え、その後、組織をルックアップするために、サブドメインsamplecompanyを使用します、次のとおりです。これは、そのクラスには次のようになりますテナントが自分の情報にアクセスできるように、アプリのライフサイクルの早い段階でそのDBにアクセスしてください。同じOrganizationモデルをアプリケーション内の他の場所に使用すると完全に動作しますが、ここでメソッドのAppServiceProviderクラスで使用すると、接続がまったく解決されません。このテストのドメインはhttp://bestfinance.global.dev:8000でした。ここで

は、完全なスタックトレースです:

Whoops, looks like something went wrong. 

1/1 FatalThrowableError in Model.php line 3293: 
Call to a member function connection() on null 
1. in Model.php line 3293 
2. at Model::resolveConnection(null) in Model.php line 3259 
3. at Model->getConnection() in Model.php line 1880 
4. at Model->newBaseQueryBuilder() in Model.php line 1853 
5. at Model->newQueryWithoutScopes() in Model.php line 1823 
6. at Model->newQuery() in Model.php line 3503 
7. at Model->__call('where', array('slug', 'bestfinance')) 
8. at call_user_func_array(array(object(Organization), 'where'), array('slug', 'bestfinance')) in Model.php line 3519 
9. at Model::__callStatic('where', array('slug', 'bestfinance')) in AppServiceProvider.php line 52 
10. at AppServiceProvider->App\Providers\{closure}(object(Application), array()) in Container.php line 731 
11. at Container->build(object(Closure), array()) in Container.php line 629 
12. at Container->make('tenant', array()) in Application.php line 697 
13. at Application->make('tenant') in Facade.php line 217 
14. at Facade::__callStatic('make', array('tenant')) in AppServiceProvider.php line 59 
15. at AppServiceProvider->register() in Application.php line 554 
16. at Application->register(object(AppServiceProvider)) in ProviderRepository.php line 74 
17. at ProviderRepository- >load(array('Illuminate\Auth\AuthServiceProvider', 'Illuminate\Broadcasting\BroadcastServiceProvider', 'Illuminate\Bus\BusServiceProvider', 'Illuminate\Cache\CacheServiceProvider', 'Illuminate\Foundation\Providers\ConsoleSupportServiceProvider', 'Illuminate\Cookie\CookieServiceProvider', 'Illuminate\Database\DatabaseServiceProvider', 'Illuminate\Encryption\EncryptionServiceProvider', 'Illuminate\Filesystem\FilesystemServiceProvider', 'Illuminate\Foundation\Providers\FoundationServiceProvider', 'Illuminate\Hashing\HashServiceProvider', 'Illuminate\Mail\MailServiceProvider', 'Illuminate\Pagination\PaginationServiceProvider', 'Illuminate\Pipeline\PipelineServiceProvider', 'Illuminate\Queue\QueueServiceProvider', 'Illuminate\Redis\RedisServiceProvider', 'Illuminate\Auth\Passwords\PasswordResetServiceProvider', 'Illuminate\Session\SessionServiceProvider', 'Illuminate\Translation\TranslationServiceProvider', 'Illuminate\Validation\ValidationServiceProvider', 'Illuminate\View\ViewServiceProvider', 'App\Providers\AppServiceProvider', 'App\Providers\AuthServiceProvider', 'App\Providers\EventServiceProvider', 'App\Providers\RouteServiceProvider')) in Application.php line 530 
18. at Application->registerConfiguredProviders() in RegisterProviders.php line 17 
19. at RegisterProviders->bootstrap(object(Application)) in Application.php line 203 
20. at Application->bootstrapWith(array('Illuminate\Foundation\Bootstrap\DetectEnvironment', 'Illuminate\Foundation\Bootstrap\LoadConfiguration', 'Illuminate\Foundation\Bootstrap\ConfigureLogging', 'Illuminate\Foundation\Bootstrap\HandleExceptions', 'Illuminate\Foundation\Bootstrap\RegisterFacades', 'Illuminate\Foundation\Bootstrap\RegisterProviders', 'Illuminate\Foundation\Bootstrap\BootProviders')) in Kernel.php line 232 
21. at Kernel->bootstrap() in Kernel.php line 127 
22. at Kernel->sendRequestThroughRouter(object(Request)) in Kernel.php line 99 
23. at Kernel->handle(object(Request)) in index.php line 53 
24. at require_once('/Users/exampleuser/Sites/multitenant/public/index.php') in server.php line 21 

答えて

0

私が持っていた問題を解決するために、私がやったことは、カスタムサービスプロバイダを作成(私もこれが提供AppServiceProviderクラスで働いていた非常に確信している)とboot()までの私の$this->app->singleton()$this->app->runningInConsole()を移動することでしたが方法と私は$this->app->bind('setDbConnection', ...)register()メソッドに残しました。すべての場所で、すべてのテナントのそれぞれのテストデータをシームレスに読み込むことができました。

namespace App\Providers; 

use App\Organization; 
use Illuminate\Support\Facades\App; 
use Illuminate\Support\Facades\Config; 
use Illuminate\Support\Facades\Request; 
use Illuminate\Support\ServiceProvider; 

class TenantServiceProvider extends ServiceProvider 
{ 
    /** 
    * Bootstrap the application services. 
    * 
    * @return void 
    */ 
    public function boot() 
    { 
     $this->app->singleton('tenant', function($app) { 
      $server = explode('.', Request::getHost()); 

      if (count($server) === 3 && $server !== 'www') { 
       return Organization::where('slug', $server[0])->firstOrFail(); 
      } 
     }); 

     // dd(App::make('tenant')); 

     if (! $this->app->runningInConsole()) { 
      $this->app->make('setDbConnection', [$this->app->make('tenant')->slug]); 
      Config::set('database.connections', $this->app->make('tenant')->slug); 
     } 
    } 

    /** 
    * Register the application services. 
    * 
    * @return void 
    */ 
    public function register() 
    { 
     $this->app->bind('setDbConnection', function($app, $db) { 
      Config::set("database.connections.{$db[0]}", [ 
       'driver' => env('DB_CONNECTION'), 
       'host'  => env('DB_HOST'), 
       'port'  => env('DB_PORT'), 
       'database' => "{$db[0]}", 
       'username' => env('DB_USERNAME'), 
       'password' => env('DB_PASSWORD'), 
       'charset' => 'utf8', 
       'collation' => 'utf8_unicode_ci', 
       'prefix' => '', 
       'strict' => false, 
       'engine' => null, 
      ]); 
     }); 
    } 
} 

はまた、私は人が存在しないテナントをロードしようとする状況を処理するために私のroutesファイルにRoute::bind()を追加しました:

は、ここに私のTenantServiceProviderクラスです。それは次のようになります。

Route::bind('tenant', function($value) { 
     $tenant = $this->app->make('tenant'); 

     if ($tenant->slug === $value) { 
      return $tenant; 
     } else { 
      return $this->app->abort(404); 
     } 
    }); 

マルチテナントアプリケーションを構築する上で、このアプローチのためのオリジナルの実装はLaravel 4.2にTutsplusMatthew Machugaによって行われました。しかし、私はLaravel 5.2で非常に似たようなことをしたいと思っていましたが、これは少し問題があったセクションでした。私はこれが誰かを助けることを望む。

0

んが、実際に動作して文字列を返すdd(App::make('tenant')->slug);コメントアウト行? にそれを渡している場合は、'database' => "{$db[0]}",が文字列であると正しく発音しないと、スラッグの最初の文字が表示されます。

+0

いいえ、何も返されません。上記のエラーが表示され続けます。私は実際に価値を取り戻しているかどうかを確認していました。 – k4r3y