PHPLego:日曜大工のWebサイトプラグイン





おはよう、ホームリーダーの皆さん!



モジュールをフォルダーに入れるだけで十分で、接続するためにこれ以上作業する必要がないように、サイトのモジュールを目立たないようにしたいことがありますか。 そのため、構造に関係なく、サイトの記述ブロックを新しいプロジェクトで再び使用できるようになります。



この記事では、サイト構築のハードビジネスに役立つ控えめなマイクロバイクを共有したいと思います。



あなたとの最初の知り合いは非常に興味深いことがわかりました。建設的な批判に心から感謝します。 同じ流れで続けていきたいと思います。



そこで、私自身のために、次の基準に従って問題を定式化しました。



1)各モジュールには、テンプレート、モデル、コントローラーという1つのフォルダーで作業するために必要なすべてのものが含まれている必要があります。 これにより、新しいモジュールを簡単にコピー、調整、および出来上がります。

2)モジュールは、それを作成した人について何も知る必要はありません-コンストラクタを介して動作するために必要なすべてのデータを受け取ります。 これは、モジュールが自分のサイトだけでなく、ファイルをカットすることなく友人やクライアントのすべてのサイトでも機能するようにするためです。

3)モジュールを使用するために、どこかに追加のファイルを登録または含める必要はありません。 これはばかげている。

4)モジュールはモジュールで構成できます。 つまり ネストされたモジュールのサポートが必要です。

5)モジュールテンプレート内のリンク(a href = ...)は、ネストモジュールの深さに関係なく、相対的でなければなりません。 ある親モジュールから別のモジュールにモジュールを移動した場合、テンプレートを使用しないようにするため。

6)サイト自体もモジュールである必要があります。 作業サイトを友人から購入するには、それをフォルダに入れ、余分な変更なしですべてをページに埋め込みます。



さて、ある記事では、十分に考えて、実装に取り​​かかりましょう。





プロジェクトファイル構造



まず、プロジェクトファイルの構造をスケッチします。

/nome/user/www/ |---[.myengine] //    | |---[classes] //   | |---autoload.php //    ,     | `---README.TXT //      |---[classes] //   | |---[articles] //   | | |---[m] //   | | | `---article.class.php //   ( : articles_m_article) | | |---[view] //   CSS   | | | |---[css] //    | | | | `---style.css //    | | | |---article_list.tpl //    | | | `---one_article.tpl //     | | `---controller.class.php //    ( : articles_controller) | |---[comments] //   | |---[fotos] //   | |---[site] //    ( ) | `---[users] //   |---.setup.php //   (  ,    ..) `---index.php //    
      
      







モジュールの起動



システムを純粋にするために、モジュールを使用するサイトの任意の場所に、autoload.phpという1つのファイルのみを含める必要があるように調整します。 そして、モジュールフォルダーへのパスをカスタマイズ可能にします(ある種のグローバル変数)。または、さらに良いことに、複数のパスにします。 まあ、これは、たとえば、モジュールの2つのフォルダーを作成するために必要な場合があります。



私たちの場合、フォルダ/.myengine/classesがあります-これらはエンジンのモジュールであり、すべてのプロジェクトで使用するいくつかのモジュールです。 /クラスは、プロジェクト自体のモジュールのフォルダーです。



したがって、 autoload.phpファイル自体は次のとおりです。

 <?php //    PHP,    ,    new SomeClass() function __autoload($class_name){ $class_folder = 'classes'; //      //   //       $class_folder.      $class_paths[] = dirname($_SERVER['SCRIPT_FILENAME'])."/$class_folder/"; //   $class_paths[] = __DIR__."/classes/"; //     $CLASS_PATHS if(!empty($GLOBALS["CLASS_PATHS"])){ if(!is_array($GLOBALS["CLASS_PATHS"])) throw new Exception('$CLASS_PATHS must be array!'); $class_paths = array_merge($class_paths, $GLOBALS["CLASS_PATHS"]); } //   (      A_B_C) $slashed_class_name = str_replace("_", "/", $class_name); // A/B/C $short_path = substr($slashed_class_name, 0, strrpos($slashed_class_name, '/')); // A/B foreach($class_paths as $class_path){ //   A_B_C    /A/B/C.class.php $file_full_name = "{$class_path}/{$slashed_class_name}.class.php"; if(file_exists($file_full_name)){ require_once($file_full_name); return; } //   A_B_C    /A/B/C/A_B_C.class.php //     -      (     ) $file_full_name = "{$class_path}/{$slashed_class_name}/{$class_name}.class.php"; if(file_exists($file_full_name)){ require_once($file_full_name); return; } } } ?>
      
      







