Data_model_processing::crud_argument_preparation()   C
last analyzed

Complexity

Conditions 14
Paths 22

Size

Total Lines 49
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 14

Importance

Changes 0
Metric Value
cc 14
eloc 30
nc 22
nop 3
dl 0
loc 49
rs 5.1329
c 0
b 0
f 0
ccs 31
cts 31
cp 1
crap 14

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package CleverStyle Framework
4
 * @author  Nazar Mokrynskyi <[email protected]>
5
 * @license 0BSD
6
 */
7
namespace cs\CRUD;
8
use
9
	cs\Event,
10
	cs\Language,
11
	cs\Text;
12
13
/**
14
 * @property array  $data_model
15
 * @property string $data_model_ml_group
16
 * @property string $data_model_files_tag_prefix
17
 * @property string $table
18
 */
19
trait Data_model_processing {
20
	/**
21
	 * @return int
22
	 */
23
	abstract protected function cdb ();
24
	/**
25
	 * @param callable[]|string[] $data_model
26
	 * @param array               $arguments
27
	 *
28
	 * @return array
29
	 */
30 72
	private function fix_arguments_order ($data_model, $arguments) {
31 72
		if (is_array_indexed($arguments)) {
32 27
			return $arguments;
33
		}
34 57
		$arguments_ = [];
35 57
		foreach (array_keys($data_model) as $argument) {
36 57
			if (array_key_exists($argument, $arguments)) {
37 57
				$arguments_[] = $arguments[$argument];
38
			}
39
		}
40 57
		return $arguments_;
41
	}
42
	/**
43
	 * @param callable[]|string[] $data_model
44
	 * @param array               $arguments
45
	 * @param false|int           $id            On update id should be specified to work properly with multilingual fields
46
	 * @param bool                $update_needed If on creation request without specified primary key and multilingual fields present - update needed
47
	 *                                           after creation (there is no id before creation)
48
	 *
49
	 * @return array[]
50
	 */
51 72
	private function crud_arguments_preparation ($data_model, $arguments, $id = false, &$update_needed = false) {
52 72
		$arguments     = array_combine(array_keys($data_model), $arguments);
53 72
		$joined_tables = [];
54 72
		foreach ($arguments as $item => &$argument) {
55 72
			$model = $data_model[$item];
56 72
			if (is_callable($model)) {
57 6
				$argument = $model($argument);
58 6
				continue;
59
			}
60 72
			if (isset($model['data_model'])) {
61 12
				$joined_tables[$item] = $this->prepare_joined_tables_model($model, $argument);
62 12
				unset($arguments[$item]);
63 12
				continue;
64
			}
65 72
			list($type, $format) = explode(':', $model, 2) + [1 => null];
66 72
			$multilingual_field = false;
67
			/**
68
			 * If field is multilingual
69
			 */
70 72
			if ($type == 'ml') {
71 6
				$multilingual_field = true;
72 6
				list($type, $format) = explode(':', $format, 2) + [1 => null];
73
			}
74 72
			$argument = $this->crud_argument_preparation($type, $format, $argument);
75
			/**
76
			 * If field is multilingual - handle multilingual storing of value automatically
77
			 */
78 72
			if ($multilingual_field && $this->is_multilingual()) {
79 6
				if ($id !== false) {
80 6
					$argument = Text::instance()->set($this->cdb(), "$this->data_model_ml_group/$item", $id, $argument);
81
				} else {
82 72
					$update_needed = true;
83
				}
84
			}
85
		}
86
		return [
87 72
			$arguments,
88 72
			$joined_tables
89
		];
90
	}
91
	/**
92
	 * @param array $structure
93
	 * @param array $arguments
94
	 *
95
	 * @return array
96
	 */
97 12
	private function prepare_joined_tables_model ($structure, $arguments) {
98 12
		if (!$arguments) {
99 9
			return [];
100
		}
101
		$new_structure = [
102 12
			'id_field' => array_keys($structure['data_model'])[0],
103 12
			'fields'   => array_slice($structure['data_model'], 1),
104
			'data'     => []
105
		];
106 12
		if (isset($structure['language_field'])) {
107 6
			$new_structure['language_field'] = $structure['language_field'];
108
		}
109 12
		$arguments       = is_array_assoc($arguments) ? [$arguments] : _array((array)$arguments);
110 12
		$arguments_assoc = is_array_assoc($arguments[0]);
111 12
		foreach ($new_structure['fields'] as $field_name => $field_model) {
112
			/**
113
			 * Both associative and indexed arrays are supported - that is why we determine key for array
114
			 */
115 12
			$key = $arguments_assoc ? $field_name : array_search($field_name, array_keys($new_structure['fields']));
116 12
			if (is_callable($field_model)) {
117 6
				foreach ($arguments as $index => $arguments_local) {
118 6
					$new_structure['data'][$index][$field_name] = $field_model($arguments_local[$key]);
119
				}
120 6
				continue;
121
			}
122 12
			list($type, $format) = explode(':', $field_model, 2) + [1 => null];
123 12
			foreach ($arguments as $index => $arguments_local) {
124 12
				$new_structure['data'][$index][$field_name] = $this->crud_argument_preparation($type, $format, $arguments_local[$key]);
125
			}
126
		}
127 12
		return $new_structure;
128
	}
129
	/**
130
	 * @param string $type
131
	 * @param mixed  $format
132
	 * @param mixed  $argument
133
	 *
134
	 * @return float|int|mixed|string|\string[]
135
	 */
136 72
	private function crud_argument_preparation ($type, $format, $argument) {
137 72
		switch ($type) {
138 72
			case 'int':
139 72
			case 'float':
140 60
				$argument = $type == 'int' ? (int)$argument : (float)$argument;
141
				/**
142
				 * Ranges processing
143
				 */
144 60
				if ($format !== null) {
145
					/**
146
					 * After this `$format[0]` will contain minimum and `$format[1]` if exists - maximum
147
					 */
148 60
					$format   = explode('..', $format);
149 60
					$argument = max($argument, $format[0]);
150 60
					if (isset($format[1])) {
151 6
						$argument = min($argument, $format[1]);
152
					}
153
				}
154 60
				break;
155 72
			case 'text':
156 69
			case 'html':
157 66
			case 'html_iframe':
158 66
				$argument = xap(
159 66
					$argument,
160 66
					$type == 'text' ? 'text' : true,
161 66
					$type == 'html_iframe'
162
				);
163
				/**
164
				 * Truncation
165
				 */
166 66
				if ($format !== null) {
167
					/**
168
					 * After this `$format[0]` will contain length to truncation and `$format[1]` if exists - ending
169
					 */
170 6
					list($length, $ending) = explode(':', $format) + [1 => '...'];
171 6
					$argument = truncate($argument, $length, $ending, true);
172
				}
173 66
				break;
174 66
			case 'set':
175 6
				$allowed_arguments = explode(',', $format);
176 6
				if (!in_array($argument, $allowed_arguments)) {
177 6
					$argument = $allowed_arguments[0];
178
				}
179 6
				break;
180 66
			case 'json':
181 60
				$argument = _json_encode($argument);
182 60
				break;
183
		}
184 72
		return $argument;
185
	}
186
	/**
187
	 * @return bool
188
	 */
189 33
	private function is_multilingual () {
190 33
		return isset($this->data_model_ml_group) && $this->data_model_ml_group;
191
	}
192
	/**
193
	 * @return bool
194
	 */
195 72
	private function with_files_support () {
196 72
		return isset($this->data_model_files_tag_prefix) && $this->data_model_files_tag_prefix;
197
	}
198
	/**
199
	 * @param int|string     $id
200
	 * @param int[]|string[] $data_before
201
	 * @param int[]|string[] $data_after
202
	 */
203 72
	private function find_update_files_tags ($id, $data_before, $data_after) {
204 72
		if (!$this->with_files_support()) {
205 66
			return;
206
		}
207 6
		$prefix = $this->data_model_files_tag_prefix;
208 6
		$clang  = Language::instance()->clang;
209 6
		$this->update_files_tags(
210 6
			"$prefix/$id/$clang",
211 6
			$this->find_urls($data_before ?: []),
212 6
			$this->find_urls($data_after ?: [])
213
		);
214 6
	}
215
	/**
216
	 * Find URLs (any actually) in attributes values (wrapped with `"`, other quotes are not supported) or if field itself is URL
217
	 *
218
	 * @param array $data
219
	 *
220
	 * @return string[]
221
	 */
222 6
	protected function find_urls ($data) {
223
		/**
224
		 * At first we search URLs among attributes values, then whether some field looks like URL itself, and lastly do recursive scan
225
		 */
226 6
		return array_merge(
227 6
			preg_match_all('/"((http[s]?:)?\/\/.+)"/Uims', $this->recursive_implode(' ', $data), $files)
228 6
				? array_unique($files[1])
229 6
				: [],
230 6
			array_filter(
231 6
				$data,
232 6
				function ($data) {
233 6
					return !is_array($data) && preg_match('/^(http[s]?:)?\/\/.+$/Uims', $data);
234 6
				}
235
			),
236 6
			$data ? array_merge(
237 6
				... array_map(
0 ignored issues
show
Bug introduced by
array_map(function(...) ...}, array_values($data)) is expanded, but the parameter $array1 of array_merge() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

237
				/** @scrutinizer ignore-type */ ... array_map(
Loading history...
238 6
						function ($data) {
239 6
							return is_array($data) ? $this->find_urls($data) : [];
240 6
						},
241 6
						array_values($data)
242
					)
243 6
			) : []
244
		);
245
	}
246
	/**
247
	 * @param string $glue
248
	 * @param array  $pieces
249
	 *
250
	 * @return string
251
	 */
252 6
	protected function recursive_implode ($glue, $pieces) {
253 6
		foreach ($pieces as &$p) {
254 6
			if (is_array($p)) {
255 6
				$p = $this->recursive_implode($glue, $p);
256
			}
257
		}
258 6
		return implode($glue, $pieces);
259
	}
260
	/**
261
	 * @param string   $tag
262
	 * @param string[] $old_files
263
	 * @param string[] $new_files
264
	 */
265 6
	protected function update_files_tags ($tag, $old_files, $new_files) {
266 6
		if ($old_files || $new_files) {
267 6
			foreach (array_diff($old_files, $new_files) as $file) {
268 6
				Event::instance()->fire(
269 6
					'System/upload_files/del_tag',
270
					[
271 6
						'tag' => $tag,
272 6
						'url' => $file
273
					]
274
				);
275
			}
276 6
			foreach (array_diff($new_files, $old_files) as $file) {
277 3
				Event::instance()->fire(
278 3
					'System/upload_files/add_tag',
279
					[
280 3
						'tag' => $tag,
281 3
						'url' => $file
282
					]
283
				);
284
			}
285
		}
286 6
	}
287
	/**
288
	 * @param int|string $id
289
	 */
290 30
	private function delete_files_tags ($id) {
291 30
		if (!$this->with_files_support()) {
292 27
			return;
293
		}
294 3
		Event::instance()->fire(
295 3
			'System/upload_files/del_tag',
296
			[
297 3
				'tag' => "$this->data_model_files_tag_prefix/$id%"
298
			]
299
		);
300 3
	}
301
}
302