単一のYIIインスタンスにサイトの複数のバージョンを展開する方法

この記事では、yiiフレームワークの1つのプロジェクトでサイトの作業を整理する方法について説明します。 最初の部分では、これが必要な場合と、これに必要なものについて少し理論を示します。 そして、第2部では、技術的な実装に進みます。



パート1



はじめに


多くの企業は、複数のサイトの運営をサポートして、さまざまな市場で製品を宣伝しています。 だから私たちも。 ロシア、アメリカ、ヨーロッパなどの市場向けのサイト、モバイルデバイス用の別個のサイト、アフィリエイトプログラムのサイトもあり、これらは国によって異なります。 開発では、yiiフレームワークを使用します。このフレームワークには、昨年メインサイトAlawar.ruを移管し、今年はAlawar.com、Alawar.pl、iOSデバイスサイトも移管しました。 yiiでのサイトの展開の特徴の1つは、これらすべてがこのすばらしいフレームワークの1つのインスタンスで動作することです。



この問題を解決するのに問題はありません。1つの特定の実装を検討します。



サイトの違い


同じプラットフォームに複数のサイトを配置する方法を理解するには、それらのサイトの共通点と相違点を把握する必要があります。 異なるサイトでは、ローカライズだけでなく、ビューの異なるYii::t('messageFile', 'messageCode')



によって解決される違いと、異なるロケールの異なるmessageFile



を意味することに注意してください。



また、サイトにはさまざまなものがあります。



各項目を詳細に分析します。



充填


記入することは、サイトごとに異なるコンテンツを意味します。 サイトは国によって異なるため、異なる市場で製品を販売しているため、ある市場で販売されている製品を別の市場で販売から削除する必要がある場合があります。 サイトごとに、訪問者に対する特別なオファーや割引が異なる場合があります。



最終的には、静的な情報ページでさえ異なる場合があります。 したがって、フレームワークの1つのインスタンスでデータの不一致を解決できるはずです。



個々のブロック/フロントエンドモジュールの機能


たとえば、Alawar.comのWebサイトには、無制限の機能があります-ユーザーが選択した期間、すべてのゲームのサブスクリプションを対応する費用で。 Alawar.plもこの機能をユーザーに提供しますが、英語サイトとは異なるサブスクリプションオプションを提供します。 ロシアのサイトでは、Unlimitedの作品はまったく提供されていません。



テーマ/スキン


この段落では、さまざまなオプションが可能です。

  1. さまざまなサイトトピック(たとえば、モバイルサイトにはメイントピックとは別のトピックがあります)。
  2. サイトのテーマは同じですが、デザインオプションが異なります(たとえば、Alawar.ruの5月9日のお祝いスキン、Alawar.comの7月4日のスキン)。


Yiiエンジンのテーマを作成することを学ぶのは難しいことではありません。 フレームワーク内でトピックを構造的に分割することにより、サイトでデザインを分割する問題が解決されます。



言語/ローカリゼーション


そして、最後に述べたのはローカライズのみです。



サイトの類似点


次に、すべてのサイトの共通点を見てみましょう。



最も重要な類似性と、同じプラットフォームで異なるサイトを作成する必要がある唯一の重要かつ十分な理由は、同じバックエンドです。 バックエンドがサイトで異なる場合、おそらくそれらを1つのプロジェクトに混在させないでください。 はい、異なるサイトで販売する異なる製品を選択します。 ただし、すべての製品は同じデータベースにあり、同じ管理領域で利用できます。 サイトごとに異なるスキンを描画しますが、それらはすべて1つのトピックにねじ込むことができます。 特に、バックエンドがmysql管理領域に限定されない場合。



この場合、バックエンドとは次のことを意味します。



新しいサイトごとに、これらすべてのテクノロジーをゼロから作成する必要があると想像してください。



タスク


サイトの類似点と相違点の比較をまとめると、サイト間でどのタスクを分割する必要があるかが決まります。

  1. 共通のバックエンドを維持しながら、異なるサイトに異なるコンテンツや独自のコンテンツを表示します。
  2. 異なるサイトモジュールの機能を互いに個別にカスタマイズする機能。
  3. 異なるテーマまたは異なるスキンテーマ、異なるテキストローカリゼーションのサポート。


