CodeIgniterでのマルチサイト

*※ CodeIgniter v3 をもとにしています。*  

はじめまして。年賀状の進捗どうですか。

[CodeIgniter](http://www.codeigniter.com/)でマルチサイトにしたいことがないですかね。
たとえば公開用と管理画面用とを分けるなど。
さらに、分けたサイトを別々のGitリポジトリで管理したりとかにも少し触れます。
<span class="small">ちなみに、海外では「Multi Site」よりも「Multi Application」という呼称の方がよく使われるようなので、ググる場合などご参考に。</span>

-----

以下のような構成にしたい場合を想定して話を進めます。

- 公開側用  
  - URL：**/**
  - フォルダ名：**www**
- 管理画面用  
  - URL：**/admin/**
  - フォルダ名：**admin**


## 公式な方法

まず、CodeIgniterのデフォルトのフォルダ構成は以下のようになっています。（最低限必要なもの）

> 
<i class="fa fa-fw fa-folder-open-o"></i>application
<i class="fa fa-fw">├</i><i class="fa fa-fw fa-folder-o"></i>cache
<i class="fa fa-fw">│</i><i class="text-muted">（略）</i>
<i class="fa fa-fw">└</i><i class="fa fa-fw fa-folder-o"></i>views
<i class="fa fa-fw fa-folder-o"></i>system
<i class="fa fa-fw fa-file-code-o"></i>index.php

このままでもルーティングや Controller をがんばればマルチサイトにできなくもないんですが、[v2のユーザーガイド](http://codeigniter.jp/user_guide_ja/general/managing_apps.html) や [v3のユーザーガイド](http://www.codeigniter.com/user_guide/general/managing_apps.html) にて、以下のように分割する方法が書かれています。

> 
<i class="fa fa-fw fa-folder-open-o"></i>application
<i class="fa fa-fw">├</i><i class="fa fa-fw fa-folder-open"></i>**www** (公開用)
<i class="fa fa-fw">│</i><i class="fa fa-fw">├</i><i class="fa fa-fw fa-folder-o"></i>cache
<i class="fa fa-fw">│</i><i class="fa fa-fw">│</i><i class="text-muted">（略）</i>
<i class="fa fa-fw">│</i><i class="fa fa-fw">└</i><i class="fa fa-fw fa-folder-o"></i>views
<i class="fa fa-fw">└</i><i class="fa fa-fw fa-folder-open"></i>**admin** (管理画面用)
<i class="fa fa-fw"></i><i class="fa fa-fw">├</i><i class="fa fa-fw fa-folder-o"></i>cache
<i class="fa fa-fw"></i><i class="fa fa-fw">│</i><i class="text-muted">（略）</i>
<i class="fa fa-fw"></i><i class="fa fa-fw">└</i><i class="fa fa-fw fa-folder-o"></i>views
<i class="fa fa-fw fa-folder-o"></i>system
<i class="fa fa-fw fa-file"></i>**index.php** (公開用)
<i class="fa fa-fw fa-file"></i>**admin.php** (管理画面用)

まず <i class="fa fa-fw fa-folder-open-o"></i>application の中にサイトごとのフォルダを用意し、もともと <i class="fa fa-fw fa-folder-open-o"></i>application にあったフォルダ群をコピー。
そして、直下の <i class="fa fa-fw fa-file"></i>index.php を公開用に使い、それを <i class="fa fa-fw fa-file"></i>admin.php としてコピーして管理画面用にします。

あとは、<i class="fa fa-fw fa-file"></i>index.php と <i class="fa fa-fw fa-file"></i>admin.php の[116行目](https://github.com/bcit-ci/CodeIgniter/blob/develop/index.php#L116)を編集。

```diff:index.php
- $application_folder = 'application';
+ $application_folder = 'application/www';
```

```diff:admin.php
- $application_folder = 'application';
+ $application_folder = 'application/admin';
```

そのうえでサイトごとにURLリライト設定。

```apache:Apacheの場合
RewriteEngine On

# 管理画面用
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^admin(/(.*))?$ admin.php/$2 [L]

# 公開用
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
```

```nginx:Nginxの場合
server {
    #...

    # 管理画面用
    location /admin {
        try_files $uri $uri/ /admin.php;
    }

    # 公開用
    location / {
        try_files $uri $uri/ /index.php;
    }

    #...
}
```

でもこれ・・・サイト増えたりとかするとわりとめんどくさいですよね？
あと、直下と <i class="fa fa-fw fa-folder-open-o"></i>application 内とでファイルが分かれてて Git の管理なんかもやりにくい・・・。
そこで。



## ひと手間かける

各サイトのフォルダは <i class="fa fa-fw fa-folder-open-o"></i>application 内のものだけで完結させたい。（独立して Git 管理したい）
なので、全てのサイトで直下の <i class="fa fa-fw fa-file"></i>index.php を使うことにする。

フォルダ構成はこのような感じ。

> 
<i class="fa fa-fw fa-folder-open-o"></i>application
<i class="fa fa-fw">├</i><i class="fa fa-fw fa-folder-open"></i>**www** (公開用)
<i class="fa fa-fw">│</i><i class="fa fa-fw">├</i><i class="fa fa-fw fa-folder-o"></i>cache
<i class="fa fa-fw">│</i><i class="fa fa-fw">│</i><i class="text-muted">（略）</i>
<i class="fa fa-fw">│</i><i class="fa fa-fw">└</i><i class="fa fa-fw fa-folder-o"></i>views
<i class="fa fa-fw">└</i><i class="fa fa-fw fa-folder-open"></i>**admin** (管理画面用)
<i class="fa fa-fw"></i><i class="fa fa-fw">├</i><i class="fa fa-fw fa-folder-o"></i>cache
<i class="fa fa-fw"></i><i class="fa fa-fw">│</i><i class="text-muted">（略）</i>
<i class="fa fa-fw"></i><i class="fa fa-fw">└</i><i class="fa fa-fw fa-folder-o"></i>views
<i class="fa fa-fw fa-folder-o"></i>system
<i class="fa fa-fw fa-file"></i>**index.php** (全サイト共用)

Apache または Nginx は通常の記述で。

```apache:Apacheの場合
RewriteEngine On

# 全サイト共用
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
```

```nginx:Nginxの場合
server {
    #...

    # 全サイト共用
    location / {
        try_files $uri $uri/ /index.php;
    }

    #...
}
```

<i class="fa fa-fw fa-file"></i>index.php の[116行目](https://github.com/bcit-ci/CodeIgniter/blob/develop/index.php#L116)のあとに、マルチサイト用の記述を追加。

```php:index.php
	// これはもともとある記述（116行目）
	// $application_folder = 'application';

	// 公開用をデフォルトにする
	$application_folder = 'application/www';

/*
 * -------------------------------------------------------------------
 *  マルチサイト用（追加部分）
 * -------------------------------------------------------------------
 *
 * Nginxを通してる場合は $_SERVER['REQUEST_URI'] ではなく
 * $_SERVER['HTTP_X_ORIGINAL_REQUEST'] である場合があるため対応してます。
 */

	$uri = NULL;
	if (isset($_SERVER['REQUEST_URI'])) {
		$uri = $_SERVER['REQUEST_URI'];
	} elseif (isset($_SERVER['HTTP_X_ORIGINAL_REQUEST'])) {
		$uri = $_SERVER['HTTP_X_ORIGINAL_REQUEST'];
	}
	// URLの第一階層と同じディレクトリが application 内にあれば $application_folder に追記
	if ($uri && ($dir = strtok(trim($uri, '/'), '/')) && is_dir($application_folder.'/'.$dir)) {
		$application_folder .= '/'.$dir;
	}
```

これで自動的に `$application_folder` が各フォルダに自動設定されるので、マルチサイト化完了。
また、<i class="fa fa-fw fa-folder-open-o"></i>application 内のフォルダをコピーすることでいくらでもサイトを増やせます。



## Gitで管理する例

これは自分の例だけど、ルートフォルダと <i class="fa fa-fw fa-folder-open-o"></i>application フォルダ内とで [submodule](https://git-scm.com/docs/git-submodule) を使ってリポジトリを分けてます。
管理が分轄できていいかなと。
このような感じ。

>
<i class="fa fa-fw fa-folder"></i>.git 
<i class="fa fa-fw fa-folder-open-o"></i>application
<i class="fa fa-fw">├</i><i class="fa fa-fw fa-folder-open-o"></i>www (<i class="fa fa-fw fa-code-fork"></i>submodule)
<i class="fa fa-fw">│</i><i class="fa fa-fw">│</i><i class="text-muted">（略）</i>
<i class="fa fa-fw">│</i><i class="fa fa-fw">└</i><i class="fa fa-fw fa-file-text"></i>.git
<i class="fa fa-fw">└</i><i class="fa fa-fw fa-folder-open-o"></i>admin (<i class="fa fa-fw fa-code-fork"></i>submodule)
<i class="fa fa-fw"></i><i class="fa fa-fw">│</i><i class="text-muted">（略）</i>
<i class="fa fa-fw"></i><i class="fa fa-fw">└</i><i class="fa fa-fw fa-file-text"></i>.git
<i class="fa fa-fw fa-folder-o"></i>system
<i class="fa fa-fw fa-file-text"></i>.gitmodules
<i class="fa fa-fw fa-file-code-o"></i>index.php



## <a name="routing"></a>ルーティング記述を少し楽にする

ここまでで問題はないんですが、サブサイトでのルーティングで少し面倒な点が。
例えばURLが `/admin/login` とかの場合、`application/admin/config/routes.php` の記述は以下のようになります。

```php:application/admin/config/routes.php
<?php

$route['default_controller'] = 'welcome';
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;

// 管理画面トップページ用
$route['admin'] = 'welcome';

// 管理画面ログイン用
$route['admin/login'] = 'login';
```

面倒じゃないですかね。すべてのルーティングに「admin」を付けなければならないのが。
なので、以下のように加工したりしてます。

```php:application/admin/config/routes.php
<?php

$route['default_controller'] = 'welcome';
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;

// -- ↓ すこし変更 --

// 管理画面トップページ用
$route['/'] = 'welcome';

// 管理画面ログイン用
$route['login'] = 'login';

// -- ↓ 以下追加部分 --

// 各キー名に「admin/」を追加
$route = array_combine(array_map(function($k) use($route) {
	if('default_controller' == $k || '404_override' == $k || 'translate_uri_dashes' == $k) {
		return $k;
	}
	return trim('admin/'.$k, '/');
}, array_keys($route)), $route);
```

このあたりは [Hooks](http://www.codeigniter.com/userguide3/general/hooks.html)とかで対応できないかな～なんていろいろ試したんですが無理っぽかったです。
あと、コアの `Routing.php` に[そういうことできるっぽいやつ](https://github.com/bcit-ci/CodeIgniter/blob/develop/system/core/Router.php#L130)がある！と思ったけどURLの書き換えとかではなさげでした。

なくても動くので少しでも高速化したい場合は不要ですかね。



## サブドメインの場合

サブドメインの場合はすこし楽です。
ディレクトリ構成はここまでのものと同じで、<i class="fa fa-fw fa-file"></i>index.php をサブドメイン用に変更するだけです。

```php:index.php
	// これはもともとある記述（116行目）
	// $application_folder = 'application';

	// 公開用をデフォルトにする
	$application_folder = 'application/www';

/*
 * -------------------------------------------------------------------
 *  マルチサイト用（追記部分）
 * -------------------------------------------------------------------
 */

	if (isset($_SERVER['HTTP_HOST'])) {
		// サブドメイン名と同じディレクトリが application 内にあれば $application_folder に追記
		if (($dir = current(explode('.', $_SERVER['HTTP_HOST']))) && is_dir($application_folder.'/'.$dir)) {
			$application_folder .= '/'.$dir;
		}
	}
```

で、前述の[ルーティングを楽にする](#routing)部分は必要なくなります。

---

以上です。
ちなみに年賀状は12月15日～25日のあいだに投函すれば元旦に届くそうです。