Simple and elegant tree builder that transforms hierarchical data structures into flat selectbox-friendly arrays with visual indentation.
- Transforms parent-child tree structures into flat arrays suitable for HTML
<select>elements - Automatic visual indentation using pipe characters (
|) and non-breaking spaces to represent hierarchy depth - Built-in SQL query builder helper for fetching hierarchical data from databases
- Configurable maximum depth to prevent infinite recursion
- Custom name formatting through the
NameFormatterinterface - Optional integration with
Baraja\Localization\Translationfor multilingual support - Supports both raw array input and typed
SelectboxItemobjects - Zero external dependencies (PHP 8.0+ only)
The library consists of three main components:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SelectboxTree β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β process(array $data): array β β
β β - Accepts SelectboxItem[] or raw arrays β β
β β - Builds hierarchical structure β β
β β - Returns flat array with visual indentation β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β serializeCategoriesToSelectbox() β β
β β - Recursive tree traversal β β
β β - Tracks used IDs to prevent duplicates β β
β β - Respects maxDepth limit β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββ βββββββββββββββββββββββββββ
β SelectboxItem β β NameFormatter β
β βββββββββββββββββ β β (interface) β
β β id β β β βββββββββββββββββββββ β
β β name β β β β format(string): β β
β β parentId β β β β string β β
β βββββββββββββββββ β β βββββββββββββββββββββ β
βββββββββββββββββββββββ βββββββββββββββββββββββββββ
| Component | Type | Description |
|---|---|---|
SelectboxTree |
Class | Main processor that converts tree data to selectbox array |
SelectboxItem |
Class | Immutable value object representing a single tree node |
NameFormatter |
Interface | Contract for custom name transformation logic |
The library takes hierarchical data where each item has an id, name, and optional parent_id, then:
- Normalizes input - Accepts either
SelectboxItemobjects or raw associative arrays - Applies name formatting - If a
NameFormatteris set, transforms each item's name - Builds tree recursively - Starting from root items (where
parent_idisnull), traverses children depth-first - Generates visual indentation - Prepends pipe characters and non-breaking spaces based on depth level
- Returns flat array - Keys are item IDs, values are formatted names with indentation
Each level of depth adds the pattern | (pipe followed by three non-breaking spaces):
Root Item
| First Level Child
| | Second Level Child
| | | Third Level Child
The process() method accepts an array of items in two formats:
Array format:
[
['id' => 1, 'name' => 'Electronics', 'parent_id' => null],
['id' => 2, 'name' => 'Phones', 'parent_id' => 1],
['id' => 3, 'name' => 'iPhone', 'parent_id' => 2],
]SelectboxItem format:
[
new SelectboxItem(1, 'Electronics', null),
new SelectboxItem(2, 'Phones', 1),
new SelectboxItem(3, 'iPhone', 2),
]The process() method returns an associative array where:
- Keys are the original item IDs (
int|string) - Values are the formatted names with visual indentation (
string)
[
1 => 'Electronics',
2 => '| Phones',
3 => '| | iPhone',
]It's best to use Composer for installation, and you can also find the package on Packagist and GitHub.
To install, simply use the command:
$ composer require baraja-core/selectbox-treeYou can use the package manually by creating an instance of the internal classes, or register a DIC extension to link the services directly to the Nette Framework.
- PHP 8.0 or higher
use Baraja\SelectboxTree\SelectboxTree;
$tree = new SelectboxTree();
$data = [
['id' => 1, 'name' => 'Main category', 'parent_id' => null],
['id' => 2, 'name' => 'Phone', 'parent_id' => 1],
['id' => 3, 'name' => 'iPhone', 'parent_id' => 2],
['id' => 4, 'name' => 'Computer', 'parent_id' => 1],
['id' => 5, 'name' => 'Mac', 'parent_id' => 4],
['id' => 6, 'name' => 'MacBook', 'parent_id' => 5],
['id' => 7, 'name' => 'iMac', 'parent_id' => 5],
['id' => 8, 'name' => 'Windows', 'parent_id' => 4],
];
$selectboxOptions = $tree->process($data);
// Result:
// [
// 1 => 'Main category',
// 2 => '| Phone',
// 3 => '| | iPhone',
// 4 => '| Computer',
// 5 => '| | Mac',
// 6 => '| | | MacBook',
// 7 => '| | | iMac',
// 8 => '| | Windows',
// ]use Baraja\SelectboxTree\SelectboxTree;
$tree = new SelectboxTree();
$options = $tree->process($categories);
echo '<select name="category">';
foreach ($options as $id => $name) {
echo sprintf('<option value="%s">%s</option>', $id, htmlspecialchars($name));
}
echo '</select>';use Baraja\SelectboxTree\SelectboxTree;
use Baraja\SelectboxTree\SelectboxItem;
$tree = new SelectboxTree();
$items = [
new SelectboxItem(1, 'Electronics', null),
new SelectboxItem(2, 'Phones', 1),
new SelectboxItem(3, 'Computers', 1),
];
$selectboxOptions = $tree->process($items);To prevent infinite recursion or limit the tree depth, use setMaxDepth():
$tree = new SelectboxTree();
$tree->setMaxDepth(5); // Limit to 5 levels deep
$options = $tree->process($data);Constraints:
- Minimum value:
1 - Maximum value:
1000 - Default value:
32
Implement the NameFormatter interface to transform item names:
use Baraja\SelectboxTree\SelectboxTree;
use Baraja\SelectboxTree\NameFormatter;
class UppercaseFormatter implements NameFormatter
{
public function format(string $name): string
{
return strtoupper($name);
}
}
$tree = new SelectboxTree();
$tree->setNameFormatter(new UppercaseFormatter());
$options = $tree->process($data);
// All names will be uppercaseThe library includes a helper method to generate SQL queries for fetching hierarchical data:
$tree = new SelectboxTree();
// Basic usage
$sql = $tree->sqlBuilder('categories');
// SELECT `id`, `name`, `parent_id` FROM `categories` ORDER BY `name` ASC
// Custom columns
$sql = $tree->sqlBuilder(
table: 'products',
primaryCol: 'title',
parentCol: 'category_id',
);
// SELECT `id`, `title`, `category_id` FROM `products` ORDER BY `title` ASC
// With WHERE conditions
$sql = $tree->sqlBuilder(
table: 'categories',
wheres: ['active = 1', 'deleted_at IS NULL'],
);
// SELECT `id`, `name`, `parent_id` FROM `categories`
// WHERE (active = 1) AND (deleted_at IS NULL) ORDER BY `name` ASC
// Custom ORDER BY
$sql = $tree->sqlBuilder(
table: 'categories',
orderCol: 'position',
);
// SELECT `id`, `name`, `parent_id` FROM `categories` ORDER BY `position` ASCParameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
$table |
string |
required | Database table name |
$primaryCol |
string |
'name' |
Column containing the display name |
$parentCol |
string |
'parent_id' |
Column containing the parent reference |
$wheres |
array |
[] |
Array of WHERE conditions |
$orderCol |
?string |
null |
Column for ORDER BY (defaults to $primaryCol) |
use Baraja\SelectboxTree\SelectboxTree;
$tree = new SelectboxTree();
// Generate SQL query
$sql = $tree->sqlBuilder('categories', 'name', 'parent_id', ['active = 1']);
// Fetch data from database (using your preferred method)
$pdo = new PDO('mysql:host=localhost;dbname=mydb', 'user', 'pass');
$stmt = $pdo->query($sql);
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Process into selectbox format
$options = $tree->process($data);The library automatically integrates with Baraja\Localization\Translation if available. Names starting with T:{ pattern are automatically translated:
$data = [
['id' => 1, 'name' => 'T:{"cs":"Kategorie","en":"Category"}', 'parent_id' => null],
];
$tree = new SelectboxTree();
$options = $tree->process($data);
// The name will be automatically translated based on current localeThis feature is optional and only activates when the baraja-core/localization package is installed.
The library tracks processed IDs to prevent duplicate entries in the output, even if the same item appears multiple times in the input data.
Two mechanisms prevent infinite loops:
- Max depth limit - Configurable via
setMaxDepth(), defaults to 32 levels - Used ID tracking - Items are only processed once, preventing circular references
- Max depth must be between 1 and 1000
- Empty or null input arrays are handled gracefully (return empty array)
Jan Barasek https://baraja.cz
baraja-core/selectbox-tree is licensed under the MIT license. See the LICENSE file for more details.