パート2



サイト定義の構成


この記事では、アプリケーションをロシア語と英語の2つのサイトに分離する方法を示します。 実際には、この方法で、必要な数のサイトを分割できます。



次のパラメータにより、どのサイトでどのサイトを表示するかを決定できます。



次の内容のprotected/config/sites.php



を作成します。

 <?php return array( 'mywebsite.ru' => array( 'host' => array( 'mywebsite.ru', 'www.mywebsite.ru', ), 'userAgent' => false, ), 'mywebsite.com' => array( 'host' => array( 'mywebsite.com', 'www.mywebsite.com', ), 'userAgent' => false, ), );
      
      







この例では、ドメインのみでサイト定義を分析します。 この場合、ユーザーエージェントの定義は使用されないため、 'userAgent' => false



指定します。 このキーは、1つのドメインが異なるバージョンのサイトを表示する必要がある場合に設定されます。



個々のサイトの個別の構成


サイトごとに個別の設定を持つ個別の構成ファイルを作成します。 そして、これらのファイルに上記の配列のキーに対応する名前を付けます。 つまり、ファイルを作成します。

protected/config/mywebsite.ru.php





protected/config/mywebsite.com.php







以下は、 protected/config/mywebsite.ru.php



ファイルの内容です。

 <?php return CMap::mergeArray( array( 'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', 'name'=>'Application name', 'theme' => 'mywebsite', 'language' => 'ru', 'modules'=>array( ), 'controllerMap'=>array( 'site'=>'application.sites.common.controllers.SiteController', 'promo'=>array( 'class' => 'application.sites.mywebsite-ru.controllers.PromoController', 'viewPrefix' => '/mywebsite-ru/promo/', ), 'support'=>array( 'class' => 'application.sites.common.controllers.SupportController', 'viewPrefix' => '/mywebsite-ru/support/', ), ), 'components'=>array( 'urlManager'=>array( 'urlFormat'=>'path', 'showScriptName' => false, 'urlSuffix' => '/', 'rules' => array( '' => 'site/index', 'promo/' => 'promo/index', 'support/' => 'support/index', ), ), 'errorHandler'=>array( 'errorAction'=>'site/error', ), ), 'params'=>array( 'adminEmail'=>'admin@mywebsite.ru', 'arCustomParams' => array( 'customBannerPath' => '/promo/mywebsite.ru/promo-banner.png' ), ), ), require_once(dirname(__FILE__).'/main.php') );
      
      







ご覧のとおり、すべてのサイト固有の設定を個別のファイルに移動しました。これは特定のサイトの構成です。 サイト名、テーマ、ロケールはこの設定で定義されています。



特別なサイトコンテンツを実装したり、特別なリクエストを処理したりするために、カスタムロジックを含むコントローラーを個別に発行します。 すべてのサイトとサイト固有のコントローラーに共通する方法を定義します。 このサイトが使用するcontrollerMap



は、 controllerMap



パラメーターで指定されます。

  'controllerMap'=>array( 'site'=>'application.sites.common.controllers.SiteController', 'promo'=>array( 'class' => 'application.sites.mywebsite-ru.controllers.PromoController', 'viewPrefix' => '/mywebsite-ru/promo/', ), 'support'=>array( 'class' => 'application.sites.common.controllers.SupportController', 'viewPrefix' => '/mywebsite-ru/support/', ), ),
      
      





ここでは、 SiteController



が両方のサイトに共通のSiteControllerを使用していることがわかります。 このコントローラーの表示パスは標準であり、追加の指示は必要ありません。

PromoController



PromoController



はこのサイトにのみ適用されます。 特定のサイトにのみ適用されるさまざまなプロモーションに関するデータを含めることができます。 SupportController



は両方のサイト間で共有されますが、このコントローラーにはカスタムマッピングが使用されます。



それぞれのrender



