Skip to content

Widget PHP Implementation

Each widget is powered by a PHP class that implements Hyva\AdminDashboardApi\Api\V1\WidgetTypeInterface, or one of the chart-type marker interfaces that extend it. The contract is shipped by the Hyva_AdminDashboardApi module, and only that package needs to be required by a module that exposes a widget. The dashboard runtime is not a build-time dependency.

Migrating from the legacy contract

Earlier versions of the dashboard used an inheritance-based contract in which widgets extended Hyva\AdminDashboardFramework\Model\WidgetType\AbstractWidgetType. That contract continues to work, since the dashboard runtime bridges both APIs through WidgetTypeDispatcher, but new widgets should use the composition-based API described on this page. See the 1.3.0 upgrade notes for the mechanical migration steps, or the legacy reference for the pre-2.0.0 method signatures.

Picking an Interface

A widget class implements either:

  • Hyva\AdminDashboardApi\Api\V1\WidgetTypeInterface directly.
  • One of the chart-type marker interfaces in Hyva\AdminDashboardApi\Api\V1\ChartType\, when the widget matches a built-in chart shape. Implementing one of these interfaces tells the framework to wire the matching defaults provider into the WidgetContextInterface, so the standard configurable and display properties for that chart type appear in the context object.
Marker interface Matching display_type Bundled defaults
BarChartWidgetTypeInterface bar_chart Chart appearance options.
LineChartWidgetTypeInterface line_chart Chart appearance options.
PieChartWidgetTypeInterface pie_chart Chart appearance options.
NumberWidgetTypeInterface number Number tile formatting.
DateIntervalWidgetTypeInterface varies default_interval display property and helpers.

WidgetContextInterface

Every method on WidgetTypeInterface receives a Hyva\AdminDashboardApi\Api\V1\WidgetContextInterface $ctx object as its first argument. The context is a read-only value object built by the framework's WidgetContextFactory from the merged widget XML and, when applicable, the chart-type defaults provider. Wherever a legacy widget called parent::method(), a new-contract widget calls $ctx->method() instead.

The WidgetContextInterface context exposes:

  • getId(): string, the widget's XML id attribute.
  • getTitle(): Phrase, the title taken from the merged widget XML.
  • getAcl(): ?string, the ACL resource declared in the widget XML, if any.
  • getTrailingAction(): array, the trailing-action array{title?, href?, target?} from the widget XML.
  • getOption(string $key): mixed, a generic accessor for any merged-config option.
  • toArray(): array, the full merged-config map, used by the framework's GetHtml controller as the type_data JS payload.
  • isAllowed(?WidgetInstanceInterface $widgetInstance): bool, the default ACL plus ownership check.
  • getDisplayProperties(): array, the chart-type default display properties.
  • getConfigurableProperties(): array, the chart-type default configurable properties.

A Minimal Widget Example

Here's what a minimal widget type class looks like. This example bar-chart widget returns data to the frontend via getDisplayData() and uses a fetchSeries() helper, but otherwise defers entirely to the widget context object:

use Hyva\AdminDashboardApi\Api\V1\ChartType\BarChartWidgetTypeInterface;
use Hyva\AdminDashboardApi\Api\V1\WidgetContextInterface;
use Hyva\AdminDashboardApi\Api\V1\WidgetInstanceInterface;
use Magento\Framework\Phrase;

class OrderVolume implements BarChartWidgetTypeInterface
{
    public function __construct(private OrderRepositoryInterface $orderRepo) {}

    public function getDisplayData(WidgetContextInterface $ctx, WidgetInstanceInterface $i): mixed
    {
        return [
            'series' => [['name' => 'Orders', 'data' => $this->fetchSeries($i)]],
            'xaxis'  => ['categories' => ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']],
        ];
    }

    public function getTitle(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): Phrase                   { return $ctx->getTitle(); }
    public function getConfigurableProperties(WidgetContextInterface $ctx): array                                { return $ctx->getConfigurableProperties(); }
    public function getDisplayProperties(WidgetContextInterface $ctx): array                                     { return $ctx->getDisplayProperties(); }
    public function getTrailingAction(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): array           { return $ctx->getTrailingAction(); }
    public function isAllowed(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): bool                    { return $ctx->isAllowed($i); }
    public function beforeSave(WidgetContextInterface $ctx, WidgetInstanceInterface $i): WidgetInstanceInterface { return $i; }
    public function afterSave(WidgetContextInterface $ctx, WidgetInstanceInterface $i): WidgetInstanceInterface  { return $i; }

    private function fetchSeries(WidgetInstanceInterface $i): array { /* ... */ }
}

Extending Defaults

To add a widget-specific property on top of the chart-type defaults, merge the existing array:

use Hyva\AdminDashboardApi\Api\V1\ChartType\DateIntervalWidgetTypeInterface;
use Hyva\AdminDashboardApi\Api\V1\Service\WidgetDateIntervalHelperInterface;
use Hyva\AdminDashboardApi\Api\V1\WidgetContextInterface;
use Hyva\AdminDashboardApi\Api\V1\WidgetInstanceInterface;

class DailyOrderVolume implements DateIntervalWidgetTypeInterface
{
    public function __construct(private WidgetDateIntervalHelperInterface $intervalHelper) {}

