2

I have a custom module, on EVENT_BEFORE_SAVE I am looping through a matrix field, this matrix field has a block which has an entry field, a number field and a lightswitch (to know if this block has been processed - more on this below).

When saving the entry (containing the matrix field) I am currently looping through all the matrix blocks, getting the value form the numbers field and updating a field in the related entry from the entry field. The related entries are then all resaved.

On successful save for each related entry I want to update each the matrix block lightswitch field, from the original entry, to indicate that the related entry was updated and resaved. If there were any errors saving this lightswitch field would not be updated, and the entry can be reprocessed in the future.

The issue I am having is, on the successful entry save of the matrix entry field Entry, the lightswitch field is not being updated.

You can see where I am trying to update the lightswitch field in the current block here (Full code is below)

if (Craft::$app->elements->saveElement($getRelatedEntry))
{
    // Update lightswitch field in current block in Entry
    $block->setFieldValue('stockAdded', 1);

    // Save block
    Craft::$app->elements->saveElement($block);
}

I would be interested to see how others may handle this situation, whether it may be possible to hold off going past EVENT_BEFORE_SAVE until it require it to, or if this process could be handled differently and more effectively.

Here is the code I am using, I have cleaned up a few bits for simplicity.

foreach ($entry->matrixField->blockProcessed(0)->all() as $block) {
    $processed = $block->blockProcessed;

    // Check to see if this has been processed already
    if ($processed) {
        // Do nothing
        return;
    }

    // Get entry
    $relatedEntry = $block->blockEntry->one() ?? null;

    if (!$relatedEntry) {
        // Entry does no exist - Do nothing
        return;
    }

    // Get ID from the related Entry
    $relatedEntryId = $relatedEntry->id;

    // Get value to from block
    $value = $block->blockValue;

    // Get product entry
    $getRelatedEntry = Entry::find()->id($relatedEntryId)->one() ?? null;

    // Check of entry exists and value is greater than zero
    if ($getRelatedEntry && $value > 0) {
        // Get current value
        $currentValue = $getRelatedEntry->value;

        // New stock amount
        $newValue = $currentValue + $value;

        // Update stock level
        $getRelatedEntry->setFieldValue('value', $newValue);

        // Save product entry
        if (Craft::$app->elements->saveElement($getRelatedEntry))
        {
            // Update lightswitch field in current block in Entry
            $block->setFieldValue('blockProcessed', 1);

            // Save block
            Craft::$app->elements->saveElement($block);
        }
    }

}

Update

It was pointed out that the block was not being saved before the entry was saved, so it lost any data. being passed I have updated the code examples above to include the change.

This partially solved the issue, the other part of the issue seems to be with the propagation of the matrix blocks to the new entry which I need to overcome.

Saving the entry now, with the block saving code (Craft::$app->elements->saveElement($block);), causes the entry to lose the block data until the page is refreshed. The behaviour seems to show the updated block data on the initial save, when viewing the entry it shows the blocks with the lightswitch field turned on, however when if the entry is saved again it loses the matrix field block data.

What is the best way to continue saving the entry and propagate the updated matrix blocks. Would it be best to move this to EVENT_AFTER_PROPAGATE or am I missing something when saving the block initially with the updated data?

##Update 2 After some investigating the issue with the lost matrix block was resolved moving to using EVENT_AFTER_PROPAGATE, running the code at this point and only saving the block after the new data was passed solved the whole issue I was having.

1 Answer 1

1

Looks like you're not actually saving the block with the lightswitch field. Try adding this after setting the blockProcessed value:

// Update lightswitch field in current block in Entry
$block->setFieldValue('blockProcessed', 1);
// Save the block to persist the lightswitch value
Craft::$app->elements->saveElement($block);

You might also have to save the entry the block is on as well, I'm not sure if that is required if all you're doing is updating individual fields on an existing matrix block without adding/removing blocks. See also: How to save Matrix data programmatically in Craft 3

I would be interested to see how others may handle this situation, whether it may be possible to hold off going past EVENT_BEFORE_SAVE until it require it to, or if this process could be handled differently and more effectively.

Hard to tell without knowing what the purpose of your hook is. Going only by the simplified code (my conclusion may be wrong) I'd guess the value field is there to keep track of how many other entries the entry related through blockEntry is related to? In this case, you can get rid of the hook altogether and just query that information when you need it. It will be more accurate anyway. This can be solved using a relations query. See Going through Matrix in particular.

5
  • For my case you were correct, I thought passing the field change in the matrix field before the save event would save the matrix block. Saving the block did work, Thanks for this!
    – zizther
    Aug 9, 2021 at 18:39
  • Saving the blocks did work, the issue I am now facing is that I lose the block data when saving the entry, these are not propagated. So now I need to find the right way to handle this. I have updated the question based on the changes I have made and have a new question, which may someone could help with.
    – zizther
    Aug 10, 2021 at 1:03
  • I forgot to mention, the value is related to updating the related entry, I do some math on the other entry and resave that entry, I then mark this as being processed with the lightswitch field which shows the related entry has been updated when the original entry has been saved (with some other field value checks).
    – zizther
    Aug 10, 2021 at 1:07
  • I did some more playing around, thanks to MoritzLost for his support on getting this on track. The issue was mostly related to the block not being saved. The issue with the data not being propagated solved moving to EVENT_AFTER_PROPAGATE , doing so solved the issue with the lost data and everything seems to be working without having to resave the entry, only the block needed saving working with this event
    – zizther
    Aug 10, 2021 at 1:39
  • @zizther Glad you got it working!
    – MoritzLost
    Aug 10, 2021 at 7:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.