関数をさらに再定義してこのプロパティを定義せずに、すべてのフロントエンドコントローラーでviewPrefix



プロパティの正しい処理を実現するには、CControllerクラスから継承protected/components/AController.php



protected/components/AController.php



クラスを作成します。

 <?php /** * Controller is the customized base controller class. * All controller classes for this application should extend from this base class. */ class AController extends CController { /** * @var string the default layout for the controller view. Defaults to '//layouts/column1', * meaning using a single column layout. See 'protected/views/layouts/column1.php'. */ public $layout='//layouts/main'; public $baseHref=false; public $viewPrefix=''; /** * @var array context menu items. This property will be assigned to {@link CMenu::items}. */ public $menu=array(); /** * @var array the breadcrumbs of the current page. The value of this property will * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links} * for more details on how to specify this property. */ public $breadcrumbs=array(); /** * @var string page description */ public $pageDescription = ''; public function render($view,$data=null,$return=false) { return parent::render($this->viewPrefix.$view,$data,$return); } }
      
      





アプリケーションのすべてのフロントエンドコントローラーは、このクラスを継承します。 その中で、 viewPrefix



プロパティを定義し、それを考慮してrender($view,$data=null,$return=false)



メソッドrender($view,$data=null,$return=false)



再定義しました。これにより、指定されたプレフィックスでコントローラーの表示を構造的に分離できます。



これは柔軟なシステムであり、非常に異なるディスプレイ/コントローラのみを作成できるため、個別のファイルを使用する価値があります。 すべてのサイトが同じバックエンドを使用しているため、フロントエンドロジックのみを共有する必要があります。 コントローラーとビュー、モデル、フォーム、コンポーネント、拡張機能、その他すべては常にすべてのサイトに共通です。 また、URLディスパッチはサイトごとに異なります。 したがって、別のurlManager



urlManager



urlManager



ます。 さて、すべてのサイトに共通する他のすべての設定は、通常どおりmain.php



ファイルに保存され、 CMap::mergeArray



介して現在のファイルとマージされます



Sitedispatcher


サイトで使用されるすべてのコントローラーとディスプレイを作成します。 次のファイルとそれらに必要なフォルダーを作成します。

protected/sites/common/controllers/SiteController.php





protected/sites/common/controllers/SupportController.php





protected/sites/mywebsite-ru/controllers/PromoController.php





protected/sites/mywebsite-com/controllers/PromoController.php





public/themes/mywebsite/views/site/index.php





public/themes/mywebsite/views/mywebsite-ru/promo/index.php





public/themes/mywebsite/views/mywebsite-ru/support/index.php





public/themes/mywebsite/views/mywebsite-com/promo/index.php





public/themes/mywebsite/views/mywebsite-com/support/index.php







作成された各コントローラーは、 AController



クラスから継承する必要があります。 したがって、YIIの標準構造に若干違反しますが、心配する必要はありません。 プロジェクトの論理構造を保持しました。



作成した構成を使用して必要なコントローラーを起動するようyiiに教えるために、特定の状況に応じて目的の構成ファイルを選択することを目的とする別のコンポーネントクラスprotected/omponents/SiteDispatcher



を作成します。

 <?php class SiteDispatcher { //      public static function setCurrentSiteConfig( $configName ) { @session_start(); $_SESSION['CURRENT_SITE_CONFIG'] = array( 'host' => $_SERVER['HTTP_HOST'], 'configName' => $configName ); @session_write_close(); @session_destroy(); } //       public static function getCurrentSiteConfig() { @session_start(); $res = isset( $_SESSION['CURRENT_SITE_CONFIG'] ) ? $_SESSION['CURRENT_SITE_CONFIG'] : false; @session_write_close(); @session_destroy(); return $res; } public static function getConfigPath() { $arSites = self::getAvailableConfigs(); /*      ,     ,   ,   -          */ if ( ($arCurrent = self::getCurrentSiteConfig()) && $arCurrent['host'] == $_SERVER['HTTP_HOST'] && isset($arSites[$arCurrent['configName']]) ) { return 'protected/config/' . $arCurrent['configName'] . '.php'; } foreach ( $arSites as $configName => $arSiteConfig ) { $res = true; $res &= in_array( $_SERVER['HTTP_HOST'], $arSiteConfig['host'] ); if ( $res && $arSiteConfig['userAgent'] && isset( $_SERVER['HTTP_USER_AGENT'] ) ) { $m = false; $res &= preg_match( $arSiteConfig['userAgent'], $_SERVER['HTTP_USER_AGENT'], $m); } if ( $res ) { return 'protected/config/' . $configName . '.php'; } } error_log('Can\'t determine config to site: ' . var_export( array( 'host' => $_SERVER['HTTP_HOST'], 'userAgent' => $_SERVER['HTTP_HOST'], ), 1)); throw new Exception('Can\'t determine config to site'); } /** * * @static * @return mixed */ protected static function getAvailableConfigs() { return require( dirname(dirname(__FILE__)) . '/config/sites.php' ); } }
      
      





これで、どのケースでどの設定を実行するかを決定できます。 getConfigPath()



メソッドは、選択した構成へのパスを返します。 同時に、選択した設定は、さらにアクセスしやすいようにセッションに保存されます。



このメソッドを呼び出すには、 index.php



ファイルの先頭で次の行を置き換えます



 $config=dirname(__FILE__).'/../protected/config/main.php';
      
      





 require dirname(__FILE__).'/../protected/components/SiteDispatcher.php'; $config=dirname(__FILE__).'/../'.SiteDispatcher::getConfigPath();
      
      







したがって、構成、いくつかのコントローラー、および異なるサイトのマッピングを分離することにより、最初の部分で設定した最初の2つのタスクを解決しました。



肌の分離


さまざまなサイトに独自のスキンを使用するかどうかは、サイト構成でロケールを指定することによって決定されます。 原則として、スキンはテーマと画像の別々のcssファイルとjsファイルです。 このプロジェクトでは、すべてのサイトに共通のテーマの構造レイアウトを定義するいくつかのグローバルcssおよびjsファイルを使用します。 この構造の設計を定義する個別のローカルcssとjsも同様です。 これらすべてのスタイルとスクリプトは、1つのファイルの結果として独自のミニファイヤによって収集され、次のようにレイアウトで接続されます。

 <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->theme->baseUrl . '/css/'. Yii::app()->getLanguage() .'/main.minified.css'; ?>" /> <script type="text/javascript" src="<?php echo Yii::app()->theme->baseUrl . '/js/' . Yii::app()->getLanguage() . '/main_minified.js'; ?>"></script>
      
      







Yii::app()->getLanguage()



は特定のサイトの個別の構成ファイルで定義されたロケールを返すため、テーマ構造に同じ名前のフォルダーを作成することで、そこにcssおよびjsファイルを保存できます。 ロケールでの作業をサポートするために、すべてのファイルを1つに収集するミニファイナーをファイナライズするだけです。 しかし、これは別の記事のトピックです。



それとは別に、私たちのサイトにカウンターを置く問題に注意したいと思います。 多くの異なるスクリプト、Google Analytics、yandex metrika、addthisなどがあります。 サイトごとに、異なるカウンターが使用されます。 Yii::app()->getLanguage()



スイッチで松葉杖を取り除き、特定のサイトで目的のカウンターを正しく表示するために、カウンターをローカライズファイルに転送することにしました。



ファイルを作成します。

protected/messages/ru/scripts.php





protected/messages/en/scripts.php







それらに次のコードを追加します。

 <?php return array ( 'GoogleAnalitics' => ' <!-- GoogleAnalitics begin --> <script type="text/javascript"> //    RU </script> <!-- GoogleAnalitics end --> ', );
      
      





マッピングファイルでecho Yii::t('scripts', 'GoogleAnalitics')



を呼び出して、目的のサイトの目的のコードを表示するだけで十分です。



おわりに


サイトを共有する方法や、あなた自身が使用する他の方法についてのコメントをお待ちしております。



ご清聴ありがとうございました!



All Articles