Phalcon

一、安装

不做分享,请参考官方文档

1.1 开启重写

  1. 在主文档根目录下

```

根目录下

RewriteEngine on RewriteRule ^$ public/ [L] RewriteRule ((?s).*) public/$1 [L] ```

  1. public文件夹下

```

public/.htaccess

RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^((?s).*)$ index.php?_url=/$1 [QSA,L] ```

二、开发工具

2.1 下载

我们可以从 Github 上下载或克隆下来这个跨平台的开发辅助工具

2.2 安装(Installation)

下面详尽的说明了如何在不同的操作系统平台上安装这个辅助开发工具:

2.3 获取可用的命令

bash phalcon commands

2.4 生成项目框架

我们可以使用Phalcon开发辅助工具生成预先定义的项目架构。 默认情况下,phalcon开发辅助工具会使用apache的mod_rewrite来生成程序的骨架. 要创建项目我们只需要在我们的 web服务器根目录下输入如下命令:

bash $ phalcon create-project store

2.5 查看帮助

同其他linux命令 --help

shell phalcon project --help

2.6 生成控制器

  1. phalcon create-controller –name test
  2. phalcon controller –name test

2.7 数据库配置

在新版本的phalcon工具中,新生成的配置文件为config.php

2.8 生成模型

  1. phalcon model products
  2. phalcon model --name tablename

```php class Products extends Model { /** * @var integer */ public $id;

/**
 * @var integer
 */
public $typesId;

/**
 * @var string
 */
public $name;

/**
 * @var string
 */
public $price;

/**
 * @var integer
 */
public $quantity;

/**
 * @var string
 */
public $status;

} ```

使用phalcon开发辅助工具我们可以有若干种方式来生成模型。 可以有选择的生成若干个模型或是全部生成。 亦可以指定生成公有属性或是生成setter和getter方法。

Options:

| options | 说明 | | --- | --- | | --name=s | Table name 表名 | | --schema=s | Name of the schema. [optional] schema名 | | --namespace=s | Model’s namespace [optional] 模型命名空间 | | --get-set | Attributes will be protected and have setters/getters. [optional] 设置字段访问属性为私有 并添加setters/getters方法 | | --extends=s | Model extends the class name supplied [optional] 指定扩展类名 | | --excludefields=l | Excludes fields defined in a comma separated list [optional] | | --doc | Helps to improve code completion on IDEs [optional] 辅助IDE的自动完成功能 | | --directory=s | Base path on which project will be created [optional] 项目的根目录 | | --force | Rewrite the model. [optional] 重写模型 | | --trace | Shows the trace of the framework in case of exception. [optional] 出错时显示框架trace信息 | | --mapcolumn | Get some code for map columns. [optional] 生成字映射的代码 | | --abstract | Abstract Model [optional] 抽象模型 |

2.9 生成基本的 CRUD

使用phalcon开发辅助工具我们可以直接快速的生成一个模型的CRUD操作。 如果我们想快速的生成模型的CRUD操作只需要使用phalcon辅助开发工具的中scaffold命令即可。

代码生成后,你可以根据自己的需要修改生成的代码。很多开发者可能不会去使用这个功能,其实这东西有时不是太好用,很多时候开发者往往会手动的书写相关代码。使用scaffold产生的代码可以 帮助我们理解框架是如何工作的当然也可以帮助我们制作出快速原型来。 下面的截图展示了基于products表的scaffold:

bash phalcon scaffold --table-name products

2.10 工具的 Web 界面

基本不会用到,略过,感兴趣的自己研究下

2.11 集成工具到 PhpStorm

见视频

三、 教程

3.1 Hello World

完整代码下载地址:https://github.com/phalcon/tutorial

3.1.1 文件结构

Phalcon不会强制要求应用程序的开发遵循特定的文件结构。因为它是松散耦合的,你可以实现Phalcon驱动的应用程序,以及使用对你来说最舒服的文件结构。

tutorial ├── app │   ├── controllers │   ├── models │   └── views └── public ├── css ├── img └── js 需要注意的是,你不需要任何有关Phalcon的 “library” 目录。该框架已经被加载到内存中,供您使用。

3.1.2 引导程序

  1. 设置自动加载器.
  2. 配置依赖注入.
  3. 处理应用请求.

3.1.3 自动加载

Phalcon\Loader 组件

  • 注册一个自动加载器,用于加载控制器和模型类
  • 可以为控制器注册一个或多个目录来增加应用程序的灵活性的
  • 可以加载使用各种策略类

在这个例子中,我们选择了在预定义的目录中查找类

```php $loader = new Loader();

$loader->registerDirs( [ "../app/controllers/", "../app/models/", ] );

$loader->register(); ```

3.1.4 依赖管理

  1. 依赖注入容器

    服务容器是一个全局存储的将要被使用的应用程序功能包。每次框架需要的一个组件时,会请求这个使用协定好名称的服务容器。因为Phalcon是一个高度解耦的框架, Phalcon\Di 作为黏合剂,促使不同组件的集成,以一个透明的方式实现他们一起进行工作。

    ```php use Phalcon\Di\FactoryDefault;

    // ...

    // 创建一个 DI $di = new FactoryDefault(); ```

    Phalcon\Di\FactoryDefault 是 Phalcon\Di 的一个变体。为了让事情变得更容易,它已注册了Phalcon的大多数组件。 因此,我们不需要一个一个注册这些组件。在以后更换工厂服务的时候也不会有什么问题。

  2. 注册视图服务

    在接下来的部分,我们注册了“视图(view)”服务,指示框架将去指定的目录寻找视图文件。由于视图并非PHP类,它们不能被自动加载器加载。

    服务可以通过多种方式进行登记,但在我们的教程中,我们将使用一个匿名函数 anonymous function:

    ```php use Phalcon\Mvc\View;

    // ...

    // 设置视图组件 $di->set( "view", function () { $view = new View();

       $view->setViewsDir("../app/views/");
    
    
       return $view;
    

    } ); ```

  3. 注册基础URI

    接下来,我们注册一个基础URI,这样通过Phalcon生成包括我们之前设置的“tutorial”文件夹在内的所有的URI。 我们使用类 Phalcon\Tag 生成超链接,这将在本教程后续部分很重要。

    ```php use Phalcon\Mvc\Url as UrlProvider;

    // ...

    // 设置一个基础URI, 这样所有生成的URI都包含"tutorial"文件夹 $di->set( "url", function () { $url = new UrlProvider();

       $url->setBaseUri("/tutorial/");
    
    
       return $url;
    

    } ); ```

3.1.5 处理应用请求

在这个文件的最后部分,我们发现 Phalcon\Mvc\Application。其目的是初始化请求环境,并接收路由到来的请求,接着分发任何发现的动作;收集所有的响应,并在过程完成后返回它们。

