Newer
Older
## Installation
In `composer.json` insert into `repositories` section
```
{
"type": "vcs",
"url": "ssh://git@gitlab.mondayfactory.cz:2222/mondayfactory/mptt.git"
}
```
and require package using `composer require monday-factory/mptt`
----
## CoreMPTT
`CoreMPTT` implements the logic of MPTT. Extend that class in your custom MPTT class implementation.
## Node
`Node` coresponds to row in database table. It contains some data as `id`, `parent`, `left`, `right` etc. that are important for MPTT functionality, but can contain some custom data as well.
`NodeCollection` is collection of objects of `Node` type. It works as dictionary/hash map/associative array, where key is node id and value is node.
----
## Example Usage
### Database table
Let's have table called `mptt_warehouses` in our database with following scheme
| id | name | parent_id | left | right | some other columns... |
| ---- | ---- | --------- | ---- | ----- | --------------------- |
| Uuid | root | null | 1 | 4 | my custom data |
| Uuid | cat1 | null | 2 | 3 | my custom data |
Every database table, that we want to use for mptt, should have columns `id (char(36))`, `name (varchar)`, `parent_id (char(36))`, `left (int(11))`, `right (int(11))`. Names of these column can be modified by setting/overriding CoreMPTT's properties.
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
### MPTT Class
Now we create class extending `MondayFactory\MPTT\CoreMPTT` class for our previously defined table
```php
<?php
declare(strict_types=1);
namespace App\MPTT;
use Dibi\Connection;
use Dibi\Fluent;
use MondayFactory\MPTT\CoreMPTT;
use MondayFactory\MPTT\Node\Node;
final class WarehousesMPTT extends CoreMPTT
{
/**
* @var string
*/
protected $tablename = 'mptt_warehouses'; // We override CoreMPPT property $tablename with name of our table
public function __construct(Connection $connection)
{
parent::__construct($connection);
}
protected function restrict(Fluent $query, string $alias): Fluent
{
return $query;
}
protected function appendResource(Fluent $query): Fluent
{
return $query;
}
protected function getDataFromRow(array $row): ?array
{
return null;
}
protected function getCustomFields(Node $node): array
{
return [];
}
protected function appendSelect(): string
{
return '';
}
}
```
Now we have minimal MPTT class implementation and we are good to go.
### Advanced usage
#### Custom fields
We can have some custom columns in our table.
There is function `getCustomFields(Node $node): array` to specify column names and values for inserting.
Let's add new column `company_id` into our table where we will store for which company the node is assigned to.
We return key, value array of our custom columns and these will be inserted into table with our node.
```php
protected function getCustomFields(Node $node): array
{
return [
'company_id' => SOME_ID,
];
}
```
To get and work with these custom fields, see **Append Resource** section
#### Restrict
Previously shown example is only usable, when there is one tree in one table. If we want to have multiple trees in table, we have to somehow restrict selection of tree.
In that case there is function `restrict(Fluent $query, string $alias): Fluent`.
```php
protected function restrict(Fluent $query, string $alias): Fluent
{
return $query->where(
"%n.company_id = ?", $alias, SOME_ID
);
}
```
We add constraint on our query to select rows only where `company_id` equals to `SOME_ID` (`SOME_ID` is placeholder, can be passed in constructor or anywhere else).
`alias` is used to distinguish between `node` and `parent` tables, but resticting should be almost always same for both of them.
#### Append Resource
Sometimes we have some data in other tables, related to nodes, that we would like to get with our tree nodes.
Let's have another table called `warehouses` with some columns and column `mptt_id`. `mptt_id` is reference to `id` in `mptt_warehouses`.
We would like to select row from `mptt_warehouses` with appended rows from `warehouses`. We use function `appendResource(Fluent $query): Fluent`.
```php
protected function appendResource(Fluent $query): Fluent
{
return $query
->leftJoin('warehouses')
->on('`warehouses`.`mptt_id` = %n.%n', $this->nodeAlias, $this->primaryKey);
}
```
We basically just join table with `warehouses`.
Now we modify query selection to select some column from `warehouses` table, so we can work with these columns during tree build.
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
```php
protected function appendSelect(): string
{
return '`warehouses`.`id` as warehouses_id, `warehouses`.`description` as warehouses_name';
}
```
Next, we implement function `getDataFromRow(array $row): ?array`, so we have array of our custom data from `warehouses` in Node `data` array.
We can use this method to insert custom fields into Node's data.
```php
protected function getDataFromRow(array $row): ?array
{
if ($row['warehouses_id'] === null || $row['warehouses_name'] === null) {
return null;
}
return [
'id' => $row['warehouses_id'],
'name' => $row['warehouses_name'],
];
}
```
Array returned by this function is appended into node's data property.
#### Clean up
Sometimes we need to do something when we delete nodes, for example set `mptt_id` in related rows in `warehouses` to parent of deleted node.
We can simply do that via function `onDeleteNode(string $key, string $parent): void`.
Example usage:
```php
public function onDelete(string $key, string $parent): void
{
$this->connection
->update('warehouses', ['mptt_id' => $parent])
->where('`mptt_id` = ?', $key)
->execute();
}
```
and register callback in constructor (`onNodeInsert` and `onNodeUpdate` are also avaible)