教程 2: è§£è¯»åˆ†æž INVO 项目 =========================== 在第二个教程ä¸ï¼Œæˆ‘们将解读分æžä¸€ä¸ªæ›´å®Œæ•´çš„应用程åºï¼Œä»¥å¼ºåŒ–ä½ å¯¹Phalconçš„ç†è§£ï¼ŒINVO是我们已ç»åˆ›å»ºäº†çš„作为示例程åºçš„应用程åºä¹‹ä¸€ã€‚ä½ å¯ä»¥ä»Ž Github_ 获得INVO的全部代ç 。 æ¤å¤–还需è¦è¯´æ˜Žçš„æ˜¯ï¼ŒINVOçš„html实现是使用 `Twitter Bootstrap <http://twitter.github.com/>`_ CSS frameworkæ¥å®Œæˆçš„,在这个示例项目ä¸ï¼Œå¹¶ä¸çœŸæ£çš„生æˆå‘票(这是一个类似于进销å˜çš„相关的应用),但它作为一个例å还是å¯ä»¥å‘Šè¯‰ä½ 整个框架是如何工作的。 项目目录结构 ------------------ 从Github上克隆了æºä»£ç åŽï¼Œä½ å¯ä»¥å‘çŽ°ç›®å½•ç»“æž„æ˜¯è¿™æ ·çš„ï¼š .. code-block:: bash invo/ app/ app/config/ app/controllers/ app/library/ app/models/ app/plugins/ app/views/ public/ public/bootstrap/ public/css/ public/js/ schemas/ 在å‰é¢çš„ç« èŠ‚å·²ç»è®²è¿‡ï¼ŒPhalcon并没有固定的目录结构,该项目æä¾›äº†ä¸€ä¸ªç®€å•çš„MVC目录结构。 通过æµè§ˆå™¨æ‰“å¼€åº”ç”¨ç¨‹åº http://localhost/invo 显示效果如下: .. figure:: ../_static/img/invo-1.png :align: center INVO应用程åºåˆ†ä¸ºä¸¤éƒ¨åˆ†ï¼Œå³é€šå¸¸æˆ‘们说的å‰å°åŽå°ã€‚å‰å°éƒ¨åˆ†ï¼Œç”¨æˆ·å¯ä»¥é€šè¿‡INVO查看一些信æ¯ï¼ŒåŒæ—¶å¯ä»¥æäº¤è”系方å¼ã€‚åŽå°éƒ¨åˆ†ï¼Œç›¸å½“于管ç†åŒºåŸŸï¼Œåœ¨è¿™é‡Œé¢æ³¨å†Œç”¨æˆ·å¯ä»¥ç®¡ç†è‡ªå·±çš„产å“和客户。 æ ‡å‡†è·¯ç”±å™¨ --------------- INVOä½¿ç”¨æ ‡å‡†çš„å†…å¥¸è·¯ç”±å™¨ç»„ä»¶ï¼Œæ¤è·¯ç”±çš„åŒ¹é…æ¨¡å¼å¦‚下 /:controller/:action/:params ,这æ„味ç€ï¼ŒURLä¸çš„第一部分是控制器,第二个是action方法。 路由 /session/register å°†è¦æ‰§è¡ŒSessionControllerä¸çš„RegisterAction方法 Configuration ------------- INVO有一个é…置文件,用于设置一些常用的数æ®ï¼Œæ¯”如数æ®åº“è¿žæŽ¥å‚æ•°ï¼Œç›®å½•结构ç‰ã€‚在引导文件 (public/index.php) 的第一部分,å¯ä»¥è¿™æ ·è¯»å–é…置文件 .. code-block:: php <?php //Read the configuration $config = new Phalcon\Config\Adapter\Ini(__DIR__.'/../app/config/config.ini'); :doc:`Phalcon\\Config <config>` 使得读å–é…置内容是é¢åƒå¯¹è±¡çš„,é…置文件的定义如下: .. code-block:: ini [database] host = localhost username = root password = secret name = invo [application] controllersDir = /../app/controllers/ modelsDir = /../app/models/ viewsDir = /../app/views/ pluginsDir = /../app/plugins/ libraryDir = /../app/library/ baseUri = /invo/ ;[metadata] ;adapter = "Apc" ;suffix = my-suffix ;lifetime = 3600 Phalconçš„é…置文件å¯ä»¥åˆ†ç±»è¿›è¡Œå®šä¹‰ï¼Œåœ¨è¿™ä¸ªæ–‡ä»¶ä¸ï¼Œå…±å®šä¹‰äº†ä¸‰ä¸ªéƒ¨åˆ† database,application,metadata Autoloaders ----------- 在引导文件 (public/index.php) 的第二部分是autoloader,autoloaderæ³¨å†Œäº†ä¸€äº›ç›®å½•ï¼Œåœ¨è¿™äº›ç›®å½•ä¸æ”¾ç½®çš„æ˜¯æˆ‘们应用程åºéœ€è¦ç”¨åˆ°çš„类文件 .. code-block:: php <?php $loader = new \Phalcon\Loader(); $loader->registerDirs( array( __DIR__.$config->application->controllersDir, __DIR__.$config->application->pluginsDir, __DIR__.$config->application->libraryDir, __DIR__.$config->application->modelsDir, ) )->register(); éœ€è¦æ³¨æ„的是,注册的这些目录并ä¸åŒ…括 viewsDir,å› ä¸ºviewsDirä¸å¹¶ä¸åŒ…å«classes文件,而是html+php文件 处ç†è¯·æ±‚ -------------------- 在引导文件的最åŽéƒ¨åˆ†ï¼Œæˆ‘们使用 Phalcon\\Mvc\\Application ,这个类åˆå§‹åŒ–并执行用户的请求 .. code-block:: php <?php $application = new \Phalcon\Mvc\Application(); $application->setDI($di); echo $application->handle()->getContent(); ä¾èµ–注入 -------------------- 看上é¢ä»£ç ä¸çš„第二段,å˜é‡$application通过setDI()方法接收了å˜é‡$di,该å˜é‡çš„目的是什么呢? Phalcon是一个æ¾è€¦åˆçš„æ¡†æž¶ï¼Œæ‰€ä»¥æˆ‘们需è¦ä¸€ä¸ªç»„件,把它们整åˆåˆ°ä¸€èµ·ï¼Œè®©å®ƒä»¬ä¸€èµ·å·¥ä½œï¼Œè¯¥ç»„件便是 Phalcon\\DI 注册到容器的方法有很多,在INVOä¸ï¼Œå¤§éƒ½é‡‡ç”¨åŒ¿å函数的方å¼è¿›è¡Œæ³¨å†Œï¼Œå› 为æ¤ç§æ–¹å¼æ˜¯lazy loadçš„åŠ è½½æ–¹å¼ï¼Œå‡å°‘了应用程åºè¯·æ±‚èµ„æºæŽ§åˆ¶ã€‚ 例如,在下é¢çš„代ç 片æ–ä¸çš„sessionä¼šè¯æœåŠ¡ï¼Œé‡‡ç”¨çš„æ˜¯åŒ¿å函数的方å¼è¿›è¡Œæ³¨å†Œçš„ï¼Œå› æ¤å½“使用session的时候,æ‰ä¼šè¢«åŠ è½½ã€‚ .. code-block:: php <?php //Start the session the first time when some component request the session service $di->set('session', function(){ $session = new Phalcon\Session\Adapter\Files(); $session->start(); return $session; }); 在这里,我们å¯ä»¥è‡ªç”±çš„æ›´æ”¹é€‚é…器,以使它执行更多的åˆå§‹åŒ–任务,请注æ„,æœåŠ¡æ³¨å†Œçš„"session"请ä¸è¦éšæ„修改,这是一个命å约定。 译者注:更多的æœåŠ¡ç»„ä»¶å‘½å约定å¯è§ :doc:`dependency injection container <di>` 一个请求å¯èƒ½ä½¿ç”¨å¤šä¸ªæœåŠ¡ç»„ä»¶ï¼Œä¸€ä¸ªä¸€ä¸ªçš„æ³¨å†Œè¿™äº›ç»„ä»¶æ˜¯ä¸€é¡¹ç¹é‡çš„ä»»åŠ¡ï¼Œå‡ºäºŽè¿™ä¸ªåŽŸå› ï¼Œè¯¥æ¡†æž¶æä¾›äº† Phalcon\\DI 的一个实现,就是 Phalcon\\DI\\FactoryDefault 译者注:其实 Phalcon\\DI\\FactoryDefault 就是 Phalcon\\DI 的一个åç±» .. code-block:: php <?php // The FactoryDefault Dependency Injector automatically registers the // right services providing a full stack framework $di = new \Phalcon\DI\FactoryDefault(); It registers the majority of services with components provided by the framework as standard. If we need to override the definition of some it could be done as above with "session". Now we know the origin of the variable $di. 大多数的æœåŠ¡ç»„ä»¶éƒ½ç”±æ¡†æž¶æœ¬èº«æä¾›ï¼Œå¦‚果我们需è¦è¦†ç›–一些定义的è¯ï¼Œæ¯”如"session".(翻译的å¯èƒ½ä¸å¯¹ï¼Œè‹±æ–‡éƒ¨åˆ†å°±ä¸åŽ»æŽ‰äº†) Log into the Application ------------------------ 登录将使用åŽç«¯æŽ§åˆ¶å™¨ï¼ŒæŽ§åˆ¶å™¨å‰åŽç«¯åˆ†ç¦»æ˜¯åˆä¹Žé€»è¾‘的,所有的控制器被放置到相åŒçš„目录ä¸ã€‚è¦ç™»å½•系统,我们必须有一个有效的用户å和密ç ,用户信æ¯è¢«å˜å‚¨åœ¨æ•°æ®åº“"invo"çš„"users"æ•°æ®è¡¨ä¸ã€‚ 在我们登录系统之å‰ï¼Œæˆ‘们需è¦åœ¨åº”用程åºä¸é…置数æ®åº“连接。一个命å为"db"çš„æœåŠ¡ç»„ä»¶è¢«æ³¨å†Œï¼Œä¸Žautoloader相åŒï¼Œæˆ‘们也从é…置文件ä¸è¯»å–相关é…ç½®è¿žæŽ¥å‚æ•° .. code-block:: php <?php // Database connection is created based in the parameters defined in the configuration file $di->set('db', function() use ($config) { return new \Phalcon\Db\Adapter\Pdo\Mysql(array( "host" => $config->database->host, "username" => $config->database->username, "password" => $config->database->password, "dbname" => $config->database->name )); }); 这时,会返回一个MySQL的连接适é…器的实例,如果需è¦çš„è¯ï¼Œä½ å¯ä»¥åšä¸€äº›å…¶ä»–é¢å¤–çš„æ“ä½œï¼Œä¾‹å¦‚ï¼Œä½ è¿˜å¯ä»¥å®šä¹‰ä¸€ä¸ªè®°å½•器,分æžå™¨æˆ–更改为其他适é…å™¨ã€‚æˆ–è€…è®¾ç½®ä½ æƒ³è¦çš„其他东西 那么,下é¢çš„这个表å•示例 (app/views/session/index.phtml) 是一个登录入å£ï¼Œå·²ç»åˆ 除了一些HTML代ç ï¼Œä½¿è¿™ä¸ªä¾‹åæ›´ç®€æ´ï¼š .. code-block:: html+php <?php echo Tag::form('session/start') ?> <label for="email">Username/Email</label> <?php echo Tag::textField(array("email", "size" => "30")) ?> <label for="password">Password</label> <?php echo Tag::passwordField(array("password", "size" => "30")) ?> <?php echo Tag::submitButton(array('Login')) ?> </form> SessionController::startAction (app/controllers/SessionController.phtml) 验è¯ç”¨æˆ·ç™»å½•,通过查询数æ®åº“的用户的登录åç§°å’Œå¯†ç æ˜¯å¦æ£ç¡® .. code-block:: php <?php class SessionController extends ControllerBase { // ... private function _registerSession($user) { $this->session->set('auth', array( 'id' => $user->id, 'name' => $user->name )); } public function startAction() { if ($this->request->isPost()) { //Taking the variables sent by POST $email = $this->request->getPost('email', 'email'); $password = $this->request->getPost('password'); $password = sha1($password); //Find for the user in the database $user = Users::findFirst("email='$email' AND password='$password' AND active='Y'"); if ($user != false) { $this->_registerSession($user); $this->flash->success('Welcome '.$user->name); //Forward to the invoices controller if the user is valid return $this->dispatcher->forward(array( 'controller' => 'invoices', 'action' => 'index' )); } $this->flash->error('Wrong email/password'); } //Forward to the login form again return $this->dispatcher->forward(array( 'controller' => 'session', 'action' => 'index' )); } } éœ€è¦æ³¨æ„çš„æ˜¯æŽ§åˆ¶å™¨ä¸æœ‰å¤šä¸ªå…¬å…±å±žæ€§ï¼Œå¦‚$this->flash,$this->request,$this->session。这些属性在引导文件ä¸ä½¿ç”¨ Phalcon\\DI æ³¨å†Œçš„ï¼Œå¦‚æžœä½ ä»”ç»†çœ‹è¿‡å‰é¢çš„ç« èŠ‚ï¼Œåº”è¯¥èƒ½æƒ³åˆ°ã€‚å› æ¤å¯ä»¥åœ¨æŽ§åˆ¶å™¨ä¸ç›´æŽ¥ä½¿ç”¨ä»–们 这些æœåŠ¡æ˜¯å…±äº«çš„ï¼Œè¿™æ„å‘³ç€æˆ‘们访问的是相åŒçš„å®žä¾‹ï¼Œæ— è®ºæˆ‘ä»¬åœ¨ä»»ä½•åœ°æ–¹è°ƒç”¨å®ƒä»¬ã€‚ 举个例å,在这里我们å¯ä»¥ç›´æŽ¥è°ƒç”¨ "session", åŒæ—¶æŠŠç”¨æˆ·çš„ä¿¡æ¯å˜å‚¨åˆ°å˜é‡authä¸ .. code-block:: php <?php $this->session->set('auth', array( 'id' => $user->id, 'name' => $user->name )); Securing the Backend -------------------- åŽç«¯æ˜¯ä¸€ä¸ªç§æœ‰åŒºåŸŸï¼Œåªæœ‰æ³¨å†Œçš„用户æ‰å¯ä»¥è®¿é—®ã€‚å› æ¤ï¼Œå®ƒå¿…须进行检查验è¯ï¼Œåªæœ‰æ³¨å†Œç”¨æˆ·æ‰å¯ä»¥è®¿é—®è¿™äº›æŽ§åˆ¶å™¨ã€‚å¦‚æžœä½ æ²¡æœ‰ç™»å½•åº”ç”¨ç¨‹åºï¼Œä½ å°è¯•è®¿é—®çš„æ—¶å€™ï¼Œä½ ä¼šçœ‹åˆ°è¿™æ ·çš„ç•Œé¢ï¼š .. figure:: ../_static/img/invo-2.png :align: center æ¯å½“有人试图访问任何控制器和动作,应用程åºå°±ä¼šéªŒè¯å½“å‰ç”¨æˆ·çš„角色是å¦èƒ½å¤Ÿè®¿é—®ï¼Œå¦åˆ™ä¼šæ˜¾ç¤ºä¸€ä¸ªä¿¡æ¯ï¼ŒåŒæ—¶è·³è½¬åˆ°é¦–页é¢ã€‚ 现在,我们æ¥çœ‹çœ‹åº”用程åºå¦‚何实现这一点。首先è¦çŸ¥é“的是,有一个组件å«åˆ†å‘器(Dispatcher)ï¼Œä½ è¿˜éœ€è¦äº†è§£ä¸€ä¸ªè·¯ç”±ã€‚在æ¤åŸºç¡€ä¸Šï¼Œè´Ÿè½½åŠ è½½ç›¸åº”çš„æŽ§åˆ¶å™¨å’Œæ‰§è¡Œç›¸åº”çš„åŠ¨ä½œã€‚ 通常情况下,框架会自动创建分å‘器,在这个例åä¸ï¼Œæˆ‘们è¦ä¸“门创建一个动作,显示出用户æˆåŠŸè®¿é—®å’Œä¸æˆåŠŸè®¿é—®çš„æƒ…å†µã€‚ä¸ºäº†å®žçŽ°è¿™ä¸€ç›®æ ‡ï¼Œæˆ‘ä»¬æ›´åœ¨å¼•å¯¼æ–‡ä»¶(bootstrap)ä¸åˆ›å»ºä¸€ä¸ªå‡½æ•°ï¼š .. code-block:: php <?php $di->set('dispatcher', function() use ($di) { $dispatcher = new Phalcon\Mvc\Dispatcher(); return $dispatcher; }); 现在,我们的应用程åºä¸å°±æœ‰äº†æŽ§åˆ¶åˆ†å‘器,现实ä¸ï¼Œæˆ‘们需è¦ä¿®æ”¹æ¡†æž¶ä¸æœ‰è®¸å¤šç»„件的内部æµç¨‹ï¼Œè¿™æ—¶ä¸€ä¸ªæ–°çš„组件EventsManager出æ¥äº†ï¼Œå®ƒå¯ä»¥æä¾›åœ¨ç»„ä»¶ä¸åŠ å…¥ä¸€äº›å…¶ä»–å¯¹åƒã€‚ 译者注:如在分å‘器ä¸åŠ å…¥éªŒè¯ï¼Œåœ¨æ•°æ®åº“连接ä¸åŠ å…¥è®°å½•å™¨ç‰ äº‹ä»¶ç®¡ç† ^^^^^^^^^^^^^^^^^ 一个事件管ç†å™¨ï¼Œå¯ä»¥è®©æˆ‘们针å¬ä¸€ä¸ªç‰¹å®šç±»åž‹çš„事件,下é¢çœ‹ä¸€ä¸‹åœ¨åˆ†å‘器ä¸åŠ å…¥å®‰å…¨éªŒè¯çš„例å: .. code-block:: php <?php $di->set('dispatcher', function() use ($di) { //Obtain the standard eventsManager from the DI $eventsManager = $di->getShared('eventsManager'); //Instantiate the Security plugin $security = new Security($di); //Listen for events produced in the dispatcher using the Security plugin $eventsManager->attach('dispatch', $security); $dispatcher = new Phalcon\Mvc\Dispatcher(); //Bind the EventsManager to the Dispatcher $dispatcher->setEventsManager($eventsManager); return $dispatcher; }); 安全æ’件是一个类文件(app/plugins/Security.php),这个类实现了"beforeExecuteRoute"方法. 译者注:都å¯ä»¥å®žçŽ°å“ªäº›æ–¹æ³•ï¼Œå¯ä»¥æŸ¥çœ‹ :doc:`分å‘器 <dispatching>` Dispatch Loop Events 部分 .. code-block:: php <?php use \Phalcon\Events\Event; use \Phalcon\Mvc\Dispatcher; class Security extends Phalcon\Mvc\User\Plugin { // ... public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher) { // ... } } æ’ä»¶ç¨‹åºæŽ¥æ”¶ä¸¤ä¸ªå‚æ•°ï¼Œç¬¬ä¸€ä¸ªå‚数是event上下文信æ¯ï¼Œç¬¬äºŒä¸ªæ˜¯äº‹ä»¶ç®¡ç†å™¨è¦ç®¡ç†çš„对象,æ’件程åºå¹¶ä¸ä¸€å®šéžå¾—继承自 :doc:`Phalcon\\Mvc\\User\\Plugin <../api/Phalcon_Mvc_User_Plugin>` ,ä½†å¦‚æžœè¿™æ ·ç»§æ‰¿äº†ï¼Œä»–ä»¬æ›´å®¹æ˜“çš„è®¿é—®åº”ç”¨ç¨‹åºçš„å…¶ä»–æœåŠ¡ç»„ä»¶ã€‚ 译者注:目å‰çš„ Phalcon\\Mvc\\User\\Plugin ä»¥åŠ Phalcon\\Mvc\\User\\Component æ˜¯ä¸€æ ·çš„ï¼Œå…¶å®žä¸¤è€…çš„ä¾§é‡ç‚¹åº”该是ä¸åŒçš„ï¼Œåªæ˜¯ä½œè€…还未完善而已。具体请看stackoverflow的贴å http://stackoverflow.com/questions/12879284/whats-different-between-phalcon-mvc-user-component-and-phalcon-mvc-user-plugin 现在,我们验è¯ç™»å½•用户的æƒé™ï¼Œçœ‹ä»–çš„æƒé™æ˜¯å¦åœ¨ACL列表ä¸ï¼Œå¦‚果没有(也就是说没有æƒé™çš„è¯),分å‘器将使æµç¨‹è·³è½¬åˆ°ä¸»é¡µï¼š .. code-block:: php <?php use \Phalcon\Events\Event; use \Phalcon\Mvc\Dispatcher; class Security extends Phalcon\Mvc\User\Plugin { // ... public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher) { //Check whether the "auth" variable exists in session to define the active role $auth = $this->session->get('auth'); if (!$auth) { $role = 'Guests'; } else { $role = 'Users'; } //Take the active controller/action from the dispatcher $controller = $dispatcher->getControllerName(); $action = $dispatcher->getActionName(); //Obtain the ACL list $acl = $this->_getAcl(); //Check if the Role have access to the controller (resource) $allowed = $acl->isAllowed($role, $controller, $action); if ($allowed != Phalcon\Acl::ALLOW) { //If he doesn't have access forward him to the index controller $this->flash->error("You don't have access to this module"); $dispatcher->forward( array( 'controller' => 'index', 'action' => 'index' ) ); //Returning "false" we tell to the dispatcher to stop the current operation return false; } } } Providing an ACL list ^^^^^^^^^^^^^^^^^^^^^ æƒé™ç®¡ç†éƒ¨åˆ†ï¼Œæˆ‘一般ä¸å¤ªå–œæ¬¢ä½¿ç”¨è¿™ç§æ–¹å¼çš„æƒé™éªŒè¯ï¼Œä¸è¿‡å¤§å¤šæ•°æ¡†æž¶éƒ½æä¾›äº†è¿™ç§éªŒè¯ï¼ŒåŒ…括ZF。 In the previous example we obtain the ACL using the method $this->_getAcl(). This method is also implemented in the Plugin. Now explain step by step how we built the access control list: .. code-block:: php <?php //Create the ACL $acl = new Phalcon\Acl\Adapter\Memory(); //The default action is DENY access $acl->setDefaultAction(Phalcon\Acl::DENY); //Register two roles, Users is registered users //and guests are users without a defined identity $roles = array( 'users' => new Phalcon\Acl\Role('Users'), 'guests' => new Phalcon\Acl\Role('Guests') ); foreach($roles as $role){ $acl->addRole($role); } Now we define the respective resources of each area. Controller names are resources and their actions are the accesses in the resources: .. code-block:: php <?php //Private area resources (backend) $privateResources = array( 'companies' => array('index', 'search', 'new', 'edit', 'save', 'create', 'delete'), 'products' => array('index', 'search', 'new', 'edit', 'save', 'create', 'delete'), 'producttypes' => array('index', 'search', 'new', 'edit', 'save', 'create', 'delete'), 'invoices' => array('index', 'profile') ); foreach($privateResources as $resource => $actions){ $acl->addResource(new Phalcon\Acl\Resource($resource), $actions); } //Public area resources (frontend) $publicResources = array( 'index' => array('index'), 'about' => array('index'), 'session' => array('index', 'register', 'start', 'end'), 'contact' => array('index', 'send') ); foreach($publicResources as $resource => $actions){ $acl->addResource(new Phalcon\Acl\Resource($resource), $actions); } The ACL now have knowledge of the existing controllers and their related actions. The role "Users" has access to all the resources of both the frontend and the backend. The role "Guests" only have access to the public area: .. code-block:: php <?php //Grant access to public areas to both users and guests foreach ($roles as $role) { foreach ($publicResources as $resource => $actions) { $acl->allow($role->getName(), $resource, '*'); } } //Grant access to private area only to role Users foreach ($privateResources as $resource => $actions) { foreach ($actions as $action) { $acl->allow('Users', $resource, $action); } } Hooray!, the ACL is now complete. 用户自定义组件 --------------- 本应用所有的UIç»„ä»¶å’Œæ˜¾ç¤ºé£Žæ ¼éƒ½æ˜¯ä½¿ç”¨çš„Twitterçš„CSS Framework。 这部分被实现使用æˆComponent (api/library/Elements.php)。 译者注:在上é¢è®²Plugins的时候,专门介ç»äº†Component,没注æ„çš„å¯ä»¥å¾€ä¸Šçœ‹ä¸€ä¸‹ã€‚ .. code-block:: php <?php class Elements extends Phalcon\Mvc\User\Component { public function getMenu() { //... } public function getTabs() { //... } } 这个类继承自 Phalcon\\Mvc\\User\\Component,虽然框架本身ä¸å¼ºåˆ¶è¦æ±‚ç»§æ‰¿ï¼Œä½†å¦‚æžœä½ ç»§æ‰¿äº†å®ƒï¼Œå°†æ›´æ–¹ä¾¿çš„è®¿é—®åº”ç”¨ç¨‹åºä¸çš„其他组件。现在,我们把它注入到容器ä¸ï¼š .. code-block:: php <?php //Register an user component $di->set('elements', function(){ return new Elements(); }); 在控制器ä¸ä»¥åŠè§†å›¾ä¸ï¼Œæ’件以åŠç»„ä»¶å¯ä»¥é€šè¿‡æ³¨å†Œçš„å称很方便的被调用 .. code-block:: html+php <div class="navbar navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </a> <a class="brand" href="#">INVO</a> <?php echo $this->elements->getMenu() ?> </div> </div> </div> <div class="container"> <?php echo $this->getContent() ?> <hr> <footer> <p>© Company 2012</p> </footer> </div> é‡ç‚¹çœ‹è¿™å¥ï¼š .. code-block:: html+php <?php echo $this->elements->getMenu() ?> å¢žåˆ æŸ¥æ”¹ --------------------- 大多数èœå•选项数æ®(如公å¸ï¼Œäº§å“,产å“类型ç‰)ï¼Œæˆ‘ä»¬å¼€å‘æŒ‰ç…§æ™®éçš„ CRUD_ (Create, Read, Update and Delete)æ–¹å¼ï¼Œæ¯ä¸ªCURD包å«ä»¥ä¸‹æ–‡ä»¶ï¼š .. code-block:: bash invo/ app/ app/controllers/ ProductsController.php app/models/ Products.php app/views/ products/ edit.phtml index.phtml new.phtml search.phtml æ¯ä¸ªæŽ§åˆ¶å™¨åŒ…å«ä»¥ä¸‹ä¸€äº›åŠ¨ä½œ(控制器类ä¸çš„æ–¹æ³•): 译者注:这些动作åç§°å¹¶ä¸æ˜¯çº¦å®šçš„,å¯ä»¥æŒ‰ä½ 的喜好自由修改,比如searchAction,ä½ å¯ä»¥å†™æˆsoAction都没问题。但请求的时候就ä¸å†è¯·æ±‚到products/search了,而是需è¦è¯·æ±‚到products/so .. code-block:: php <?php class ProductsController extends ControllerBase { /** * The start action, it shows the "search" view */ public function indexAction() { //... } /** * Execute the "search" based on the criteria sent from the "index" * Returning a paginator for the results */ public function searchAction() { //... } /** * Shows the view to create a "new" product */ public function newAction() { //... } /** * Shows the view to "edit" an existing product */ public function editAction() { //... } /** * Creates a product based on the data entered in the "new" action */ public function createAction() { //... } /** * Updates a product based on the data entered in the "edit" action */ public function saveAction() { //... } /** * Deletes an existing product */ public function deleteAction($id) { //... } } æ£€ç´¢è¡¨å• ^^^^^^^^^^^^^^^ æ£€ç´¢è¡¨å•æ˜¾ç¤ºäº†æ•°æ®è¡¨(products)ä¸çš„æ‰€æœ‰å¯æŸ¥è¯¢çš„å—æ®µï¼Œå…è®¸ç”¨æˆ·æ ¹æ®è‡ªå®šä¹‰æ£€ç´¢å†…容。 æ•°æ®è¡¨"products",关è”了数æ®è¡¨"products_types"ï¼Œåœ¨è¿™ç§æƒ…况下,我们在检索页é¢è¿™æ ·å†™ï¼š .. code-block:: php <?php /** * The start action, it shows the "search" view */ public function indexAction() { $this->persistent->searchParams = null; $this->view->setVar("productTypes", ProductTypes::find()); } 所有"product types"将通过å˜é‡"productTypes"显示到视图文件ä¸ï¼Œè§†å›¾æ–‡ä»¶(app/views/index.phtml)的代ç 如下: .. code-block:: php <?php <div> <label for="product_types_id">Product Type</label> <?php echo Tag::select(array( "product_types_id", $productTypes, "using" => array("id", "name"), "useDummy" => true )) ?> </div> å˜é‡$productTypes包å«çš„æ•°æ®é€šè¿‡ :doc:`Phalcon\\Tag::select <../api/Phalcon_Tag>` 填充到视图进行显示。一旦æäº¤æ£€ç´¢è¡¨å•,它会请求到 products/searchï¼Œå¹¶æ ¹æ®ç”¨æˆ·æäº¤çš„æ•°æ®è¿›è¡Œæ•°æ®æ£€ç´¢ 执行一个检索 ^^^^^^^^^^^^^^^^^^^ "search",å³products/search 这个动作具有åŒé‡è¡Œä¸ºï¼Œå½“通过POSTè®¿é—®æ—¶ï¼Œå®ƒä¼šæ ¹æ®ç”¨æˆ·æäº¤çš„æ•°æ®è¿›è¡Œæ¡ä»¶æ£€ç´¢ã€‚但是,当我们通过GET访问时,将显示所有产å“的列表。这些都是通过HTTP方法æ¥è¿›è¡ŒåŒºåˆ†çš„。详情请查看 :doc:`Request <request>` component: .. code-block:: php <?php /** * Execute the "search" based on the criteria sent from the "index" * Returning a paginator for the results */ public function searchAction() { if ($this->request->isPost()) { //create the query conditions } else { //paginate using the existing conditions } //... } 使用 :doc:`Phalcon\\Mvc\\Model\\Criteria <../api/Phalcon_Mvc_Model_Criteria>` ,我们å¯ä»¥å¾ˆæ–¹ä¾¿çš„æŠŠè¡¨å•æäº¤çš„æ•°æ®(值)和数æ®ç±»åž‹(å±žæ€§æˆ–å—æ®µ)绑定到一起 .. code-block:: php <?php $query = Criteria::fromInput($this->di, "Products", $_POST); è¯¥æ–¹æ³•çš„ç»‘å®šè¿‡ç¨‹æ˜¯è¿™æ ·çš„ï¼Œé¦–å…ˆéªŒè¯å®¢æˆ·ç«¯æäº¤çš„è¡¨å•æ•°æ®æ˜¯å¦ä¸ºç©º""(空å—符串)ï¼Œå¦‚æžœä¸æ˜¯ï¼Œå°†ç»‘定到数æ®å—段上。如果æäº¤çš„è¡¨å•æ•°æ®æ˜¯å—符串类型的(CHAR, VARCHAR, TEXTç‰),将使用 "like '%%'"è¿™æ ·çš„å½¢å¼æ¥è¿›è¡Œæ£€ç´¢æ•°æ®ã€‚å¦‚æžœä¸æ˜¯æˆ–ä¸ç±»ä¼¼äºŽå—符串,它会直接使用æ“作符"="进行检索。 æ¤å¤–,如果æäº¤çš„æ•°æ®ä¸ä¸åŒ…括在数æ®è¡¨å—段(也å¯ä»¥è¯´æˆæ˜¯modelå—æ®µï¼‰ä¸ï¼Œè¿™äº›æ•°æ®å°†è¢«å¿½ç•¥ã€‚æ¤å¤–,æäº¤çš„æ•°æ®ä¼šè‡ªåŠ¨ä½¿ç”¨bound parameter的方å¼è¿›è¡Œç»‘定。 我们把æäº¤çš„绑定数æ®å˜å‚¨åˆ°sessionä¸ï¼Œæ¤å¤„使用的是 :doc:`Session Bag <../api/Phalcon_Session_Bag>` .. code-block:: php <?php $this->persistent->searchParams = $query->getParams(); Session Bag是一个特殊的属性,它å˜åœ¨äºŽæŽ§åˆ¶å™¨ä¸ã€‚这个属性注入的其实是 :doc:`Phalcon\\Session\\Bag <../api/Phalcon_Session_Bag>` 组件。 è¯‘è€…æ³¨ï¼šç»æµ‹è¯•,使用 $this->persistent->xxx,åªèƒ½åœ¨åŒä¸€æŽ§åˆ¶å™¨ä¸çš„ä¸åŒActionä¸è¿›è¡Œè®¿é—®ï¼Œä¸èƒ½åœ¨å…¶ä»–控制器ä¸è®¿é—®åˆ°æ•°æ®ã€‚如果需è¦åœ¨ä¸åŒçš„æŽ§åˆ¶å™¨è®¿é—®åˆ°å˜é‡xxx的数æ®ï¼Œå¯ä»¥ä½¿ç”¨session å°è£…绑定好数æ®åŽï¼Œæˆ‘ä»¬é€šè¿‡è¿™ä¸ªå‚æ•°æ¥è¿›è¡Œæ•°æ®æ£€ç´¢ï¼š .. code-block:: php <?php $products = Products::find($parameters); if (count($products) == 0) { $this->flash->notice("The search did not found any products"); return $this->forward("products/index"); } 如果检索ä¸åˆ°ä»»ä½•产å“,将跳转到 products/index 页é¢ã€‚å¦åˆ™ï¼Œè¯»å–检索到的数æ®ï¼Œè¿›è¡Œåˆ†é¡µæ˜¾ç¤ºï¼š .. code-block:: php <?php $paginator = new Phalcon\Paginator\Adapter\Model(array( "data" => $products, //Data to paginate "limit" => 5, //Rows per page "page" => $numberPage //Active page )); //Get active page in the paginator $page = $paginator->getPaginate(); 最åŽï¼ŒæŠŠåˆ†é¡µçš„æ•°æ®ç»‘å®šåˆ°è§†å›¾ä¸Šã€‚å³æŠŠå˜é‡$page绑定到视图的page上: .. code-block:: php <?php $this->view->setVar("page", $page); 在视图文件(app/views/products/search.phtml) ä¸,æˆ‘ä»¬è¿™æ ·è¿›è¡Œæ•°æ®æ˜¾ç¤ºï¼š .. code-block:: html+php <?php foreach($page->items as $product){ ?> <tr> <td><?= $product->id ?></td> <td><?= $product->getProductTypes()->name ?></td> <td><?= $product->name ?></td> <td><?= $product->price ?></td> <td><?= $product->active ?></td> <td><?= Tag::linkTo("products/edit/".$product->id, 'Edit') ?></td> <td><?= Tag::linkTo("products/delete/".$product->id, 'Delete') ?></td> </tr> <?php } ?> åˆ›å»ºä»¥åŠæ›´æ–°ä¸€æ¡æ•°æ®è®°å½• ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 现在,让我们æ¥çœ‹çœ‹å¦‚何使用CURD创建和更新一个记录。通过控制器的"new"å’Œ"edit"两个Action,我们å¯ä»¥æäº¤æ•°æ®è¾“入。他们分别能过"create"å’Œ"save"两个Actionæ¥ä¿å˜æäº¤çš„æ•°æ®ã€‚ 译者注:说白了就是 newAction就是新建产å“页é¢ï¼Œç‚¹å‡»å³ä¸Šè§’çš„Save按钮ä¿å˜æ—¶ï¼Œä¼šè°ƒç”¨createAction。åŒç†.... 在创建的情况下,我们把用户æäº¤çš„æ•°æ®å’Œ"products"这个产å“实例进行绑定。 è¯‘è€…æ³¨ï¼šå³æŠŠç”¨æˆ·æäº¤çš„æ•°æ®é€šè¿‡ç»‘定到model上,以实现ä¿å˜åˆ°æ•°æ®åº“的目的。 .. code-block:: php <?php /** * Creates a product based on the data entered in the "new" action */ public function createAction() { $products = new Products(); $products->id = $request->getPost("id", "int"); $products->product_types_id = $request->getPost("product_types_id", "int"); $products->name = $request->getPost("name", "striptags"); $products->price = $request->getPost("price", "double"); $products->active = $request->getPost("active"); //... } æäº¤çš„æ•°æ®è¢«è¿‡æ»¤ï¼Œç„¶åŽå†èµ‹å€¼åˆ°å¯¹è±¡çš„属性,ä¿å˜æ—¶ï¼Œæˆ‘们就å¯ä»¥çŸ¥é“用户æäº¤çš„æ•°æ®æœ‰æ²¡æœ‰ç¬¦åˆä¸šåŠ¡è§„åˆ™ã€‚åŒæ—¶ï¼Œå¯ä»¥åœ¨ Products Modelä¸å®žçŽ°éªŒè¯ã€‚ .. code-block:: php <?php /** * Creates a product based on the data entered in the "new" action */ public function createAction() { //... if (!$products->save()) { //The store failed, the following messages were produced foreach ($products->getMessages() as $message) { $this->flash->error((string) $message); } return $this->forward("products/new"); } else { $this->flash->success("Product was created successfully"); return $this->forward("products/index"); } } 现在æ¥è¯´äº§å“编辑部分,首先得ä¿è¯æ•°æ®åº“䏿œ‰å¯ç¼–辑的数æ®ï¼š .. code-block:: php <?php /** * Shows the view to "edit" an existing product */ public function editAction($id) { //... $product = Products::findFirst("id = '$id'"); Tag::displayTo("id", $product->id); Tag::displayTo("product_types_id", $product->product_types_id); Tag::displayTo("name", $product->name); Tag::displayTo("price", $product->price); Tag::displayTo("active", $product->active); } 通过 displayTo helper设置从数æ®åº“ä¸å–得的数æ®åˆ°é¡µé¢ï¼Œç„¶åŽç”¨æˆ·å¯ä»¥æ›´æ”¹è¿™äº›æ•°æ®ï¼Œç„¶åŽå†é€šè¿‡saveActionä¿å˜åˆ°æ•°æ®åº“。 .. code-block:: php <?php /** * Updates a product based on the data entered in the "edit" action */ public function saveAction() { //... //Find the product to update $id = $request->getPost("id", "int"); $products = Products::findFirst("id='$id'"); if ($products == false) { $this->flash->error("products does not exist ".$id); return $this->forward("products/index"); } //... assign the values to the object and store it } åŠ¨æ€æ›´æ”¹æ ‡é¢˜ ------------------------------ å½“ä½ æµè§ˆä¸åŒçš„æŽ§åˆ¶å™¨åŠåŠ¨ä½œæ—¶ï¼Œç½‘é¡µæ ‡é¢˜ä¼šä¸åŒï¼Œå¦‚æžœæ›´æ”¹æ ‡é¢˜å‘¢ï¼Œå¯ä»¥åœ¨æ¯ä¸ªæŽ§åˆ¶å™¨è¿›è¡Œåˆå§‹åŒ–: .. code-block:: php <?php class ProductsController extends ControllerBase { public function initialize() { //Set the document title Tag::setTitle('Manage your product types'); parent::initialize(); } //... } 注æ„,上é¢çš„æ–¹æ³•ä¸è°ƒç”¨äº† parent::initialize() ï¼Œä½ å¯ä»¥åœ¨ parent::initialize() 方法ä¸åŠ å…¥æ›´å¤šçš„å†…å®¹åˆ°æ ‡é¢˜ï¼š .. code-block:: php <?php class ControllerBase extends Phalcon\Mvc\Controller { protected function initialize() { //Prepend the application name to the title Phalcon\Tag::prependTitle('INVO | '); } //... } 最åŽï¼Œæˆ‘们在视图文件 (app/views/index.phtml) ä¸è¿™æ ·èŽ·å¾—æ ‡é¢˜ï¼š .. code-block:: html+php <?php use Phalcon\Tag as Tag ?> <!DOCTYPE html> <html> <head> <?php echo Tag::getTitle() ?> </head> <!-- ... --> </html> 结æŸè¯ ---------- 本教程从å„个方é¢è®²è§£äº†å¦‚何使用Phalconæ¥åˆ›å»ºä¸€ä¸ªåº”用程åºï¼Œå¸Œæœ›ä½ 也能æä¾›ç¤ºä¾‹ç¨‹åºï¼ŒåŒæ—¶å¦ä¹ 更多的内容。 .. _Github: https://github.com/phalcon/invo .. _CRUD: http://en.wikipedia.org/wiki/Create,_read,_update_and_delete