Cms: Error when publishing draft entry that has a matrix field modified only in the source entry

Created on 25 Mar 2021  Â·  5Comments  Â·  Source: craftcms/cms

Description

Getting an exception "Attempting to duplicate an unsaved element." when publishing a draft that contains a matrix that was not modified in the draft but was modified in the source entry (has a yellow M).

Steps to reproduce

  1. Start with a vanilla craft install
  2. Create a matrix field with a single block type that has a single plain text field
  3. Create a section and add the matrix field to its default entry type
  4. Create an entry in that section, add a single block to the matrix and give the plain text field of that block some value and save
  5. Save a draft of that entry
  6. Switch to Current of that entry, make a change to the plain text field of the single matrix block, and save
  7. Edit the draft created in step 5 and note that it has a yellow M because the current version was updated in step 6
  8. Hit the publish changes button

Workaround

I have found that clearing the cache at this point prevents this error, but I do not fully understand the ramifications of clearing the cache at this point.

    public function duplicateBlocks(MatrixField $field, ElementInterface $source, ElementInterface $target, bool $checkOtherSites = false)
    {
        $elementsService = Craft::$app->getElements();
        /** @var MatrixBlockQuery $query */
        $query = $source->getFieldValue($field->handle);
/* add this line to prevent "Attempting to duplicate an unsaved element." */ $query->clearCachedResult();
        /** @var MatrixBlock[] $blocks */
        if (($blocks = $query->getCachedResult()) === null) {
            $blocksQuery = clone $query;
            $blocks = $blocksQuery->anyStatus()->all();
        }
        $newBlockIds = [];

Stack trace

  1. in /var/app/vendor/craftcms/cms/src/services/Elements.php at line 954
  2. in /var/app/vendor/craftcms/cms/src/services/Matrix.php at line 865– craft\services\Elements::duplicateElement(craft\elements\MatrixBlock, ['ownerId' => 5, 'owner' => craft\elements\Entry, 'siteId' => 1, 'propagating' => false])
  3. in /var/app/vendor/craftcms/cms/src/fields/Matrix.php at line 993– craft\services\Matrix::duplicateBlocks(craft\fields\Matrix, craft\elements\Entry, craft\elements\Entry, true)

image

Additional info

  • Craft version: 3.5.18
  • PHP version: 7.4
  • Database driver & version: mysql 5.7
  • Plugins & versions: none
bug

All 5 comments

Able to reproduce this, and will look into it.

Figured out this temporary workaround that doesnt require an internal code modification and works for any field type that has similar issues, like super table.

@brandonkelly can you think of any obvious reason this could cause probs if we put this into service while awaiting a permanent fix?

use craft\elements\db\ElementQuery;
use craft\events\ElementEvent;
use craft\services\Elements;
use yii\base\Event;

        Event::on(
            Elements::class,
            Elements::EVENT_AFTER_SAVE_ELEMENT,
            function(ElementEvent $event) {
                $duplicatedElement = $event->element->duplicateOf;
                if(!$duplicatedElement || !$duplicatedElement->getFieldLayout()) {
                    return;
                }

                foreach ($duplicatedElement->getFieldValues() as $query) {
                    if($query instanceof ElementQuery) {
                        $query->clearCachedResult();
                    }
                }
            }
        );

The root issue here is that Matrix block changes weren’t getting propagated to drafts when Current revision changes were getting merged into the draft, leaving the Matrix field in a bit of a weird state for the draft. Just fixed that for the next release, which also fixes the “Attempting to duplicate an unsaved element” error. Going to PR similar fixes to Super Table and Neo now.

Craft 3.6.11.2 is out now with the fix for Matrix fields ✨

Was this page helpful?
0 / 5 - 0 ratings