yii1の名前空間について

はじめに



みなさん、こんにちは、この記事は私のドラフトに1年間入っています。 時間があれば、後で延期しましたが、yii2の差し迫ったリリースに関連して、私はそれを完成させて読者のレビューに載せることにしました。



3年間、私はメガフラワーの1つの非常に大きなプロジェクトに取り組んできました。 そして、開発のある時点で、クラスが多すぎて、それらの名前がContentDiscount



ItemDiscount



になったItemDiscount



、私はそれについて何かをする必要があることに気づき、プロジェクトに名前空間を導入することにしました。 まあ、彼らが言うように、もしあなたがそのような散歩に出たら、それからどこでもすぐに、そしてそこに少しだけではなく、他の場所ではありません。



それでは、アプリケーションのクラスの主要なタイプを「調理」する方法を見てみましょう。



基本



どこでも名前空間を使用することにしたので、 app



ルートをルート名前空間として選択しました( application



が長すぎます)。 ただし、yiiはそれを理解しないため、configで定義する必要がありました(index.phpでも可能)が、configはパスに沿って接続されていたため、初期化時にYii::setPathOfAlias



使用できませんYii::setPathOfAlias



(状況が変わった可能性がありますか? )、index.phpを変更する必要がありました。



これが私のindex.phpの見え方です
 $yii=dirname(__FILE__).'/yii/framework/yii.php'; $config=dirname(__FILE__).'/protected/config/main.php'; // remove the following lines when in production mode defined('YII_DEBUG') or define('YII_DEBUG',true); // specify how many levels of call stack should be shown in each log message defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3); //   Yii,      require_once($yii); // ,  ,       $config=require($config); Yii::createWebApplication($config)->run();
      
      







そしてそれに応じて設定
 // -   yii,     Yii::getPathOfAlias Yii::setPathOfAlias('app', dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR); //       define(NS_SEPARATOR,NAMESPACE_SEPARATOR); //   return array( // .... );
      
      









コントローラー



ここではすべてがシンプルであるように思われます-configで示されているcontrollerNamespace、たとえば、エイリアスapp



などはすべて正常に機能します。 そしていや! とりあえず、しばらくの間、これは、コントローラーが何らかの種類のフォルダー(たとえば、testおよびapp\test



名前空間)にある場合に当てはまります。 Yiiは、テストフォルダーでapp



名前空間で検索します。 作業が必要であり、バグレポートを作成してプールリクエストを行う時間はありませんでした(ただし、それを行うことはできます)ので、決定を書くことにしました。 これを行うには、 CWepApplication



から継承し、 createController



メソッドを再定義しました。 多くのコードを複製しなければならなかったので、あまり美しくありませんでしたが、プロジェクトの内部問題を解決するためにこの方法をブロックしなければなりませんでした。