スタートアップファイルに関する簡単な説明。 ここで、モジュールパスをグローバルな$ CLASS_PATHS配列に追加できるようにしました。 自動ロードは、次の順序でモデルパスを循環します。

1)最初に、呼び出されたファイルの隣のクラスフォルダーを確認します

2)見つからない場合は、エンジンモジュールを調べます

2)そこに見つからなかった場合、$ CLASS_PATHSに追加されたすべてのフォルダーを反復処理します。



$ CLASS_PATHSにあまり多くのパスを追加することはお勧めしません-結局のところ、ファイルの存在に関するファイルシステムへのすべての呼び出しは時間です。 小さいですが、それでも。



また、すべてのプロジェクトファイルの利便性と移植性のために、特定の.setup.phpというファイルを作成することをお勧めします。 プロジェクトのルートと、モジュールを使用するPHPファイルが保存されているすべてのサブフォルダーに作成します。 ルートの.setup.phpには、モジュールのスタートアップファイルが含まれます。



 <?php include __DIR__.'.myengine/autoload.php'; //    //     ,      ?>
      
      







プロジェクトのサブフォルダーにあるすべての.setup.phpファイルには、最上位の.setup.phpが含まれます。



 <?php //  .setup.php  : include __DIR__.'/../.setup.php'; ?>
      
      







モジュールを作成するすべてのファイルには常に同じ行が含まれるため、これは便利です。



 <?php include '.setup.php'; // --         //   ,   $m = new SomeModule(); $m->run(); echo $m->getOutput(); ?>
      
      







次に、ファイルをフォルダーからフォルダーに移動します(これはクリエイティブプロジェクトの必然性です)、包含のパスを修正する必要があります。 すべてがすぐに機能します。



一般に、ファイルを含めることは、プロジェクトが電気テープで接着しているようなものです。それらが多くて複雑な場合-プログラムはそれをパスタ付きのプレートに変えます.1つのファイルが別のファイルを引っ張り、3番目のファイルは不便です。 したがって、除外を取り除きました-モジュールクラスには何も含まれていません-それら自体はスタートアップに含まれています。 また、モジュールを実行するPHPファイルには常に「.setup.php」を含む1つのインクルードのみが含まれます。



注:一部のプロジェクトファイルは「。」で始まります。 (ドット)で、名前で並べ替えるときに先頭に来るようにします




モジュールコントローラークラス



