Yii用のシンプルな設定ファイルエディター

こんばんは、ハブラハブル。



今日は、すばらしいPHP Yiiフレームワークのために偶然書いた小さなフォームコンポーネントについてお話します。 このコンポーネント(または、フォームモデル)を使用すると、Webから直接構成ファイルを編集できます。 この記事は、同様の機能に関する最近の投稿に触発されましたが、その実装はデータベースに基づいています。 これはYii設定​​ファイルに対して完全に非アクティブではありません。 さらに、そのような決定のためにデータベース/キャッシュに追加のリクエストを支払う必要があり、トラフィックの多いプロジェクトにそれらを保存する必要があります。



この記事には多くのコードが含まれますが、論理的な部分に分割してみます。



アイデア



Yiiの設定ファイルは、配列を返す通常のphpスクリプトです。

例:

return array( 'name' => 'My Awesome Web Site', 'lang' => 'ru', 'sourceLang' => 'en', );
      
      







構成では、サイトの一部の静的パラメーターが時々表示されますが、これは年に一度変更されるか、まったく変更されません。 たとえば、ロゴの横に表示される管理者のメールアドレスまたは電話番号を入力します。 これらのパラメーターは、サイト管理者による編集を許可する必要がありますが、コードをクロールさせないでください。 (:



実装



実装自体は非常に単純ですが、同時に混乱を招きます。 私は物事を整理しようとします。



モデル


モデルはデータです。 そして、構成ファイルにはどのようなデータがありますか? 構成の配列です。 そして、彼のためにモデルを作成する必要があります。



 class ConfigForm extends CFormModel { /** @var array ,      */ private $_config = array(); /** *   * @param array $config    * @param string $scenario   */ public function __construct($config = array(), $scenario = '') { parent::__construct($scenario); $this->setConfig($config); } public function setConfig($config) { $this->_config = $config; } public function getConfig() { return $this->_config; } }
      
      





これまでのところとても簡単ですよね?

実際、$ _config変数のプライバシーは必要ありませんが、突然ゲームのルールを変更したい場合は不要ではありません



次に、属性名を形成するルールを確立する必要があります。 毎回新しいフィールドをモデルに追加する必要はありません(ただし、何かを追加する必要がありますが、それについては後で詳しく説明します)。 そのため、このような構成配列があるとします。

 array( 'name' => 'My Awesome Site', //   ,    -   'params' => array( 'adminEmail' => 'admin@example.com', 'phoneNumber' => '555-555-555', 'motto' => 'the best of the most awesome', ), );
      
      







この配列から、次の属性を取得する必要があります: nameparams [adminEmail]params [phoneNumber]params [motto] 。 したがって、あなたはこれを再帰的に行う必要があり、ここに私の解決策があります:



私の決断
  /** *       * * @return array */ public function getAttributes() { $this->attributesRecursive($this->_config, $output); return $output; } /** *     * * @return array */ public function attributeNames() { $this->attributesRecursive($this->_config, $output); return array_keys($output); } /** *      * * @param array $config * @param array $output * @param string $name */ public function attributesRecursive($config, &$output = array(), $name = '') { foreach ($config as $key => $attribute) { if ($name == '') $paramName = $key; else $paramName = $name . "[{$key}]"; if (is_array($attribute)) $this->attributesRecursive($attribute, $output, $paramName); else $output[$paramName] = $attribute; } }
      
      







出力で、目的の配列を取得します。これは、検証ルールを作成するのに便利です。

  public function rules() { $rules = array(); $attributes = array_keys($this->_config); $rules[] = array(implode(', ', $attributes), 'safe'); return $rules; }
      
      





ここではすべてが簡単です。 フォームparams [motto]の属性を安全と見なすためには、親属性のみを安全にすれば十分です。

実際には、params配列には何でも書き込むことができますが、configに追加のルート属性を追加しても機能しないことを理解しておく必要があります。 質問があれば、コメントでこの点を説明しようとすることができます。



$ model- > $ attributeを介してこれらの属性に直接アクセスするには、__ set()および__get()メソッドを拡張します。

  public function __get($name) { //      -  .   -      if (isset($this->_config[$name])) return $this->_config[$name]; else return parent::__get($name); } public function __set($name, $value) { //      -    if (isset($this->_config[$name])) $this->_config[$name] = $value; else parent::__set($name, $value); }
      
      





何がもっと簡単だろうか?



これで、モデルフレームワークの準備ができました。 今、彼女は自分が持っている属性を言う方法を知っており、フォームから属性を食べる。 このモデルをテストするには、通常のアクションを記述してPOSTリクエストを処理し、フォームを作成します。

  public function run() { $path = YiiBase::getPathOfAlias('application.config') . '/params.php'; $model = new ConfigForm(require($path)); if (isset($_POST['ConfigForm'])) { $model->setAttributes($_POST['ConfigForm']); if($model->save($path)) { Yii::app()->user->setFlash('success config', ' '); $this->controller->refresh(); } } $this->controller->render('config', compact('model')); }
      
      





このアクションは例であり、完全にコピーするのではなく、本質を把握するだけです



何か? CFormModelにはsave()メソッドはありませんか? 本当ですが、結果をファイルに保存するためにここに書きます。 属性の構築と同様に、ここでは再帰が必要です。



ファイルに保存
  public function save($path) { $config = $this->generateConfigFile(); //    ,       if(!is_writable($path)) throw new CException("Cannot write to config file!"); file_put_contents($path, $config, FILE_TEXT); return true; } public function generateConfigFile() { $this->generateConfigFileRecursive($this->_config, $output); $output = preg_replace('#,$\n#s', '', $output); //    return "<?php\nreturn " . $output . ";\n"; } public function generateConfigFileRecursive($attributes, &$output = "", $depth = 1) { $output .= "array(\n"; foreach ($attributes as $attribute => $value) { if (!is_array($value)) $output .= str_repeat("\t", $depth) . "'" . $this->escape($attribute) . "' => '" . $this->escape($value) . "',\n"; else { $output .= str_repeat("\t", $depth) . "'" . $this->escape($attribute) . "' => "; $this->generateConfigFileRecursive($value, $output, $depth + 1); } } $output .= str_repeat("\t", $depth - 1) . "),\n"; //  ,     } private function escape($value) { /** *   ,       (php-injection). *  ,   php  -  , *   ,       */ return str_replace("'", "\'", $value); }
      
      







良いKeepYourMindは、php関数var_export()を使用して生成できることをコメントで提案しました。これは、この自転車ジェネレーターを書く前は知りませんでした



また、ビューファイルも必要です。このファイルでは、フォーム自体が既存の属性によって生成されます。

 <?php $form = $this->beginWidget('CActiveForm', array( 'id' => 'config-form', 'enableAjaxValidation' => false, // Ajax-  Client-    , ..     'enableClientValidation' => false, )); foreach ($model->attributeNames() as $attribute) { echo CHtml::openTag('div', array('class' => 'row')); { echo $form->labelEx($model, $attribute); echo $form->textField($model, $attribute); } echo CHtml::closeTag('div'); } echo CHtml::submitButton(''); $this->endWidget();
      
      





属性に美しいキャプションを付けるには、モデルでそれらをハード定義する必要があります。

  public function attributeLabels() { return array( 'name' => ' ', 'params[adminEmail]' => 'Email ', 'params[phoneNumber]' => ' ', 'params[motto]' => ' ', ); }
      
      





これはくて失礼ですが、これを行う別の通常の方法は見つかりませんでした。 追加のファイルにそれらを置くことができますが、これは本質を変更しません-とにかく、2つのファイルを編集する必要があるオプションを追加するには。



それは基本的にそれです。 詳細なコメントなしで完全なモデルコードを提供します。



フルモデルコード
 class ConfigForm extends CFormModel { private $_config = array(); /** *   * @param array $config    * @param string $scenario   */ public function __construct($config = array(), $scenario = '') { parent::__construct($scenario); $this->setConfig($config); } public function setConfig($config) { $this->_config = $config; } public function getConfig() { return $this->_config; } public function __get($name) { if (isset($this->_config[$name])) return $this->_config[$name]; else return parent::__get($name); } public function __set($name, $value) { if (isset($this->_config[$name])) $this->_config[$name] = $value; else parent::__set($name, $value); } public function save($path) { $config = $this->generateConfigFile(); if(!is_writable($path)) throw new CException("Cannot write to config file!"); file_put_contents($path, $config, FILE_TEXT); return true; } public function generateConfigFile() { $this->generateConfigFileRecursive($this->_config, $output); $output = preg_replace('#,$\n#s', '', $output); return "<?php\nreturn " . $output . ";\n"; } public function generateConfigFileRecursive($attributes, &$output = "", $depth = 1) { $output .= "array(\n"; foreach ($attributes as $attribute => $value) { if (!is_array($value)) $output .= str_repeat("\t", $depth) . "'" . $this->escape($attribute) . "' => '" . $this->escape($value) . "',\n"; else { $output .= str_repeat("\t", $depth) . "'" . $this->escape($attribute) . "' => "; $this->generateConfigFileRecursive($value, $output, $depth + 1); } } $output .= str_repeat("\t", $depth - 1) . "),\n"; } private function escape($value) { return str_replace("'", "\'", $value); } /** *       * * @return array */ public function getAttributes() { $this->attributesRecursive($this->_config, $output); return $output; } /** *     * * @return array */ public function attributeNames() { $this->attributesRecursive($this->_config, $output); return array_keys($output); } /** *      * * @param array $config * @param array $output * @param string $name */ public function attributesRecursive($config, &$output = array(), $name = '') { foreach ($config as $key => $attribute) { if ($name == '') $paramName = $key; else $paramName = $name . "[{$key}]"; if (is_array($attribute)) $this->attributesRecursive($attribute, $output, $paramName); else $output[$paramName] = $attribute; } } public function attributeLabels() { return array( 'name' => ' ', 'params[adminEmail]' => 'Email ', 'params[phoneNumber]' => ' ', 'params[motto]' => ' ', ); } public function rules() { $rules = array(); $attributes = array_keys($this->_config); $rules[] = array(implode(', ', $attributes), 'safe'); return $rules; } }
      
      







エラーとタイプミスは、プライベートメッセージで報告してください。 事前にタイプミスをおaびします-長い間、そのようなボリュームのコード以外は何も書きませんでした



All Articles