Skip to content

Latest commit

 

History

History
93 lines (69 loc) · 6.85 KB

modularity-and-providers.md

File metadata and controls

93 lines (69 loc) · 6.85 KB

✔ Modularity & Providers

Providers

A provider is a design pattern to integrate and swap a code extension more easily and flexibly. A good example are payment methods. If a developer wants to implement multiple payment methods of one payment company, he can create a single module (representing the payment company), which contains a provider for each payment method. Besides IPaymentMethod, there are other providers like IExportProvider, IShippingRateComputationMethod, IExchangeRateProvider, IExternalAuthenticationMethod etc.

Each of these provider interfaces is derived from the marker interface IProvider in order to be able to identify it uniformly as a provider. The generic provider class encapsulates the respective interface and enriches it with general properties like ProviderMetadata. As a result you get an abstract, handy and API friendly construct such as Provider<IPaymentMethod>.

Metadata

Name Implement Description
FriendlyName FriendlyNameAttribute The English name of the provider. Localizable by using resource key Plugins.FriendlyName.<ProviderSystemName>
Description FriendlyNameAttribute The English description of the provider. Localizable by using resource key Plugins.Description.<ProviderSystemName>
DisplayOrder OrderAttribute The display order in the providers list.
DependentWidgets DependentWidgetsAttribute Widgets which are automatically (de)activated when the provider gets (de)activated. Useful in scenarios where separate widgets are responsible for displaying provider data.
ExportFeatures ExportFeaturesAttribute Data processing types supported by an export provider.
IsConfigurable IConfigurable A value indicating whether the provider is configurable.
IsEditable IUserEditable A value indicating whether the metadata is editable by the user.
IsHidden IsHiddenAttribute A value indicating whether the provider is hidden. A hidden provider can only be used programmatically but not by the user through the user interface.
SystemName * SystemNameAttribute Unique SystemName of the provider, e.g. Payments.AmazonPay.

{% hint style="info" %} A provider can be activated or deactivated using the provider list. For example, a deactivated payment provider does not appear in the checkout payment method list. {% endhint %}

Configuration

Providers are configurable via IConfigurable. It specifies a RouteInfo that points to an action method used for configuration.

public RouteInfo GetConfigurationRoute()
    => new(nameof(MyModuleAdminController.Configure), "MyModuleAdmin", new { area = "Admin" });

Typically the configuration consists of two Configure methods, one called by HttpGet to load the configuration and one called by HttpPost to save it. Since we are configuring a provider and its configuration view is just a portion of the actual Razor layout page, we need to tell it what provider we are configuring. This is done via the metadata and ViewBag.Provider.

private readonly IProviderManager _providerManager;

[LoadSetting]
public IActionResult Configure(MyProviderSettings settings)
{
    var model = MiniMapper.Map<MyProviderSettings, MyConfigurationModel>(settings);
    
    ViewBag.Provider = _providerManager.GetProvider("MyProviderSystemName").Metadata;
    
    return View(model);
}

Use IProviderManager to load any provider (including its metadata) or all providers of a certain type. Since we are configuring settings inherited from ISetting, we can use the LoadSettingAttribute to load an instance of our settings as an action parameter. The HttpPost method for saving may look like this:

[HttpPost, SaveSetting]
public IActionResult Configure(MyConfigurationModel model,
    MyProviderSettings settings)
{
    if (!ModelState.IsValid)
    {
        return Configure(settings);
    }

    MiniMapper.Map(model, settings);
    
    return RedirectToAction(nameof(Configure));
}

The SaveSettingAttribute automatically saves the updated settings to database. The configuration view may look like this:

@model MyConfigurationModel

@{
    Layout = "_ConfigureProvider";
}

<widget target-zone="admin_button_toolbar_before">
    <button id="SaveConfigButton" type="submit" name="save" value="save"
        class="btn btn-warning">
        <i class="fa fa-check"></i>
        <span>@T("Admin.Common.Save")</span>
    </button>
</widget>

@await Component.InvokeAsync("StoreScope")

<form asp-action="Configure">
    <div asp-validation-summary="All"></div>
    @* TODO: multi-store configuration using setting-editor tag helper. *@
</form>