```php use Phalcon\Mvc\Application;

// ...

$application = new Application($di);

$response = $application->handle();

$response->send(); ```

3.1.6 创建控制器

默认情况下Phalcon会寻找一个名为“Index”的控制器。当请求中没有控制器或动作时,则使用“Index”控制器作为起点。这个“Index”控制器 (app/controllers/IndexController.php) 看起来类似:

```php use Phalcon\Mvc\Controller;

class IndexController extends Controller { public function indexAction() { echo "

Hello!

"; } } ```

注意:该控制器类必须有“Controller”后缀,且控制器动作必须有“Action”后缀。

3.1.7 输出到视图

使用类 Phalcon\Tag 去生成标记。 这是一个让我们构建HTML标记的实用类。 关于生成HTML更详细的文章可以查看 视图助手

  1. 创建view

    ```php

    echo "

    Hello!

    ";

    echo PHP_EOL;

    echo PHP_EOL;

    echo $this->tag->linkTo( "signup", "Sign Up Here!" );

    ```

    上述代码等同于以下代码

    ```html

    Hello!

    Sign Up Here! ```

  2. 创建signup/index.phtml

    ```html

    Sign up using this form

    <?php echo $this->tag->form("signup/register"); ?>

    Name

       <?php echo $this->tag->textField("name"); ?>
    

    E-Mail

       <?php echo $this->tag->textField("email"); ?>
    

    <?php echo $this->tag->submitButton("Register"); ?>

    ```

Phalcon\Tag 还提供了有用的方法来构建表单元素。

Phalcon\Tag::form()方法只接受一个参数实例, 一个相对uri到这个应用的一个控制器/动作。

通过单击“Send”按钮,您将注意到框架抛出了一个异常,这表明我们是错过了在控制器中注册“register”动作。我们的 public/index.php 文件抛出这个异常:

Exception: Action “register” was not found on handler “signup”

