MVC 应用MVC Applications

在Phalcon,策划MVC操作背后的全部困难工作通常都可以 通过 :doc:`Phalcon\Mvc\Application <../api/Phalcon_Mvc_Application>`做到。这个组件封装了全部后端所需要的复杂 操作,实例化每一个需要用到的组件并与项目整合在一起,从而使得MVC模式可以如期地运行。

All the hard work behind orchestrating the operation of MVC in Phalcon is normally done by Phalcon\Mvc\Application. This component encapsulates all the complex operations required in the background, instantiating every component needed and integrating it with the project, to allow the MVC pattern to operate as desired.

单模块或多模块应用Single or Multi Module Applications

通过这个组件,你可以运行各式各样的MVC结构:

With this component you can run various types of MVC structures:

单模块Single Module

单一的MVC应用仅仅包含了一个模块。可以使用命名空间,但不是必需的。 这样类型的应用可能会有以下文件目录结构:

Single MVC applications consist of one module only. Namespaces can be used but are not necessary. An application like this would have the following file structure:

  1. single/
  2. app/
  3. controllers/
  4. models/
  5. views/
  6. public/
  7. css/
  8. img/
  9. js/

如果未使用命名空间,以下的启动文件可用于编排MVC工作流:

If namespaces are not used, the following bootstrap file could be used to orchestrate the MVC flow:

  1. <?php
  2. use Phalcon\Loader;
  3. use Phalcon\Mvc\View;
  4. use Phalcon\Mvc\Application;
  5. use Phalcon\DI\FactoryDefault;
  6. $loader = new Loader();
  7. $loader->registerDirs(
  8. array(
  9. '../apps/controllers/',
  10. '../apps/models/'
  11. )
  12. )->register();
  13. $di = new FactoryDefault();
  14. // Registering the view component
  15. $di->set('view', function() {
  16. $view = new View();
  17. $view->setViewsDir('../apps/views/');
  18. return $view;
  19. });
  20. try {
  21. $application = new Application($di);
  22. echo $application->handle()->getContent();
  23. } catch (\Exception $e) {
  24. echo $e->getMessage();
  25. }

如果使用了命名空间,则可以使用以下启动文件(译者注:主要区别在于使用$loader的方式):

If namespaces are used, the following bootstrap can be used:

  1. <?php
  2. use Phalcon\Loader;
  3. use Phalcon\Mvc\View;
  4. use Phalcon\Mvc\Dispatcher;
  5. use Phalcon\Mvc\Application;
  6. use Phalcon\DI\FactoryDefault;
  7. $loader = new Loader();
  8. // Use autoloading with namespaces prefixes
  9. $loader->registerNamespaces(
  10. array(
  11. 'Single\Controllers' => '../apps/controllers/',
  12. 'Single\Models' => '../apps/models/',
  13. )
  14. )->register();
  15. $di = new FactoryDefault();
  16. // Register the dispatcher setting a Namespace for controllers
  17. $di->set('dispatcher', function() {
  18. $dispatcher = new Dispatcher();
  19. $dispatcher->setDefaultNamespace('Single\Controllers');
  20. return $dispatcher;
  21. });
  22. // Registering the view component
  23. $di->set('view', function() {
  24. $view = new View();
  25. $view->setViewsDir('../apps/views/');
  26. return $view;
  27. });
  28. try {
  29. $application = new Application($di);
  30. echo $application->handle()->getContent();
  31. } catch(\Exception $e){
  32. echo $e->getMessage();
  33. }

多模块Multi Module

多模块的应用使用了相同的文档根目录但拥有多个模块。在这种情况下,可以使用以下的文件目录结构:

A multi-module application uses the same document root for more than one module. In this case the following file structure can be used:

  1. multiple/
  2. apps/
  3. frontend/
  4. controllers/
  5. models/
  6. views/
  7. Module.php
  8. backend/
  9. controllers/
  10. models/
  11. views/
  12. Module.php
  13. public/
  14. css/
  15. img/
  16. js/

在apps/下的每一个目录都有自己的MVC结构。Module.php文件代表了各个模块不同的配置,如自动加载器和自定义服务:

Each directory in apps/ have its own MVC structure. A Module.php is present to configure specific settings of each module like autoloaders or custom services:

  1. <?php
  2. namespace Multiple\Backend;
  3. use Phalcon\Loader;
  4. use Phalcon\Mvc\View;
  5. use Phalcon\Mvc\Dispatcher;
  6. use Phalcon\Mvc\ModuleDefinitionInterface;
  7. class Module implements ModuleDefinitionInterface
  8. {
  9. /**
  10. * Register a specific autoloader for the module
  11. */
  12. public function registerAutoloaders()
  13. {
  14. $loader = new Loader();
  15. $loader->registerNamespaces(
  16. array(
  17. 'Multiple\Backend\Controllers' => '../apps/backend/controllers/',
  18. 'Multiple\Backend\Models' => '../apps/backend/models/',
  19. )
  20. );
  21. $loader->register();
  22. }
  23. /**
  24. * Register specific services for the module
  25. */
  26. public function registerServices($di)
  27. {
  28. //Registering a dispatcher
  29. $di->set('dispatcher', function() {
  30. $dispatcher = new Dispatcher();
  31. $dispatcher->setDefaultNamespace("Multiple\Backend\Controllers");
  32. return $dispatcher;
  33. });
  34. //Registering the view component
  35. $di->set('view', function() {
  36. $view = new View();
  37. $view->setViewsDir('../apps/backend/views/');
  38. return $view;
  39. });
  40. }
  41. }

