PHP MVC 传递数据以响应 object 到 body(视图)?

PHP MVC pass data in response object to body(view)?

我正在使用 php + blade 并编写了简单的 mvc,现在我想知道如何将数据传递给视图。 我的回复 object 包含 header、状态代码和 body(视图)。可以将数据传递到视图旁边的响应 body 部分吗?
->setBody(['view'=>'result','somedata'=>$somedata]);

或响应 object 应具有状态码、header、body(视图)和与 body(视图)分开的数据?

现在在控制器中我有:

return Response::create()
    ->setStatusCode(200)
    ->setHeader(['Content-Type' => 'text/html'])
    ->setBody(['view'=>'result']);

我的回复class:

class response implements ResponseInterface
{
    protected $statusCode;
    protected $headers;
    protected $body=[];

    public function setStatusCode(string $code) :object
    {
        $this->statusCode = $code;
        return $this;
    }

    public function setHeader(array $header):object
    {
        $this->headers=$header;
        return $this;
    }

    public function setBody(array $body): object
    {
        $this->body = $body;
        return $this;
    }

    public function getStatusCode():string
    {
        return $this->statusCode;
    }
    public function getHeaders():array
    {
        return $this->headers;
    }
    public function getBody():array
    {
        return $this->body;
    }
    public static function create(string $statusCode = null, array $headers = null, string $body = null): response
    {
        return new Response($statusCode, $headers, $body);
    }
    public function send(): void
    {
        foreach ($this->headers as $header => $value) {
            header(strtoupper($header).': '.$value);
        }

        if ($this->headers['Content-Type']==="text/html" && $this->statusCode === "200") {
            View::renderTemplate($this->body['view']);
        }


    }
}

通过在控制器中添加 fx 数据表变量,我能够像这样传递数据并且它有效:

 return Response::create()
        ->setStatusCode(200)
        ->setHeader(['Content-Type' => 'text/html'])
        ->setBody(['view'=>'result','datatable'=>$datatable]);

然后需要将响应发送操作调整为:

public function send(): void
{
    foreach ($this->headers as $header => $value) {
        header(strtoupper($header).': '.$value);
    }

    if ($this->headers['Content-Type']==="text/html" && $this->statusCode === "200") {
        View::renderTemplate($this->body['view'],'datatable'=>$this->body['datatable']);
    }

}

响应工厂

Response 实例不应负责创建 Response object。此任务应由 响应工厂 执行。一个例子:

class ResponseFactory extends MessageFactory implements ResponseFactoryInterface {

    //...

    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface {
        return new Response($this->body, $code, $reasonPhrase, $this->headers, $this->protocolVersion);
    }

    //...

}

响应发射器

Response 实例不应负责发送 HTTP 响应的响应 headers。此任务连同将响应 body 的内容打印到屏幕,应由 响应发射器 执行。一个例子:

class ResponseEmitter implements ResponseEmitterInterface {

    //...

    public function emit(ResponseInterface $response): void {
        $this
            ->checkHeadersSent()
            ->sendStatusLine($response)
            ->sendResponseHeaders($response)
            ->outputBody($response)
        ;

        exit();
    }

    //...

}

模板renderer/engine

Response 实例不应该负责呈现模板文件(也称为 views)。因此,不应将其耦合到任何模板渲染器或 View 实例以渲染模板文件。渲染模板文件的任务应该由模板renderer/engine执行。一个例子:

class TemplateRenderer extends BaseTemplateRenderer implements TemplateRendererInterface {

    //...

    public function render(string $templateName, array $context = []): string {
        $templateFile = $this->templatesPath . $this->buildTemplateName($templateName);

        $this
            ->validateTemplateFile($templateFile)
            ->saveContextValuesToContextCollection($context)
        ;

        return $this->renderTemplateFile($templateFile);
    }

    private function renderTemplateFile(string $filename): string {
        ob_start();

        extract($this->contextCollection->all());

        require $filename;

        $content = ob_get_contents();

        ob_end_clean();

        return $content;
    }

    //...

}

呈现的内容(一个字符串!)然后被传递到一个响应实例的body。

例子

这里是一个class的例子,它的方法addUser负责渲染给定的模板文件,创建一个Response实例,将渲染的内容写入Responsebody并返回响应。

/**
 * A view to handle the users.
 */
class Users extends Primary {

    //...

    /**
     * Add a user.
     * 
     * @return ResponseInterface The response to the current request.
     */
    public function addUser(): ResponseInterface {
        $bodyContent = $this->templateRenderer->render('@Templates/Users/Users.html.twig', [
            'activeNavItem' => 'Users',
            'message' => 'User successfully added',
            'users' => $this->getAllUsers(),
        ]);

        $response = $this->responseFactory->createResponse();
        $response->getBody()->write($bodyContent);

        return $response;
    }

    //...

}

Users:addUser 返回的 Response 由 Response 发射器进一步处理。它的 body 内容最终打印在屏幕上。

// Create a request.
$request = $container->get('request');

// Create a middleware queue.
$middlewareQueue = $container->get('middleware-queue');

// Handle the request and produce a response.
$response = $middlewareQueue->handle($request);

// Emit the response.
$responseEmitter = $container->get('response-emitter');
$responseEmitter->emit($response);

返回...您的简化回复

因此,对于您的情况,Response 可以简化为以下内容。由您来开发其余的组件 - 我上面提到的组件。

class Response implements ResponseInterface {

    public function getStatusCode(): int {
        //...
    }

    public function setStatusCode(int $code): static {
        //...
    }

    public function getHeaders(): array {
        //...
    }

    public function setHeader(array $header): static {
        //...
    }

    public function getBody(): string {
        //...
    }

    public function setBody(string $content): static {
        //...
    }

}

注意:如果您在方法中使用return $this;,则使用static而不是object作为返回数据类型。

一些建议

如果可以的话,我建议您开发一个 MVC-based 应用程序,该应用程序的组件使用外部库。例如,与其开发自己的 HTTP 消息实现(请求、响应、URI、上传的文件等)——这会花费你很长时间!我建议使用 Laminas Diactoros package. Or to use Twig 作为模板引擎和您的应用程序的渲染器。等等。

注意:对了,Twig模板引擎的the render method会给你解答你的问题(Is it ok pass数据响应 body 视图旁边的部分?).

此外,对于您的 PHP 应用程序,如果您还没有这样做,我建议您遵循 PHP Standards Recommendations.

资源