Comments

miro_dietiker’s picture

Priority: Normal » Major
arla’s picture

In the current state, this feature would help with

  • labelling the model correctly (if the same entity type and/or bundle does not exist on the server)
  • recreating the full bundle, including label and more (e.g. node bundles have description, publishing options etc.)

A potential complication is that this might not be logically well covered by the FieldDefinition plugin (already in the name there is a mismatch). Perhaps it's in order to create a new entity-focused definition plugin, just like we are already considering for CollectJson/EntityJson. Discussable.

mbovan’s picture

Status: Active » Needs review
StatusFileSize
new5.35 KB

Providing a first patch...

Added bundle data to the fields definition container (in case there is a bundle).

Serialized bundle (e.g article) data looks like:

"bundle": {
        "uuid": "69DED0F6-A534-41DC-AB6C-7F089C2CF111",
        "langcode": "en",
        "status": true,
        "dependencies": [],
        "name": "Article",
        "type": "article",
        "description": "Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.",
        "help": "",
        "new_revision": false,
        "preview_mode": 1,
        "display_submitted": true
    }

Now, we have some more information about the bundle (like name, description etc.) that are used in recreation.

Do we need uuid in the bundle data? Currently, it is unset during bundle recreation, but it might be removed on the client, before serialization?

Status: Needs review » Needs work

The last submitted patch, 3: add_bundle_info_in_the-2460641-3.patch, failed testing.

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new5.64 KB
new650 bytes

Small test improvement and fix.

Status: Needs review » Needs work

The last submitted patch, 5: add_bundle_info_in_the-2460641-5.patch, failed testing.

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new6.16 KB
new699 bytes

But first, we need to add description to assert it. :)

miro_dietiker’s picture

