Desenvolvimento

12 jan, 2018

Resolvendo o “Problema de Widget” em ADR

Publicidade

O “problema do widget” é quando você tem vários painéis ou áreas de conteúdo em uma página HTML que possui diferentes fontes de dados. Você deve ter uma área de conteúdo principal, depois um calendário do lado, com talvez uma lista de notícias recentes ou postagens de blog, um widget de tarefas a fazer ou lembretes, e talvez outros painéis de informações.

O problema é que cada um tem fontes de dados diferentes e podem nem sempre ser exibidos em todas as circunstâncias – talvez sejam apenas exibidos para alguns usuários com base em suas preferências ou sob certas condições.

Então, como em Action-Domain-Responder, podemos obter os dados “corretos” para o conjunto de widgets que realmente serão exibidos? (Presumirei aqui que toda a página está sendo processada do lado do servidor, para a entrega como um todo para o cliente.)

A resposta é “do mesmo modo que com qualquer outra coisa” – apenas temos mais tipos de dados para obter do domínio. O domínio possui todos os dados necessários e o conhecimento necessário para descobrir quais elementos de dados retornar.

Vamos começar com a Ação, que é intencionalmente muito livre: ela só coleta entrada, chama o Domínio com essa entrada e, então, invoca o Responder:

<?php
class PageAction
{
    public function __construct(
        PageService $domain,
        PageResponder $responder
    ) {
        $this->domain = $domain;
        $this->responder = $responder;
    }

    public function __invoke(HttpRequest $request)
    {
        $payload = $this->domain->fetchPageData(
            $request->getAttribute('sessionId'),
            $request->getAttribute('pageName')
        );
        return $this->responder->respond($request, $payload);
    }
}

O trabalho do domínio é onde o levantamento pesado ocorre. O exemplo abaixo retorna os objetos de domínio e os dados envolvidos em um objeto Payload de Domínio.

<?php
class PageService
{
    // presume $userService, $sessionService, and $database
    // dependencies are injected via constructor

    public function fetchPageData($sessionId, $pageName)
    {
        $session = $this->sessionService->resume($sessionId);
        $user = $this->userService->fetch($session->userId);

        // the main page data
        $mainData = $this->fetchMainData($pageName);
        if (! $mainData) {
            return new Payload('NOT_FOUND');
        }

        // an array of widgets to show
        $widgets = [];

        // different users might prefer to see different widgets
        foreach ($user->getWidgetsToShow() as $widgetName) {
            $method = "fetch{$widgetName}Data";
            $widgets[$widgetName] = $this->$method();
        }

        $this->sessionService->commit($session);

        return new Payload('FOUND', [
            'user' => $user,
            'page_name' => $pageName,
            'main_data' => $mainData,
            'widgets' => $widgets
        ]);
    }

    protected function fetchMainData($page_name)
    {
        return $this->database->fetchRow(
            "SELECT * FROM pages WHERE page_name = ? LIMIT 1",
            $page_name
        );
    }

    protected function fetchTodoData() { ... }

    protected function fetchRemindersData() { ... }

    protected function fetchUpcomingEventsData() { ... }

    protected function fetchCalendarData() { ... }

}

Finalmente, o trabalho do Respondente torna-se tão direto como: “Há dados a fazer para apresentar? Em seguida, processe o widget A fazer através de um auxiliar A fazer, usando os dados A fazer.” Pode ser tão simples quanto isso:

<?php
class PageResponder
{
    // ...

    public function respond(Request $request, Payload $payload)
    {
        if ($payload->getStatus() == 'NOT_FOUND') {
            return new Response(404);
        }

        $output = $payload->getOutput();

        $html = '';
        $html .= $this->renderHeader($output['page_name'];
        $html .= $this->renderNav();
        $html .= $this->renderMain($output['main_data']);

        foreach ($output['widgets'] as $widgetName => $widgetData) {
            $method = "render{$widgetName}Html";
            $html .= $this->$method($widgetData);
        }

        return new Response(200, $html);
    }

    protected function renderHeader($request, $pageName) { ... }

    protected function renderNav($request) { ... }

    protected function renderMain($request, $mainData) { ... }

    protected function renderTodoHtml($request, $widgetData) { ... }

    protected function renderRemindersHtml($request, $widgetData) { ... }

    protected function renderUpcomingEventsHtml($request, $widgetData) { ... }

    protected function renderCalendarHtml($request, $widgetData) { ... }
?>

Uma alternativa aqui é para algum Javascript do lado do cliente fazer uma chamada adicional por widget ou painel para recuperar dados específicos do widget e, em seguida, processar esses dados no cliente. O trabalho do lado do servidor torna-se menos complexo (uma ação por widget e transforma os dados em JSON em vez de HTML) – mas o trabalho do lado do cliente torna-se mais complexo e você tem mais chamadas HTTP de ida e volta para criar a página.

Você está preso a um aplicativo PHP legado? Você deveria comprar o meu livro, porque ele lhe dá um guia passo a passo para melhorar sua base de código, tudo isso mantendo-a funcionando o tempo todo.

***

Paul M. Jones faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela Redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://paul-m-jones.com/archives/6760