Problem/Motivation

Steps to reproduce:

1. Create a new user
2. Add a new paragraph to an existing node with that user and save.
3. Delete the user
4. Try to save the node with a different user.

Result: The referenced entity (user: X) does not exist validation error.

No way to fix it.

This affects the uid and also the revision uid, including all existing revisions.

Proposed resolution

I never understood why we even have a uid and revision_uid field anyway, there's not really a use case for that to be different than the host. There's no way to change/control it through the UI.

Remove it, fall back to the parent entity for if called? Not sure if we can get away with that under BC.

Alternatively, we could also implement hook_user_cancel()/ delete().

Remaining tasks

User interface changes

API changes

Data model changes

Comments

Berdir created an issue. See original summary.

miro_dietiker’s picture

I would vote to drop them..

The field is also not exposed in the field UI and thus can not be configured for display or so.
And we do not officially support complex Paragraph views so i don't fear there's risk people output this uid field in Paragraph views and we destroy the views.
And we will still implement the interface and can return the host entity uid instead.
So i don't see yet any true promise we break.

While doing those changes to avoid too many storage changes - it would be good to add the missing parent revision reference field as well.
#2949412: Nested paragraphs automatically publish, even if the parent is a draft and child issues...

seanb’s picture

Just ran into this. I also can't think of any reason why we should record an extra copy of the uid / revision_uid? +1 for removing the fields.

berdir’s picture

Priority: Minor » Major

Not sure why set to minor.

miro_dietiker’s picture

Added this to the #2954487: New Roadmap

arpad.rozsa’s picture

Status: Active » Needs review
StatusFileSize
new5.36 KB

Removed the uid, revision_uid fields and changed the related getters and setters.

Status: Needs review » Needs work

The last submitted patch, 6: paragraphs_validation_can_fail-2994286-6.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

berdir’s picture

Next step is an update function, what you need to do there is truncate the fields first in the data and revision table and then uninstall the field storage definition, see path_update_8200() for example.

arpad.rozsa’s picture

Status: Needs work » Needs review
StatusFileSize
new7.94 KB
new2.58 KB

Added the update function, but when I wanted to uninstall the revision_uid field storage, I got an error, if it had data in the database, so I ended up truncating the both of the fields, just in case. Also fixed the failing test.

berdir’s picture

