控制器基础知识

在基本层面上,Controller 是当你在 XF 中访问一个页面时运行的代码。 在基本层面上,Controller 一般负责处理用户的输入,并将用户的输入传递到适当的地方,通常是运行某种数据库操作(Model)或加载可视化内容(View)。

当用户点击链接时,请求的 URL 会被路由导到特定的 Controller 和 Controller action。 参见 路由基础知识。 例如,在 XF 中,如果你点击一个类似于 index.php?conversations/add 的 URL,你将被路由导到 XF\Pub\Controller\Conversation Controller 和 add action。

如果你在文件系统中查看这个 class(参见 自动加载器,以了解 class 和文件路径如何相互映射的描述),你会注意到有许多方法以 action 为前缀命名。 所有这些方法都表示一个特定的 Controller action。 所以,要想在查看上面提到的 conversations/add 页面时看到相关代码,请在这个文件中查找 public function actionAdd()

XF Controller 负责返回一个 reply 物件,该物件一般包括以下类型之一:

View 回应

这是 XF 开发过程中最常见的回应之一。 一个返回 View 回应的 Controller 通常需要传递多达三个参数。 一个 View class (下面会有更多的介绍),一个模板名称,以及一个由 $viewParams 组成的数组,这个数组是模板应该有的数据。

这里是一个典型的 Controller action 的例子,它返回一个 View 回应:

public function actionExample()
{
    $hello = 'Hello';
    $world = 'world!';

    $viewParams = [
        'hello' => $hello,
        'world' => $world
    ];
    return $this->view('Demo:Example', 'demo_example', $viewParams);
}

第一个参数是一个特定 View class 的简短 class 名。 这个 class 可能存在,也可能不存在(通常情况下,它不需要存在,我们在后面会介绍更多地 View class ),但它应该有一个 Controller 和 action 的大致唯一名称。 与其他 短类名 一样,上面的特定短类名将解析为 Demo/Pub/View/Example。 同样,Pub 是由 Controller 类型自动推断出来的。

第二个参数是模板名称。 在本例中,我们正在寻找一个名为 demo_example 的模板。

第三个参数是 应该提供给 view 模板 参数/值 的数组。 这个数组一般应该是一对 key => value。 上面的例子是将两个模板参数传递给模板。 数组中的 key 部分表示模板中可用的变量名称。 数组中的 value 部分表示值。

因此,如果我们在 demo_example 模板中有以下内容:

{$hello} {$world}

该模板将输出以下内容:

Hello world!

重新导向回应

当你希望在用户完成某种操作后将其重新导向到不同的 URL 时,会返回这个回应。

一个常见的使用案例是在用户通过表单提交数据后,你可能希望将他们重新导向到不同的页面,例如将用户返回到一个项目清単。

下面是一个典型的 Controller action 的例子,它运行了一个重新导向:

public function actionRedirect()
{
    return $this->redirect($this->buildLink('demo/example'), '这是一个重定向信息。', 'permanent');
}

第一个参数是要重新导向的 URL。 这个例子将把用户重新导向到 index.php?demo/example 的 URL。

第二个参数只有在表单通过 AJAX 请求提交时才会显示,AJAX 请求选择防止重新导向。 结果将是一个 "即时消息",从荧幕顶部出现您选择的消息。 您不必提供您自己的信息。 如果没有提供,它将默认为 "您的更改已被保存"。

第三个参数默认为 temporary,但你也可以选择将其设置为 permanent,就象本例一样。 这里唯一的区别是服务器提供的 HTTP 回应码的类型。 在大多数情况下,临时是理想的,这将以 303 码回应。 permanent 将发出 301 回应码。

虽然你可以通过这种方式触发一个 permanent 重新导向,但实际上有一个特定的方法,可以使用如下。 它也需要一个 'message' 参数,但和上面一样,它是可选的。

public function actionRedirect()
{
    return $this->redirectPermanently($this->buildLink('demo/example'));
}

错误回应

顾名思义,这个回应就是你需要向用户显示错误时返回的内容。 这稍稍简単,下面是一个例子:

public function actionError()
{
    return $this->error('很不幸,你要找的东西无法找到。', 404);
}

这里只支援两个参数。 第一个是你想显示的错误信息,第二个是你想让服务器发送的 HTTP 回应码。 404 将代表在没有找到东西时的适当回应。

消息回应

这个回应与错误回应非常相似,支援相同的参数。 主要的区别是,在外観上,显示的信息不是以错误的形式出现。

异常回应

有时有必要中断 Controller 代码的正常流程,并用异常回应来代替。 异常回应不一定代表一个错误;例如,它们可以用来强制 Controller 运行一个重新导向。 然而,通常情况下,它们通常会被用来中断 Controller 的流程以显示一个错误,就象下面的例子一样:

public function actionException()
{
    throw $this->exception($this->error('发生意外错误'));
}

异常回应只接受一个参数,实际上这个参数必须是其他形式的 Reply 物件,比如 错误回应。 这个特殊的例子会抛出一个异常,此时整个控制器的代码会停止,并显示一个标准的错误。

请注意,异常回应必须用 throw 来 "抛出",而不是用 return 来 "返回"。

重定路由回应

在某些情况下,需要将用户重新导向到一个完全不同的 Controller 或同一 Controller 内的 action,而不需要运行完全的重新导向,不需要改变用户登陆的 URL,也不需要重复目标 action 的代码。

这看起来有点象这样:

public function actionReroute()
{
    return $this->rerouteController(__CLASS__, 'error');
}

public function actionError()
{
    return $this->error('糟了!出了点问题!');
}

在这个特殊的例子中,如果用户导览到 index.php?demo/reroute URL,他们会看到 actionError() 方法的错误回应。 他们不会被重新导向,浏览器中的 URL 也不会改变,他们只是収到错误 action 的回应。

重定路由回应还支持第三个参数,它允许将各种参数从一个 Controller action 传递到另一个 Controller。 这可以是一个数组或一个 ParameterBag 物件(稍后将详细介绍)。

修改 Controller Action 回应 (properly)

继承 class 章节中,我们已经看到了继承一个 class 是多么的简単,但是当继承一个已经存在的 Controller action 时,需要格外小心。

除非你有特殊的须求,需要完全复盖一个现有的 action,并用新的东西来代替它(一般不建议这样做),否则你应该修改父类的现有回应。 这非常简単,举个例子,让我们修改上面 view 回应 例子中的 view 回应。

public function actionExample()
{
    $reply = parent::actionExample();

    return $reply;
}

假设上面的内容被添加到一个已经存在 actionExample() 方法的继承 Controller 中,上面的内容除了返回原来的 view 回应外,实际上并没有做任何事情。 现在让我们把现有的 hello 参数改为 "Bonjour" 而不是 "Hello"。

public function actionExample()
{
    $reply = parent::actionExample();

    if ($reply instanceof \XF\Mvc\Reply\View)
    {
        $reply->setParam('hello', 'Bonjour');
    }

    return $reply;
}

因为一个 Controller 回应实际上可以代表许多不同的物件,这些物件具有不同的行为和方法,所以我们必须只尝试继承正确的回应类型。 在上面的例子中,我们通过检查父 $reply 物件是否真的是 View 类型来做到这一点。 如果我们没有这样做,我们继承了这个 action,而 Controller action 却用重新导向来回应,那麽很可能会出现错误。

在继承这个动作之前,访问这个页面会显示 "Hello world!"。 继承后,现在 view 将显示 "Bonjour world!"。