    public function getDisplayProperties(WidgetContextInterface $ctx): array
    {
        return array_merge($ctx->getDisplayProperties(), [
            'highlight_today' => [
                'label' => __('Highlight today'),
                'input' => ['type' => 'toggle'],
            ],
        ]);
    }

    public function getConfigurableProperties(WidgetContextInterface $ctx): array
    {
        return array_merge($ctx->getConfigurableProperties(), [
            'store_ids' => [
                'label' => __('Store Views'),
                'input' => ['type' => 'scope', 'attributes' => ['multiple' => true, 'required' => true]],
            ],
        ]);
    }

    public function getDisplayData(WidgetContextInterface $ctx, WidgetInstanceInterface $i): mixed
    {
        return [
            'intervals' => $this->intervalHelper->getIntervalDataWithTimestamps(),
            // ...
        ];
    }

    // The remaining methods delegate to $ctx exactly as in the previous example.
}

As a general rule: read everything from $ctx, and only array_merge(...) when you need to add your own properties. Methods that don't add anything (getTitle, getTrailingAction, isAllowed, the save hooks) can just return the context object's method calls.

Method Reference

Each method below uses the signature from Hyva\AdminDashboardApi\Api\V1\WidgetTypeInterface. All $ctx parameters are an instance of WidgetContextInterface.

getDisplayData()

public function getDisplayData(WidgetContextInterface $ctx, WidgetInstanceInterface $widgetInstance): mixed

Produces the data the widget renders. The shape largely depends on the widget's display_type or the expectations in whatever template consumes it.

$widgetInstance is a saved configuration for this type of widget. Configured values can be read via $widgetInstance->getPropertyValue(...).

getTitle()

public function getTitle(WidgetContextInterface $ctx, ?WidgetInstanceInterface $widgetInstance): Phrase

Returns the title rendered at the top of the widget card. $widgetInstance is null when the widget has not yet been created. Implementations that vary the title by configuration should fall back to $ctx->getTitle() in the null case.

getConfigurableProperties()

public function getConfigurableProperties(WidgetContextInterface $ctx): array

Returns the input options that configure the behavior of the widget (which store views, which order statuses, and so on). The expected shape is documented in Configurable Inputs.

The default implementation should be:

return $ctx->getConfigurableProperties();

getDisplayProperties()

public function getDisplayProperties(WidgetContextInterface $ctx): array

Returns the input options that configure the appearance of the widget. Same shape and default implementation pattern as getConfigurableProperties().

getTrailingAction()

public function getTrailingAction(WidgetContextInterface $ctx, ?WidgetInstanceInterface $widgetInstance): array

Returns the link rendered at the bottom of the widget card (typically a "View all" action). The default implementation should be return $ctx->getTrailingAction();. The framework reads the <trailing_action> element from the widget XML and exposes it through $ctx.

isAllowed()

public function isAllowed(WidgetContextInterface $ctx, ?WidgetInstanceInterface $widgetInstance): bool

Custom permission logic. The default $ctx->isAllowed($widgetInstance) implementation returns false when the current admin user does not have access to the resource defined in the widget's <acl> XML configuration, or when the widget instance was created by another admin user.

beforeSave() / afterSave()

public function beforeSave(WidgetContextInterface $ctx, WidgetInstanceInterface $widgetInstance): WidgetInstanceInterface
public function afterSave(WidgetContextInterface $ctx, WidgetInstanceInterface $widgetInstance): WidgetInstanceInterface

Intercept the save lifecycle of a widget instance. Both must return a WidgetInstanceInterface, typically the same instance passed in. These methods are invoked by Hyva\AdminDashboardFramework\Model\WidgetInstance\WidgetInstanceRepository::save().

Tip

The Checklist, Links, and Google CrUX History widgets are good examples of where these hooks are used to transform configuration values or trigger external data fetches.

Registering the Widget

Widgets are registered in etc/adminhtml/hyva_dashboard_widget.xml. See XML Configuration for the full reference; the schema lives in the Hyva_AdminDashboardApi module: urn:magento:module:Hyva_AdminDashboardApi:etc/adminhtml/hyva_dashboard_widget.xsd.

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Hyva_AdminDashboardApi:etc/adminhtml/hyva_dashboard_widget.xsd">
    <widget id="order_volume">
        <title>Order Volume</title>
        <class>Acme\OrderVolumeWidget\Model\Widget\OrderVolume</class>
        <display_type>bar_chart</display_type>
        <category>sales</category>
        <icon>chart-column</icon>
        <min_height>6</min_height>
        <min_width>2</min_width>
    </widget>
</config>