LoginSignup
0
0

More than 3 years have passed since last update.

Symfony + Propel でマルチドメイン対応

Last updated at Posted at 2018-09-24

ひとつの Web アプリを、複数の顧客に対し、ドメインを分けてそれぞれにサービスを提供したい。各顧客はそれぞれでユーザーを登録し、ログインをおこなう。データはドメインごとに完全に独立させたい。

環境

  • Symfony 2.8
  • Propel 1.5

方針

  • 顧客をアクセス時のサブドメインで識別する。(DNS や証明書はワイルドカードで何でもアクセスできるようにしておく。)
  • すべてのテーブルに customer_id フィールドを持たせる。
  • 読み出すときは検索条件に常に customer_id による絞り込みをおこなう。
  • 書き込むときは常に customer_id を設定する。

実装

顧客IDをアクセスドメインから取得する

アクセスドメインから顧客IDを取得するためのサービスを作成する。RequestStack->getMasterRequest()->getHost() でドメインを取得し、先頭の要素を顧客IDとする。

<?php
namespace AppBundle\Library;

use Symfony\Component\HttpFoundation\RequestStack;

class CustomerManager
{
    protected $requestStack;

    function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }

    function getCurrentCustomerId()
    {
        $request = $this->requestStack->getMasterRequest();
        $h = explode('.', $request->getHost());
        return $h[0];
    }
}

取得した顧客IDをセッションに保存するためのイベントハンドラを作成する。

<?php
namespace AppBundle\Filter\Propel;

use AppBundle\Library\CustomerManager;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class Configurator
{
    protected $customerManager;
    private $session;

    public function __construct(CustomerManager $customerManager, Session $session)
    {
        $this->customerManager = $customerManager;
        $this->session = $session;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        $this->session->set('customer_id',
            $this->customerManager->getCurrentCustomerId());
    }
}

これらをサービスに登録する。

app/config/services.yml
services:
    app.customer:
        class: AppBundle\Library\CustomerManager
        arguments: [ "@request_stack" ]
    app.propel.customer_listener:
        class: AppBundle\Filter\Propel\Configurator
        arguments: [ "@app.customer", "@session" ]
        tags:
          - { name: kernel.event_listener, event: kernel.request }

Behavior の作成

以下の処理をおこなう Behavior を作成する。

  • すべてのテーブルに customer_id フィールドを持たせる
  • DBからの読み出し時、常に customer_id による絞り込みをおこなう
  • 保存時に customer_id を設定する

比較対象の顧客IDはセッションに保存しておいたものを使う。

<?php

namespace AppBundle\Filter\Propel;

class CustomerFilterBehavior extends \Behavior
{
    const GET_CUSTOMER_ID = '
global $kernel;
$customerId = $kernel->getContainer()->get(\'session\')->get(\'customer_id\');
';

    public function modifyTable()
    {
        $table = $this->getTable();
        if (!$table->hasColumn('customer_id')) {
            $table->addColumn([
                'name' => 'customer_id',
                'type' => 'VARCHAR',
                'size' => '255',
            ]);
        }
    }

    public function preSelectQuery(\QueryBuilder $builder)
    {
        return self::GET_CUSTOMER_ID .
            '$this->filterByCustomerId($customerId);';
    }

    public function preSelect(\PeerBuilder $builder)
    {
        $column = $this->getTable()->getColumn('customer_id');
        $columnConstant = $builder->getColumnConstant($column);

        return self::GET_CUSTOMER_ID .
            '$criteria->add(' . $columnConstant . ', $customerId);';
    }

    public function preInsert(\PeerBuilder $builder)
    {
        return self::GET_CUSTOMER_ID .
            '$this->setCustomerId($customerId);';
    }
}

この Behavior を登録する。

app/config/config.yml
propel:
    behaviors:
        customer_filter: AppBundle\Filter\Propel\CustomerFilterBehavior

顧客毎に分離する必要があるすべてのテーブルに Behavior を登録する。

src/AppBundle/Resources/config/schema.xml
<?xml version="1.0" encoding="UTF-8"?>
<database defaultPhpNamingMethod="underscore" heavyIndexing="false" name="propel" defaultIdMethod="native" namespace="AppBundle\Entity\Propel">
    <table skipSql="false" abstract="false" name="users" phpName="User">
        ...
        <behavior name="customer_filter" />
    </table>
</database>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0