Updated: Comment #N

Problem/Motivation

Right now EntityManager calls out to \Drupal::formBuilder() in its getForm() method.
EntityManager is a service, the rest of its dependencies are injected except FormBuilder.

When trying to make FormBuilder an explicit dependency of EntityManager, I got this:
Circular reference detected for service "url_generator", path: "router_listener -> router -> router.dynamic -> url_generator -> route_processor_manager -> route_processor_csrf -> csrf_token -> current_user -> entity.manager -> form_builder -> url_generator

The functionality of getForm() is NOT actually related to what EntityManager does, and it only uses public methods. It is really just a wrapper around the entity manager's getFormController and the FormBuilder.

Proposed resolution

Make a separate EntityFormBuilder service, breaking the circular dependency.

Remaining tasks

N/A

User interface changes

N/A

API changes

EntityManager::getForm() is moved to EntityFormBuilder::getForm(), with no changes
ControllerBase gets a new entityFormBuilder() method

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Status: Needs review » Needs work

The last submitted patch, entity-manager-container.patch, failed testing.

tim.plunkett’s picture

Title: Remove Drupal:: call from EntityManager » Break the circular dependency in EntityManager
Issue summary: View changes
FileSize
43.38 KB

Updated the issue summary, and changed approach.

tim.plunkett’s picture

Status: Needs work » Needs review
dawehner’s picture

I do consider this as a duplicate of #2182055: Comment module causes Circular reference error on a REST request. The actual issue is that the user is authenticated way to early.

As a developer I would totally not understand why entity forms as sooo different to entity lists or entity view builder. This fix sort of just fixes the symptons of the issue. The real fix would be that the current user is actually some sort of factory and the authentication manager is injected via setter injection. Damian wanted to work on it.

PS: please also try to provide the code you did to get to this statement.

When trying to make FormBuilder an explicit dependency of EntityManager, I got this:

dawehner’s picture

Oh btw. I think that using setter injection for the form builder would already solve it.

tim.plunkett’s picture

I ran Drupal\basic_auth\Tests\Authentication\BasicAuthTest locally with the patch from https://qa.drupal.org/pifr/test/715563, that's how I got it.

I would totally not understand why entity forms as sooo different to entity lists or entity view builder.

They all use Drupal\Core\Entity\EntityControllerInterface. Entity forms use Drupal\Core\DependencyInjection\ContainerInjectionInterface, because that's what FormInterface uses.

So they are separate, and I think that's fine. The EntityManager has getFormController, I don't really think the formBuilder parts of it are relevant to EntityManager at all.

tim.plunkett’s picture

Yes, setter injection would also "solve" it. But when do we use setter injection on services? And how does that make form building part of entity managing?

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Controller/ControllerBase.php
    @@ -114,6 +121,19 @@ protected function entityManager() {
       /**
    +   * Retrieves the entity form builder.
    +   *
    +   * @return \Drupal\Core\Entity\EntityFormBuilderInterface
    +   *   The entity form builder.
    +   */
    +  protected function entityFormBuilder() {
    

    It seems to be quite common, especially for core, to build entity forms on arbitrary controllers.

  2. +++ b/core/modules/forum/lib/Drupal/forum/Controller/ForumController.php
    @@ -21,7 +20,7 @@
    +class ForumController extends ControllerBase implements ContainerInjectionInterface {
    ...
    -class ForumController implements ContainerInjectionInterface {
    

    So why don't we just inject the entity form builder now? We already have quite a lot of services in your create method.

tim.plunkett’s picture

FileSize
43.42 KB

Rerolled.

1) We need this for all 'add' forms, as well as other custom usages like Views UI.

2) This follows the pattern for other dependencies exposed by ControllerBase. It won't be as bad when #2110501: ControllerBase should implement ContainerInjectionInterface like FormBase is finally committed.

Status: Needs review » Needs work

The last submitted patch, 9: entity-form-2183923-9.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
43.43 KB
1.43 KB
dawehner’s picture

Status: Needs review » Reviewed & tested by the community

2) This follows the pattern for other dependencies exposed by ControllerBase. It won't be as bad when #2110501: ControllerBase should implement ContainerInjectionInterface like FormBase is finally committed.

Well, I think that using ControllerBase for the sake of a single service is not worth it. Declaring explicit dependencies is still far better.

alexpott’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

entity-form-2183923-11.patch no longer applies.

xjm’s picture

Issue summary: View changes
tim.plunkett’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs reroll
FileSize
41.13 KB
alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

entity-form-2183923-15.patch no longer applies.

error: patch failed: core/modules/options/lib/Drupal/options/Tests/OptionsFieldTest.php:35
error: core/modules/options/lib/Drupal/options/Tests/OptionsFieldTest.php: patch does not apply
error: patch failed: core/modules/search/lib/Drupal/search/Controller/SearchController.php:87
error: core/modules/search/lib/Drupal/search/Controller/SearchController.php: patch does not apply
error: patch failed: core/modules/system/tests/modules/entity_test/entity_test.module:287
error: core/modules/system/tests/modules/entity_test/entity_test.module: patch does not apply

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
41.08 KB

Just a context conflict with #1857442: Make $values optional on entity_create, no changes.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

It feels wrong to not edit the issue all the time :P

alexpott’s picture

Assigned: tim.plunkett » Unassigned
Status: Reviewed & tested by the community » Fixed
Issue tags: -Needs reroll

Committed fe61b43 and pushed to 8.x. Thanks!

slewazimuth’s picture

Before this change:

$advancedtour = $this->manager->getStorageController('advancedtour')->create(array());
return \Drupal::entityManager()->getForm($advancedtour);

Worked just fine.

However now with getForm function moving to EntityFormBuilder :

$advancedtour = $this->manager->getStorageController('advancedtour')->create(array());
return \Drupal::service('entity.form_builder')->getForm($advancedtour);

produces the error:

Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException: The "advancedtour" entity type did not specify a "default" form class. in Drupal\Core\Entity\EntityManager->getFormController() (line 206 of C:\wamp\www\d8_feb09\core\lib\Drupal\Core\Entity\EntityManager.php).

Adding a default form to the annotation of the advancedtour and clearing the cache changed nothing. Still getting the error. Suggestions?

slewazimuth’s picture

Nevermind, figured it out.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.