Widgets
=======

Introduction
------------

Widgets are like a portable controller action. You can have many on your page at once, and they'll all do something 
specific.

For instance you could have a calendar widget that shows planned events. On one page you might want to show it very 
small, with just the events of the upcoming week, on another you might want to show the complete month. Maybe sometimes 
you only want to show events belonging to a specific category.

So a widget is a configurable thingamabob that can be rendered inside a template. They are especially useful when 
provided by a bundle, where you can easily put them on whatever page is suitable, and configure them to your liking.

The uniweb core-bundle provides the core widget component, and also some usable widgets.

Basic usage
-----------

Most of the times, widgets will be prepared in a controller action, and rendered in a twig template.

In the controller action, you can inject a (unique instance of a) widget, and configure it. In the following example we
inject a TwoFactorSetupWidget and tell it to use the current logged in user.

```php
<?php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;use Uniweb\AuthBundle\Widget\Setup\TwoFactorSetupConfiguration;use Uniweb\AuthBundle\Widget\Setup\TwoFactorSetupWidget;use Uniweb\CoreBundle\Component\Controller\AbstractController;

class UserController extends AbstractController
{
    public function twoFactorSetup(TwoFactorSetupWidget $setupWidget): Response 
    {
        $setupWidget->prepare(new TwoFactorSetupConfiguration(
            user: $this->getUser(),
        ));
        return $this->render('two-factor-login.html.twig', [
            'setupWidget' => $setupWidget
        ]);
    }
}
```

The configuration object will differ per widget, but it will always implement 
`Uniweb\CoreBundle\Component\Widget\Configuration\ConfigurationInterface`. Check the documentation of the widget you 
want to use to see which configuration type it expects.

To render this widget, simply use the `widget` twig function in the `two-factor-login.html.twig` template.

```html.twig
{{ widget(setupWidget) }}
```

And hey presto, there's a widget being rendered on your page!

Configuring a widget
--------------------

The most common way to configure widgets style-wise is with named arguments:
```php
$widget->prepare(new SomeConfiguration(
    foo: $bar,
    bas: $baz,
    template: 'my-template.html.twig',
));
```

If you pass the wrong type of configuration, eg. when you provide a `PasswordLoginConfiguration` to the
`TwoFactorSetupWidget`, this will result in an `InvalidArgumentException`.

All widgets can be configured, but not all widgets require a configuration to work. If the widget has a configuration 
model that has no required options, it is allowed to omit the configuration argument. Widgets that do not require
configuration will be auto-prepared by the calling the controller's render method.

```php
    public function someAction(SomeWidget $widget): Response 
    {
        // Verbose way to declare a configuration-less widget and pass it to the view
        $widget->prepare();
        return $this->render([
            'widget' => $widget,
        ]);
        
        // since prepare returns the widget, you can just move it inline it to save some space
        return $this->render([
            'widget' => $widget->prepare(),
        ]);
        
        // render will automatically prepare widgets when possible, so the following is also allowed
        return $this->render([
            'widget' => $widget,
        ]);
    }
```

If the widget requires a configuration that has mandatory options, calling prepare without an argument will cause a 
`LogicException`. (This exception will also be thrown by the controller's `render` method if you passed the widget to the
view without preparing it.)

Customizing the output
----------------------

When a widget is provided by a bundle, chances are you'll want to change the look and feel of the widget.

### Overriding templates using twig

You can override templates from widgets in bundles by using Symfony's twig override mechanism. For example if a widget
renders its output with the `@UniwebCore/widgets/some_widget.html.twig` template that is provided by the bundle, you
can just create a local template at the `templates/bundles/UniwebCoreBundle/widgets/some_widget.html.twig`.

This is a fast and easy way to override the default template that is used by the widget.

### Overriding the template in widget configuration

Every widget can be configured in its `prepare` method with a configuration object that implements
`Uniweb\CoreBundle\Component\Widget\Configuration\ConfigurationInterface`. Just set the `template` argument in the 
constructor, or call `$configuration->setTemplate('my-custom-template.twig.html');`

### Passing custom template variables

Sometimes you might want to pass custom variables to your template that aren't provided by the widget. In this case
you can set the `customOptions` option on the configuration, either in the constructor or with the setter/adder methods.

```php
class UserController extends AbstractController
{
    public function twoFactorSetup(TwoFactorSetupWidget $setupWidget): Response 
    {
        $setupWidget->prepare(new TwoFactorSetupConfiguration(
            template: 'my-custom-template.html.twig',
            customOptions: ['show_something' => 'something']
        ));
        return $this->render('two-factor-login.html.twig', [
            'setupWidget' => $setupWidget
        ]);
    }
}
```

In the widget's template (`my-custom-template.html.twig` in this case), you can now access `show_something` like this:

```twig
{% if custom_options.show_something is defined %}
    {{ custom_options.show_something }}
{% endif %}
```

Advanced concepts
-----------------

### Handling responses

Some widgets will also be able to provide a Response. Imagine using a login widget, this should be able to redirect you
when the user is already logged in. Or a download widget that should be able to provide a BinaryFileResponse.
This is why widgets can have a Response that should be returned instead of the view you would normally render.

In simple cases this won't be a problem. Uniweb's `Uniweb\CoreBundle\Component\Controller\AbstractController` will
automagically respond the response of your widget when you call the controller's `render` method. (It will even change
the status code if there's an invalid form in the widget!)

Sometimes this won't be enough however. Maybe you have multiple widgets and one should have precedence, maybe you 
already wanted to redirect yourself, maybe you're not even in a controller. In this case you need to do this check 
yourself after preparing the widget:

```php
$widget->prepare();
if ($widget->hasResponse()) {
    return $widget->getResponse();
}
```

Note that you can only check for a response after the widget has been prepared. A `LogicException` will be thrown 
otherwise.

Note that trying to render a widget that has a response will result in a `LogicException`, so make sure to always check
for responses on widgets that implement them.

Provided widgets by core-bundle
-------------------------------

None so far!

Other resources
---------------

- Creating a custom widget