# Create custom blocks programmatically

**URL:** <https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137>\
**Category:** Questions\
**Tags:** v3\
**Created:** [May 1, 2021, 7:35pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137 "2021-05-01T19:35:41Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![seriamlo](https://avatars.discourse-cdn.com/v4/letter/s/45deac/32.png) [@seriamlo](https://forum.getkirby.com/u/seriamlo)\
**Post date:** [May 1, 2021, 7:35pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/1 "2021-05-01T19:35:41Z")

</div>

I also have the problem that blocks do not return image paths, which I need in a headless setup.  
I read this post: [Update blocks field programmatically if we use Layout? - #5 by bastianallgeier](https://forum.getkirby.com/t/update-blocks-field-programmatically-if-we-use-layout/21435/5)  
which does come pretty close to what I need.

My plan currently is to create a custom block for each image block I find. On the update hook I loop through all blocks and if I find an image block I want to create a custom image block, which includes the full URL.  
(This because I did not succeed in changing an existing block).

Unfortunately I don’t succeed either in creating a custom block in the factory.  
I followed this cookbook recipe: [Creating a custom block type from scratch | Kirby CMS](https://getkirby.com/docs/cookbook/panel/custom-block-type)  
so I have that wanted custom block type which also can be added normally.  
But I can’t use the factory to create a custom block.

Thanks for any help.  
cheers

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [May 1, 2021, 8:18pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/2 "2021-05-01T20:18:07Z")

</div>

Just to understand this correctly: you have successfully created a custom block that works as expected and can be used when manually creating blocks in the Panel, right?

Now you want to update your existing blocks programmatically and exchange all image block fields with this newly created block?

And you want to do this in a layout field, not in a blocks field?

---

<div class="post-metadata">

**Author:** ![seriamlo](https://avatars.discourse-cdn.com/v4/letter/s/45deac/32.png) [@seriamlo](https://forum.getkirby.com/u/seriamlo)\
**Post date:** [May 1, 2021, 8:43pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/3 "2021-05-01T20:43:03Z")

</div>

Yes the block is there and works.  
I want to either update the existing image block, which I was incapable.  
The only thing I managed was adding blocks, like so:

```auto

Kirby::plugin('seriamlo/mh-image-block', [
  'blueprints' => [
    'blocks/mh-image' => __DIR__. '/blueprints/blocks/mh-image.yml',
    'files/mh-image' => __DIR__. '/blueprints/files/mh-block-image.yml',
  ],
  'hooks' => [
    'page.update:after' => function ($newPage, $oldPage) {
      $blocks = $newPage->teaserText()->toBlocks();
      
      $mh = new Kirby\Cms\Block([
        'content' => [
          'text' => '<p>Hug them if you like. They might not appreciate it though.</p><p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum id ligula porta felis euismod semper. Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod.</p>',
        ],
        'type' => 'text',
      ]);

      $blocks = $blocks->add(new Kirby\Cms\Blocks([$mh]));

      $kirby = kirby();
      $kirby->impersonate('kirby');

      $newPage->update([
        'teaserText' => $blocks,
      ]);
    }
  ]
]);

```

This works fine.  
Now I thought, when I am not able to update a block, and I can add blocks, I could add a custom block to include more info.

but when adding a custom block I fail.

```auto
      $mh = new Kirby\Cms\Block([
        "content" => [
          "image" => [
            "screenshot-2021-04-14-at-20.41.20.png"
          ],
          "alt" => "alttext",
          "caption" => "acse",
          "link" => ""
        ],
        "type" => "mh-image"
      ]);

```

This would not create that block.

I then tried to use the [factory](https://forum.getkirby.com/t/update-blocks-field-programmatically-if-we-use-layout/21435/5) which bastian proposed:

```auto
     $blocks = Kirby\Cms\Blocks::factory([
        [
          'content' => [
            'text' => 'oo Nice heading'
          ],
          'type' => 'heading'
        ],
        [
          "content" => [
            "image" => [
              "screenshot-2021-04-14-at-20.41.20.png"
            ],
            "alt" => "alttext",
            "caption" => "acse",
            "link" => ""
          ],
          "type" => "mh-image"
        ],
        [
          'content' => [
            'text' => 'oo This is some text'
          ],
          'type' => 'text'
        ],
      ]);

```

Again the heading and the text are created but not my `mh-image` block…

Your question about layout fields and block fields I cannot answer.  
I don’t fully understand what layout fields are. do you mean this: [Layout | Kirby CMS](https://getkirby.com/docs/reference/panel/fields/layout)  
So far I have not used them and read about them the first time now.  
So I guess I want to do it in a blocks field:

```auto
title: Blog Entry

columns:
  left:
    width: 1/2
    sections:
      content:
        type: fields
        fields:
          date:
            label: Date and start time of show
            type: date
            time: true
          teasertext: // <------------------
            label: Teaser Text
            type: blocks
            fieldsets:
              - heading
              - text
              - list
              - image
              - mh-image

```

Thanks in advance. I struggled for a whole day now and can’t figure it out ;S.  
Cheers

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [May 1, 2021, 9:07pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/4 "2021-05-01T21:07:29Z")

</div>

I just tried it with one of my custom blocks and that worked without issues. I wonder if the issue is related to your field name with the dash, which might not work.

---

<div class="post-metadata">

**Author:** ![seriamlo](https://avatars.discourse-cdn.com/v4/letter/s/45deac/32.png) [@seriamlo](https://forum.getkirby.com/u/seriamlo)\
**Post date:** [May 1, 2021, 9:16pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/5 "2021-05-01T21:16:40Z")

</div>

I think I removed every dash now, but it still does not work with the factory…

```auto
'blueprints' => [
    'blocks/mhimage' => __DIR__. '/blueprints/blocks/mhimage.yml',
    'files/mhimage' => __DIR__. '/blueprints/files/mhblockimage.yml',
  ],

```

```auto
teasertext:
            label: Teaser Text
            type: blocks
            fieldsets:
              - heading
              - text
              - list
              - image
              - mhimage

```

So again I have a working custom block type, which I can add without problems.  
When using the factory like:

```auto
$blocks = Kirby\Cms\Blocks::factory([
        [
          'content' => [
            'text' => 'oo Nice heading'
          ],
          'type' => 'heading'
        ],
        [
          "content" => [
            "image" => [
              "screenshot-2021-04-14-at-20.41.20.png"
            ],
            "alt" => "alttext",
            "caption" => "acse",
            "link" => ""
          ],
          "type" => "mhimage"
        ],
        [
          'content' => [
            'text' => 'oo This is some text'
          ],
          'type' => 'text'
        ],
      ]);

```

It would create the heading and the text but the mhimage in between is not created…

---

<div class="post-metadata">

**Author:** ![seriamlo](https://avatars.discourse-cdn.com/v4/letter/s/45deac/32.png) [@seriamlo](https://forum.getkirby.com/u/seriamlo)\
**Post date:** [May 1, 2021, 9:18pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/6 "2021-05-01T21:18:36Z")

</div>

And now I just tried the other method:

```auto
      $mh = new Kirby\Cms\Block([
        "content" => [
          "image" => [
            "screenshot-2021-04-14-at-20.41.20.png"
          ],
          "alt" => "alttext",
          "caption" => "acse",
          "link" => ""
        ],
        "type" => "mhimage"
      ]);

      $blocks = $blocks->add(new Kirby\Cms\Blocks([$mh]));

```

Which did not work either…

* * *

Edit: I just thought that maybe upgrading from 3.5.1 to 3.5.4 helps, but no change either…

---

<div class="post-metadata">

**Author:** ![seriamlo](https://avatars.discourse-cdn.com/v4/letter/s/45deac/32.png) [@seriamlo](https://forum.getkirby.com/u/seriamlo)\
**Post date:** [May 1, 2021, 9:28pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/7 "2021-05-01T21:28:39Z")

</div>

I just built a new audio custom block type:

```auto
<?php

Kirby::plugin('cookbook/superaudio', [
  'blueprints' => [
    'blocks/audio' => __DIR__. '/blueprints/blocks/audio.yml',
  ],
  'hooks' => [
    'page.update:after' => function ($newPage, $oldPage) {
      $blocks = $newPage->teaserText()->toBlocks();

      $mh = new Kirby\Cms\Block([
        "content" => [
          "description" => "what a nice description",
        ],
        "type" => "audio"
      ]);

      $blocks = $blocks->add(new Kirby\Cms\Blocks([$mh]));

      $kirby = kirby();
      $kirby->impersonate('kirby');

      $newPage->update([
        'teaserText' => $blocks,
      ]);
    }
  ]
]);

```

the blueprint:

```auto
name: Audio
icon: file-audio
tabs:
  main:
    label: Main
    fields:
      description:
        type: writer
        icon: text
        inline: true
        placeholder: Description
        marks:
          - bold
          - italic

```

And within my page template I have now:

```auto
         teasertext:
            label: Teaser Text
            type: blocks
            fieldsets:
              - heading
              - text
              - list
              - audio

```

I can again add the audio block type, save a description etc.  
But when exeuting the update hook, it would not create a new audio block…  
☹  
What am I missing?

Thanks for the help btw!  
Really appreciate it.  
And sorry for the spam on discord as well. Just thought that maybe someone more knowledgeable there could help me as well…

Cheers

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [May 1, 2021, 9:47pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/8 "2021-05-01T21:47:14Z")

</div>

Hm, I don’t know. I thought it had worked, but now when I tried it again, the field is updated but with a default block type. Too tired now to have a deeper look what’s happening and why.

---

<div class="post-metadata">

**Author:** ![seriamlo](https://avatars.discourse-cdn.com/v4/letter/s/45deac/32.png) [@seriamlo](https://forum.getkirby.com/u/seriamlo)\
**Post date:** [May 2, 2021, 6:57am UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/9 "2021-05-02T06:57:31Z")

</div>

Hm, alright. I am glad that there seems to be a problem and that I am not completely nuts :).

For now, it would help as well, if you could tell me, how I could loop through the blocks, and if the type is `$block->type()=== image` I would like to change one field `$block['content]['image']`.  
I got all the code except for changing a specifics blocks field… can this be done?

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [May 2, 2021, 8:31am UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/11 "2021-05-02T08:31:06Z")

</div>

Hm, there seems to be a bug of some sort, because even when modifying such a block type slighty, the block gets ignored.

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [May 2, 2021, 10:26am UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/12 "2021-05-02T10:26:52Z")

</div>

Ok, I played around a bit more, and it seems to work when I convert the blocks to array when updating the page:

```auto
$newBlocks = new Kirby\Cms\Blocks();
$blocks = $page->text()->toBlocks();
foreach ($blocks as $block) :
  if ($block->type() !== 'image') {
    $newBlocks->add($block);
  } else {
    $url = $block->image()->toFile()->url();
    $images = $block->image()->toFiles()->pluck('filename');
    $newBlock = new \Kirby\Cms\Block(
      [
        "content" => [
          'location' => 'kirby',
          'image' => $images,
          'url' => $url,
          'alt' => $block->alt()->value(),
          'caption' => $block->caption()->value(),
          'link' => $block->link()->value(),
        ],
        'id' => $block->id(),
        'type' => $block->type()
      ],
    );
    $newBlocks->add($newBlock);
  }
endforeach;

$page->update(
  [
    'text' => $newBlocks->toArray(),
  ]
);

```

In the example above I added a new url field to the image block blueprint to store the url of the image.

---

<div class="post-metadata">

**Author:** ![seriamlo](https://avatars.discourse-cdn.com/v4/letter/s/45deac/32.png) [@seriamlo](https://forum.getkirby.com/u/seriamlo)\
**Post date:** [May 2, 2021, 1:14pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/13 "2021-05-02T13:14:55Z")

</div>

Thanks a lot for looking into this and for the help.  
As for a kirby noob like me this answer is not fully complete, I post my code here to maybe help someone in the future:

A prerequisite for this to work is, that you override the image.yml:

> **[image](https://getkirby.com/docs/reference/panel/blocks/image#default-files__blueprint)**
>
> Kirby is the content management system that adapts to any project. Made for developers, designers, creators and clients.

and add a url property only then it can be set.

Also the part of how to convert it to an array is missing. As I did not know there is a kirby method for that I needed to find out first:

> **[$blocksfield-\>toArray()](https://getkirby.com/docs/reference/objects/form-field/blocks-field/to-array)**
>
> Converts the field to a plain array

```auto
<?php

Kirby::plugin('seriamlo/mh-image-block', [
  // we dont need any blueprints, as this plugin just adds a hook to change image blocks
  'hooks' => [
    'page.update:after' => function ($newPage, $oldPage) {
        $newBlocks = new Kirby\Cms\Blocks();
        $blocks = $newPage->teaserText()->toBlocks();

        foreach ($blocks as $block) :
        if ($block->type() !== 'image') {
            $newBlocks->add($block);
        } else {
            $url = $block->image()->toFile()->url();
            $images = $block->image()->toFiles()->pluck('filename');
            $newBlock = new \Kirby\Cms\Block(
                [
              "content" => [
                'location' => 'kirby',
                'image' => $images,
                'alt' => $block->alt()->value(),
                'caption' => $block->caption()->value(),
                'url' => $url,
                'link' => $block->link()->value(),
              ],
              'id' => $block->id(),
              'type' => $block->type()
            ],
            );
            $newBlocks->add($newBlock);
        }
        endforeach;

      $newPage->update([
        'teaserText' => $newBlocks->toArray(),
      ]);
    }
  ]
]);

```

Hope this helps somebody.  
And thanks a lot for helping out on sundays! Appreciate it a lot!  
🙌

* * *

Just a last question, because the title of this thread is `Create custom blocks programmatically`. I assume this will now also work with custom block types, right?

If I can I will try it out, but right now I need to proceed a bit as I have waisted some time getting here.

---

<div class="post-metadata">

**Author:** ![texnixe](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/texnixe/32/5754_2.png) [@texnixe](https://forum.getkirby.com/u/texnixe)\
**Post date:** [May 2, 2021, 1:49pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/14 "2021-05-02T13:49:42Z")

</div>

> [@seriamlo](#):
>
> If I can I will try it out, but right now I need to proceed a bit as I have waisted some time getting here.

Yes, that also works for custom blocks

---

<div class="post-metadata">

**Author:** ![jukra00](https://dub1.discourse-cdn.com/flex017/user_avatar/forum.getkirby.com/jukra00/32/16161_2.png) [@jukra00](https://forum.getkirby.com/u/jukra00)\
**Post date:** [September 21, 2021, 1:36pm UTC](https://forum.getkirby.com/t/create-custom-blocks-programmatically/22137/15 "2021-09-21T13:36:48Z")

</div>

Thanks so much for your input regarding the toArray() method. I came across the same issue, that my custom `subline` block would automatically be converted to `type: text` when updating the page. Super weird issue, a `type: heading` did work fine. Using the `toArray()` method in the update method fixes my issue, too. Maybe you could add it as a hint in your cookbook article? ([Quicktip: Add blocks to a blocks field programmatically | Kirby CMS](https://getkirby.com/docs/cookbook/templating/update-blocks-programmatically))
