Skip to content
This repository was archived by the owner on Dec 18, 2025. It is now read-only.

Commit d335cf4

Browse files
authored
Merge pull request #81 from creode/feature/get_child_block_by_path_function
Feature/get child block by path function
2 parents 8516146 + deb7686 commit d335cf4

5 files changed

Lines changed: 348 additions & 82 deletions

File tree

docs/.vitepress/config.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ export default defineConfig({
7979
{ text: 'render_inner_blocks_in_post_context', link: '/helpers/render_inner_blocks_in_post_context' },
8080
{ text: 'render_blocks_with_dynamic_context', link: '/helpers/render_blocks_with_dynamic_context' },
8181
{ text: 'add_dynamic_context_to_blocks', link: '/helpers/add_dynamic_context_to_blocks' },
82-
{ text: 'replace_child_block_by_path', link: 'helpers/replace_child_block_by_path' },
82+
{ text: 'get_child_block_by_path', link: '/helpers/get_child_block_by_path' },
83+
{ text: 'replace_child_block_by_path', link: '/helpers/replace_child_block_by_path' },
8384
{ text: 'set_acf_block_mode', link: '/helpers/set_acf_block_mode' },
8485
]
8586
},
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
---
2+
title: get_child_block_by_path
3+
editLink: false
4+
---
5+
6+
# get_child_block_by_path
7+
8+
## Description
9+
10+
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.
11+
12+
### Responsibility
13+
14+
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.
15+
16+
### Arguments
17+
18+
| Parameter | Type | Required | Description |
19+
|-----------|------|----------|-------------|
20+
| `$existing_child_blocks` | `Child_Block[]` | Yes | An array of existing child blocks to search in |
21+
| `$path` | `string` | Yes | A "/" separated path to block (e.g., "table/row/cell" or "table/row/cell/cell-content") |
22+
23+
### Return Value
24+
25+
- **Type**: `Child_Block`
26+
- **Description**: Returns the matching Child_Block instance if found
27+
- **Throws**: `\InvalidArgumentException` if the path is invalid or the block cannot be found
28+
29+
## Path Format
30+
31+
The path parameter uses a forward-slash (`/`) separated format to navigate through nested child blocks:
32+
33+
- Each segment represents a child block name
34+
- The path is traversed from top to bottom
35+
- Example: `"table/row/cell"` means:
36+
- Find a child block named "table"
37+
- Within that, find a child block named "row"
38+
- Within that, find a child block named "cell"
39+
- Return that "cell" block
40+
41+
## Pairing with replace_child_block_by_path
42+
43+
This function pairs exceptionally well with `replace_child_block_by_path()`. The typical workflow is:
44+
45+
1. Use `get_child_block_by_path()` to retrieve the existing child block you want to modify
46+
2. Make your modifications to the retrieved block (e.g., add fields, change configuration)
47+
3. Use `replace_child_block_by_path()` to replace the original block with your modified version
48+
49+
This pattern allows you to extend child blocks defined in parent classes without having to completely recreate the entire child block hierarchy.
50+
51+
## Examples
52+
53+
### Basic Usage - Retrieving a Child Block
54+
55+
This example shows how to retrieve a child block from a nested structure:
56+
57+
```php
58+
use Creode_Blocks\Helpers;
59+
60+
protected function child_blocks(): array {
61+
$existing_child_blocks = parent::child_blocks();
62+
63+
// Get a specific child block by path
64+
$cell_block = Helpers::get_child_block_by_path(
65+
$existing_child_blocks,
66+
'table/row/cell'
67+
);
68+
69+
// Now you can access the block's properties
70+
$fields = $cell_block->fields;
71+
$template = $cell_block->template;
72+
}
73+
```
74+
75+
### Extending a Child Block with Additional Fields
76+
77+
This is the most common use case - extending a child block defined in a parent class by adding new fields:
78+
79+
```php
80+
use Creode_Blocks\Table_Block as Base_Table_Block;
81+
use Creode_Blocks\Helpers;
82+
83+
class Table extends Base_Table_Block {
84+
85+
/**
86+
* {@inheritdoc}
87+
*/
88+
protected function name(): string {
89+
return 'table';
90+
}
91+
92+
/**
93+
* {@inheritdoc}
94+
*/
95+
protected function label(): string {
96+
return 'Comparison Table';
97+
}
98+
99+
/**
100+
* {@inheritdoc}
101+
*/
102+
protected function child_blocks(): array {
103+
$existing_child_blocks = parent::child_blocks();
104+
105+
// Get the existing cell-content child block
106+
$cell_content_child_block = Helpers::get_child_block_by_path(
107+
$existing_child_blocks,
108+
'table/row/cell/cell-content'
109+
);
110+
111+
// Get the existing fields array and add the new field to it
112+
$fields = $cell_content_child_block->fields;
113+
$fields[] = array(
114+
'key' => 'creode_field',
115+
'label' => 'Creode Field',
116+
'name' => 'creode_field',
117+
'type' => 'select',
118+
'choices' => array(
119+
'' => 'hooray',
120+
'1' => 'woohoo',
121+
'2' => 'I did it',
122+
),
123+
);
124+
$cell_content_child_block->fields = $fields;
125+
126+
// Replace the original block with the modified version
127+
$replaced_child_blocks = Helpers::replace_child_block_by_path(
128+
$existing_child_blocks,
129+
'table/row/cell/cell-content',
130+
$cell_content_child_block
131+
);
132+
133+
return $replaced_child_blocks;
134+
}
135+
}
136+
```
137+
138+
### Modifying Multiple Properties
139+
140+
You can modify multiple properties of the retrieved child block:
141+
142+
```php
143+
use Creode_Blocks\Helpers;
144+
145+
protected function child_blocks(): array {
146+
$existing_child_blocks = parent::child_blocks();
147+
148+
$target_block = Helpers::get_child_block_by_path(
149+
$existing_child_blocks,
150+
'section/header'
151+
);
152+
153+
// Modify multiple properties
154+
// Get the existing fields array, modify it, then reassign it
155+
$fields = $target_block->fields;
156+
$fields[] = array(
157+
'key' => 'new_field',
158+
'label' => 'New Field',
159+
'name' => 'new_field',
160+
'type' => 'text',
161+
);
162+
$target_block->fields = $fields;
163+
164+
$target_block->template = __DIR__ . '/templates/custom-header.php';
165+
166+
// Replace with modified version
167+
return Helpers::replace_child_block_by_path(
168+
$existing_child_blocks,
169+
'section/header',
170+
$target_block
171+
);
172+
}
173+
```
174+
175+
## Error Handling
176+
177+
The method throws `\InvalidArgumentException` in two scenarios:
178+
179+
1. **Invalid path**: If the path is empty or contains no valid segments
180+
2. **Block not found**: If any segment in the path cannot be matched to an existing child block
181+
182+
```php
183+
use Creode_Blocks\Helpers;
184+
185+
try {
186+
$block = Helpers::get_child_block_by_path(
187+
$existing_child_blocks,
188+
'invalid/path/to/block'
189+
);
190+
} catch ( \InvalidArgumentException $e ) {
191+
// Handle the error - block not found at specified path
192+
error_log( $e->getMessage() );
193+
}
194+
```
195+
196+
## Use Cases
197+
198+
This helper is particularly useful when:
199+
200+
- **Extending parent block classes**: You want to add fields or modify properties of a child block defined in a parent class
201+
- **Selective modifications**: You only need to modify specific child blocks in a complex hierarchy
202+
- **Dynamic child block manipulation**: You need to programmatically access and modify nested child blocks
203+
- **Pairing with replace_child_block_by_path**: You want to retrieve, modify, and replace a child block in one workflow
204+
205+
## Notes
206+
207+
- The path matching is case-sensitive and must exactly match the child block names
208+
- 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)
209+
- The method validates the entire path before returning, ensuring the block exists at the specified location
210+
- Always use this method in conjunction with `replace_child_block_by_path()` when you need to persist your modifications
211+

docs/helpers/replace_child_block_by_path.md

Lines changed: 65 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -38,84 +38,78 @@ The path parameter uses a forward-slash (`/`) separated format to navigate throu
3838
- Within that, find a child block named "cell"
3939
- Replace that "cell" block with the new one
4040

41+
## Pairing with get_child_block_by_path
42+
43+
This function pairs exceptionally well with [`get_child_block_by_path()`](/helpers/get_child_block_by_path). The recommended workflow is:
44+
45+
1. Use `get_child_block_by_path()` to retrieve the existing child block you want to modify
46+
2. Make your modifications to the retrieved block (e.g., add fields, change configuration)
47+
3. Use `replace_child_block_by_path()` to replace the original block with your modified version
48+
49+
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.
50+
4151
## Examples
4252

43-
### Basic Usage - Overriding a Child Block
53+
### Basic Usage - Extending a Child Block
4454

45-
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:
55+
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:
4656

4757
```php
58+
use Creode_Blocks\Table_Block as Base_Table_Block;
4859
use Creode_Blocks\Helpers;
49-
use Creode_Blocks\Child_Block;
5060

51-
/**
52-
* {@inheritdoc}
53-
*/
54-
protected function child_blocks(): array {
55-
$existing_child_blocks = parent::child_blocks();
56-
57-
// Replace the cell-content child block with an enhanced version
58-
$replaced_child_blocks = Helpers::replace_child_block_by_path(
59-
$existing_child_blocks,
60-
'table/row/cell/cell-content',
61-
new Child_Block(
62-
'cell-content',
63-
'Table Cell Content',
64-
array(
65-
array(
66-
'key' => 'field_table_cell_style',
67-
'label' => 'Style',
68-
'name' => 'style',
69-
'type' => 'select',
70-
'choices' => array(
71-
'' => 'None',
72-
'1' => 'No Padding',
73-
),
74-
),
75-
array(
76-
'key' => 'field_table_cell_curved_corners',
77-
'label' => 'Curved corners',
78-
'name' => 'curved_corners',
79-
'type' => 'checkbox',
80-
'choices' => array(
81-
'top-left' => 'Top Left',
82-
'top-right' => 'Top Right',
83-
'bottom-right' => 'Bottom Right',
84-
'bottom-left' => 'Bottom Left',
85-
),
86-
),
87-
$this->get_icon_field_schema( 'field_table_cell_icon', true ),
88-
array(
89-
'key' => 'field_table_cell_icon_color',
90-
'label' => 'Icon Color',
91-
'name' => 'icon_color',
92-
'type' => 'radio',
93-
'choices' => $this->get_color_choices(),
94-
'conditional_logic' => array(
95-
array(
96-
array(
97-
'field' => 'field_table_cell_icon',
98-
'operator' => '!=',
99-
'value' => '',
100-
),
101-
),
102-
),
103-
),
104-
),
105-
CREODE_BLOCKS_PLUGIN_FOLDER . '/blocks/table/templates/cell-content.php',
106-
array(),
107-
'text',
108-
array(
109-
'mode' => false,
110-
'color' => array(
111-
'text' => true,
112-
'background' => true,
113-
),
61+
class Table extends Base_Table_Block {
62+
63+
/**
64+
* {@inheritdoc}
65+
*/
66+
protected function name(): string {
67+
return 'table';
68+
}
69+
70+
/**
71+
* {@inheritdoc}
72+
*/
73+
protected function label(): string {
74+
return 'Comparison Table';
75+
}
76+
77+
/**
78+
* {@inheritdoc}
79+
*/
80+
protected function child_blocks(): array {
81+
$existing_child_blocks = parent::child_blocks();
82+
83+
// Get the existing cell-content child block
84+
$cell_content_child_block = Helpers::get_child_block_by_path(
85+
$existing_child_blocks,
86+
'table/row/cell/cell-content'
87+
);
88+
89+
// Get the existing fields array and add the new field to it
90+
$fields = $cell_content_child_block->fields;
91+
$fields[] = array(
92+
'key' => 'creode_field',
93+
'label' => 'Creode Field',
94+
'name' => 'creode_field',
95+
'type' => 'select',
96+
'choices' => array(
97+
'' => 'hooray',
98+
'1' => 'woohoo',
99+
'2' => 'I did it',
114100
),
115-
),
116-
);
117-
118-
return $replaced_child_blocks;
101+
);
102+
$cell_content_child_block->fields = $fields;
103+
104+
// Replace the original block with the modified version
105+
$replaced_child_blocks = Helpers::replace_child_block_by_path(
106+
$existing_child_blocks,
107+
'table/row/cell/cell-content',
108+
$cell_content_child_block
109+
);
110+
111+
return $replaced_child_blocks;
112+
}
119113
}
120114
```
121115

@@ -155,6 +149,7 @@ This helper is particularly useful when:
155149
- **Extending parent block classes**: You want to add fields to a child block defined in a parent class without modifying the parent
156150
- **Customizing child blocks**: You need to modify the template, fields, or configuration of a nested child block
157151
- **Selective overrides**: You only want to change specific child blocks in a complex hierarchy rather than redefining everything
152+
- **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
158153

159154
## Notes
160155

0 commit comments

Comments
 (0)