Completed
Branch develop-webpack (f0754b)
by Daniel
09:36
created

builder::load_scripts()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 1
dl 0
loc 18
ccs 15
cts 15
cp 1
crap 1
rs 9.7666
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A builder::set_sql_where() 0 5 1
1
<?php
2
/**
3
 *
4
 * @package sitemaker
5
 * @copyright (c) 2013 Daniel A. (blitze)
6
 * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
7
 *
8
 */
9
10
namespace blitze\sitemaker\services\tree;
11
12
/**
13
* Manage nested sets
14
*/
15
abstract class builder extends \phpbb\tree\nestedset
16
{
17
	/**
18
	 * Set additional sql where restrictions
19
	 * @param string $sql_where
20
	 * @return $this
21
	 */
22
	public function set_sql_where($sql_where)
23
	{
24
		$this->sql_where = $sql_where;
25
26
		return $this;
27
	}
28
29
	/**
30
	 * Get item data
31
	 * @param int $item_id
32
	 * @return mixed
33
	 */
34
	public function get_item_info($item_id = 0)
35
	{
36
		$sql = 'SELECT * FROM ' . $this->table_name . ' ' . $this->get_sql_where('WHERE') .
37
			(($item_id) ? " AND {$this->column_item_id} = " . (int) $item_id : '');
38
39
		$result = $this->db->sql_query($sql);
40
		$row = $this->db->sql_fetchrow($result);
41
		$this->db->sql_freeresult($result);
42
43
		return $row;
44
	}
45
46
	/**
47
	 * Update a single item
48
	 *
49
	 * @param	integer	$item_id		The ID of the item to update.
50
	 * @param	array	$sql_data		Other item attributes to insert in the database ex. array('title' => 'Item 1')
51
	 * @return	array
52
	 */
53
	public function update_item($item_id, array $sql_data)
54
	{
55
		$sql = "UPDATE {$this->table_name}
56
			SET " . $this->db->sql_build_array('UPDATE', $sql_data) . "
57
			WHERE $this->column_item_id = " . (int) $item_id;
58
		$this->db->sql_query($sql);
59
60
		return $sql_data;
61
	}
62
63
	/**
64
	 * Update tree
65
	 *
66
	 * @param	array	$tree 	Array of parent-child items
67
	 * @return	array
68
	 */
69
	public function update_tree(array $tree)
70
	{
71
		$items_data = $this->get_all_tree_data();
72
73
		/**
74
		 * Remove any new nodes in the tree that did not exist before
75
		 * The intent of this method is to update an existing tree, not add new nodes
76
		 */
77
		$tree = array_intersect_key($tree, $items_data);
78
79
		// we do it this way because array_merge_recursive, would append numeric keys rather than overwrite them
80
		foreach ($tree as $key => $data)
81
		{
82
			$tree[$key] = array_merge($items_data[$key], $data);
83
		}
84
85
		$this->acquire_lock();
86
		$this->db->sql_transaction('begin');
87
88
		// Rather than updating each item individually, we will just delete all items
89
		// then add them all over again with new parent_id|left_id|right_id
90
		$this->db->sql_query("DELETE FROM {$this->table_name} " . $this->get_sql_where('WHERE'));
91
92
		// Now we add it back
93
		$sql_data = $this->add_sub_tree($tree, 0);
94
95
		$this->db->sql_transaction('commit');
96
		$this->lock->release();
97
98
		return $sql_data;
99
	}
100
101
	/**
102
	 * Takes a new line delimited string and returns an associative array of parent/child relationships
103
	 * that can be saved to a database or converted to a nested set model
104
	 *
105
	 * @param	string	$structure		The structure to build ex:
106
	 *										Home|index.php
107
	 *										News|index.php?p=news
108
	 *											Texas|index.php?p=news&cat=Texas
109
	 *										About Us|index.php?p=about
110
	 * @param	array	$table_fields	The expected information to get for each line (order is important) ex: array('title' => '', 'url' => '')
111
	 *									This will then assign 'Home' to 'title' and 'index.php' to 'url' in example above (line 1)
112
	 * @param array     $data
113
	 * @return	array					associative array of parent/child relationships ex: the above examples will produce
114
	 *										array(
115
	 *											1   => array('title' => 'Home', 'url' => 'index.php', parent_id => 0),
116
	 *											2   => array('title' => 'News', 'url' => 'index.php?p=news', parent_id => 0),
117
	 *											3   => array('title' => 'Texas', 'url' => 'index.php?p=news&cat=Texas', parent_id => 2),
118
	 *											4   => array('title' => 'About Us', 'url' => 'index.php?p=about', parent_id => 0),
119
	 *										)
120
	 */
121
	public function string_to_nestedset($structure, array $table_fields, $data = array())
122
	{
123
		$field_size = sizeof($table_fields);
124
		$fields = array_keys($table_fields);
125
		$values = array_fill(0, $field_size, '');
126
		$lines = array_filter(explode("\n", $structure));
127
		$max_id = $this->get_max_id($this->column_item_id, false);
128
129
		$adj_tree = $parent_ary = array();
130
		foreach ($lines as $i => $string)
131
		{
132
			$depth = strspn($string, "\t");
133
			$parent_id = (isset($parent_ary[$depth - 1])) ? $parent_ary[$depth - 1] : 0;
134
135
			if ($depth && !$parent_id)
136
			{
137
				throw new \RuntimeException($this->message_prefix . 'MALFORMED_TREE');
138
			}
139
140
			$key = $i + $max_id + 1;
141
			$field_values = array_map('trim', explode('|', trim($string))) + $values;
142
143
			$adj_tree[$key] = array_merge($data, array_combine($fields, $field_values));
144
			$adj_tree[$key][$this->column_item_id] = $key;
145
			$adj_tree[$key]['parent_id'] = $parent_id;
146
147
			$parent_ary[$depth] = $key;
148
		}
149
150
		return $adj_tree;
151
	}
152
153
	/**
154
	 * Adds a new branch to the tree from an array
155
	 *
156
	 * @param	array	$branch		Array of nodes to add to tree of form:
157
	 * 									array(
158
	 * 										1   => array('title' => 'Home', 'url' => 'index.php', parent_id => 0),
159
	 * 										2   => array('title' => 'News', 'url' => 'index.php?p=news', parent_id => 0),
160
	 * 										3   => array('title' => 'Texas', 'url' => 'index.php?p=news&cat=Texas', parent_id => 2),
161
	 * 										4   => array('title' => 'About Us', 'url' => 'index.php?p=about', parent_id => 0),
162
	 * 									);
163
	 * @param	int		$parent_id  Parent id of the branch we're adding
164
	 * @return	int[]	ids of newly added items
165
	 */
166
	public function add_branch(array $branch, $parent_id = 0)
167
	{
168
		$this->acquire_lock();
169
		$this->db->sql_transaction('begin');
170
171
		$this->add_sub_tree($branch, $parent_id);
172
173
		$this->db->sql_transaction('commit');
174
		$this->lock->release();
175
176
		return array_keys($branch);
177
	}
178
179
	/**
180
	 * @param array $branch
181
	 * @param int   $parent_id
182
	 * @return mixed
183
	 */
184
	protected function add_sub_tree(array $branch, $parent_id = 0)
185
	{
186
		$sql_data = $this->prepare_branch($parent_id, $branch);
187
188
		$this->db->sql_multi_insert($this->table_name, $sql_data);
189
190
		return $sql_data;
191
	}
192
193
	/**
194
	 * @param int   $parent_id
195
	 * @param array $branch
196
	 * @return array
197
	 */
198
	protected function prepare_branch($parent_id, array $branch)
199
	{
200
		$starting_data = $this->get_starting_data($parent_id, $branch);
201
202
		$depth = $starting_data['depth'];
203
		$right_id = $starting_data['right_id'];
204
205
		$sql_data = array();
206
		foreach ($branch as $i => $row)
207
		{
208
			$left_id	= $right_id + 1;
209
			$right_id   = $right_id + 2;
210
211
			$sql_data[$i] = $row;
212
			$sql_data[$i]['parent_id']	= $parent_id;
213
			$sql_data[$i]['left_id']	= $left_id;
214
			$sql_data[$i]['right_id']	= $right_id;
215
			$sql_data[$i]['depth']		= $depth;
216
217
			if ($row['parent_id'])
218
			{
219
				$left_id	= $sql_data[$row['parent_id']]['right_id'];
220
				$right_id   = $left_id + 1;
221
222
				$sql_data[$i]['parent_id']	= $row['parent_id'];
223
				$sql_data[$i]['depth']		= $sql_data[$row['parent_id']]['depth'] + 1;
224
				$sql_data[$i]['left_id']	= $left_id;
225
				$sql_data[$i]['right_id']	= $right_id;
226
227
				$this->update_right_side($sql_data, $right_id, $row['parent_id'], $branch);
228
			}
229
		}
230
231
		return array_values($sql_data);
232
	}
233
234
	/**
235
	 * @param int   $parent_id
236
	 * @param array $branch
237
	 * @return array
238
	 * @throws \RuntimeException
239
	 */
240
	protected function get_starting_data($parent_id, array $branch)
241
	{
242
		if ($parent_id)
243
		{
244
			$new_parent = $this->get_item_info($parent_id);
245
246
			if (!$new_parent)
247
			{
248
				$this->db->sql_transaction('rollback');
249
				$this->lock->release();
250
251
				throw new \RuntimeException($this->message_prefix . 'INVALID_PARENT');
252
			}
253
254
			// adjust items in affected branch
255
			$this->prepare_adding_subset(array_keys($branch), $new_parent);
256
257
			return array(
258
				'depth'		=> $new_parent['depth'] + 1,
259
				'right_id'	=> --$new_parent['right_id'],
260
			);
261
		}
262
		else
263
		{
264
			return array(
265
				'depth'		=> 0,
266
				'right_id'	=> $this->get_max_id($this->column_right_id),
267
			);
268
		}
269
	}
270
271
	/**
272
	 * @param string $column
273
	 * @param bool   $use_sql_where
274
	 * @return int|mixed
275
	 */
276
	protected function get_max_id($column, $use_sql_where = true)
277
	{
278
		$sql = "SELECT MAX($column) AS $column
279
			FROM {$this->table_name} " .
280
			(($use_sql_where) ? $this->get_sql_where('WHERE') : '');
281
		$result = $this->db->sql_query($sql);
282
		$max_id = $this->db->sql_fetchfield($column);
283
		$this->db->sql_freeresult($result);
284
285
		return ($max_id) ? $max_id : 0;
286
	}
287
288
	/**
289
	 * Update right side of tree
290
	 * @param array $data
291
	 * @param int   $right_id
292
	 * @param int   $index
293
	 * @param array $branch
294
	 */
295
	protected function update_right_side(array &$data, &$right_id, $index, array $branch)
296
	{
297
		$right_id++;
298
		$data[$index]['right_id'] = $right_id;
299
300
		if ($branch[$index]['parent_id'])
301
		{
302
			$this->update_right_side($data, $right_id, $branch[$index]['parent_id'], $branch);
303
		}
304
	}
305
}
306