还需要一个指定的启动文件来加载多模块的MVC架构:

A special bootstrap file is required to load the a multi-module MVC architecture:

  1. <?php
  2. use Phalcon\Mvc\Router;
  3. use Phalcon\Mvc\Application;
  4. use Phalcon\DI\FactoryDefault;
  5. $di = new FactoryDefault();
  6. //Specify routes for modules
  7. $di->set('router', function () {
  8. $router = new Router();
  9. $router->setDefaultModule("frontend");
  10. $router->add("/login", array(
  11. 'module' => 'backend',
  12. 'controller' => 'login',
  13. 'action' => 'index',
  14. ));
  15. $router->add("/admin/products/:action", array(
  16. 'module' => 'backend',
  17. 'controller' => 'products',
  18. 'action' => 1,
  19. ));
  20. $router->add("/products/:action", array(
  21. 'controller' => 'products',
  22. 'action' => 1,
  23. ));
  24. return $router;
  25. });
  26. try {
  27. //Create an application
  28. $application = new Application($di);
  29. // Register the installed modules
  30. $application->registerModules(
  31. array(
  32. 'frontend' => array(
  33. 'className' => 'Multiple\Frontend\Module',
  34. 'path' => '../apps/frontend/Module.php',
  35. ),
  36. 'backend' => array(
  37. 'className' => 'Multiple\Backend\Module',
  38. 'path' => '../apps/backend/Module.php',
  39. )
  40. )
  41. );
  42. //Handle the request
  43. echo $application->handle()->getContent();
  44. } catch(\Exception $e){
  45. echo $e->getMessage();
  46. }

如果你想在启动文件保持模块的配置,你可以使用匿名函数来注册对应的模块:

If you want to maintain the module configuration in the bootstrap file you can use an anonymous function to register the module:

  1. <?php
  2. use Phalcon\Mvc\View;
  3. //Creating a view component
  4. $view = new View();
  5. //Set options to view component
  6. //...
  7. // Register the installed modules
  8. $application->registerModules(
  9. array(
  10. 'frontend' => function($di) use ($view) {
  11. $di->setShared('view', function() use ($view) {
  12. $view->setViewsDir('../apps/frontend/views/');
  13. return $view;
  14. });
  15. },
  16. 'backend' => function($di) use ($view) {
  17. $di->setShared('view', function() use ($view) {
  18. $view->setViewsDir('../apps/backend/views/');
  19. return $view;
  20. });
  21. }
  22. )
  23. );

Phalcon\Mvc\Application 有多个模块注册时,通常每个都是需要的,以便每一个被匹配到的路由都能返回一个有效的模块。每个已经注册的模块都有一个相关的类来提供建立和启动自身的函数。 而每个模块定义的类都必须实现registerAutoloaders()和registerServices()这两个方法,这两个函数会在模块即被执行时被:doc:Phalcon\Mvc\Application <../api/Phalcon_Mvc_Application> 调用。

When Phalcon\Mvc\Application have modules registered, always is necessary that every matched route returns a valid module. Each registered module has an associated class offering functions to set the module itself up. Each module class definition must implement two methods: registerAutoloaders() and registerServices(), they will be called by Phalcon\Mvc\Application according to the module to be executed.

理解默认行为Understanding the default behavior

如果你已经看过了 tutorial 或者已经通过 Phalcon Devtools 生成了代码, 你将很容易识别以下的启动文件:

If you’ve been following the tutorial or have generated the code using Phalcon Devtools, you may recognize the following bootstrap file:

  1. <?php
  2. use Phalcon\Mvc\Application;
  3. try {
  4. // Register autoloaders
  5. //...
  6. // Register services
  7. //...
  8. // Handle the request
  9. $application = new Application($di);
  10. echo $application->handle()->getContent();
  11. } catch (\Exception $e) {
  12. echo "Exception: ", $e->getMessage();
  13. }

控制器中全部核心的工作都会在handle()被回调时触发执行。

The core of all the work of the controller occurs when handle() is invoked:

  1. <?php
  2. echo $application->handle()->getContent();

手动启动Manual bootstrapping

如果你不想使用 Phalcon\Mvc\Application,以上的代码可以改成这样:

