Completed
Push — master ( 265d0d...5b91fe )
by Nazar
07:12
created

Data_model_processing::is_multilingual()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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

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