モジュールクラスは、本質的には前回の記事で説明したレゴコントローラーです。 ここで、このコントローラーが置かれているフォルダーを判別し、このパスに関するテンプレート、Javaスクリプト、Cssを取得できるようにする関数をいくつか追加します。



 abstract class Lego{ .................. //     http://habrahabr.ru/company/microset/blog/109481/ abstract public function getDir(); //  ,       //     (.. ,     ) public function getWebDir(){ $viewdir = str_replace('\\', '/', $this->getViewDir()); //  ,     //, !      DOCUMENT_ROOT: return str_ireplace($_SERVER['DOCUMENT_ROOT'], '', $viewdir).'/'.$dirname; } //    -,    public function getJavascripts(){ $js = array(); $h = @opendir($this->getDir()."/js"); while($file = @readdir($h)) if(preg_match("/(\.js|\.js\.php)$/i", $file)) $js[] = $this->getWebDir()."/js/".$file; return $js; } //    ,    public function getStylesheets(){ $css = array(); $h = @opendir($this->getDir()."/view/css"); while($file = @readdir($h)) if(preg_match("/(\.css|\.css\.php)$/i", $file)) $css[] = $this->getWebDir()."/view/css/".$file; return $css; } //  ,    ,        public function getHeaderBlock(){ $csses = $this->getStylesheets(); $jses = $this->getJavascripts(); $ret = ""; foreach($csses as $one) $ret .= "\n<link rel='stylesheet' href='{$one}' type='text/css' media='screen' />\n"; foreach($jses as $one) $ret .= "\n<script type='text/javascript' src='{$one}'></script>\n"; return $ret; } //    (   ,  ) public function fetch($template){ return Smarty::fetch($this->getDefaultDir().'/view/'.$template); } }
      
      







したがって、プロジェクト全体からスタイルシート、Javaスクリプト、およびテンプレートの場所を解放しました。 モジュールはフォルダからフォルダに移動し、名前を変更することができ、システムは引き続き動作します。 ここで最も興味深い部分があります:テンプレート内でリンクを指定する方法は? 結局のところ、それらもプロジェクト全体およびモジュールの場所から解く必要があります。



テンプレート内の相対リンク



前の記事を思い出すと、各Legoオブジェクトは、その名前がモジュールの名前に一致する配列変数をアドレスバーにレンタルします。 モジュールコントローラーのメソッドを参照するには、現在のアドレスバーを取得し、現在のモジュールのみに関連するデータをその中に置き換えるだけで十分です。 アドレスバーのGETパラメーターを使用した遅延作業のために、UriConstrucorクラスを作成しましょう。

 //       class UriConstructor{ public $arr; public function __construct($arr = false){ $this->arr = $arr ? $arr : $_GET; } //      (     ) public function put($key, $val){ $this->arr = array_replace_recursive($this->arr, array($key => $val)); return $this; } //      public function remove($key){ unset($this->arr[$key]); return $this; } //         public function clear(){ $this->arr = array(); return $this; } //         public function set($lego_name, $action /*....*/){ if(isset($this->arr[$lego_name]) && !is_array($this->arr[$lego_name])) unset($this->arr[$lego_name]); $this->arr[$lego_name]['act'] = $action; $params = func_get_args(); array_shift($params); array_shift($params); foreach($params as $key=>$one){ $this->arr[$lego_name][$action][$key] = $one; } return $this; } //    ,        public function setAct($action /*....*/){ $lego = Lego::current(); $params = array($lego->getName()); $params = array_merge($params, func_get_args()); return call_user_func_array( array($this, "set"), $params ); } //    get-,   (    ) public function url($path = false){ if(!$path) $path = $_SERVER['SCRIPT_NAME']; return $path.'?'.$this; } //      -    GET- public function __toString(){ return http_build_query($this->arr); } //      GET-   public function asArray(){ return $this->arr; } }
      
      







そして、レゴの基本クラスでは、さらにいくつかのメソッドを追加します。



 abstract class Lego{ .................. //    public function uri(){ return new UriConstructor(); } //  ,   ,         href //    action- ,    ,    public function actUri($action /* params */){ $params = func_get_args(); array_unshift($params, $this->getName()); return call_user_func_array( array($this->uri(), "set"), $params ); } }
      
      







そして、テンプレートに次のようなリンクを書きます。

 <a href="{$lego->actUri('allfotos')->url()}"> </a>  ,  ,  : <a href="{$lego->actUri('showonefoto', $id)->url()}"> </a> //       : <a href="/index.php?.....__...&fotos[act]=showonefoto&fotos[0]=123">....</a>
      
      







最初に、もちろん、レゴオブジェクト自体をテンプレートエンジンに渡して、$ lego変数を使用できるようにする必要があります。 これは、レゴ基本クラスのrun()メソッドで実行できます。

 abstract class Lego{ .................. //    public function run(){ Smarty::assign("lego", $this); //   $lego -     ..... //    } }
      
      







これで、テンプレートもほどけました。 モジュールは、どのプロジェクトがそれらを作成し、どのモジュールがプロジェクト内およびプロジェクト間で簡単に移動できるようになるかを知ることをやめました。



もちろん、モジュールは何らかの形でプロジェクトに接続する必要があります。そうしないと、モジュールは完全に無意味になります。 したがって、データはコンストラクターを介して受信する必要があります。



典型的なモジュールのコードは次のとおりです。



 /*  ,   ,    $entity_id  $entity_name */ class fotos_controller extends Lego{ private $entity_id; private $entity_name; private $num_for_page = 5; //     ( ),     function __construct($name = false, $entity_id = 0, $entity_name = "User"){ parent::__construct($name); $this->entity_id = $entity_id; $this->entity_name = $entity_name; } //   ,       Lego. public function getDir(){ return __DIR__; } //   -    //   ,    function action_index(){ Database::query("select * from `fotos` where `entity_name`='{$this->entity_name}' and `entity_id`={$this->entity_id} and deleted = 0 order by created desc"); $fotos = Database::fetchObjects(); Output::assign("fotos", $fotos); return $this->fetch("allfotos.tpl"); } //        function action_mainbar(){ $offset = $this->_get($this->getName()."_offset", 0); Database::query("select * from `fotos` where `entity_name`='{$this->entity_name}' and `entity_id`={$this->entity_id} and deleted = 0 order by created desc limit {$offset}, ".($this->num_for_page+1)); $fotos = Database::fetchObjects(); Output::assign("fotos", $fotos); Output::assign("offset", $offset); Output::assign("num_for_page", $this->num_for_page); return $this->fetch("lego_fotos.tpl"); } //             function action_sidebar(){ return $this->action_mainbar(); } //     (   POST) function action_submit(){ $f = new tbl_fotos(); $f['entity_name'] = $this->entity_name; $f['entity_id'] = $this->entity_id; $f['user_id'] = User::getCurrentUser()->getId(); $f['text'] = $this->_post($this->getName()."_text"); $f['file_id'] = FotoStorage::putFromPost($this->getName()."_file"); if($f['file_id']) $f->insert(); $this->_goto($this->actUri("mainbar")->url()); //_goto -   header("Location: ... } //       function action_showone($foto_id){ $f = new tbl_fotos($foto_id); Output::assign("foto", $f); $ret = $this->fetch("showone.tpl"); // .    $c = new comments_controller("foto_comments", "tbl_fotos", $f->getId()); $c->run(); return $ret.$c->getOutput(); //    } //  "  " function action_set_as_main($foto_id){ Auth::authorize(); $f = new tbl_fotos($foto_id); $user = $f->getOwner(); $user['foto'] = $f['file_id']; $user->update(); $this->_goto($this->actUri("showone", $foto_id)->url()); } //  " " function action_delete($foto_id){ Auth::authorize(); $f = new tbl_fotos($foto_id); //FileStorage::delete($f['file_id']); if($f->isMain()){ $user = User::getCurrentUser(); $user['foto'] = ""; $user->update(); } $f['deleted'] = 1; $f->update(); $this->_goto($this->actUri("showone", $foto_id)->url()); } //  "  " function action_restore($foto_id){ Auth::authorize(); $f = new tbl_fotos($foto_id); $f['deleted'] = 0; $f->update(); $this->_goto($this->actUri("showone", $foto_id)->url()); } }
      
      







サイトのルートモジュールを含む各モジュールは、次のコード行によって実行されます。

たとえば、これはプロジェクトのルートにあるindex.phpです。

 <?php include ".setup.php"; //    $lego = new site_controller(); //  -  $lego->run(); //   echo $lego->getOutput();// !      ?>
      
      







それだけです。

Lego-modulesに生命を与えるAJAXを追加する方法については、 こちらをご覧ください

ここで 、AJAX これがどのように機能するかを実際にテストできます



この記事が誰かに役立つことを願っています。

すべての成功したレゴプログラミング。 :)

ご清聴ありがとうございました! いつもあなた、ジョジク。



All Articles