If you do not wish to use Phalcon\Mvc\Application, the code above can be changed as follows:

  1. <?php
  2. // Get the 'router' service
  3. $router = $di['router'];
  4. $router->handle();
  5. $view = $di['view'];
  6. $dispatcher = $di['dispatcher'];
  7. // Pass the processed router parameters to the dispatcher
  8. $dispatcher->setControllerName($router->getControllerName());
  9. $dispatcher->setActionName($router->getActionName());
  10. $dispatcher->setParams($router->getParams());
  11. // Start the view
  12. $view->start();
  13. // Dispatch the request
  14. $dispatcher->dispatch();
  15. // Render the related views
  16. $view->render(
  17. $dispatcher->getControllerName(),
  18. $dispatcher->getActionName(),
  19. $dispatcher->getParams()
  20. );
  21. // Finish the view
  22. $view->finish();
  23. $response = $di['response'];
  24. // Pass the output of the view to the response
  25. $response->setContent($view->getContent());
  26. // Send the request headers
  27. $response->sendHeaders();
  28. // Print the response
  29. echo $response->getContent();

以下代码替换了 Phalcon\Mvc\Application,虽然缺少了视图组件, 但却更适合Rest风格的API接口:

The following replacement of Phalcon\Mvc\Application lacks of a view component making it suitable for Rest APIs:

  1. <?php
  2. // Get the 'router' service
  3. $router = $di['router'];
  4. $router->handle();
  5. $dispatcher = $di['dispatcher'];
  6. // Pass the processed router parameters to the dispatcher
  7. $dispatcher->setControllerName($router->getControllerName());
  8. $dispatcher->setActionName($router->getActionName());
  9. $dispatcher->setParams($router->getParams());
  10. // Dispatch the request
  11. $dispatcher->dispatch();
  12. //Get the returned value by the latest executed action
  13. $response = $dispatcher->getReturnedValue();
  14. //Check if the action returned is a 'response' object
  15. if ($response instanceof Phalcon\Http\ResponseInterface) {
  16. //Send the request
  17. $response->send();
  18. }

另外一个修改就是在分发器中对抛出异常的捕捉可以将请求转发到其他的操作:

Yet another alternative that catch exceptions produced in the dispatcher forwarding to other actions consequently:

  1. <?php
  2. // Get the 'router' service
  3. $router = $di['router'];
  4. $router->handle();
  5. $dispatcher = $di['dispatcher'];
  6. // Pass the processed router parameters to the dispatcher
  7. $dispatcher->setControllerName($router->getControllerName());
  8. $dispatcher->setActionName($router->getActionName());
  9. $dispatcher->setParams($router->getParams());
  10. try {
  11. // Dispatch the request
  12. $dispatcher->dispatch();
  13. } catch (Exception $e) {
  14. //An exception has occurred, dispatch some controller/action aimed for that
  15. // Pass the processed router parameters to the dispatcher
  16. $dispatcher->setControllerName('errors');
  17. $dispatcher->setActionName('action503');
  18. // Dispatch the request
  19. $dispatcher->dispatch();
  20. }
  21. //Get the returned value by the latest executed action
  22. $response = $dispatcher->getReturnedValue();
  23. //Check if the action returned is a 'response' object
  24. if ($response instanceof Phalcon\Http\ResponseInterface) {
  25. //Send the request
  26. $response->send();
  27. }

尽管上面的代码比使用 Phalcon\Mvc\Application 而需要的代码远远要累赘得很, 但它为启动你的应用提供了一个可修改、可定制化的途径。 因为根据你的项目需要,你可以想对实例什么和不实例化什么进行完全的控制,或者想用你自己的组件来替代那些确定和必须的组件从而扩展默认的功能。

Although the above implementations are a lot more verbose than the code needed while using Phalcon\Mvc\Application, it offers an alternative in bootstrapping your application. Depending on your needs, you might want to have full control of what should be instantiated or not, or replace certain components with those of your own to extend the default functionality.

应用事件Application Events

Phalcon\Mvc\Application 可以把事件发送到 EventsManager (如果它激活的话)。 事件将被当作”application”类型被消费掉。目前已支持的事件如下:

Phalcon\Mvc\Application is able to send events to the EventsManager (if it is present). Events are triggered using the type “application”. The following events are supported:

Event NameTriggered
bootExecuted when the application handles its first request
beforeStartModuleBefore initialize a module, only when modules are registered
afterStartModuleAfter initialize a module, only when modules are registered
beforeHandleRequestBefore execute the dispatch loop
afterHandleRequestAfter execute the dispatch loop

以下示例演示了如何将侦听器绑定到组件:

The following example demonstrates how to attach listeners to this component:

  1. <?php
  2. use Phalcon\Events\Manager as EventsManager;
  3. $eventsManager = new EventsManager();
  4. $application->setEventsManager($eventsManager);
  5. $eventsManager->attach(
  6. "application",
  7. function($event, $application) {
  8. // ...
  9. }
  10. );

外部资源

External Resources