1
|
|
|
<?php |
2
|
|
|
namespace arc\store; |
3
|
|
|
|
4
|
|
|
final class TreeStore implements Store { |
5
|
|
|
|
6
|
|
|
private $tree; |
7
|
|
|
private $queryParser; |
8
|
|
|
private $resultHandler; |
9
|
|
|
private $path; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* MemStore constructor. |
13
|
|
|
* @param \arc\tree\Node $tree |
14
|
|
|
* @param callable $queryParser |
15
|
|
|
* @param callable $resultHandler |
16
|
|
|
* @param string $path |
17
|
|
|
*/ |
18
|
|
|
public function __construct($tree = null, $queryParser = null, $resultHandler = null, $path = '/') |
19
|
|
|
{ |
20
|
|
|
$this->tree = $tree; |
21
|
|
|
$this->queryParser = $queryParser; |
22
|
|
|
$this->resultHandlerFactory = $resultHandler; |
|
|
|
|
23
|
|
|
$this->resultHandler = $resultHandler($this->tree); |
24
|
|
|
$this->path = \arc\path::collapse($path); |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* change the current path, returns a new store instance for that path |
29
|
|
|
* @param string $path |
30
|
|
|
* @return PSQLStore |
31
|
|
|
*/ |
32
|
|
|
public function cd($path) |
33
|
|
|
{ |
34
|
|
|
return new self( $this->tree, $this->queryParser, $this->resultHandlerFactory, \arc\path::collapse($path, $this->path) ); |
|
|
|
|
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* creates sql query for the search query and returns the resulthandler |
39
|
|
|
* @param string $query |
40
|
|
|
* @param string $path |
41
|
|
|
* @return mixed |
42
|
|
|
*/ |
43
|
|
|
public function find($query, $path='') |
44
|
|
|
{ |
45
|
|
|
$path = \arc\path::collapse($path, $this->path); |
46
|
|
|
$root = $this->tree->cd($path); |
|
|
|
|
47
|
|
|
$f = $this->queryParser->parse($query, $path); |
|
|
|
|
48
|
|
|
return call_user_func($this->resultHandler, $f ); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* get a single object from the store by path |
53
|
|
|
* @param string $path |
54
|
|
|
* @return mixed |
55
|
|
|
*/ |
56
|
|
|
public function get($path='') |
57
|
|
|
{ |
58
|
|
|
$path = \arc\path::collapse($path, $this->path); |
59
|
|
|
$result = $this->tree->cd($path); |
60
|
|
|
return $result; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* list all parents, including self, by path, starting from the root |
65
|
|
|
* @param string $path |
66
|
|
|
* @param string $top |
67
|
|
|
* @return mixed |
68
|
|
|
*/ |
69
|
|
|
public function parents($path='', $top='/') |
70
|
|
|
{ |
71
|
|
|
$path = \arc\path::collapse($path, $this->path); |
72
|
|
|
return ($this->resultHandler)( |
73
|
|
|
'strtolower($node->path)==strtolower(substring("' |
74
|
|
|
.$path.'",1,'.sizeof($path) |
|
|
|
|
75
|
|
|
.') && like(strtolower($node->path),strtolower("'.$top.'")' |
76
|
|
|
); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* list all child objects by path |
81
|
|
|
* @param string $path |
82
|
|
|
* @return mixed |
83
|
|
|
*/ |
84
|
|
|
public function ls($path='') |
85
|
|
|
{ |
86
|
|
|
$path = \arc\path::collapse($path, $this->path); |
87
|
|
|
return $this->tree->cd($path)->ls(function($node) { |
88
|
|
|
return json_decode(json_encode($node->nodeValue),false); |
89
|
|
|
}); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* returns true if an object with the given path exists |
94
|
|
|
* @param string $path |
95
|
|
|
* @return bool |
96
|
|
|
*/ |
97
|
|
|
public function exists($path='') |
98
|
|
|
{ |
99
|
|
|
$path = \arc\path::collapse($path, $this->path); |
100
|
|
|
if ($path!='/') { |
101
|
|
|
$parent = ($path=='/' ? '' : \arc\path::parent($path)); |
102
|
|
|
$name = basename($path); |
103
|
|
|
return $this->tree->cd($parent)->childNodes->offsetExists($name); |
104
|
|
|
} |
105
|
|
|
return true; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* initialize the postgresql database, if it wasn't before |
110
|
|
|
* @return bool|mixed |
111
|
|
|
*/ |
112
|
|
|
public function initialize() { |
113
|
|
|
return $this->save(\arc\prototype::create([ |
114
|
|
|
'name' => 'Root' |
115
|
|
|
]),'/'); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* save (insert or update) a single object on the given path |
120
|
|
|
* @param $data |
121
|
|
|
* @param string $path |
122
|
|
|
* @return mixed |
123
|
|
|
*/ |
124
|
|
|
public function save($data, $path='') { |
125
|
|
|
$path = \arc\path::collapse($path, $this->path); |
126
|
|
|
$parent = ($path=='/' ? '' : \arc\path::parent($path)); |
127
|
|
|
if ($path!='/' && !$this->exists($parent)) { |
128
|
|
|
throw new \arc\IllegalRequest("Parent $parent not found.", \arc\exceptions::OBJECT_NOT_FOUND); |
129
|
|
|
} |
130
|
|
|
$parentNode = $this->tree->cd($parent); |
131
|
|
|
$name = ($path=='/' ? '' : basename($path)); |
132
|
|
|
if ($name) { |
133
|
|
|
$parentNode->appendChild($name, $data); |
134
|
|
|
} else { |
135
|
|
|
$parentNode->nodeValue = $data; |
136
|
|
|
} |
137
|
|
|
return true; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* remove the object with the given path and all its children |
142
|
|
|
* won't remove the root object ever |
143
|
|
|
* @param string $path |
144
|
|
|
* @return mixed |
145
|
|
|
*/ |
146
|
|
|
public function delete($path = '') { |
147
|
|
|
$path = \arc\path::collapse($path, $this->path); |
148
|
|
|
$parent = \arc\path::parent($path); |
149
|
|
|
$name = basename($path); |
150
|
|
|
$this->tree->cd($parent)->removeChild($name); |
151
|
|
|
return true; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
|
155
|
|
|
public static function getResultHandler($tree) |
156
|
|
|
{ |
157
|
|
|
return function($callback) use ($tree) { |
158
|
|
|
$dataset = \arc\tree::filter($tree, $callback); |
159
|
|
|
foreach($dataset as $path => $node) { |
160
|
|
|
$node = json_decode(json_encode($node),false); |
161
|
|
|
$dataset[$path] = $node; |
162
|
|
|
} |
163
|
|
|
return $dataset; |
164
|
|
|
}; |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
|