Status: Needs review » Needs work
+++ b/src/Controller/CreateMissingFieldsController.php
@@ -71,17 +71,17 @@ class CreateMissingFieldsController extends ControllerBase {
+      $typed_data = $this->getContainerTypedData($collect_model);
+      $bundle_data = $typed_data->getParsedData()['bundle'];

@@ -130,14 +130,30 @@ class CreateMissingFieldsController extends ControllerBase {
+   * @return \Drupal\collect\TypedData\CollectDataInterface
...
+  public function getContainerTypedData(ModelInterface $collect_model) {
+    if ($field_definition_container_id = FieldDefinition::findDefinitionContainer($collect_model)) {
...
+      return $this->typedDataProvider->getTypedData($stored_field_definition_container);

This can return NULL and you don't consider it.
Other than that, looking good.

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new6.17 KB
new670 bytes

Actually, in CreateMissingFieldsController::checkAccess we call getMissingFields() which calls getContainerTypedData(). If it fails, the button "Create missing fields" won't be displayed, so I think in current design there is no case that button is displayed and $field_definition_container_id or $typed_data is null.
Based on this, I would only change getContainerTypedData() doc comment.

LKS90’s picture

Status: Needs review » Needs work

There is an exception when I try to recreate an entity without first recreating it's content type. I'd say catch that exception (as discussed).

mbovan’s picture

Thanks for the review. :)

Yes, that's a bug as we don't check if the bundle exists (in the current system) before reconstruction. Not directly related to this issue, but I guess we can fix it here.

This doesn't happen with the fields as we skip them if they are not available in the current system.

Providing a fix... I added a check for the bundle before entity reconstruction and displaying an error if it is the case. Maybe we can change the message... I was thinking about adding a link to the model plugin (the page where we create missing bundle/fields), but then we should load the model just to display the url of it, so skipping it for now.

One of the options (for the follow-ups) could be to move CreateMissingFields logic to the service, so we can call the method to create missing bundle/fields in this case. Discuss.

Added tests for the bug...

The last submitted patch, 12: add_bundle_info_in_the-2460641-12-TEST-ONLY.patch, failed testing.

LKS90’s picture

Status: Needs review » Reviewed & tested by the community

Works as intended.

Btw: I thought you'd just catch the exception, but your solution is definitely nicer. What I had in mind:

CreateEntityController.php

Line 146ff

    try{
      $violations = $entity->validate();
    }
    catch(\Exception $e) {
      drupal_set_message($e->getMessage(), 'error');
      return new RedirectResponse($collect_container->urlInfo()->setAbsolute()->toString());
    }
    if (!empty($violations)) {

Edit: Almost forgot, can you create a (Meta) Issue with the follow-up issues?

mbovan’s picture

Yes, there are lot of expensive operations after it, so I don't think we should go that deep...

Regarding follow-ups, not sure if suggestions about automatic missing bundle/fields creation is the Collect-way of doing it... :)
I'll wait for some feedback about it and then we can open (followup) issues.

miro_dietiker’s picture

Status: Reviewed & tested by the community » Needs work

Yes, would love to commit this. Here some clarifications about the concepts.

  1. +++ b/src/CaptureEntity.php
    @@ -147,12 +147,20 @@ class CaptureEntity {
    +    if ($bundle_entity_type && $bundle_entity_type != 'bundle') {
    ...
    +      $data['bundle'] = $this->serializer->normalize($bundle_storage->load($entity->bundle()));
    
    +++ b/src/Controller/CreateEntityController.php
    @@ -58,10 +70,20 @@ class CreateEntityController extends ControllerBase {
    +      $bundle_entity = $this->entityManager->getStorage($entity_definition->getBundleEntityType())->load($bundle);
    +      if (!$bundle_entity) {
    

    OK the bundle key is thus missing if an entity has only a default bundle. Can these really be properly reconstructed? We need a test case for reconstructing an entity without a bundle.

    Also i think we should additionally persist entity type as key and not match them from the URL.

  2. +++ b/src/Controller/CreateEntityController.php
    @@ -58,10 +70,20 @@ class CreateEntityController extends ControllerBase {
         $entity_type = $matches['entity_type'];
    ...
           $bundle = $matches['bundle'];
    

    Then this should not consider matches at all. Instead it should be loaded from the data.

Finally there are lots of lines like this in collect:

    if (!$matches = CollectJson::matchSchemaUri($collect_container->getSchemaUri())) {

When persisting entity type and bundle to the container, we should no more consider the SchemaUri to match things. It should just be used for a lookup address. The original container then contains the actual (entity type and bundle) data. The reason is that we don't have any hardcoded assumptions anymore in the Schema URI except possibly for the auto schema suggestions.

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new15.25 KB
new10.01 KB

OK the bundle key is thus missing if an entity has only a default bundle. Can these really be properly reconstructed? We need a test case for reconstructing an entity without a bundle.

Yes. If an entity type has only default bundle then we don't need to specify bundle when recreating. Adding a test in this patch.

Added entity type as a part of field definitions data.
Not sure if we want to have those information in values container instead of fields definition or in both? In field definition container we can have normalized bundle data and entity type and in values container only entity type id and bundle key?

As now, when we have values container and want to get container entity type and bundle key, we have to:
- Load a model
- Find a field definition container ID
- Load a field definition container
- Call typed data service and get typed data
- Get parsed data and access the 'entity_type' and 'bundle' elements,

I'll wait for some opinions about this before making the changes in other classes that use matchSchemaUri(). By the way, in CreateEntityController we load the typed data with FieldDefinition::getContainerTypedData($model). If it fails we throw 404.
Do we need to add one more elseif condition between, so we use matchSchemaUri() as a fallback in case typed data fails. Also not sure when this case is possible. though.

Status: Needs review » Needs work

The last submitted patch, 17: add_bundle_info_in_the-2460641-17.patch, failed testing.

mbovan’s picture

Status: Needs work » Needs review

Test fails are not related to this issue. They will be fixed in #2554445: Tests broken due to core change safemarkup::XssFilter()

Status: Needs review » Needs work

The last submitted patch, 17: add_bundle_info_in_the-2460641-17.patch, failed testing.

giancarlosotelo’s picture

Rebase needed.

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new15.25 KB

Rerolled.

Status: Needs review » Needs work

The last submitted patch, 23: add_bundle_info_in_the-2460641-23.patch, failed testing.

The last submitted patch, 23: add_bundle_info_in_the-2460641-23.patch, failed testing.

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new15.25 KB

Tested locally. All test fails are related to #2580413: Remove target_bundle setting from Collect Demo config files. Uploading a rerolled patch after #2578899: Title block core change.

giancarlosotelo’s picture

Status: Needs review » Reviewed & tested by the community

Looks good.

arla’s picture

Came up with a few review comments. They're mostly DX and UX, nothing super important, and I'm fine with committing without them too.

  1. +++ b/src/Controller/CreateEntityController.php
    @@ -51,18 +64,35 @@ class CreateEntityController extends ControllerBase {
    +    if ($typed_data = FieldDefinition::getContainerTypedData($model)) {
    +      $parsed_data = $typed_data->getParsedData();
    +      $entity_type = $parsed_data['entity_type'];
    +      $bundle_data = isset($parsed_data['bundle']) ? $parsed_data['bundle'] : NULL;
    
    +++ b/src/Controller/CreateMissingFieldsController.php
    @@ -71,17 +71,18 @@ class CreateMissingFieldsController extends ControllerBase {
    +      $typed_data = FieldDefinition::getContainerTypedData($collect_model);
    +      $bundle_data = $typed_data->getParsedData()['bundle'];
    

    I think the FieldDefinition plugin should offer the bundle data and entity type ID as properties, just like it does with ['fields']. Then this can be $bundle_data = $typed_data->get('bundle').

  2. +++ b/src/Tests/EntityWebTest.php
    @@ -461,11 +467,54 @@ class EntityWebTest extends WebTestBase {
    +    // Create a new user.
    ...
    +    // Capture a user.
    ...
    +    // Remove a user entity.
    ...
    +    // Recreate a captured entity.
    ...
    

    I suppose the point here is to assert that you can recreate an entity whose bundle is not missing? Perhaps that should be stated clearly in a comment.

  3. +++ b/src/Controller/CreateEntityController.php
    @@ -51,18 +64,35 @@ class CreateEntityController extends ControllerBase {
    +    if ($typed_data = FieldDefinition::getContainerTypedData($model)) {
    ...
    +    else {
    +      throw new NotFoundHttpException('Container data is not found.');
         }
    

    Please clarify: what container, and why does it have to be found?

  4. +++ b/src/Controller/CreateEntityController.php
    @@ -51,18 +64,35 @@ class CreateEntityController extends ControllerBase {
    +      // In case the bundle does not exist in the system, entity reconstruction
    +      // is not possible.
    +      $bundle_entity = $this->entityManager->getStorage($entity_definition->getBundleEntityType())->load($bundle);
    +      if (!$bundle_entity) {
    +        drupal_set_message(t('The entity could not be recreated. Create a missing %bundle bundle to recreate the entity.', ['%bundle' => $bundle]), 'error');
    

    It would be helpful if this error message contained some instructions on how to recreate the bundle. Like "use the 'Create entity' button" or whatever makes the most sense in this context.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 26: add_bundle_info_in_the-2460641-26.patch, failed testing.

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new16.38 KB
new4.06 KB

Applied the suggestions above.

Status: Needs review » Needs work

The last submitted patch, 30: add_bundle_info_in_the-2460641-30.patch, failed testing.

The last submitted patch, 30: add_bundle_info_in_the-2460641-30.patch, failed testing.

The last submitted patch, 30: add_bundle_info_in_the-2460641-30.patch, failed testing.

mbovan’s picture

Status: Needs work » Needs review
arla’s picture

Status: Needs review » Needs work

The testbot run is passing (although it says "Queued for testing" in the comment) but it failed on DrupalCI. I'm not sure about the error message, "The test did not complete due to a fatal error", what is that about?

mbovan’s picture

Status: Needs work » Needs review
StatusFileSize
new16.38 KB

From the logs:

Fatal error: Call to undefined method Drupal\Core\Render\MetadataBubblingUrlGenerator::generateFromPath() in /var/www/html/modules/collect/client/src/Tests/CollectClientWebTest.php on line 79
FATAL Drupal\collect_client\Tests\CollectClientWebTest: test runner returned a non-zero error code (255).

It seems more related to #2582989: Core Change remove generatefrompath() from core.

Uploading again to see the results. (rerolled)

  • Arla committed 2e2c02a on 8.x-1.x authored by mbovan
    Issue #2460641 by mbovan: Add bundle info in the field definition data
    
arla’s picture

Status: Needs review » Fixed

Okay, looks good. Committed, with the attached minor change. (ControllerBase has a special take on service injection...)

arla’s picture

StatusFileSize
new599 bytes

The change I made.

Status: Fixed » Closed (fixed)

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