## Apache Zeta Components Manual :: File Source for db_materialized_path.php

## Source for file db_materialized_path.php

Documentation is available at db_materialized_path.php

1. <?php
2. /**
3. * File containing the ezcTreeDbMaterializedPath class.
4. *
5. * Licensed to the Apache Software Foundation (ASF) under one
6. * or more contributor license agreements. See the NOTICE file
7. * distributed with this work for additional information
8. * regarding copyright ownership. The ASF licenses this file
9. * to you under the Apache License, Version 2.0 (the
10. * "License"); you may not use this file except in compliance
11. * with the License. You may obtain a copy of the License at
12. *
13. * http://www.apache.org/licenses/LICENSE-2.0
14. *
15. * Unless required by applicable law or agreed to in writing,
16. * software distributed under the License is distributed on an
17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18. * KIND, either express or implied. See the License for the
19. * specific language governing permissions and limitations
20. * under the License.
21. *
22. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
23. * @version //autogentag//
24. * @filesource
25. * @package TreeDatabaseTiein
26. */
28. /**
29. * ezcTreeDbMaterializedPath implements a tree backend which stores parent/child
30. * information in a path like string (such as /1/4/6/8).
31. *
32. * The table that stores the index (configured using the $indexTableName argument
33. * of the {@link __construct} method) should contain at least three fields. The
34. * first one 'id' will contain the node's ID, the second one 'parent_id' the ID
35. * of the node's parent. Both fields should be of the same database field type.
36. * Supported field types are either integer or a string type. The third field
37. * 'path' will contain the path string. This should be a text field. The size
38. * of the field determines the maximum depth the tree can have.
39. * In order to use auto-generated IDs, the 'id' field needs to be an
40. * auto-incrementing integer field, by using either an auto-increment field, or
41. * a sequence.
42. *
43. * @property-read ezcTreeDbDataStore $store
44. * The data store that is used for retrieving/storing data.
45. * @property-read string $separationChar
46. * The character that is used to separate node IDs internally.
47. * This character can then *not* be part of a node ID.
48. * @property string $nodeClassName
49. * Which class is used as tree node - this class *must* inherit
50. * the ezcTreeNode class.
51. *
52. * @package TreeDatabaseTiein
53. * @version //autogentag//
54. * @mainclass
55. */
56. class ezcTreeDbMaterializedPath extends ezcTreeDb
57. {
58. /**
59. * Constructs a new ezcTreeDbMaterializedPath object.
60. *
61. * The different arguments to the constructor configure which database
62. * connection ($dbh) is used to access the database and the $indexTableName
63. * argument which table is used to retrieve the relation data from. The
64. * $store argument configure which data store is used with this tree.
65. *
66. * The $separationChar argument defaults to / and is used to separate node
67. * IDs internally. This character can *not* be part of a node ID, and should be
68. * the same character that was used when creating the tree.
69. *
70. * Just like the others, this database backend requires the index table to
71. * at least define the field 'id', which can either be a string or an
72. * integer field.
73. *
74. * @param ezcDbHandler $dbh
75. * @param string $indexTableName
76. * @param ezcTreeDbDataStore $store
77. * @param string $separationChar
78. */
79. public function __construct ( ezcDbHandler $dbh, $indexTableName, ezcTreeDbDataStore $store, $separationChar = '/' )
80. {
81. parent:: __construct ( $dbh, $indexTableName, $store );
82. $this-> properties ['separationChar'] = $separationChar;
83. }
85. /**
86. * Returns the value of the property $name.
87. *
88. * @throws ezcBasePropertyNotFoundException if the property does not exist.
89. * @param string $name
90. * @ignore
91. */
92. public function __get( $name )
93. {
94. switch ( $name )
95. {
96. case 'separationChar':
97. return $this-> properties [$name];
98. }
99. return parent::__get( $name );
100. }
102. /**
103. * Sets the property $name to $value.
104. *
105. * @throws ezcBasePropertyNotFoundException if the property does not exist.
106. * @throws ezcBasePropertyPermissionException if a read-only property is
107. * tried to be modified.
108. * @param string $name
109. * @param mixed $value
110. * @ignore
111. */
112. public function __set( $name, $value )
113. {
114. switch ( $name )
115. {
116. case 'separationChar':
117. throw new ezcBasePropertyPermissionException ( $name, ezcBasePropertyPermissionException ::READ );
119. default:
120. return parent::__set( $name, $value );
121. }
122. }
124. /**
125. * Returns true if the property $name is set, otherwise false.
126. *
127. * @param string $name
128. * @return bool
129. * @ignore
130. */
131. public function __isset( $name )
132. {
133. switch ( $name )
134. {
135. case 'separationChar':
136. return isset( $this-> properties [$name] );
138. default:
139. return parent::__isset( $name );
140. }
141. }
143. /**
144. * This method checks whether a node ID is valid to be used in a backend.
145. *
146. * @throws ezcTreeInvalidNodeIDException if the node is not valid.
147. *
148. * @param string $nodeId
149. */
150. protected function checkNodeId ( $nodeId )
151. {
152. if ( strchr ( $nodeId, $this-> properties ['separationChar'] ) != false )
153. {
154. throw new ezcTreeInvalidIdException ( $nodeId, $this-> properties ['separationChar'] );
155. }
156. }
158. /**
159. * Creates a new ezcTreeDbMaterializedPath object.
160. *
161. * The different arguments to the method configure which database
162. * connection ($dbh) is used to access the database and the $indexTableName
163. * argument which table is used to retrieve the relation data from. The
164. * $store argument configure which data store is used with this tree.
165. *
166. * The $separationChar argument defaults to / and is used to separate node
167. * IDs internally. This character can *not* be part of a node ID, and the same
168. * character should be used when re-opening the tree upon instantiation.
169. *
170. * It is up to the user to create the database table and make sure it is
171. * empty.
172. *
173. * @param ezcDbHandler $dbh
174. * @param string $indexTableName
175. * @param ezcTreeDbDataStore $store
176. * @param string $separationChar
177. */
178. public static function create ( ezcDbHandler $dbh, $indexTableName, ezcTreeDbDataStore $store, $separationChar = '/' )
179. {
180. return new ezcTreeDbMaterializedPath ( $dbh, $indexTableName, $store, $separationChar );
181. }
183. /**
184. * Returns the parent id and path the node with ID $nodeId as an array.
185. *
186. * The format of the array is:
187. * - 0: parent id
188. * - 1: path
189. *
190. * @param string $nodeId
191. * @return array(int)
192. */
193. protected function fetchNodeInformation ( $nodeId )
194. {
195. $db = $this-> dbh ;
197. // SELECT parent_id, path
198. // FROM indexTable
199. // WHERE id = $nodeId
200. $q = $db->createSelectQuery();
201. $q->select( 'parent_id, path' )
202. ->from( $db->quoteIdentifier( $this-> indexTableName ) )
203. ->where( $q->expr->eq( 'id', $q->bindValue( $nodeId ) ) );
204. $s = $q->prepare();
205. $s->execute();
206. $r = $s->fetchAll( PDO::FETCH_NUM );
207. return $r[0];
208. }
210. /**
211. * Runs SQL to get all the children of the node with ID $nodeId as a PDO
212. * result set.
213. *
214. * @param string $nodeId
215. * @return PDOStatement
216. */
217. protected function fetchChildRecords ( $nodeId )
218. {
219. $db = $this-> dbh ;
220. $q = $db->createSelectQuery();
222. // SELECT id, parent_id
223. // FROM indexTable
224. // WHERE parent_id = $nodeId
225. $q->select( 'id, parent_id' )
226. ->from( $db->quoteIdentifier( $this-> indexTableName ) )
227. ->where( $q->expr->eq( 'parent_id', $q->bindValue( $nodeId ) ) );
229. $s = $q->prepare();
230. $s->execute();
231. return $s;
232. }
234. /**
235. * Returns all the children of the node with ID $nodeId.
236. *
237. * @param string $nodeId
238. * @return ezcTreeNodeList
239. */
240. public function fetchChildren ( $nodeId )
241. {
242. $className = $this-> properties ['nodeClassName'];
243. $list = new ezcTreeNodeList ;
244. foreach ( $this-> fetchChildRecords ( $nodeId ) as $record )
245. {
246. $list->addNode( new $className( $this, $record['id'] ) );
247. }
248. return $list;
249. }
251. /**
252. * Returns all the nodes in the path from the root node to the node with ID
253. * $nodeId, including those two nodes.
254. *
255. * @param string $nodeId
256. * @return ezcTreeNodeList
257. */
258. public function fetchPath ( $nodeId )
259. {
260. $className = $this-> properties ['nodeClassName'];
261. $list = new ezcTreeNodeList ;
263. // Fetch node information
264. list( $parentId, $path ) = $this-> fetchNodeInformation ( $nodeId );
266. $parts = explode ( $this-> properties ['separationChar'], $path );
267. array_shift ( $parts );
269. foreach ( $parts as $pathNodeId )
270. {
271. $list->addNode( new $className( $this, $pathNodeId ) );
272. $pathNodeId = $this-> getParentId ( $pathNodeId );
273. }
275. $list->addNode( new $className( $this, $nodeId ) );
276. return $list;
277. }
279. /**
280. * Returns the node with ID $nodeId and all its children, sorted according to
281. * the {@link http://en.wikipedia.org/wiki/Depth-first_search Depth-first sorting}
282. * algorithm.
283. *
284. * @param string $nodeId
285. * @return ezcTreeNodeList
286. */
287. public function fetchSubtreeDepthFirst ( $nodeId )
288. {
289. $className = $this-> properties ['nodeClassName'];
290. $list = new ezcTreeNodeList ;
291. $list->addNode( new $className( $this, $nodeId ) );
293. // Fetch information for node
294. list( $parentId, $path ) = $this-> fetchNodeInformation ( $nodeId );
296. $db = $this-> dbh ;
297. $q = $db->createSelectQuery();
299. // SELECT id
300. // FROM materialized_path
301. // WHERE path LIKE '$path/%'
302. $q->select( 'id' )
303. ->from( $db->quoteIdentifier( $this-> indexTableName ) )
304. ->where( $q->expr->like( 'path', $q->bindValue( "$path{$this-> properties ['separationChar']}%

" ) ) );
- $s = $q->prepare();
- $s->execute();
- foreach ( $s as $record )
- {
- $list->addNode( new $className( $this, $record['id'] ) );
- }
- return $list;
- }
- /**
- * Alias for fetchSubtreeDepthFirst().
- *
- * @param string $nodeId
- * @return ezcTreeNodeList
- */
- public function fetchSubtree ( $nodeId )
- {
- return $this-> fetchSubtreeDepthFirst ( $nodeId );
- }
- /**
- * Adds the children nodes of the node with ID $nodeId to the
- * ezcTreeNodeList $list.
- *
- * @param ezcTreeNodeList $list
- * @param string $nodeId
- */
- protected function addChildNodesBreadthFirst ( ezcTreeNodeList $list, $nodeId )
- {
- $className = $this-> properties ['nodeClassName'];
- $childRecords = $this-> fetchChildRecords ( $nodeId )->fetchAll();
- foreach ( $childRecords as $record )
- {
- $list->addNode( new $className( $this, $record['id'] ) );
- }
- foreach ( $childRecords as $record )
- {
- $this-> addChildNodesBreadthFirst ( $list, $record['id'] );
- }
- }
- /**
- * Returns the node with ID $nodeId and all its children, sorted according to
- * the {@link http://en.wikipedia.org/wiki/Breadth-first_search Breadth-first sorting}
- * algorithm.
- *
- * @param string $nodeId
- * @return ezcTreeNodeList
- */
- public function fetchSubtreeBreadthFirst ( $nodeId )
- {
- $className = $this-> properties ['nodeClassName'];
- $list = new ezcTreeNodeList ;
- $list->addNode( new $className( $this, $nodeId ) );
- $this-> addChildNodesBreadthFirst ( $list, $nodeId );
- return $list;
- }
- /**
- * Returns the number of direct children of the node with ID $nodeId.
- *
- * @param string $nodeId
- * @return int
- */
- public function getChildCount ( $nodeId )
- {
- $db = $this-> dbh ;
- $q = $db->createSelectQuery();
- // SELECT count(id)
- // FROM indexTable
- // WHERE parent_id = $nodeId
- $q->select( 'count(id)' )
- ->from( $db->quoteIdentifier( $this-> indexTableName ) )
- ->where( $q->expr->eq( 'parent_id', $q->bindValue( $nodeId ) ) );
- $s = $q->prepare();
- $s->execute();
- return (int) $s->fetchColumn( 0 );
- }
- /**
- * Returns the number of children of the node with ID $nodeId, recursively.
- *
- * @param string $nodeId
- * @return int
- */
- public function getChildCountRecursive ( $nodeId )
- {
- // Fetch information for node
- list( $parentId, $path ) = $this-> fetchNodeInformation ( $nodeId );
- $db = $this-> dbh ;
- $q = $db->createSelectQuery();
- // SELECT count(id)
- // FROM materialized_path
- // WHERE path LIKE '$path/%'
- $q->select( 'count(id)' )
- ->from( $db->quoteIdentifier( $this-> indexTableName ) )
- ->where( $q->expr->like( 'path', $q->bindValue( "$path{$this-> properties ['separationChar']}%" ) ) );
- $s = $q->prepare();
- $s->execute();
- $r = $s->fetch( PDO::FETCH_NUM );
- return (int) $r[0];
- }
- /**
- * Returns the distance from the root node to the node with ID $nodeId.
- *
- * @param string $nodeId
- * @return int
- */
- public function getPathLength ( $nodeId )
- {
- // Fetch information for node
- list( $parentId, $path ) = $this-> fetchNodeInformation ( $nodeId );
- return substr_count ( $path, $this-> properties ['separationChar'] ) - 1;
- }
- /**
- * Returns whether the node with ID $nodeId has children.
- *
- * @param string $nodeId
- * @return bool
- */
- public function hasChildNodes ( $nodeId )
- {
- return $this-> getChildCount ( $nodeId ) > 0;
- }
- /**
- * Returns whether the node with ID $childId is a direct child of the node
- * with ID $parentId.
- *
- * @param string $childId
- * @param string $parentId
- * @return bool
- */
- public function isChildOf ( $childId, $parentId )
- {
- $nodeId = $this-> getParentId ( $childId );
- $parentId = (string) $parentId;
- return $parentId === $nodeId;
- }
- /**
- * Returns whether the node with ID $childId is a direct or indirect child
- * of the node with ID $parentId.
- *
- * @param string $childId
- * @param string $parentId
- * @return bool
- */
- public function isDescendantOf ( $childId, $parentId )
- {
- // Fetch node information
- list( $dummyParentId, $path ) = $this-> fetchNodeInformation ( $childId );
- $parts = explode ( $this-> properties ['separationChar'], $path );
- array_shift ( $parts );
- return in_array ( $parentId, $parts ) && ( $childId !== $parentId );
- }
- /**
- * Returns whether the nodes with IDs $child1Id and $child2Id are siblings
- * (ie, they share the same parent).
- *
- * @param string $child1Id
- * @param string $child2Id
- * @return bool
- */
- public function isSiblingOf ( $child1Id, $child2Id )
- {
- $nodeId1 = $this-> getParentId ( $child1Id );
- $nodeId2 = $this-> getParentId ( $child2Id );
- return $nodeId1 === $nodeId2 && (string) $child1Id !== (string) $child2Id;
- }
- /**
- * Sets a new node as root node, this also wipes out the whole tree.
- *
- * @param ezcTreeNode $node
- */
- public function setRootNode ( ezcTreeNode $node )
- {
- $db = $this-> dbh ;
- $q = $db->createDeleteQuery();
- $q->deleteFrom( $db->quoteIdentifier( $this-> indexTableName ) );
- $s = $q->prepare();
- $s->execute();
- $this->store->deleteDataForAllNodes();
- $q = $db->createInsertQuery();
- $q->insertInto( $db->quoteIdentifier( $this-> indexTableName ) )
- ->set( 'parent_id', "null" )
- ->set( 'id', $q->bindValue( $node->id ) )
- ->set( 'path', $q->bindValue( $this-> properties ['separationChar'] . $node->id ) );
- $s = $q->prepare();
- $s->execute();
- $this->store->storeDataForNode( $node, $node->data );
- }
- /**
- * Creates the query to insert an empty node into the database, so that the last-inserted ID can be obtained.
- *
- * @return ezcQueryInsert
- */
- protected function createAddEmptyNodeQuery ()
- {
- $db = $this-> dbh ;
- $q = $db->createInsertQuery();
- $q->insertInto( $db->quoteIdentifier( $this-> indexTableName ) )
- ->set( 'path', 0 );
- return $q;
- }
- /**
- * Adds the node $childNode as child of the node with ID $parentId.
- *
- * @param string $parentId
- * @param ezcTreeNode $childNode
- */
- public function addChild ( $parentId, ezcTreeNode $childNode )
- {
- if ( $this-> inTransaction )
- {
- $this-> addTransactionItem ( new ezcTreeTransactionItem( ezcTreeTransactionItem::ADD, $childNode, null, $parentId ) );
- return;
- }
- $db = $this-> dbh ;
- // Fetch parent information
- list( $parentParentId, $path ) = $this-> fetchNodeInformation ( $parentId );
- $q = $this-> createAddNodeQuery ( $childNode->id );
- $q->set( 'parent_id', $q->bindValue( $parentId ) )
- ->set( 'id', $q->bindValue( $childNode->id ) )
- ->set( 'path', $q->bindValue( $path . $this-> properties ['separationChar'] . $childNode->id ) );
- $s = $q->prepare();
- $s->execute();
- $this->store->storeDataForNode( $childNode, $childNode->data );
- }
- /**
- * Deletes all nodes in the node list $list.
- *
- * @param ezcTreeNodeList $list
- */
- private function deleteNodes( ezcTreeNodeList $list )
- {
- $db = $this-> dbh ;
- $q = $db->createDeleteQuery();
- $nodeIdList = array();
- foreach ( array_keys ( $list->nodes ) as $nodeId )
- {
- $nodeIdList[] = (string) $nodeId;
- }
- // DELETE FROM indexTable
- // WHERE id in ( $list );
- $q->deleteFrom( $db->quoteIdentifier( $this-> indexTableName ) );
- $q->where( $q->expr->in( 'id', $nodeIdList ) );
- $s = $q->prepare();
- $s->execute();
- }
- /**
- * Deletes the node with ID $nodeId from the tree, including all its children.
- *
- * @param string $nodeId
- */
- public function delete ( $nodeId )
- {
- if ( $this-> inTransaction )
- {
- $this-> addTransactionItem ( new ezcTreeTransactionItem( ezcTreeTransactionItem::DELETE, null, $nodeId ) );
- return;
- }
- $nodeList = $this-> fetchSubtree ( $nodeId );
- $this->deleteNodes( $nodeList );
- $this->store->deleteDataForNodes( $nodeList );
- }
- /**
- * Moves the node with ID $nodeId as child to the node with ID $targetParentId.
- *
- * @param string $nodeId
- * @param string $targetParentId
- */
- public function move ( $nodeId, $targetParentId )
- {
- if ( $this-> inTransaction )
- {
- $this-> addTransactionItem ( new ezcTreeTransactionItem( ezcTreeTransactionItem::MOVE, null, $nodeId, $targetParentId ) );
- return;
- }
- list( $origParentId, $origPath ) = $this-> fetchNodeInformation ( $nodeId );
- list( $targetParentParentId, $targetParentPath ) = $this-> fetchNodeInformation ( $targetParentId );
- // Get path to parent of $nodeId
- // - position of last /
- $pos = strrpos ( $origPath, $this-> properties ['separationChar'] );
- // - parent path and its length
- $parentPath = substr ( $origPath, 0, $pos );
- $parentPathLength = strlen ( $parentPath ) + 1;
- $db = $this-> dbh ;
- // Update parent ID in node $nodeId
- $q = $db->createUpdateQuery();
- $q->update( $db->quoteIdentifier( $this-> indexTableName ) )
- ->set( 'parent_id', $q->bindValue( $targetParentId ) )
- ->where( $q->expr->eq( 'id', $q->bindValue( $nodeId ) ) );
- $s = $q->prepare();
- $s->execute();
- // Update paths for subtree
- // UPDATE indexTable
- // SET path = $targetParentPath || SUBSTR( path, $parentPathLength ) )
- // WHERE id = $nodeId
- // OR path LIKE '$origPath/%'
- $q = $db->createUpdateQuery();
- $q->update( $db->quoteIdentifier( $this-> indexTableName ) )
- ->set( 'path', $q->expr->concat(
- $q->bindValue( $targetParentPath ),
- $q->expr->subString( 'path', $q->bindValue( $parentPathLength ) )
- ) )
- ->where( $q->expr->lOr(
- $q->expr->eq( 'id', $q->bindValue( $nodeId ) ),
- $q->expr->like( 'path', $q->bindValue( "$origPath{$this-> properties ['separationChar']}%" ) )
- ) );
- $s = $q->prepare();
- $s->execute();
- }
- /**
- * Fixates the transaction.
- */
- public function fixateTransaction ()
- {
- }
- }
- ?>
Documentation generated by phpDocumentor 1.4.3