3.1.8 创建模型

  1. sql

    ``sql CREATE TABLEusers( idint(10) unsigned NOT NULL AUTO_INCREMENT, namevarchar(70) NOT NULL, email` varchar(70) NOT NULL,

    PRIMARY KEY (id) ); ```

  2. models/User.php 模型

    ```php use Phalcon\Mvc\Model;

    class Users extends Model { public $id;

    public $name;

    public $email; } ```

3.1.9 设置数据库连接

```php use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

// 设置数据库服务 $di->set( "db", function () { return new DbAdapter( [ "host" => "localhost", "username" => "root", "password" => "mysql", "dbname" => "test", ] ); } );

```

3.1.10 使用模型保存数据

registerAction方法:

```php public function registerAction() { // 实例一个用户类 $user = new Users();

// 存储和检验错误 $success = $user->save( $this->request->getPost(), [ "name", "email", ] );

if ($success) { echo "Thanks for registering!"; } else { echo "Sorry, the following problems were generated: ";

   $messages = $user->getMessages();

   foreach ($messages as $message) {
       echo $message->getMessage(), "<br/>";
   }

}

$this->view->disable(); } ```

类的公共属性映射到用户表中的记录的字段。 在新记录中设置相应的值并调用:code:save()将在数据库中存储的数据记录。:code:save()方法返回一个布尔值,表示存储的数据是否成功。

ORM自动转义输入以防止SQL注入,所以我们只需要将请求传递给:code:save()方法。

3.2 INVO简介

INVO是一个简单的用来允许用户生成发票的网站,并且可以做其他的任务,比如管理他们的客户或者产品。你可以从 Github 中复制它的代码。

同样,INVO使用 Bootstrap 做的前端框架。虽然这个应用不能生成发票,但是它仍然可以作为一个例子来理解框架是如何工作的。

3.2.1 文件结构

3.2.2 路由

INVO使用内置的标准路由. Router 组件. 路由符合以下格式:/:controller/:action/:params. 这就意味着第一部分URI是控制器,第二部分是方法,剩余的是参数。

下面的路由 /session/register 执行的是 SessionController 控制器和它的 registerAction方法。

3.2.3 配置

INVO有一个设置应用常规参数的配置文件。这个文件位于 app/config/config.ini,并且他在应用引导文件的最开始就开始加载 (public/index.php)

```php

use Phalcon\Config\Adapter\Ini as ConfigIni;

// ...

// 读取配置 $config = new ConfigIni( APP_PATH . "app/config/config.ini" ); ```

Phalcon\Config 允许我们使用面向对象的方式来操作文件。在这个例子中,我们使用ini文件作为配置文件,然而,它对于配置文件有更多的适配支持

```ini [database] host = localhost username = root password = secret name = invo

[application] controllersDir = app/controllers/ modelsDir = app/models/ viewsDir = app/views/ pluginsDir = app/plugins/ formsDir = app/forms/ libraryDir = app/library/ baseUri = /invo/ ```

3.2.4 自动加载

  1. 在启动文件(public/index.php)的第二部分是自动加载器

    php require APP_PATH . "app/config/loader.php";

  2. 自动加载器

    ```php $loader = new Phalcon\Loader();

    // We're a registering a set of directories taken from the configuration file $loader->registerDirs( [ APP_PATH . $config->application->controllersDir, APP_PATH . $config->application->pluginsDir, APP_PATH . $config->application->libraryDir, APP_PATH . $config->application->modelsDir, APP_PATH . $config->application->formsDir, ] );

    $loader->register(); ```

  3. 注意, 以上代码注册的目录是在配置文件中定义的. 唯一没有注册的目录是viewsDir, 因为它包含 HTML + PHP 文件但不是类.

3.2.5 注册服务

  1. 在引导文件中加载的另外一个文件是 (app/config/services.php). 这个文件允许我们组织 INVO 不需要的文件

    php require APP_PATH . "app/config/services.php";

  2. 利用一个闭包来实现惰性加载服务注册组件

    ```php use Phalcon\Mvc\Url as UrlProvider;

    // ...

    /**

    • URL组件被用来生成应用中的各种URL */ $di->set( "url", function () use ($config) { $url = new UrlProvider();

      $url->setBaseUri(
          $config->application->baseUri
      );
      
      
      return $url;
      

      } ); ```

3.2.6 处理请求

如果我们忽略文件(public/index.php)的结束, 请求最终会被 Phalcon\Mvc\Application 处理, 初始化并运行所有必须的代码来让程序执行

```php use Phalcon\Mvc\Application;

// ...

$application = new Application($di);

$response = $application->handle();

$response->send(); ```

2.2.7 依赖注入

看上面代码的第一行, Application 类的构造方法接受一个 <span class="pre" style="box-sizing: border-box;">$di</span> 变量作为一个参数. 这个变量的用意是什么呢? Phalcon 是一个高度解耦的框架, 所以我们一个组件来充当胶水来让每个组件都能正常工作. 这个组件就是 Phalcon\Di. 这是一个服务容器, 可以执行依赖注入和服务定位, 实例化应用所需要的所有组件.

在容器中有多种注册服务的方法. 在INVO里, 大部分服务使用匿名函数或者闭包来进行注册的. 多亏了这个, 对象以惰性的方式被实例化, 减少了应用程序所需要的资源.

例如, 下面摘录了 Session 服务的注册. 当应用程序需要访问Session数据的时候, 匿名函数才会被调用:

```php

use Phalcon\Session\Adapter\Files as Session;

// ...

// 在一个组件请求Session服务的时候, 启动Sesssion $di->set( "session", function () { $session = new Session();

    $session->start();

    return $session;
}

); ```

这里, 我们可以自动的更改适配器, 执行额外的初始化或者更多. 注意, 这个服务器是使用 “session” 名字进行注册的. 这是一个惯例, 来允许框架在服务容器中识别正在活动的服务.

一个请求可以使用多个服务和单独注册每个服务可以说是一个繁重的任务. 因此, 框架提供了 Phalcon\Di 的一个变种, 称作 Phalcon\Di\FactoryDefault 其任务是注册所有的服务来提供一个全栈框架.

```php use Phalcon\Di\FactoryDefault;

// ...

// FactoryDefault 依赖注入自动注册了正确的服务来提供了一个全栈框架 $di = new FactoryDefault(); ```

它通过框架注册大部分组件服务作为标准提供. 如果我们需要重写某些已经定义的服务, 我们仅仅需要重新定义它, 就像上面的 “session” 和 “url”一样. 这就是变量 $di 存在的原因.

在下一章, 我们将会看到如何在INVO中实施认证和授权..

3.3 保护INVO

3.3.1 登录应用

  1. 配置数据库连接

    ```php use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

    // ...

    // 数据库连接是基于配置文件已经定义的参数创建的 $di->set( "db", function () use ($config) { return new DbAdapter( [ "host" => $config->database->host, "username" => $config->database->username, "password" => $config->database->password, "dbname" => $config->database->name, ] ); } ); ```

  2. V层表单

    ``` // app/views/session/index.volt

    {{ form("session/start") }}

    Username/Email
        <div>
            {{ text_field("email") }}
        </div>
    </div>
    
    
    <div>
        <label for="password">
            Password
        </label>
        <div>
            {{ password_field("password") }}
        </div>
    </div>
    <div>
        {{ submit_button("Login") }}
    </div>
    

    {{ endForm() }} ```

    使用原生的PHP作为以前的教程, 我们开始使用 Volt. 这是一个内置的模板引擎受 Jinja 的影响而提供简单而又友好的语法来创建模板. 在你熟悉 Volt 之前, 它将不会花费你太多的时间.

  3. SessionController.php

    SessionController::startAction 方法 (app/controllers/SessionController.php) 有验证表单中输入的数据包括检查在数据库中是否为有效用户的任务:

    ```php
    class SessionController extends ControllerBase { // ...

    private function _registerSession($user)
    {
        // 调用 “session” 服务然后在变量 “auth” 中存储用户身份:
        $this->session->set(
            "auth",
            [
                "id"   => $user->id,
                "name" => $user->name,
            ]
        );
    }
    
    
    /**
     * 这个方法检验和记录一个用户到应用中
     */
    public function startAction()
    {
        // 验证是否使用的是POST请求
        if ($this->request->isPost()) {
            // 从用户获取数据,从表单中接收参数
            $email    = $this->request->getPost("email");
            $password = $this->request->getPost("password");
    
    
    
        // 在数据库中查找用户
        $user = Users::findFirst(
            [
                // ‘绑定参数’的使用, 占位符 :email: 和 :password: 
                // 要放置在替换的值的位置, 然后值的’绑定’使用参数 ‘bind’. 
                // 安全的替换列的值而没有SQL注入的危险.
                "(email = :email: OR username = :email:) AND password = :password: AND active = 'Y'",
                "bind" =&gt; [
                    "email"    =&gt; $email,
                    "password" =&gt; sha1($password),
                ]
            ]
        );
    
    
        if ($user !== false) {
            $this-&gt;_registerSession($user);
    
    
            $this-&gt;flash-&gt;success(
                "Welcome " . $user-&gt;name
            );
    
    
            // 如果用户是有效的, 转发到'invoices'控制器
            return $this-&gt;dispatcher-&gt;forward(
                [
                    "controller" =&gt; "invoices",
                    "action"     =&gt; "index",
                ]
            );
        }
    
    
        $this-&gt;flash-&gt;error(
            "Wrong email/password"
        );
    }
    
    
    // 再一次转发到登录表单
    return $this-&gt;dispatcher-&gt;forward(
        [
            "controller" =&gt; "session",
            "action"     =&gt; "index",
        ]
    );
    
    }

    } ```

  4. “共享服务”

    请注意, 多个公共属性在控制器访问, 像: $this->flash, $this->request 或者 $this->session. 这些是先前在服务容器中定义的服务 (app/config/services.php). 当它们第一次访问的时候, 它们被注入作为控制器的一部分.

    这些服务是”共享”的, 这意味着我们总是访问相同的地方, 无论我们在哪里调用它们.

3.3.2 后端安全

每次有人试图访问任何controller/action, 应用将会验证当前角色(在session中)是否能够访问它, 否则就会显示一个像上面那样的消息并转发到首页.

现在, 让我们看看应用程序是如何实现的. 首先我们知道有个组件叫做 Dispatcher. 通过 Routing 组件来找到路由. 然后, 它负责加载合适的控制器和执行相应的动作方法.

正常情况下, 框架会自动创建分发器. 对我们而言, 我们想在执行请求的方法之前执行一个验证, 校验用户是否可以访问它. 要做到这一点, 我们需要在启动文件中创建一个方法来替换组件:

```php use Phalcon\Mvc\Dispatcher;

// ...

/** * MVC 分发器 */ $di->set( "dispatcher", function () { // ...

    $dispatcher = new Dispatcher();

    return $dispatcher;
}

); ```

我们现在使用完全控制的分发器用于应用程序. 在框架中需要多组件的触发事件, 允许我们能够修改内部的操作流. 依赖注入组件作为胶水的一部分, 一个新的叫做EventsManager 的组件允许我们拦截由组件产生的事件, 路由事件到监听.我们现在使用完全控制的分发器用于应用程序. 在框架中需要多组件的触发事件, 允许我们能够修改内部的操作流. 依赖注入组件作为胶水的一部分, 一个新的叫做 EventsManager 的组件允许我们拦截由组件产生的事件, 路由事件到监听.

3.3.3 事件管理

一个 EventsManager 允许我们为一个特定类型的事件添加监听. 现在我们感兴趣的类型是 “dispatch”. 下列代码过滤了由分发器产生的所有事件:

```php

use Phalcon\Mvc\Dispatcher; use Phalcon\Events\Manager as EventsManager;

$di->set( "dispatcher", function () { // 创建一个事件管理器 $eventsManager = new EventsManager();

    // 监听分发器中使用安全插件产生的事件
    $eventsManager->attach(
        "dispatch:beforeExecuteRoute",
        new SecurityPlugin()
    );

    // 处理异常和使用 NotFoundPlugin 未找到异常
    $eventsManager->attach(
        "dispatch:beforeException",
        new NotFoundPlugin()
    );

    $dispatcher = new Dispatcher();

    // 分配事件管理器到分发器
    $dispatcher->setEventsManager($eventsManager);

    return $dispatcher;
}

); ```

SecurityPlugin

SecurityPlugin 是一个类位于(app/plugins/SecurityPlugin.php). 这个类实现了 “beforeExecuteRoute” 方法. 这是一个相同的名字在分发器中产生的事件中的一个

```php use Phalcon\Events\Event; use Phalcon\Mvc\User\Plugin; use Phalcon\Mvc\Dispatcher;

class SecurityPlugin extends Plugin { // ...

public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher) { // ... } } ```

钩子事件始终接收第一个包含上下文信息所产生的事件(<span class="pre" style="box-sizing: border-box;">$event</span>)的参数和第二个包含事件本身所产生的对象(<span class="pre" style="box-sizing: border-box;">$dispatcher</span>)的参数. 这不是一个强制性的插件扩展类 Phalcon\Mvc\User\Plugin, 但通过这样做, 它们更容易获得应用程序中可用的服务.

现在, 我们验证当前 session 中的角色, 验证用户是否可以通过ACL列表访问.如果用户没有权限, 我们将会重定向到如上所述的主页中去:

```php use Phalcon\Acl; use Phalcon\Events\Event; use Phalcon\Mvc\User\Plugin; use Phalcon\Mvc\Dispatcher;

class SecurityPlugin extends Plugin { // ...

public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher)
{
    // 检查session中是否存在"auth"变量来定义当前活动的角色
    $auth = $this->session->get("auth");

    if (!$auth) {
        $role = "Guests";
    } else {
        $role = "Users";
    }

    // 从分发器获取活动的 controller/action
    $controller = $dispatcher->getControllerName();
    $action     = $dispatcher->getActionName();

    // 获得ACL列表
    $acl = $this->getAcl();

    // 检验角色是否允许访问控制器 (resource)
    $allowed = $acl->isAllowed($role, $controller, $action);

    if (!$allowed) {
        // 如果没有访问权限则转发到 index 控制器
        $this->flash->error(
            "You don't have access to this module"
        );

        $dispatcher->forward(
            [
                "controller" => "index",
                "action"     => "index",
            ]
        );

        // 返回 "false" 我们将告诉分发器停止当前操作
        return false;
    }
}

} ```

3.3.4 提供 ACL 列表

ACL :Access Control Lists 访问控制列表

在上面的例子中我们已经获得了ACL的使用方法 $this->getAcl(). 这个方法也是在插件中实现的. 现在我们要逐步解释如何建立访问控制列表(ACL):

```php use Phalcon\Acl; use Phalcon\Acl\Role; use Phalcon\Acl\Adapter\Memory as AclList;

// 创建一个 ACL $acl = new AclList();

// 默认行为是 DENY(拒绝) 访问 $acl->setDefaultAction( Acl::DENY );

// 注册两个角色, 用户是已注册用户和没有定义身份的来宾用户 $roles = [ "users" => new Role("Users"), "guests" => new Role("Guests"), ];

foreach ($roles as $role) { $acl->addRole($role); } ```

分别为每个区域定义资源. 控制器名称是资源它们的方法是对资源的访问:

```php use Phalcon\Acl\Resource;

// ...

// 私有区域资源 (后台) $privateResources = [ "companies" => ["index", "search", "new", "edit", "save", "create", "delete"], "products" => ["index", "search", "new", "edit", "save", "create", "delete"], "producttypes" => ["index", "search", "new", "edit", "save", "create", "delete"], "invoices" => ["index", "profile"], ];

foreach ($privateResources as $resourceName => $actions) { $acl->addResource( new Resource($resourceName), $actions ); }

// 公共区域资源 (前台) $publicResources = [ "index" => ["index"], "about" => ["index"], "register" => ["index"], "errors" => ["show404", "show500"], "session" => ["index", "register", "start", "end"], "contact" => ["index", "send"], ];

foreach ($publicResources as $resourceName => $actions) { $acl->addResource( new Resource($resourceName), $actions ); } ```

ACL现在了解现有的控制器和它们相关的操作. 角色 “Users” 由权限访问前台和后台的所有资源. 角色 “Guests” 仅允许访问公共区域:

```php // 授权user和Grant访问公共区域 foreach ($roles as $role) { foreach ($publicResources as $resource => $actions) { $acl->allow( $role->getName(), $resource, "*" ); } }

// 授权仅角色Users 访问私有区域 foreach ($privateResources as $resource => $actions) { foreach ($actions as $action) { $acl->allow( "Users", $resource, $action ); } } ```

3.4 CURD

3.4.1 表单搜索

每个 CRUD 都开始于一个搜索表单. 这个表单展示了表(products)中的每个字段, 允许用户为一些字段创建一个搜索条件. 表 “products” 和表 “products_types” 是关系表. 既然这样, 我们先前查询表中的记录以便于字段的搜索:

ProductsForm 表单的实例 (app/forms/ProductsForm.php)传递给了视图. 这个表单定义了用户可见的字段:

```php use Phalcon\Forms\Form; use Phalcon\Forms\Element\Text; use Phalcon\Forms\Element\Hidden; use Phalcon\Forms\Element\Select; use Phalcon\Validation\Validator\Email; use Phalcon\Validation\Validator\PresenceOf; use Phalcon\Validation\Validator\Numericality;

class ProductsForm extends Form { /** * 初始化产品表单 */ public function initialize($entity = null, $options = []) { if (!isset($options["edit"])) { $element = new Text("id");

        $element->setLabel("Id");

        $this->add(
            $element
        );
    } else {
        $this->add(
            new Hidden("id")
        );
    }



    $name = new Text("name");

    $name->setLabel("Name");

    $name->setFilters(
        [
            "striptags",
            "string",
        ]
    );

    $name->addValidators(
        [
            new PresenceOf(
                [
                    "message" => "Name is required",
                ]
            )
        ]
    );

    $this->add($name);



    $type = new Select(
        "profilesId",
        ProductTypes::find(),
        [
            "using"      => [
                "id",
                "name",
            ],
            "useEmpty"   => true,
            "emptyText"  => "...",
            "emptyValue" => "",
        ]
    );

    $this->add($type);



    $price = new Text("price");

    $price->setLabel("Price");

    $price->setFilters(
        [
            "float",
        ]
    );

    $price->addValidators(
        [
            new PresenceOf(
                [
                    "message" => "Price is required",
                ]
            ),
            new Numericality(
                [
                    "message" => "Price is required",
                ]
            ),
        ]
    );

    $this->add($price);
}

} ```

表单是使用面向对象的方式声明的, 基于 forms 组件提供的元素. 每个元素都遵循近乎相同的结构:

```php // 创建一个元素 $name = new Text("name");

// 设置它的label $name->setLabel("Name");

// 在验证元素之前应用这些过滤器 $name->setFilters( [ "striptags", "string", ] );

// 应用此验证 $name->addValidators( [ new PresenceOf( [ "message" => "Name is required", ] ) ] );

// 增加元素到表单 $this->add($name); ```

在表单中其它元素也是这样使用:

```php // 增加一个隐藏input到表单 $this->add( new Hidden("id") );

// ...

$productTypes = ProductTypes::find();

// 增加一个HTML Select (列表) 到表单 // 数据从"product_types"中填充 $type = new Select( "profilesId", $productTypes, [ "using" => [ "id", "name", ], "useEmpty" => true, "emptyText" => "...", "emptyValue" => "", ] ); ```

注意, ProductTypes::find() 包含的必须的数据 使用 Phalcon\Tag::select() 来填充 SELECT 标签. 一旦表单传递给视图, 它会进行渲染并呈现给用户:

```php {{ form("products/search") }}

<h2>
    Search products
</h2>

<fieldset>

    {% for element in form %}
        <div class="control-group">
            {{ element.label(["class": "control-label"]) }}

            <div class="controls">
                {{ element }}
            </div>
        </div>
    {% endfor %}



    <div class="control-group">
        {{ submit_button("Search", "class": "btn btn-primary") }}
    </div>

</fieldset>

{{ endForm() }} ```

3.5 定制INVO

3.6 Vökuró

四、组件

4.1 依赖注入与服务定位器

Phalcon\Di 是一个实现依赖注入和定位服务的组件,而且它本身就是一个装载它们的容器

因为Phalcon是高度解构的,整合框架的不同组件,使用 Phalcon\Di 是必不可少的。开发者也可以使用这个组件去注入依赖和管理的应用程序中来自不同类的全局实例。

基本上,这个组件实现了 控制反转的模式。使用这种模式,组件的对象不用再使用setter或者构造函数去接受依赖实例,而是使用请求服务的依赖注入。这减少了总的复杂性,因为在组件内,只有一个方法去获取所需的依赖实例。

另外,该模式增加了代码的可测试性,从而使其不易出错。

4.1.1 为什么使用

接下来的例子解释了为什么我们使用依赖注入与服务定位器. 首先,假设我们正在开发一个组件,叫SomeComponent,它执行的内容现在还不重要。 我们的组件需要依赖数据库的连接,比较以下几种实现方式

1. 内部写死

在下面第一个例子中,数据库的连接是在组件内部建立的。这种方法是不实用的;事实上这样做的话,我们不能改变创建数据库连接的参数或者选择不同的数据库系统,因为连接是当组件被创建时建立的。

```php class SomeComponent { /** * 连接数据库的实例是被写死在组件的内部 * 因此,我们很难从外部替换或者改变它的行为 */ public function someDbTask() { $connection = new Connection( [ "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo", ] );

    // ...
}

}

$some = new SomeComponent(); $some->someDbTask(); ```

2. 创建setter

为了解决这样的情况,我们建立一个setter,在使用前注入独立外部依赖。现在,看起来似乎是一个不错的解决办法:

```php class SomeComponent { protected $_connection;

/**
 * 设置外部传入的数据库的连接实例
 */
public function setConnection($connection)
{
    $this->_connection = $connection;
}

public function someDbTask()
{
    $connection = $this->_connection;

    // ...
}

}

$some = new SomeComponent();

// 建立数据库连接实例 $connection = new Connection( [ "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo", ] );

// 向组件注入数据连接实例 $some->setConnection($connection);

$some->someDbTask(); ```

3. 使用全局注册表

```php class Registry { /** * 返回数据库连接实例 */ public static function getConnection() { return new Connection( [ "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo", ] ); } }

class SomeComponent { protected $_connection;

/**
 * 设置外部传入的数据库的连接实例
 */
public function setConnection($connection)
{
    $this->_connection = $connection;
}

public function someDbTask()
{
    $connection = $this->_connection;

    // ...
}

}

$some = new SomeComponent();

// 把注册表中的连接实例传递给组件 $some->setConnection(Registry::getConnection());

$some->someDbTask(); ```

现在,让我们设想一下,我们必须实现2个方法,第一个方法是总是创建一个新的连接,第二方法是总是使用一个共享连接:

```php

class Registry { protected static $_connection;

/**
 * 建立一个新的连接实例
 */
protected static function _createConnection()
{
    return new Connection(
        [
            "host"     => "localhost",
            "username" => "root",
            "password" => "secret",
            "dbname"   => "invo",
        ]
    );
}

/**
 * 只建立一个连接实例,后面的请求只返回该连接实例
 */
public static function getSharedConnection()
{
    if (self::$_connection === null) {
        self::$_connection = self::_createConnection();
    }

    return self::$_connection;
}

/**
 * 总是返回一个新的连接实例
 */
public static function getNewConnection()
{
    return self::_createConnection();
}

}

class SomeComponent { protected $_connection;

/**
 * 设置外部传入的数据库的连接实例
 */
public function setConnection($connection)
{
    $this->_connection = $connection;
}

/**
 * 这个方法总是需要共享连接实例
 */
public function someDbTask()
{
    $connection = $this->_connection;

    // ...
}

/**
 * 这个方法总是需要新的连接实例
 */
public function someOtherDbTask($connection)
{

}

}

$some = new SomeComponent();

// 注入共享连接实例 $some->setConnection( Registry::getSharedConnection() );

$some->someDbTask();

// 这里我们总是传递一个新的连接实例 $some->someOtherDbTask( Registry::getNewConnection() ); ```

到目前为止,我们已经看到依赖注入怎么解决我们的问题了。把依赖作为参数来传递,而不是建立在内部建立它们,这使我们的应用更加容易维护和更加解耦。不管怎么样,长期来说,这种形式的依赖注入有一些缺点。

4. 多个依赖

例如,如果这个组件有很多依赖, 我们需要创建多个参数的setter方法​​来传递依赖关系,或者建立一个多个参数的构造函数来传递它们,另外在使用组件前还要每次都创建依赖,这让我们的代码像这样不易维护:

```php // 创建依赖实例或从注册表中查找 $connection = new Connection(); $session = new Session(); $fileSystem = new FileSystem(); $filter = new Filter(); $selector = new Selector();

// 把实例作为参数传递给构造函数 $some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);

// ... 或者使用setter $some->setConnection($connection); $some->setSession($session); $some->setFileSystem($fileSystem); $some->setFilter($filter); $some->setSelector($selector); ```

假设我们必须在应用的不同地方使用和创建这些对象。如果当你永远不需要任何依赖实例时,你需要去删掉构造函数的参数,或者去删掉注入的setter。为了解决这样的问题,我们再次回到全局注册表创建组件。不管怎么样,在创建对象之前,它增加了一个新的抽象层

```php class SomeComponent { // ...

/**
 * Define a factory method to create SomeComponent instances injecting its dependencies
 */
public static function factory()
{
    $connection = new Connection();
    $session    = new Session();
    $fileSystem = new FileSystem();
    $filter     = new Filter();
    $selector   = new Selector();

    return new self($connection, $session, $fileSystem, $filter, $selector);
}

} ```

瞬间,我们又回到刚刚开始的问题了,我们再次创建依赖实例在组件内部!我们可以继续前进,找出一个每次能奏效的方法去解决这个问题。但似乎一次又一次,我们又回到了不实用的例子中。

5. 最终解决方法

一个实用和优雅的解决方法,是为依赖实例提供一个容器。这个容器担任全局的注册表,就像我们刚才看到的那样。使用依赖实例的容器作为一个桥梁来获取依赖实例,使我们能够降低我们的组件的复杂性

```php use Phalcon\Di; use Phalcon\DiInterface;

class SomeComponent { protected $_di;

public function __construct(DiInterface $di)
{
    $this->_di = $di;
}

public function someDbTask()
{
    // 获得数据库连接实例
    // 总是返回一个新的连接
    $connection = $this->_di->get("db");
}

public function someOtherDbTask()
{
    // 获得共享连接实例
    // 每次请求都返回相同的连接实例
    $connection = $this->_di->getShared("db");

    // 这个方法也需要一个输入过滤的依赖服务
    $filter = $this->_di->get("filter");
}

}

$di = new Di();

// 在容器中注册一个db服务 $di->set( "db", function () { return new Connection( [ "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo", ] ); } );

// 在容器中注册一个filter服务 $di->set( "filter", function () { return new Filter(); } );

// 在容器中注册一个session服务 $di->set( "session", function () { return new Session(); } );

// 把传递服务的容器作为唯一参数传递给组件 $some = new SomeComponent($di);

$some->someDbTask(); ```

这个组件现在可以很简单的获取到它所需要的服务,服务采用延迟加载的方式,只有在需要使用的时候才初始化,这也节省了服务器资源。这个组件现在是高度解耦。例如,我们可以替换掉创建连接的方式,它们的行为或它们的任何其他方面,也不会影响该组件。

4.1.2 使用容器注册服务

框架本身或者开发者都可以注册服务。当一个组件A需要组件B(或者它的类的实例) 去操作,它可以通过容器去请求组件B,而不是创建一个新的组件B实例。

这个工作方法给我们提供了许多优势:

  • 我们可以很容易的使用一个我们自己建立的或者是第三方的组件去替换原有的组件。
  • 我们完全控制对象的初始化,这让我们在传递它们的实例到组件之前,根据需要设置这些对象。
  • 我们可以在一个结构化的和统一组件内获取全局实例。

服务可以使用不同方式去定义:

1. 简单注册
  • 字符串

    使用字符串注册服务需要一个有效的类名称,它将返回指定的类对象,如果类还没有加载的话,将使用自动加载器实例化对象。这种类型不允许向构造函数指定参数:

    php // 返回 new Phalcon\Http\Request(); 对象 $di->set( "request", "Phalcon\\Http\\Request" );

  • 对象

    这种类型注册服务需要一个对象。实际上,这个服务不再需要初始化,因为它已经是一个对象,可以说,这是不是一个真正的依赖注入,但是如果你想强制总是返回相同的对象/值,使用这种方式还是有用的:

    ```php use Phalcon\Http\Request;

    // 返回 Phalcon\Http\Request(); 对象 $di->set( "request", new Request() ); ```

  • 闭包与匿名函数

    这个方法提供了更加自由的方式去注册依赖,但是如果你想从外部改变实例化的参数而不用改变注册服务的代码,这是很困难的:

    ```php use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

    $di->set( "db", function () { return new PdoMysql( [ "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "blog", ] ); } ); ```

    这些限制是可以克服的,通过传递额外的变量到闭包函数里面:

    ```php use Phalcon\Config; use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

    $config = new Config( [ "host" => "127.0.0.1", "username" => "user", "password" => "pass", "dbname" => "my_database", ] );

    // 把当前域的$config变量传递给匿名函数使用 $di->set( "db", function () use ($config) { return new PdoMysql( [ "host" => $config->host, "username" => $config->username, "password" => $config->password, "dbname" => $config->name, ] ); } ); ```

    You can also access other DI services using the get() method:

    ```php use Phalcon\Config; use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

    $di->set( "config", function () { return new Config( [ "host" => "127.0.0.1", "username" => "user", "password" => "pass", "dbname" => "my_database", ] ); } );

    // Using the 'config' service from the DI $di->set( "db", function () { $config = $this->get("config");

        return new PdoMysql(
            [
                "host"     => $config->host,
                "username" => $config->username,
                "password" => $config->password,
                "dbname"   => $config->name,
            ]
        );
    }
    

    ); ```

2. 复杂的注册
  • 数组方式

    如果要求不用实例化/解析服务,就可以改变定义服务的话,我们需要使用数组的方式去定义服务。使用数组去定义服务可以更加详细:

    ```php use Phalcon\Logger\Adapter\File as LoggerFile;

    // 通过类名和参数,注册logger服务 $di->set( "logger", [ "className" => "Phalcon\Logger\Adapter\File", "arguments" => [ [ "type" => "parameter", "value" => "../apps/logs/error.log", ] ] ] );

    // 使用匿名函数的方式 $di->set( "logger", function () { return new LoggerFile("../apps/logs/error.log"); } ); ```

    上面两种注册服务的方式的结果是一样的。然而,使用数组定义的话,在需要的时候可以变更注册服务的参数

    ```php // 改变logger服务的类名 $di->getService("logger")->setClassName("MyCustomLogger");

    // 不用实例化就可以改变第一个参数值 $di->getService("logger")->setParameter( 0, [ "type" => "parameter", "value" => "../apps/logs/error.log", ] ); ```

  • 构造函数注入

    这个注入方式是通过传递依赖/参数到类的构造函数。让我们假设我们有下面的组件

    ```php namespace SomeApp;

    use Phalcon\Http\Response;

    class SomeComponent { /**

    • @var Response */ protected $_response;

      protected $_someFlag;

      public function __construct(Response $response, $someFlag) { $this->_response = $response; $this->_someFlag = $someFlag; } } ```

    这个服务可以这样被注入

    ```php $di->set( "response", [ "className" => "Phalcon\Http\Response" ] );

    $di->set( "someComponent", [ "className" => "SomeApp\SomeComponent", "arguments" => [ [ "type" => "service", "name" => "response", ], [ "type" => "parameter", "value" => true, ], ] ] ); ```

    reponse服务(Phalcon\Http\Response)作为第一个参数传递给构造函数,与此同时,一个布尔类型的值(true)作为第二个参数传递。

  • 设值注入

    类中可能有setter去注入可选的依赖,前面那个class可以修改成通过setter来注入依赖的方式:

    ```php namespace SomeApp;

    use Phalcon\Http\Response;

    class SomeComponent { /** * @var Response */ protected $_response;

    protected $_someFlag;
    
    
    public function setResponse(Response $response)
    {
        $this->_response = $response;
    }
    
    
    public function setFlag($someFlag)
    {
        $this->_someFlag = $someFlag;
    }
    

    } ```

    用setter方式来注入的服务可以通过下面的方式来注册:

    ```php $di->set( "response", [ "className" => "Phalcon\Http\Response", ] );

    $di->set( "someComponent", [ "className" => "SomeApp\SomeComponent", "calls" => [ [ "method" => "setResponse", "arguments" => [ [ "type" => "service", "name" => "response", ] ] ], [ "method" => "setFlag", "arguments" => [ [ "type" => "parameter", "value" => true, ] ] ] ] ] ); ```

  • 属性注入

    这是一个不太常用的方式,这种方式的注入是通过类的public属性来注入

    ```php namespace SomeApp;

    use Phalcon\Http\Response;

    class SomeComponent { /** * @var Response */ public $response;

    public $someFlag;
    

    } ```

    通过属性注入的服务,可以像下面这样注册:

    ```php $di->set( "response", [ "className" => "Phalcon\Http\Response", ] );

    $di->set( "someComponent", [ "className" => "SomeApp\SomeComponent", "properties" => [ [ "name" => "response", "value" => [ "type" => "service", "name" => "response", ], ], [ "name" => "someFlag", "value" => [ "type" => "parameter", "value" => true, ], ] ] ] ); ```

    支持包括下面的参数类型:

    | Type | 描述 | 例子 | | :-- | :-- | :-- | | parameter | 表示一个文本值作为参数传递过去 | ["type" => "parameter", "value" => 1234] | | service | 表示作为服务 | ["type" => "service", "name" => "request"] | | instance | 表示必须动态生成的对象 | ["type" => "instance", "className" => "DateTime", "arguments" => ["now"]]|

    解析一个定义复杂的服务也许性能上稍微慢于先前看到的简单定义。但是,这提供了一个更强大的方式来定义和注入服务。

    混合不同类型的定义是可以的,每个人可以应用需要决定什么样的注册服务的方式是最适当的。

3. 数组

使用数组的方式去注册服务也是可以的

```php use Phalcon\Di; use Phalcon\Http\Request;

// 创建一个依赖注入容器 $di = new Di();

// 通过类名称设置服务 $di["request"] = "Phalcon\Http\Request";

// 使用匿名函数去设置服务,这个实例将被延迟加载 $di["request"] = function () { return new Request(); };

// 直接注册一个实例 $di["request"] = new Request();

// 使用数组方式定义服务 $di["request"] = [ "className" => "Phalcon\Http\Request", ]; ```

在上面的例子中,当框架需要访问request服务的内容,它会在容器里面查找名为‘request’的服务。 在容器中将返回所需要的服务的实例。当有需要时,开发者可能最终需要替换这个组件。

每个方法(在上面的例子证明)用于设置/注册服务方面具都具有优势和劣势。这是由开发者和特别的要求决定具体使用哪个。

通过字符串设置一个服务是很简单,但是缺乏灵活性。通过数组设置服务提供了更加灵活的方式,但是使代码更复杂。匿名函数是上述两者之间的一个很好的平衡,但是会导致比预期的更多维护。

Phalcon\Di 对每个储存的服务提供了延迟加载。除非开发者选择直接实例化一个对象并将其存储在容器中,任何储存在里面的对象(通过数组,字符串等等设置的)都将延迟加载,即只要当使用到时才实例化。

4. 服务解疑

从容器中获取一个服务是一件简单的事情,只要通过“get”方法就可以。这将返回一个服务的新实例:

php <?php $request = $di->get("request");

或者通过魔术方法的方式获取:

php $request = $di->getRequest();

或者通过访问数组的方式获取:

php $request = $di["request"];

参数可以传递到构造函数中,通过添加一个数组的参数到get方法中:

php // 将返回:new MyComponent("some-parameter", "other") $component = $di->get( "MyComponent", [ "some-parameter", "other", ] );

  • Events

    Phalcon\Di is able to send events to an EventsManager if it is present. Events are triggered using the type “di”. Some events when returning boolean false could stop the active operation. The following events are supported:

    | Event Name | Triggered | Can stop operation? | Triggered on | | :-- | :-- | :-- | :-- | | beforeServiceResolve | Triggered before resolve service. Listeners receive the service name and the parameters passed to it. | No | Listeners | | afterServiceResolve | Triggered after resolve service. Listeners receive the service name, instance, and the parameters passed to it. | No | Listeners |

5. 共享服务

服务可以注册成“shared”类型的服务,这意味着这个服务将使用 单例模式 运行, 一旦服务被首次解析后,这个实例将被保存在容器中,之后的每次请求都在容器中查找并返回这个实例

```php use Phalcon\Session\Adapter\Files as SessionFiles;

// 把session服务注册成“shared”类型 $di->setShared( "session", function () { $session = new SessionFiles();

    $session->start();

    return $session;
}

);

// 第一次获取session服务时,session服务将实例化 $session = $di->get("session");

// 第二次获取时,不再实例化,直接返回第一次实例化的对象 $session = $di->getSession(); ```

另一种方式去注册一个“shared”类型的服务是,传递“set”服务的时候,把true作为第三个参数传递过去:

php // 把session服务注册成“shared”类型 $di->set( "session", function () { // ... }, true );

如果一个服务不是注册成“shared”类型,而你又想从DI中获取服务的“shared”实例,你可以使用getShared方法:

php $request = $di->getShared("request");

6. 单独操作服务

一旦服务被注册到服务容器中,你可以单独操作它:

```php use Phalcon\Http\Request;

// 注册request服务 $di->set("request", "Phalcon\Http\Request");

// 获取服务 $requestService = $di->getService("request");

// 改变它的定义 $requestService->setDefinition( function () { return new Request(); } );

// 修改成shared类型 $requestService->setShared(true);

// 解析服务(返回Phalcon\Http\Request实例) $request = $requestService->resolve(); ```

7. 通过服务容器实例化类

当你从服务容器中请求一个服务,如果找不到具有相同名称的服务,它将尝试去加载以这个服务为名称的类。利用这个的行为, 我们可以代替任意一个类,通过简单的利用服务的名称来注册:

```php // 把一个控制器注册为服务 $di->set( "IndexController", function () { $component = new Component();

    return $component;
},
true

);

// 把一个控制器注册为服务 $di->set( "MyOtherComponent", function () { // 实际上返回另外一个组件 $component = new AnotherComponent();

    return $component;
}

);

// 获取通过服务容器创建的对象 $myComponent = $di->get("MyOtherComponent"); ```

你可以利用这种方式,通过服务容器来总是实例化你的类(即是他们没有注册为服务), DI会回退到一个有效的自动加载类中,去加载这个类。通过这样做,以后你可以轻松替换任意的类通过为它实现一个定义。

8. 自动注入 DI

如果一个类或者组件需要用到DI服务,你需要在你的类中实现 Phalcon\Di\InjectionAwareInterface 接口, 这样就可以在实例化这个类的对象时自动注入DI的服务:

```php use Phalcon\DiInterface; use Phalcon\Di\InjectionAwareInterface;

class MyClass implements InjectionAwareInterface { /** * @var DiInterface */ protected $_di;

public function setDi(DiInterface $di)
{
    $this->_di = $di;
}

public function getDi()
{
    return $this->_di;
}

} ```

按照上面这样,一旦服务被解析,$di 对象将自动传递到 setDi() 方法:

```php // 注册服务 $di->set("myClass", "MyClass");

// 解析服务(注意:将自动调用$myClass->setDi($di)方法) $myClass = $di->get("myClass"); ```

9. 使用文件组织服务

你可以更好的组织你的应用,通过移动注册的服务到独立的文件里面,而不是全部写在应用的引导文件中:

$di->set( "router", function () { return include "../app/config/routes.php"; } ); 这样,在文件(”../app/config/routes.php”)中,返回已解析的对象:

```php $router = new MyRouter();

$router->post("/login");

return $router; ```

10. 使用静态的方式访问注入器

如果需要的话,你可以访问最新创建的DI对象,通过下面这种静态方法的方式:

```php use Phalcon\Di;

class SomeComponent { public static function someMethod() { // 获取session服务 $session = Di::getDefault()->getSession(); } } ```

11. 注入器默认工厂

尽管Phalcon的解耦性质为我们提供了很大的自由度和灵活性,也许我们只是单纯的想使用它作为一个全栈框架。 为了达到这点,框架提供了变种的 Phalcon\Di 叫 Phalcon\Di\FactoryDefault 。这个类会自动注册相应的服务,并捆绑在一起作为一个全栈框架。

```php

use Phalcon\Di\FactoryDefault;

$di = new FactoryDefault(); ```

12. 服务名称约定

尽管你可以用你喜欢的名字来注册服务,但是Phalcon有一些命名约定,这些约定让你在需要的时候,可以获得正确的(内置)服务。

| 服务名称 | 介绍 | 默认 | 是否是shared服务 | | :-- | :-- | :-- | :-- | | dispatcher | 控制器调度服务 | Phalcon\Mvc\Dispatcher | 是 | | router | 路由服务 | Phalcon\Mvc\Router | 是 | | url | URL生成服务 | Phalcon\Mvc\Url | 是 | | request | HTTP 请求环境服务 | Phalcon\Http\Request | 是 | | response | HTTP响应环境服务 | Phalcon\Http\Response | 是 | | cookies | HTTP Cookie管理服务 | Phalcon\Http\Response\Cookies | 是 | | filter | 输入过滤服务 | Phalcon\Filter | 是 | | flash | 闪现信息服务 | Phalcon\Flash\Direct | 是 | | flashSession | 闪现session信息服务 | Phalcon\Flash\Session | 是 | | session | session服务 | Phalcon\Session\Adapter\Files | 是 | | eventsManager | 事件管理服务 | Phalcon\Events\Manager | 是 | | db | 底层数据库连接服务 | Phalcon\Db | 是 | | security | 安全助手 | Phalcon\Security | 是 | | crypt | 加密/解密数据 | Phalcon\Crypt | 是 | | tag | HTML生成助手 | Phalcon\Tag | 是 | | escaper | 内容(HTML)转义 | Phalcon\Escaper | 是 | | annotations | 注释分析器 | Phalcon\Annotations\Adapter\Memory | 是 | | modelsManager | model管理服务 | Phalcon\Mvc\Model\Manager | 是 | | modelsMetadata | model元数据服务 | Phalcon\Mvc\Model\MetaData\Memory | 是 | | transactionManager | model事务管理服务 | Phalcon\Mvc\Model\Transaction\Manager | 是 | | modelsCache | model的缓存服务 | None | No | | viewsCache | view的缓存服务 | None | No |

13. 自定义注入器

如果你要创建一个自定义注入器或者继承一个已有的,接口 Phalcon\DiInterface 必须被实现。

4.2 MVC架构

Phalcon 提供了面向对象的类,在应用程序中必须实现模型(Model)、视图(View)、控制器(Controller)架构(通常我们称之为 MVC)。这种设计模式被广泛的应用到其他 web 框架以及桌面应用程序中。

MVC 优点:

  • 隔离业务逻辑、用户界面和数据库层
  • 不同类型的代码之间更加明确易于维护。

如果你决定使用MVC架构来开发你的程序,那么应用程序的每个请求都将采用 MVC 架构的方式来管理。 Phalcon 的类是使用 C 语言编写而成, 这是为这种模式开发的 PHP 应用程序提供高性能的方法。

4.3 控制器

4.4 模型

4.5 Model Relationships

4.6 Model Events

4.7 Model Behaviors

附录

Phalcon : https://github.com/phalcon/cphalcon php-phalcon-mamp : https://github.com/majksner/php-phalcon-mamp 控制反转 : http://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC