<p>Apparently this was broken because of the die() statements in the above code. I'm still not sure exactly what's going on, but commenting those out fixes the issue. I'm thinking Craft must do some cleanup at the end of a request that sets everything right.</p>
<p>Rather than delete the question, I'll leave it in case anyone happens to run into something similar. Basically, don't die!</p>

CC BY-SA 4.0
2023-01-07T09:24:58.570Z
2023-01-07T09:24:58.570Z
Eli
<p>I'm somewhat new to Craft, and struggling a bit with copying matrix block types. Any help would be greatly appreciated.</p>
<p>So, I have some code that copies a matrix block and its fields, but when I go to delete the newly copied block from the admin, it doesn't go away. I can get it to go away by first deleting all the fields, but not by just deleting the block itself. Am I doing something wrong in the following code that creates the new block?</p>
<p>Thanks in advance for any insight you can give!</p>
<pre><code>private function copyMatrixBlock(string $matrix_handle, string $block_handle){

    //get the matrix field
    $matrix_field = Craft::$app-&gt;fields-&gt;getFieldByHandle($matrix_handle);

    //look up the block type id from the $block_handle
    $query = new Query;

    $query-&gt;select('mbt.id');
    $query-&gt;from(TABLE::MATRIXBLOCKTYPES . ' mbt');
    $query-&gt;where('mbt.handle = \''.$block_handle.'\'');

    $row = $query-&gt;one();

    $block_type_id = $row['id'];

    //get the block types of the matrix field
    $block_types = $matrix_field-&gt;getBlockTypes();

    //get the fields that we want to copy
    $the_fields_to_copy = $matrix_field-&gt;getBlockTypeFields([$block_type_id]);
    
    $save_these_blocks = [];

    $field_index = 0;
    $block_type_index = 0;

    foreach($block_types as $k =&gt; $block_type){

        $save_these_blocks[$block_type-&gt;id] = $block_type;

        if($block_type-&gt;id == $block_type_id){ //match on the looked up id from the $block_handle
            // copy this block type
            
            $copy_id = StringHelper::randomString(10);
            
            $config['name'] = $block_type-&gt;name . '_' . $copy_id;
            $config['handle'] = $block_type-&gt;handle . '_' . $copy_id;
            $config['fields'] = [];

            //loop through the fields to copy and add them to the block
            foreach($the_fields_to_copy as $field_to_copy){

                $field_config = [];
                
                $query = new Query;
                $query-&gt;select('f.*');
                $query-&gt;from(TABLE::FIELDS . ' f');
                $query-&gt;where('f.id = \''.$field_to_copy-&gt;id.'\''); 

                $row = $query-&gt;one();
                foreach($row as $k =&gt; $v){ //copy 
                    $field_config[$k] = $v; 
                }
                //replace the name and handle, unset 'uid'
                $field_config['name'] = $row['name'] . '_' . $copy_id;
                $field_config['handle'] = $row['handle'] . '_' . $copy_id;
                $field_config['uid'] = '';
                $field_config['settings'] = '';
                $field_config['typesettings'] = $row['settings'];

                $config['fields']['new' . ++$field_index] = $field_config; //the key needs to be non-numeric
            }

            $new_block = $this-&gt;makeBlockType($matrix_field-&gt;id, $config, $copy_id);

            $save_these_blocks['new' . ++$block_type_index] = $new_block; //the key needs to be non-numeric

        } else {
            //don't copy this block type
        }
    }

    //save the new blocks to the matrix field
    $matrix_field-&gt;setBlockTypes($save_these_blocks);

    //attempt to save
    $fieldsService = Craft::$app-&gt;getFields();
    if(!$fieldsService-&gt;saveField($matrix_field)){
        die('SOMETHING WENT WRONG');
    } else {
        die('SOMETHING WENT RIGHT');
    }
}

// mostly copied from Matrix::setBlockTypes
private function makeBlockType($matrix_field_id, $config, $copy_id):MatrixBlockType{

    $defaultFieldConfig = [
        'type' =&gt; null,
        'name' =&gt; null,
        'handle' =&gt; null,
        'instructions' =&gt; null,
        'required' =&gt; false,
        'searchable' =&gt; true,
        'translationMethod' =&gt; Field::TRANSLATION_METHOD_NONE,
        'translationKeyFormat' =&gt; null,
        'typesettings' =&gt; null,
    ];

    $blockType = new MatrixBlockType();
    $blockType-&gt;fieldId = $matrix_field_id;
    $blockType-&gt;name = $config['name'] . '_' . $copy_id;
    $blockType-&gt;handle = $config['handle'] . '_' . $copy_id;

    $fieldLayout = $blockType-&gt;getFieldLayout();
    if (($fieldLayoutTab = $fieldLayout-&gt;getTabs()[0] ?? null) === null) {
        $fieldLayoutTab = new FieldLayoutTab();
        $fieldLayoutTab-&gt;name = 'Content';
        $fieldLayoutTab-&gt;sortOrder = 1;
        $fieldLayout-&gt;setTabs([$fieldLayoutTab]);
    }

    $fields = [];
    $layoutElements = [];

    if (!empty($config['fields'])) {
        foreach ($config['fields'] as $fieldId =&gt; $fieldConfig) {
            // If the field doesn't specify a type, then it probably wasn't meant to be submitted
            if (!isset($fieldConfig['type'])) {
                continue;
            }

            $fieldConfig = array_merge($defaultFieldConfig, $fieldConfig);
            $field = $fields[] = Craft::$app-&gt;getFields()-&gt;createField([
                'type' =&gt; $fieldConfig['type'],
                'id' =&gt; is_numeric($fieldId) ? $fieldId : null,
                'name' =&gt; $fieldConfig['name'],
                'handle' =&gt; $fieldConfig['handle'],
                'columnSuffix' =&gt; $fieldConfig['columnSuffix'] ?? null,
                'instructions' =&gt; $fieldConfig['instructions'],
                'required' =&gt; (bool)$fieldConfig['required'],
                'searchable' =&gt; (bool)$fieldConfig['searchable'],
                'translationMethod' =&gt; $fieldConfig['translationMethod'],
                'translationKeyFormat' =&gt; $fieldConfig['translationKeyFormat'],
                'settings' =&gt; $fieldConfig['typesettings'],
            ]);

            $layoutElements[] = Craft::createObject([
                'class' =&gt; CustomField::class,
                'uid' =&gt; $fieldConfig['uid'] ?? null,
                'required' =&gt; (bool)$fieldConfig['required'],
                'width' =&gt; (int)($fieldConfig['width'] ?? 0) ?: 100,
            ], [$field]);
        }
    }

    $fieldLayoutTab-&gt;setElements($layoutElements);
    return $blockType;
}
</code></pre>

CC BY-SA 4.0
2023-01-06T15:42:47.743Z
Hi. just some ideas to try. ignore create layoutElements[] and setElement() . try creating new blocktype as array without setting fieldId:  $newBlockType= []; $newBlockType['handle'] = $handle; $newBlockType['name'] = $name; 
after your createField function -without passing id param- push field to new block type  $newBlockType['fields'][$fieldId] = $field; and return $newBlockType
user9975
CC BY-SA 4.0
2023-01-07T09:21:05.580Z
Thanks for the suggestions. I *think* I figured it out. By taking out the die() statements after $fieldsService->saveField(), it appears to work correctly. I think by dying, I wasn't letting Craft do some operations it needed for the changes to take hold. Thinking it's something related to ProjectConfig.
Eli
<craft3><plugin-development><matrix><craft4>
Deleting a matrix block type that was copied in code
# Deleting a matrix block type that was copied in code

(Asked by: username_0 on 2023-01-05)

I'm somewhat new to Craft, and struggling a bit with copying matrix block types. Any help would be greatly appreciated.
So, I have some code that copies a matrix block and its fields, but when I go to delete the newly copied block from the admin, it doesn't go away. I can get it to go away by first deleting all the fields, but not by just deleting the block itself. Am I doing something wrong in the following code that creates the new block?
Thanks in advance for any insight you can give!
private function copyMatrixBlock(string $matrix_handle, string $block_handle){

    //get the matrix field
    $matrix_field = Craft::$app->fields->getFieldByHandle($matrix_handle);

    //look up the block type id from the $block_handle
    $query = new Query;

    $query->select('mbt.id');
    $query->from(TABLE::MATRIXBLOCKTYPES . ' mbt');
    $query->where('mbt.handle = \''.$block_handle.'\'');

    $row = $query->one();

    $block_type_id = $row['id'];

    //get the block types of the matrix field
    $block_types = $matrix_field->getBlockTypes();

    //get the fields that we want to copy
    $the_fields_to_copy = $matrix_field->getBlockTypeFields([$block_type_id]);
    
    $save_these_blocks = [];

    $field_index = 0;
    $block_type_index = 0;

    foreach($block_types as $k => $block_type){

        $save_these_blocks[$block_type->id] = $block_type;

        if($block_type->id == $block_type_id){ //match on the looked up id from the $block_handle
            // copy this block type
            
            $copy_id = StringHelper::randomString(10);
            
            $config['name'] = $block_type->name . '_' . $copy_id;
            $config['handle'] = $block_type->handle . '_' . $copy_id;
            $config['fields'] = [];

            //loop through the fields to copy and add them to the block
            foreach($the_fields_to_copy as $field_to_copy){

                $field_config = [];
                
                $query = new Query;
                $query->select('f.*');
                $query->from(TABLE::FIELDS . ' f');
                $query->where('f.id = \''.$field_to_copy->id.'\''); 

                $row = $query->one();
                foreach($row as $k => $v){ //copy 
                    $field_config[$k] = $v; 
                }
                //replace the name and handle, unset 'uid'
                $field_config['name'] = $row['name'] . '_' . $copy_id;
                $field_config['handle'] = $row['handle'] . '_' . $copy_id;
                $field_config['uid'] = '';
                $field_config['settings'] = '';
                $field_config['typesettings'] = $row['settings'];

                $config['fields']['new' . ++$field_index] = $field_config; //the key needs to be non-numeric
            }

            $new_block = $this->makeBlockType($matrix_field->id, $config, $copy_id);

            $save_these_blocks['new' . ++$block_type_index] = $new_block; //the key needs to be non-numeric

        } else {
            //don't copy this block type
        }
    }

    //save the new blocks to the matrix field
    $matrix_field->setBlockTypes($save_these_blocks);

    //attempt to save
    $fieldsService = Craft::$app->getFields();
    if(!$fieldsService->saveField($matrix_field)){
        die('SOMETHING WENT WRONG');
    } else {
        die('SOMETHING WENT RIGHT');
    }
}

// mostly copied from Matrix::setBlockTypes
private function makeBlockType($matrix_field_id, $config, $copy_id):MatrixBlockType{

    $defaultFieldConfig = [
        'type' => null,
        'name' => null,
        'handle' => null,
        'instructions' => null,
        'required' => false,
        'searchable' => true,
        'translationMethod' => Field::TRANSLATION_METHOD_NONE,
        'translationKeyFormat' => null,
        'typesettings' => null,
    ];

    $blockType = new MatrixBlockType();
    $blockType->fieldId = $matrix_field_id;
    $blockType->name = $config['name'] . '_' . $copy_id;
    $blockType->handle = $config['handle'] . '_' . $copy_id;

    $fieldLayout = $blockType->getFieldLayout();
    if (($fieldLayoutTab = $fieldLayout->getTabs()[0] ?? null) === null) {
        $fieldLayoutTab = new FieldLayoutTab();
        $fieldLayoutTab->name = 'Content';
        $fieldLayoutTab->sortOrder = 1;
        $fieldLayout->setTabs([$fieldLayoutTab]);
    }

    $fields = [];
    $layoutElements = [];

    if (!empty($config['fields'])) {
        foreach ($config['fields'] as $fieldId => $fieldConfig) {
            // If the field doesn't specify a type, then it probably wasn't meant to be submitted
            if (!isset($fieldConfig['type'])) {
                continue;
            }

            $fieldConfig = array_merge($defaultFieldConfig, $fieldConfig);
            $field = $fields[] = Craft::$app->getFields()->createField([
                'type' => $fieldConfig['type'],
                'id' => is_numeric($fieldId) ? $fieldId : null,
                'name' => $fieldConfig['name'],
                'handle' => $fieldConfig['handle'],
                'columnSuffix' => $fieldConfig['columnSuffix'] ?? null,
                'instructions' => $fieldConfig['instructions'],
                'required' => (bool)$fieldConfig['required'],
                'searchable' => (bool)$fieldConfig['searchable'],
                'translationMethod' => $fieldConfig['translationMethod'],
                'translationKeyFormat' => $fieldConfig['translationKeyFormat'],
                'settings' => $fieldConfig['typesettings'],
            ]);

            $layoutElements[] = Craft::createObject([
                'class' => CustomField::class,
                'uid' => $fieldConfig['uid'] ?? null,
                'required' => (bool)$fieldConfig['required'],
                'width' => (int)($fieldConfig['width'] ?? 0) ?: 100,
            ], [$field]);
        }
    }

    $fieldLayoutTab->setElements($layoutElements);
    return $blockType;
}

**Tags:** craft3, plugin-development, matrix, craft4

> **Comments:**
> **username_1**: Hi. just some ideas to try. ignore create layoutElements[] and setElement() . try creating new blocktype as array without setting fieldId:  $newBlockType= []; $newBlockType['handle'] = $handle; $newBlockType['name'] = $name;  after your createField function -without passing id param- push field to new block type  $newBlockType['fields'][$fieldId] = $field; and return $newBlockType  
> **username_0**: Thanks for the suggestions. I *think* I figured it out. By taking out the die() statements after $fieldsService->saveField(), it appears to work correctly. I think by dying, I wasn't letting Craft do some operations it needed for the changes to take hold. Thinking it's something related to ProjectConfig.  

---

## 1 answers

### Answer by username_0 on 2023-01-07. Score: 0 | Accepted answer

Apparently this was broken because of the die() statements in the above code. I'm still not sure exactly what's going on, but commenting those out fixes the issue. I'm thinking Craft must do some cleanup at the end of a request that sets everything right.

Rather than delete the question, I'll leave it in case anyone happens to run into something similar. Basically, don't die!


---