ウェプアプリケーション
  class WebApplication extends CWebApplication { //         public $controllerNamespace='app'; public function createController($route,$owner=null) { if($owner===null) $owner=$this; if(($route=trim($route,'/'))==='') $route=$owner->defaultController; $caseSensitive=$this->getUrlManager()->caseSensitive; $route.='/'; while(($pos=strpos($route,'/'))!==false) { $id=substr($route,0,$pos); if(!preg_match('/^\w+$/',$id)) return null; if(!$caseSensitive) $id=strtolower($id); $route=(string)substr($route,$pos+1); if(!isset($basePath)) // first segment { if(isset($owner->controllerMap[$id])) { return array( \Yii::createComponent($owner->controllerMap[$id],$id,$owner===$this?null:$owner), $this->parseActionParams($route), ); } /** @var $module \base\BaseModule */ if(($module=$owner->getModule($id))!==null){ return $this->createController($route,$module); } $basePath=$owner->getControllerPath(); $controllerID=''; } else $controllerID.='/'; $className=ucfirst($id).'Controller'; $classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php'; //     if($owner->controllerNamespace!==null) $className=$owner->controllerNamespace.NS_SEPARATOR.str_replace('/',NS_SEPARATOR,$controllerID).$className; if(is_file($classFile)) { if(!class_exists($className,false)) require($classFile); if(class_exists($className,false) && is_subclass_of($className,'CController')) { $id[0]=strtolower($id[0]); return array( new $className($controllerID.$id,$owner===$this?null:$owner), $this->parseActionParams($route), ); } return null; } $controllerID.=$id; $basePath.=DIRECTORY_SEPARATOR.$id; } } }
      
      







そして、これが私たちのindex.phpが見え始めた方法です:
 // change the following paths if necessary $yii=dirname(__FILE__).'/yii/framework/yii.php'; $config=dirname(__FILE__).'/protected/config/main.php'; // remove the following lines when in production mode defined('YII_DEBUG') or define('YII_DEBUG',true); // specify how many levels of call stack should be shown in each log message defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3); //   Yii,      require_once($yii); // ,   $config=require($config); // ,   $app=new app\components\WebApplication($config); $app->run();
      
      









モジュール



コントローラーは既にありますが、さらに記述できますが、モジュールに入れたい場合はどうでしょうか? モジュールはYii::createComponent



設定によって定義されます。つまり、クラス名を手動で指定することで使用できます。



config
 array( 'modules'=>array( 'front'=>array( 'class'=>'front\FrontModule' ) ) )
      
      







yiiはfront



エイリアスについて何も知らないため、このメソッドは機能しません。 app



エイリアスと同じ原理で設定に登録することは可能app



が、コードの冗長性を考慮してこの方法はあまり好きではありませんでした(モジュールの名前のみを書きたい)ので、簡単に変更して子孫のCWebApplication



を変更しCWebApplication







Webアプリケーション
 class WebApplication extends CWebApplication { // .... /** *        (,  ) * @param array $modules */ public function setModules($modules) { $modulesConfig=array(); foreach($modules as $id=>$module){ if(is_int($id)) { $id=$module; $module=array(); } if(!isset($module['class'])) { //   \Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id); $module['class']=NS_SEPARATOR.$id.NS_SEPARATOR.ucfirst($id).'Module'; } $modulesConfig[$id]=$module; } parent::setModules($modulesConfig); } }
      
      







解決策は完全ではなく、バグレポートがコンパイルされます(モジュールクラス名を指定すると、yiiはそれを見つけることができませんか? app.modules.ModuleClass



の形式でapp.modules.ModuleClass



するapp.modules.ModuleClass



)。 今、私はこれをすべて変更し、 CWebApplication



よりCWebApplication



、たとえば、モジュールのあるフォルダー内の構成にエイリアスのインストールを置き、それをメイン構成に接続すると思います。



モジュールを把握しましたが、サブモジュールに関しては、すぐに同じ問題に直面します。 はい。また、モジュール内のコントローラーを正しく動作させるには、各モジュールのcontrollerNamespace



を手動で指定する必要があります。 これを修正するには、すべてのモジュールの基本クラスを定義します。



基本モジュール
 class BaseModule extends \CWebModule { /** *    +  */ protected function init() { parent::init(); //   ,      $namespace=implode(NS_SEPARATOR, array_slice(explode(NS_SEPARATOR,get_class($this)),0,-1)); $this->controllerNamespace=$namespace.NS_SEPARATOR.'controllers'; } /** *        (,  ) * @param array $modules */ public function setModules($modules) { $modulesConfig=array(); foreach($modules as $id=>$module){ if(is_int($id)) { $id=$module; $module=array(); } if(!isset($module['class'])) { \Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id); $module['class']=NS_SEPARATOR.$id.NS_SEPARATOR.ucfirst($id).'Module'; } $modulesConfig[$id]=$module; } parent::setModules($modulesConfig); } }
      
      







コードの一部は特性に入れることができますが、それはあなたにお任せします。



コンソールコマンド



初めて「名前空間」コマンドを起動できなかったとき、「CConsoleApplication」または「CConsoleCommandRunner」のいずれかにcommandNampespace



似たものがcommandNampespace



ませんでした(機能の作成リクエストがありますか?)。 私はcommandMap



に向かって掘り始めましたが、ここでも失望が待っていました。



config console.php
 //   ,       Yii::setPathOfAlias('app',dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); //... 'commandMap'=>array( 'import'=>'\app\commands\ImportCommand', ),
      
      







コードは、 ImportCom



クラスが見つからなかったという事実をImportCom







しかし、試行錯誤によって、実用的なソリューションが見つかりました。



console.php


 //   ,       Yii::setPathOfAlias('app',dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); //... 'commandMap'=>array( //   'import'=>array( 'class'=>'\app\commands\ImportCommand', ) ),
      
      







この方法のマイナス面のうち、構成内のすべてのチームの絶対名を指定する必要があることに注意する必要があります。



今日、これが問題の唯一の解決策であり、他のものを見つけることができませんでした。



モデル



それでモデルに着きました。 モデルはとにかく名前空間で使用できるため、すべてがここにあるはずですが、リレーションメソッドがどのように見えるようになったかを見たとき、私はそれを修正することにしました。 最初に、各モデルでクラス名を使用して定数を定義しましたconst CLASS_NAME=__CLASS_NAME__;







次に、ベースモデルを定義することで簡単に実行することにしました(ソリューションはyii2で調査されています)。



ベースモデルNamespaceRecord
 class NamespaceRecord extends CActiveRecord { public static function className() { return get_called_class(); } }
      
      









これらのアクションの後、モデルはよりシンプルで美しくなりました。



それは:
  public function relations(){ return array( 'country'=>'app\location\Country', ) }
      
      







次のようになりました:
  public function relations(){ return array( 'country'=>Country::className(), ) }
      
      







フォームにはまだ問題がありましたが、yiiはすでにこれを修正しました。



ウィジェット



長い間、この$this->widget(' \ ')



を自分のビューに書いていましたが、yii2のリリースで、ウィジェットをyii2のようにしました。 これを行うために、すべてのウィジェットの基本クラスを定義しました。



NSWidgetベースウィジェット
 class NSWidget extends \CWidget{ /** * @param array $options * @return \CWidget */ public static function begin($options=array()) { return \Yii::app()->controller->beginWidget(get_called_class(),$options); } /** * @return \CWidget */ public static function end() { return \Yii::app()->controller->endWidget(); } /** * @param array $options * @return string widget content */ public static function runWidget($options=array()) { return \Yii::app()->controller->widget(get_called_class(),$options,true); } }
      
      









すべて、今ではビューに書き込むことができます
 echo MyWidgetNS\MyWidget::begin($options); echo MyWidgetNS\MyWidget::end(); //... echo MyWidget2NS\MyWidget2::runWidget($options);
      
      







記事に対するコメントや提案があれば、それだけです-書いて、修正します。

UPD。 本日、サブフォルダー内のコントローラーに関するバグを修正しました。 CWebApplicationを再定義する必要はもうありません。 エイリアスの場合、設定でオプションを使用することもできます(アプリケーションが修正されているため、すでに必要です)。

config
 'aliases'=>array( 'app'=>'application' ),
      
      







同じオプションを使用して、モジュールの「クランチ」を捨てることができます



All Articles