Skip to content
This repository was archived by the owner on Dec 18, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/.vitepress/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export default defineConfig({
{ text: 'render_inner_blocks_in_post_context', link: '/helpers/render_inner_blocks_in_post_context' },
{ text: 'render_blocks_with_dynamic_context', link: '/helpers/render_blocks_with_dynamic_context' },
{ text: 'add_dynamic_context_to_blocks', link: '/helpers/add_dynamic_context_to_blocks' },
{ text: 'replace_child_block_by_path', link: 'helpers/replace_child_block_by_path' },
{ text: 'get_child_block_by_path', link: '/helpers/get_child_block_by_path' },
{ text: 'replace_child_block_by_path', link: '/helpers/replace_child_block_by_path' },
{ text: 'set_acf_block_mode', link: '/helpers/set_acf_block_mode' },
]
},
Expand Down
211 changes: 211 additions & 0 deletions docs/helpers/get_child_block_by_path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
---
title: get_child_block_by_path
editLink: false
---

# get_child_block_by_path

## Description

The `get_child_block_by_path()` method finds a child block from an array of existing child blocks based on its path. This method is particularly useful when you need to access and modify a specific child block within a nested child block structure.

### Responsibility

This method recursively searches through an array of child blocks using a path-based approach (e.g., "table/row/cell") to locate a specific child block. It validates the path and throws an exception if the block cannot be found, ensuring type safety and preventing silent failures.

### Arguments

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `$existing_child_blocks` | `Child_Block[]` | Yes | An array of existing child blocks to search in |
| `$path` | `string` | Yes | A "/" separated path to block (e.g., "table/row/cell" or "table/row/cell/cell-content") |

### Return Value

- **Type**: `Child_Block`
- **Description**: Returns the matching Child_Block instance if found
- **Throws**: `\InvalidArgumentException` if the path is invalid or the block cannot be found

## Path Format

The path parameter uses a forward-slash (`/`) separated format to navigate through nested child blocks:

- Each segment represents a child block name
- The path is traversed from top to bottom
- Example: `"table/row/cell"` means:
- Find a child block named "table"
- Within that, find a child block named "row"
- Within that, find a child block named "cell"
- Return that "cell" block

## Pairing with replace_child_block_by_path

This function pairs exceptionally well with `replace_child_block_by_path()`. The typical workflow is:

1. Use `get_child_block_by_path()` to retrieve the existing child block you want to modify
2. Make your modifications to the retrieved block (e.g., add fields, change configuration)
3. Use `replace_child_block_by_path()` to replace the original block with your modified version

This pattern allows you to extend child blocks defined in parent classes without having to completely recreate the entire child block hierarchy.

## Examples

### Basic Usage - Retrieving a Child Block

This example shows how to retrieve a child block from a nested structure:

```php
use Creode_Blocks\Helpers;

protected function child_blocks(): array {
$existing_child_blocks = parent::child_blocks();

// Get a specific child block by path
$cell_block = Helpers::get_child_block_by_path(
$existing_child_blocks,
'table/row/cell'
);

// Now you can access the block's properties
$fields = $cell_block->fields;
$template = $cell_block->template;
}
```

### Extending a Child Block with Additional Fields

This is the most common use case - extending a child block defined in a parent class by adding new fields:

```php
use Creode_Blocks\Table_Block as Base_Table_Block;
use Creode_Blocks\Helpers;

class Table extends Base_Table_Block {

/**
* {@inheritdoc}
*/
protected function name(): string {
return 'table';
}

/**
* {@inheritdoc}
*/
protected function label(): string {
return 'Comparison Table';
}

/**
* {@inheritdoc}
*/
protected function child_blocks(): array {
$existing_child_blocks = parent::child_blocks();

// Get the existing cell-content child block
$cell_content_child_block = Helpers::get_child_block_by_path(
$existing_child_blocks,
'table/row/cell/cell-content'
);

// Get the existing fields array and add the new field to it
$fields = $cell_content_child_block->fields;
$fields[] = array(
'key' => 'creode_field',
'label' => 'Creode Field',
'name' => 'creode_field',
'type' => 'select',
'choices' => array(
'' => 'hooray',
'1' => 'woohoo',
'2' => 'I did it',
),
);
$cell_content_child_block->fields = $fields;

// Replace the original block with the modified version
$replaced_child_blocks = Helpers::replace_child_block_by_path(
$existing_child_blocks,
'table/row/cell/cell-content',
$cell_content_child_block
);

return $replaced_child_blocks;
}
}
```

### Modifying Multiple Properties

You can modify multiple properties of the retrieved child block:

```php
use Creode_Blocks\Helpers;

protected function child_blocks(): array {
$existing_child_blocks = parent::child_blocks();

$target_block = Helpers::get_child_block_by_path(
$existing_child_blocks,
'section/header'
);

// Modify multiple properties
// Get the existing fields array, modify it, then reassign it
$fields = $target_block->fields;
$fields[] = array(
'key' => 'new_field',
'label' => 'New Field',
'name' => 'new_field',
'type' => 'text',
);
$target_block->fields = $fields;

$target_block->template = __DIR__ . '/templates/custom-header.php';

// Replace with modified version
return Helpers::replace_child_block_by_path(
$existing_child_blocks,
'section/header',
$target_block
);
}
```

## Error Handling

The method throws `\InvalidArgumentException` in two scenarios:

1. **Invalid path**: If the path is empty or contains no valid segments
2. **Block not found**: If any segment in the path cannot be matched to an existing child block

```php
use Creode_Blocks\Helpers;

try {
$block = Helpers::get_child_block_by_path(
$existing_child_blocks,
'invalid/path/to/block'
);
} catch ( \InvalidArgumentException $e ) {
// Handle the error - block not found at specified path
error_log( $e->getMessage() );
}
```

## Use Cases

This helper is particularly useful when:

- **Extending parent block classes**: You want to add fields or modify properties of a child block defined in a parent class
- **Selective modifications**: You only need to modify specific child blocks in a complex hierarchy
- **Dynamic child block manipulation**: You need to programmatically access and modify nested child blocks
- **Pairing with replace_child_block_by_path**: You want to retrieve, modify, and replace a child block in one workflow

## Notes

- The path matching is case-sensitive and must exactly match the child block names
- Only the first matching child block at each level is returned (if multiple child blocks share the same name, only the first one is found)
- The method validates the entire path before returning, ensuring the block exists at the specified location
- Always use this method in conjunction with `replace_child_block_by_path()` when you need to persist your modifications

135 changes: 65 additions & 70 deletions docs/helpers/replace_child_block_by_path.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,84 +38,78 @@ The path parameter uses a forward-slash (`/`) separated format to navigate throu
- Within that, find a child block named "cell"
- Replace that "cell" block with the new one

## Pairing with get_child_block_by_path

This function pairs exceptionally well with [`get_child_block_by_path()`](/helpers/get_child_block_by_path). The recommended workflow is:

1. Use `get_child_block_by_path()` to retrieve the existing child block you want to modify
2. Make your modifications to the retrieved block (e.g., add fields, change configuration)
3. Use `replace_child_block_by_path()` to replace the original block with your modified version

This approach is much easier than manually recreating the entire `Child_Block` instance, especially when you only want to add a few fields or make small modifications to an existing child block.

## Examples

### Basic Usage - Overriding a Child Block
### Basic Usage - Extending a Child Block

This example shows how to override a child block defined in a parent class by replacing it with a new version that includes additional fields:
This example shows the recommended approach for extending a child block defined in a parent class. First, retrieve the existing block using [`get_child_block_by_path()`](/helpers/get_child_block_by_path), modify it, and then replace it. This approach preserves all existing fields and configuration:

```php
use Creode_Blocks\Table_Block as Base_Table_Block;
use Creode_Blocks\Helpers;
use Creode_Blocks\Child_Block;

/**
* {@inheritdoc}
*/
protected function child_blocks(): array {
$existing_child_blocks = parent::child_blocks();

// Replace the cell-content child block with an enhanced version
$replaced_child_blocks = Helpers::replace_child_block_by_path(
$existing_child_blocks,
'table/row/cell/cell-content',
new Child_Block(
'cell-content',
'Table Cell Content',
array(
array(
'key' => 'field_table_cell_style',
'label' => 'Style',
'name' => 'style',
'type' => 'select',
'choices' => array(
'' => 'None',
'1' => 'No Padding',
),
),
array(
'key' => 'field_table_cell_curved_corners',
'label' => 'Curved corners',
'name' => 'curved_corners',
'type' => 'checkbox',
'choices' => array(
'top-left' => 'Top Left',
'top-right' => 'Top Right',
'bottom-right' => 'Bottom Right',
'bottom-left' => 'Bottom Left',
),
),
$this->get_icon_field_schema( 'field_table_cell_icon', true ),
array(
'key' => 'field_table_cell_icon_color',
'label' => 'Icon Color',
'name' => 'icon_color',
'type' => 'radio',
'choices' => $this->get_color_choices(),
'conditional_logic' => array(
array(
array(
'field' => 'field_table_cell_icon',
'operator' => '!=',
'value' => '',
),
),
),
),
),
CREODE_BLOCKS_PLUGIN_FOLDER . '/blocks/table/templates/cell-content.php',
array(),
'text',
array(
'mode' => false,
'color' => array(
'text' => true,
'background' => true,
),
class Table extends Base_Table_Block {

/**
* {@inheritdoc}
*/
protected function name(): string {
return 'table';
}

/**
* {@inheritdoc}
*/
protected function label(): string {
return 'Comparison Table';
}

/**
* {@inheritdoc}
*/
protected function child_blocks(): array {
$existing_child_blocks = parent::child_blocks();

// Get the existing cell-content child block
$cell_content_child_block = Helpers::get_child_block_by_path(
$existing_child_blocks,
'table/row/cell/cell-content'
);

// Get the existing fields array and add the new field to it
$fields = $cell_content_child_block->fields;
$fields[] = array(
'key' => 'creode_field',
'label' => 'Creode Field',
'name' => 'creode_field',
'type' => 'select',
'choices' => array(
'' => 'hooray',
'1' => 'woohoo',
'2' => 'I did it',
),
),
);

return $replaced_child_blocks;
);
$cell_content_child_block->fields = $fields;

// Replace the original block with the modified version
$replaced_child_blocks = Helpers::replace_child_block_by_path(
$existing_child_blocks,
'table/row/cell/cell-content',
$cell_content_child_block
);

return $replaced_child_blocks;
}
}
```

Expand Down Expand Up @@ -155,6 +149,7 @@ This helper is particularly useful when:
- **Extending parent block classes**: You want to add fields to a child block defined in a parent class without modifying the parent
- **Customizing child blocks**: You need to modify the template, fields, or configuration of a nested child block
- **Selective overrides**: You only want to change specific child blocks in a complex hierarchy rather than redefining everything
- **Working with existing blocks**: When combined with [`get_child_block_by_path()`](/helpers/get_child_block_by_path), you can easily retrieve, modify, and replace existing child blocks without recreating them from scratch

## Notes

Expand Down
Loading