New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Creating / editing single matrix blocks on the front end #5717
Comments
Just my opinion on this... I'll take the opportunity to kindly ask for another featuer request/suggestion about this. First of all, the only way to append/add Matrix block(s) to an existing element, without just saving the block(s) itself but the element and without loosing the rest of them can feel "ugly" (at least I have that feeling) /** @var \craft\fields\Matrix $field */
$field = Craft::$app->getFields()->getFieldByHandle('matrixField');
// get the existing matrixField Value, keep in mind, this is a Query and not an array
$existingMatrixQuery = $element->getFieldValue('matrixField');
// serialize the data in order to get an array like Brandon Kelly
// created in his answer
$serializedMatrix = $field->serializeValue($existingMatrixQuery, $element);
// append the new blocks to the existing array
$serializedMatrix['new1'] = [
'type' => 'blockTypeHandle',
'fields' => [
// the block's custom field values...
]
];
$element->setFieldValue('matrixField', $serializedMatrix);
// Save the entry
Craft::$app->elements->saveElement($element); You need to serialize the existing block elements to an array, then change it / append / modify the existing data and set the field value back. @samhibberd this would be one possible way to modify and add certain blocks, without deleting the rest of them, So my suggestion would be to to let elements of type That would allow us to do the following /** @var \craft\elements\MatrixBlock[] $existingBlocks */
/** @var \craft\base\Element $element */
$existingBlocks = $element->matrix->all();
foreach ($postData as $key => $newMatrixBlock){
$existingBlocks['new' . ($key+1)] = [
// set the properties
];
}
$element->setFieldValue($existingBlocks); @samhibberd As a current "not really secure", but really easy and fast way to let people edit only certain blocks without deleting the rest is to include the rest of them in If it's only about saving Matrix Blocks without saving the corresponding element, you can as well only work with the blocks... $newBlock = new MatrixBlock();
// ... set all required values, as well as ownerId
Craft::$app->getElements()->saveElement($newBlock); and it will be appended |
As of Craft 3.4 it is now possible to save partial Matrix data, as part of the new delta save feature. I’ve been meaning to document it, so thanks for the nudge :) https://docs.craftcms.com/v3/matrix-fields.html#saving-matrix-fields-in-entry-forms The one caveat is that you must submit a So here’s a simple example of how to create an entry form that just adds one additional {% if entry is defined %}
{# Retain existing blocks + sort order #}
{% for blockId in clone(entry.<FieldHandle>).anyStatus().ids() %}
{{ hiddenInput('fields[<FieldHandle>][sortOrder][]', blockId) }}
{% endfor %}
{% endif %}
{# Add a new text block #}
{{ hiddenInput('fields[<FieldHandle>][sortOrder][]', 'new:1') }}
{# Prefix the block's input names with `fields[<FieldHandle>][blocks][new:1]` #}
{% namespace "fields[<FieldHandle>][blocks][new:1]" %}
{{ hiddenInput('type', 'text') }}
<textarea name="fields[<TextFieldHandle>]"></textarea>
{% endnamespace %} That use yii\web\Response;
use craft\elements\Entry;
public function actionAddBlock(): Response
{
// ...
/** @var Entry $entry */
$sortOrder = (clone $entry->myMatrixField)->anyStatus()->ids();
$sortOrder[] = 'new:1';
$newBlock = [
'type' => 'text',
'fields' => [
'myTextField' => Craft::$app->request->getBodyParam('text'),
],
];
$entry->setFieldValue('myMatrixField', [
'sortOrder' => $sortOrder,
'blocks' => [
'new:1' => $newBlock,
],
]);
// ...
} |
This is awesome! Thanks @brandonkelly |
Is this new delta save feature allow to delete Matrix blocks by removing block ids from the sortOrder cloned var? I mean, this will soft/hard delete MatrixBlock elements or those block will still pointlessly live in my db? The reindex task will do the trick? As example, this is a correct way to achieve a specific block deletion? : $sortOrder = (clone $entry->myMatrixField)->anyStatus()->ids();
// [1,2,3]
//remove 2 from my array
foreach (array_keys($sortOrder, 2) as $key) {
unset($sortOrder[$key]);
}
// [1,3]
$entry->setFieldValue('myMatrixField', [
'sortOrder' => $sortOrder,
]);
// ... I want to avoid my db to become a huge orphanage of rows (at least try to keep it clean)... |
@rtrudel Yep, that should work exactly as you’ve typed it. FYI you could use ArrayHelper::withoutValue() to simplify the removal: use craft\helpers\ArrayHelper;
// ...
$sortOrder = ArrayHelper::withoutValue($sortOrder, 2); |
This is excellent! I hope that kind of matrix manipulation snippets will exists somewhere in the doc! For Twig and PHP. Thanks @brandonkelly ! Nice to know about |
I'm not sure if this is a feature request, issue, us missing something obvious. We use matrix fields on the front end regularly, but until now have always output all existing blocks, with the option to add new, reorder, delete etc etc. We basically just replicate the cp and it works a dream.
We now have a requirement where we don't want existing blocks output when adding new rows and also want to be able to edit existing rows on their own.
Our preference would be to achieve this with the existing craft controllers where possible:
We have tried posting just new blocks to the standard craft controllers, but this nukes existing rows.
1) Can nuking existing rows be avoided?
2) Is / could this behaviour be possible using the craft controllers?
We have also tested by writing our own matrix block controller, editing works nicely we just pass the
blockId
, grab the field data, validate and return the response.For saving new blocks we are passing
fieldId
typeId
&ownerId
to create a new block, but new rows are not validating, do blocks in this state have enough info to validate?3) Is this approach recommended? Would Craft permissions apply when working with Matrix Block this way.
4) Would craft ever consider adding controllers to work directly with matrix block elements?
The text was updated successfully, but these errors were encountered: