1<?php
2
3namespace Drupal\Core\Controller;
4
5use Drupal\Core\DependencyInjection\DependencySerializationTrait;
6use Drupal\Core\Form\FormBuilderInterface;
7use Drupal\Core\Form\FormState;
8use Drupal\Core\Routing\RouteMatchInterface;
9use Symfony\Component\HttpFoundation\Request;
10use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
11
12/**
13 * Common base class for form interstitial controllers.
14 */
15abstract class FormController {
16  use DependencySerializationTrait;
17
18  /**
19   * The argument resolver.
20   *
21   * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface
22   */
23  protected $argumentResolver;
24
25  /**
26   * The controller resolver.
27   *
28   * @var \Drupal\Core\Controller\ControllerResolverInterface
29   *
30   * @deprecated
31   *   Deprecated property that is only assigned when the 'controller_resolver'
32   *   service is used as the first parameter to FormController::__construct().
33   *
34   * @see https://www.drupal.org/node/2959408
35   * @see \Drupal\Core\Controller\FormController::__construct()
36   */
37  protected $controllerResolver;
38
39  /**
40   * The form builder.
41   *
42   * @var \Drupal\Core\Form\FormBuilderInterface
43   */
44  protected $formBuilder;
45
46  /**
47   * Constructs a new \Drupal\Core\Controller\FormController object.
48   *
49   * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver
50   *   The argument resolver.
51   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
52   *   The form builder.
53   */
54  public function __construct(ArgumentResolverInterface $argument_resolver, FormBuilderInterface $form_builder) {
55    $this->argumentResolver = $argument_resolver;
56    if ($argument_resolver instanceof ControllerResolverInterface) {
57      @trigger_error("Using the 'controller_resolver' service as the first argument is deprecated, use the 'http_kernel.controller.argument_resolver' instead. If your subclass requires the 'controller_resolver' service add it as an additional argument. See https://www.drupal.org/node/2959408.", E_USER_DEPRECATED);
58      $this->controllerResolver = $argument_resolver;
59    }
60    $this->formBuilder = $form_builder;
61  }
62
63  /**
64   * Invokes the form and returns the result.
65   *
66   * @param \Symfony\Component\HttpFoundation\Request $request
67   *   The request object.
68   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
69   *   The route match.
70   *
71   * @return array
72   *   The render array that results from invoking the controller.
73   */
74  public function getContentResult(Request $request, RouteMatchInterface $route_match) {
75    $form_arg = $this->getFormArgument($route_match);
76    $form_object = $this->getFormObject($route_match, $form_arg);
77
78    // Add the form and form_state to trick the getArguments method of the
79    // controller resolver.
80    $form_state = new FormState();
81    $request->attributes->set('form', []);
82    $request->attributes->set('form_state', $form_state);
83    $args = $this->argumentResolver->getArguments($request, [$form_object, 'buildForm']);
84    $request->attributes->remove('form');
85    $request->attributes->remove('form_state');
86
87    // Remove $form and $form_state from the arguments, and re-index them.
88    unset($args[0], $args[1]);
89    $form_state->addBuildInfo('args', array_values($args));
90
91    return $this->formBuilder->buildForm($form_object, $form_state);
92  }
93
94  /**
95   * Extracts the form argument string from a request.
96   *
97   * Depending on the type of form the argument string may be stored in a
98   * different request attribute.
99   *
100   * One example of a route definition is given below.
101   * @code
102   *   defaults:
103   *     _form: Drupal\example\Form\ExampleForm
104   * @endcode
105   *
106   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
107   *   The route match object from which to extract a form definition string.
108   *
109   * @return string
110   *   The form definition string.
111   */
112  abstract protected function getFormArgument(RouteMatchInterface $route_match);
113
114  /**
115   * Returns the object used to build the form.
116   *
117   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
118   *   The route match.
119   * @param string $form_arg
120   *   Either a class name or a service ID.
121   *
122   * @return \Drupal\Core\Form\FormInterface
123   *   The form object to use.
124   */
125  abstract protected function getFormObject(RouteMatchInterface $route_match, $form_arg);
126
127}
128