Solving The “Widget Problem” In ADR

The “widget problem” is when you have several panels or content areas on an HTML page that have different data sources. You might have a main content area, then a calendar off to the side, with perhaps a list of recent news items or blog posts, a todo or reminder widget, and maybe other information panels. The problem is that they each have different data sources, and may not always be displayed in every circumstance — perhaps they are only shown to some users based on their preferences, or under certain conditions.

So how, in Action-Domain-Responder, do we get the “right” data for the set of widgets that are actually going to be displayed? (We’ll presume here that the entire page is being rendered server-side, for delivery as a whole to the client.)

The answer is “the same as with anything else” – we just have more kinds of data to get from the domain. The domain has all the data needed, and the knowledge necessary to figure out which data elements to return.

Let’s start with the Action, which is intentionally very spare: it only collects input, calls the Domain with that input, then invokes the Responder:

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

    public function __invoke(HttpRequest $request)
        $payload = $this->domain->fetchPageData(
        return $this->responder->respond($request, $payload);

The domain work is where the heavy lifting happens. The example below returns the domain objects and data wrapped in a Domain Payload object.

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();


        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",

    protected function fetchTodoData() { ... }

    protected function fetchRemindersData() { ... }

    protected function fetchUpcomingEventsData() { ... }

    protected function fetchCalendarData() { ... }


Finally, the Responder work becomes as straightforward as: “Is there Todo data to present? Then render the Todo widget via a Todo helper using the Todo data.” It could be as simple as this:

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) { ... }

One alternative here is for some client-side Javascript to make one additional call per widget or panel to retrieve widget-specific data, then render that data on the client. The server-side work becomes less complex (one action per widget, and transform the data to JSON instead of HTML) – but the client-side work becomes more complex, and you have more HTTP calls back-and-forth to build the page.

Are you stuck with a legacy PHP application? You should buy my book because it gives you a step-by-step guide to improving your codebase, all while keeping it running the whole time.
Share This!Share on Google+Share on FacebookTweet about this on TwitterShare on RedditShare on LinkedIn

10 thoughts on “Solving The “Widget Problem” In ADR

  1. While this looks like a pretty good solution, the example implementation looks like it would only work for 1 page (or rather: 1 type of page). In practice, you’ll often have the same widgets on multiple pages – for instance on this blog the same widgets are used for the index, archive, articles and pages. In more complex applications, you might have some widgets that are always shown, some that are shown only on specific pages and some that are always shown except on specific pages; and their ordering could be different on every page; and user settings (and perhaps the date and/or time, or location of the visitor, or …) could come into play.

    Do you have any thoughts on how you would do that?

    For the domain part, I think I would create a WidgetService for the PageService to call onto (with a way for the PageService to include & exclude specific widgets).
    For the response my first idea is to also create a WidgetRenderer so that the PageResponder can just to $html .= $this->widgetResponder->render($output[‘widgets’]);

    That approach feels OK for a small application, but both the WidgetService and the WidgetRenderer could become huge if there are enough widgets (though I guess those could both be broken up into smaller classes if needed).

    Again, I like the idea, I’m just not sure how it would be best implemented in a more complex scenario.

  2. It strikes me that does little to uncouple the widgets from the other layers of content within the page. And it appears to require rather a lot of custom code to implement. Is there a significant advantage of ESI I’m not seeing here?

  3. Since you want to reuse widgets (components) I do not think this approach will “scale”. We have “copied” the ADR pattern and use something similar for widgets. We have an widget controller which does similar things like an action class, data is fetched from the domain layer and “rendered” by the widget renderer. The difference between the widget renderer and the responder is, that the widget renderer is only allowed to return HTML. You are not able to modify response headers and such. Is an exception thrown of any kind, the widget won’t be displayed. That way in our templates we can simply include widgets where we want, pass parameters to the different instances and get the HTML output back.

  4. Mistake: you have different parameters order in fetchPageData method in first two code samples.

    • html code was skipped 🙁

      Try again like this:
      [div id=”my-cool-widget” data-init=”{{ widget_data|json_encode() }}”]… main widget code …[/div]

Leave a Reply

Your email address will not be published. Required fields are marked *