Status: Needs review » Needs work
+++ b/paragraphs.install
@@ -242,3 +242,32 @@ function paragraphs_update_8014() {
+
+  foreach ($tables_fields as $table => $field) {
+    $storage_definition = $entity_definition_update_manager->getFieldStorageDefinition($field, 'paragraph');
+    if ($storage_definition) {
+      $entity_definition_update_manager->uninstallFieldStorageDefinition($storage_definition);
+    }

this is a bit strange as we actually don't care about tables here but it will run twice for the "uid" field. so maybe run it over array_unique($table_fields) or so?

Wouldn't fail as it checks if the field storage definition is installed but that would be a bit cleaner.

arpad.rozsa’s picture

Status: Needs work » Needs review
StatusFileSize
new7.94 KB
new555 bytes

Yeah good catch, wouldn't hurt, but it is better this way.

miro_dietiker’s picture

Status: Needs review » Needs work
+++ b/src/Entity/Paragraph.php
@@ -242,31 +229,41 @@ class Paragraph extends ContentEntityBase implements ParagraphInterface {
+    return $this->getParentEntity()->get('uid')->entity;
...
+    return $this->getParentEntity()->get('uid')->target_id;

Technically there is a moment when the Paragraph is saved and the parent is not yet set.

You can see the valid state here also:

  public function getParentEntity() {
    if (!isset($this->get('parent_type')->value) || !isset($this->get('parent_id')->value)) {
      return NULL;
    }

Not caring about this case could cause a fatal error in special cases.

Looks like we also need to fix the interface with |null.

berdir’s picture

Agree, we need to check, but we can just use if ($this->getParentEntity()) for that, no need to duplicate the specific checks and then it would also cover weird cases where the entity doesn't exist anymore...

arpad.rozsa’s picture

Status: Needs work » Needs review
StatusFileSize
new9.3 KB
new2.33 KB

Added the checks for the parent entity to the getters. Also added a hook to remove the content_translation_uid from the base field definitions, so if the content_translation module was enabled it doesn't give the user an error that the content_translation_uid field is missing from the database and it won't try to insert data into that column in the future.

miro_dietiker’s picture

Status: Needs review » Needs work

This needs a new update number.

+++ b/src/Entity/Paragraph.php
@@ -242,31 +229,49 @@ class Paragraph extends ContentEntityBase implements ParagraphInterface {
+    if ($this->getParentEntity()) {
+      return $this->getParentEntity()->get('uid')->entity;
...
+    if ($this->getParentEntity()) {
+      return $this->getParentEntity()->get('uid')->target_id;

Nitpick, but this always calls getParentEntity twice and there's quite some complexity with it. Let's try to call it only once.

Maybe invert the logic...

$parent = $this->getParentEntity():
if (!$parent) {
  return NULL;
}
return ...
berdir’s picture

Title: Paragraphs validation can fail if paragraphs were deleted by a user that was delted » Paragraphs validation can fail if paragraphs were deleted by a user that was deleted
arpad.rozsa’s picture

Status: Needs work » Needs review
StatusFileSize
new9.1 KB
new2.22 KB

Changed the update number and the parent entity checking.

berdir’s picture

Status: Needs review » Reviewed & tested by the community

Looks good now I think.

howards’s picture

I noticed some irregularities after using this patch against 8.7.x drupal core. The circumstance is a paragraph fieldset added to a taxonomy vocabulary whereby the paragraph may add taxonomy terms to the same vocabulary.

In this setup the taxonomy term created had no initial referenced paragraph created, and then was initially saved/created without the reference. If the term (with no initial referenced paragraph) is later edited to add a referenced paragraph, clicking on "Add paragraph" results in javascript errors.

AjaxError:
An AJAX HTTP error occurred.
HTTP Result Code: 500
Debugging information follows.
Path: /taxonomy/term/525/edit?ajax_form=1
StatusText: 500 Service unavailable (with message)
ResponseText: The website encountered an unexpected error. Please try again later.

The server error log contained this text:

[Sat Dec 22 22:08:56.872819 2018] [fcgid:warn] [pid 5135] [client 127.0.0.1:39598] mod_fcgid: stderr: Uncaught PHP Exception InvalidArgumentException: "Field uid is unknown." at /home/dntgs/docroot/core/lib/Drupal/Core/Entity/ContentEntityBase.php line 587, referer: https://dntgs.local/taxonomy/term/525/edit

The offending code out of ContentEntityBase.php is the following:

  protected function getTranslatedField($name, $langcode) {
    [...]
    // Populate $this->fields to speed-up further look-ups and to keep track of
    // fields objects, possibly holding changes to field values.
    if (!isset($this->fields[$name][$langcode])) {
      $definition = $this->getFieldDefinition($name);
      if (!$definition) {
        throw new \InvalidArgumentException("Field $name is unknown.");
      }

Unfortunately I do not have the expertise to trace the code back to the origin causes, but it appears as though field definitions were changed in the way that owners were fetched by the parent entity? I am not sure if there was some place that was missed in changing the way ownership was set or...

An issue or not?

howards’s picture

Status: Reviewed & tested by the community » Needs work

Removing RTBC in favor of needs work. The reproduction of the issue:

Copy and paste this into a debugParagraphs.make file:

core: 8.x
api: 2
projects:
  drupal:
    download:
      type: git
      url: https://git.drupal.org/project/drupal.git
      branch: 8.7.x
      revision:	b99b499e70929e318d846f5a83628ca01b8ba292
  entity_reference_revisions:
    type: module
    subdir: contrib
    download:
      type: git
      url: https://git.drupal.org/project/entity_reference_revisions.git
      branch: 8.x-1.x
      revision: 167be1c05fb713e030db9c94365d5ef7f09374b9
  paragraphs:
    type: module
    subdir: contrib
    download:
      type: git
      url: https://git.drupal.org/project/paragraphs.git
      branch: 8.x-1.x
      revision: d2eee2d16408bb1d2513dd4db029f67dd3c928dd
#    patch:
#      - "https://www.drupal.org/files/issues/2018-10-15/paragraphs_validation_can_fail-2994286-17.patch"

Then do the following:

$ drush make debugParagraphs.make docroot ; cd docroot ; composer install ; drush si -y standard --account-name='admin' --account-pass='admin' --db-url='mysql://your_user:your_pass/your_database' ; drush en -y field_ui paragraphs

Once the installation has completed:

  • First and foremost, create a backup of the database so it can be referenced for easier reproduction of the bug. I did that with mkdir ../database ; drush sql-dump > ../database/debugParagraphs.sql
  • Login and go to /admin/structure/paragraphs_type/add and add a new paragraph type.
  • Add an entity reference field on it to the "tags" vocabulary (automatically created in the "standard" installation), save.
  • Then go to /admin/structure/types/manage/article/fields and add a field referencing the paragraph just created.
  • Then go to /admin/structure/taxonomy/manage/tags/overview/fields and add a similar field referencing the exact same paragraph.
  • Then go back to the commandline and drush cex -y (My "sites" folder is outside the docroot, and are automatically re-linked when I run a custom drush script to rebuild the code of the site.)

Once the backup of the database is made, and the sites directory is out of the docroot and symlinked, it makes recreation of the bug easy.

Afterward, I reimported the initial database and imported the configuration changes, I went and created an article using a Test Tag in the paragraphs field. Afterward, I went to /taxonomy/term/1/edit and added a new paragraph option with another tag. Worked like a charm.

Then I went and uncommented the "patch" line and the patch itself in the debugParagraphs.make and rebuilt the code base. Then I reimported the initial database and the configuration that was saved. I repeated the process above and was unable to add another tag in the paragraph field after going to /taxonomy/term/1/edit because it threw errors similar to the ones outlined in the previous comment.

Hopefully, this sheds a bit more light on where the errors are coming from?

berdir’s picture

Thanks for the feedback. That's basically exactly what I was worried about, but it's something that we haven't yet been able to reproduce.

What would be really helpful is if you could provide a backtrace, that's most likely already enough to pinpoint the problem instead of having to set up a relatively complex site.

Enable verbose error logging on your site, then you should see a long error that shows exactly what/who is calling it like that.

howards’s picture

Is this what people are after?

The website encountered an unexpected error. Please try again later.InvalidArgumentException: Field uid is unknown. in Drupal\Core\Entity\ContentEntityBase->getTranslatedField() (line 587 of core/lib/Drupal/Core/Entity/ContentEntityBase.php).

Drupal\Core\Entity\ContentEntityBase->get('uid') (Line: 253)
Drupal\paragraphs\Entity\Paragraph->getOwnerId() (Line: 110)
Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget->formElement(Object, 0, Array, Array, Object) (Line: 335)
Drupal\Core\Field\WidgetBase->formSingleElement(Object, 0, Array, Array, Object) (Line: 202)
Drupal\Core\Field\WidgetBase->formMultipleElements(Object, Array, Object) (Line: 104)
Drupal\Core\Field\WidgetBase->form(Object, Array, Object) (Line: 171)
Drupal\Core\Entity\Entity\EntityFormDisplay->buildForm(Object, Array, Object) (Line: 650)
Drupal\paragraphs\Plugin\Field\FieldWidget\InlineParagraphsWidget->formElement(Object, 0, Array, Array, Object) (Line: 335)
Drupal\Core\Field\WidgetBase->formSingleElement(Object, 0, Array, Array, Object) (Line: 831)
Drupal\paragraphs\Plugin\Field\FieldWidget\InlineParagraphsWidget->formMultipleElements(Object, Array, Object) (Line: 104)
Drupal\Core\Field\WidgetBase->form(Object, Array, Object, NULL) (Line: 942)
Drupal\paragraphs\Plugin\Field\FieldWidget\InlineParagraphsWidget->form(Object, Array, Object) (Line: 171)
Drupal\Core\Entity\Entity\EntityFormDisplay->buildForm(Object, Array, Object) (Line: 125)
Drupal\Core\Entity\ContentEntityForm->form(Array, Object) (Line: 94)
Drupal\taxonomy\TermForm->form(Array, Object) (Line: 117)
Drupal\Core\Entity\EntityForm->buildForm(Array, Object)
call_user_func_array(Array, Array) (Line: 518)
Drupal\Core\Form\FormBuilder->retrieveForm('taxonomy_term_tags_form', Object) (Line: 275)
Drupal\Core\Form\FormBuilder->buildForm('taxonomy_term_tags_form', Object) (Line: 93)
Drupal\Core\Controller\FormController->getContentResult(Object, Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 582)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 151)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 669)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
berdir’s picture

Status: Needs work » Reviewed & tested by the community

Yes, that's what I need. I can see two things in that backtrace:

a) Not the problem for this issue, but you seem to be using paragraphs in combination with the core autocomplete widget? That's not supported because paragraphs are not designed to be re-used, they must only have one parent, otherwise some things like private file access will not work correctly. It's also risky with the work we're doing in ERR to clean up unused paragraphs.

b) The real issue here is that the getOwnerId() call still seems to be accessing the uid field. Not sure if you removed the patch again or incorrectly applied it, but that's not supported and not what the code is doing when this patch is applied. You can not remove patches again that make structure changes. Also, you should be very-very careful in any patches that have update functions, because there is a chance that these patches will be rerolled with different update function numbers and then your site is going to be very broken as it will not run certain updates.

johnchque’s picture

Assigned: Unassigned » johnchque
Status: Reviewed & tested by the community » Needs work

We need one more update function. Working on that.

johnchque’s picture

Status: Needs work » Needs review
StatusFileSize
new9.65 KB
new705 bytes

We needed to update the metadata keys too. :)

Status: Needs review » Needs work

The last submitted patch, 25: paragraphs_validation_can_fail-2994286-25.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

johnchque’s picture

Status: Needs work » Needs review
StatusFileSize
new9.68 KB
new592 bytes

This is needed, thanks @Berdir.

The last submitted patch, 17: paragraphs_validation_can_fail-2994286-17.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 27: paragraphs_validation_can_fail-2994286-27.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

berdir’s picture

Assigned: johnchque » Unassigned
Status: Needs work » Reviewed & tested by the community

Test fails are unrelated but lets open an issue to fix them. new update function looks good lets get this in.

johnchque’s picture

mbovan’s picture

Status: Reviewed & tested by the community » Needs work

I was subscribed to this issue.

+++ b/paragraphs.install
@@ -255,3 +255,46 @@ function paragraphs_update_8015() {
+    if (is_array($metadata_keys) && isset($revision_metadata_keys['revision_uid'])) {

I think isset($revision_metadata_keys['revision_uid']) should be isset($metadata_keys['revision_uid']).

berdir’s picture

True, missed that. And we should also unset the key and then set $metadata_keys.

johnchque’s picture

Status: Needs work » Needs review
StatusFileSize
new9.75 KB
new879 bytes

Sure, I think this should address both comments above. Thanks for noticing. :)

Status: Needs review » Needs work

The last submitted patch, 34: paragraphs_validation_can_fail-2994286-34.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

johnchque’s picture

Status: Needs work » Needs review

  • Berdir committed 38cd962 on 8.x-1.x authored by yongt9412
    Issue #2994286 by arpad.rozsa, yongt9412, Berdir: Paragraphs validation...
berdir’s picture

Status: Needs review » Fixed

Committed.

penyaskito’s picture

This is making Lingotek tests fail, I'm still looking at the reason.

penyaskito’s picture

StatusFileSize
new1.21 KB

So actually fails when calling getOwner in embedded paragraphs. We cannot assume there is an "uid" field.
This is not ideal, but at least better and works for me.

penyaskito’s picture

StatusFileSize
new1.23 KB

Simplifying this by @Berdir suggestion on Slack.

berdir’s picture

Status: Fixed » Needs review

  • Berdir committed d967fe1 on 8.x-1.x authored by penyaskito
    Issue #2994286 by penyaskito, Berdir: Fixed incorrect parent owner...
berdir’s picture

Status: Needs review » Fixed

Thanks for catching that so quickly, somehow read over that prat.

penyaskito’s picture

Awesome, thanks!

Status: Fixed » Closed (fixed)

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

pasqualle’s picture

How could I see this error with paragraphs version 8.x-1.6?

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'revision.revision_uid' in 'field list':
SELECT
 revision.revision_id AS revision_id,
 revision.langcode AS langcode,
 revision.revision_uid AS revision_uid,
 revision.revision_default AS revision_default,
 base.id AS id,
 base.type AS type,
 base.uuid AS uuid,
 CASE base.revision_id WHEN revision.revision_id THEN 1 ELSE 0 END AS isDefaultRevision
FROM
{paragraphs_item} base
INNER JOIN {paragraphs_item_revision} revision ON revision.revision_id = base.revision_id
WHERE base.id IN (:db_condition_placeholder_0); Array
(
    [:db_condition_placeholder_0] => 14
)
",
berdir’s picture

This change is not in 8.x-1.6, you still need the patch if you had that applied. 8.x-1.6 was only a security fix, based on 8.x-1.5.

pasqualle’s picture

I see. I was on the dev version, run these updates, then switched to 1.6.
Switching back to dev..

Thanks

trevorbradley’s picture

I'm having issues with this patch/Paragraphs 1.7/1.8 - I can't delete paragraph fields with content anymore! See #3044256: Unable to delete paragraph fields without error