はじめに
みなさん、こんにちは、この記事は私のドラフトに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' ),
同じオプションを使用して、モジュールの「クランチ」を捨てることができます