Completed
Push — master ( df8ec4...96358d )
by Nazar
04:25
created

Data_model_processing::is_multilingual()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 2
eloc 2
nc 2
nop 0
1
<?php
2
/**
3
 * @package   CleverStyle CMS
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2013-2015, 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
	private function fix_arguments_order ($data_model, $arguments) {
32
		if (is_array_indexed($arguments)) {
33
			return $arguments;
34
		}
35
		$arguments_ = [];
36
		foreach (array_keys($data_model) as $argument) {
37
			if (isset($arguments[$argument])) {
38
				$arguments_[] = $arguments[$argument];
39
			}
40
		}
41
		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
	private function crud_arguments_preparation ($data_model, $arguments, $id = false, &$update_needed = false) {
53
		$arguments     = array_combine(array_keys($data_model), $arguments);
54
		$joined_tables = [];
55
		foreach ($arguments as $item => &$argument) {
56
			$model = $data_model[$item];
57
			if (is_callable($model)) {
58
				$argument = $model($argument);
59
				continue;
60
			}
61
			if (isset($model['data_model'])) {
62
				$joined_tables[$item] = $this->prepare_joined_tables_model($model, $argument);
63
				unset($arguments[$item]);
64
				continue;
65
			}
66
			$model              = explode(':', $model, 2);
67
			$type               = $model[0];
68
			$multilingual_field = false;
69
			/**
70
			 * If field is multilingual
71
			 */
72
			if ($type == 'ml') {
73
				$multilingual_field = true;
74
				$model              = explode(':', $model[1], 2);
75
				$type               = $model[0];
76
			}
77
			$argument = self::crud_argument_preparation(
78
				$type,
79
				isset($model[1]) ? $model[1] : null,
80
				$argument
81
			);
82
			/**
83
			 * If field is multilingual - handle multilingual storing of value automatically
84
			 */
85
			if ($multilingual_field && $this->is_multilingual()) {
86
				if ($id !== false) {
87
					$argument = Text::instance()->set($this->cdb(), "$this->data_model_ml_group/$item", $id, $argument);
88
				} else {
89
					$update_needed = true;
90
				}
91
			}
92
		}
93
		return [
94
			$arguments,
95
			$joined_tables
96
		];
97
	}
98
	/**
99
	 * @param array $structure
100
	 * @param array $arguments
101
	 *
102
	 * @return array
103
	 */
104
	private function prepare_joined_tables_model ($structure, $arguments) {
105
		if (!$arguments) {
106
			return [];
107
		}
108
		$new_structure = [
109
			'id_field' => array_keys($structure['data_model'])[0],
110
			'fields'   => array_slice($structure['data_model'], 1),
111
			'data'     => []
112
		];
113
		if (isset($structure['language_field'])) {
114
			$new_structure['language_field'] = $structure['language_field'];
115
		}
116
		$arguments       = is_array_assoc($arguments) ? [$arguments] : _array((array)$arguments);
117
		$arguments_assoc = is_array_assoc($arguments[0]);
118
		foreach ($new_structure['fields'] as $field_name => $field_model) {
119
			$field_model = explode(':', $field_model, 2);
120
			$type        = $field_model[0];
121
			$format      = isset($field_model[1]) ? $field_model[1] : null;
122
			/**
123
			 * Both associative and indexed arrays are supported - that is why we determine key for array
124
			 */
125
			$key = $arguments_assoc ? $field_name : array_search($field_name, array_keys($new_structure['fields']));
126
			foreach ($arguments as $index => $arguments_local) {
127
				$new_structure['data'][$index][$field_name] = $this->crud_argument_preparation($type, $format, $arguments_local[$key]);
128
			}
129
		}
130
		return $new_structure;
131
	}
132
	/**
133
	 * @param string $type
134
	 * @param mixed  $format
135
	 * @param mixed  $argument
136
	 *
137
	 * @return float|int|mixed|string|\string[]
138
	 */
139
	private function crud_argument_preparation ($type, $format, $argument) {
140
		switch ($type) {
141
			case 'int':
142
			case 'float':
143
				$argument = $type == 'int' ? (int)$argument : (float)$argument;
144
				/**
145
				 * Ranges processing
146
				 */
147
				if ($format !== null) {
148
					/**
149
					 * After this `$format[0]` will contain minimum and `$format[1]` if exists - maximum
150
					 */
151
					$format   = explode('..', $format);
152
					$argument = max($argument, $format[0]);
153
					if (isset($format[1])) {
154
						$argument = min($argument, $format[1]);
155
					}
156
				}
157
				break;
158
			case 'text':
159
			case 'html':
160
			case 'html_iframe':
161
				$argument = xap(
162
					$argument,
163
					$type == 'text' ? 'text' : true,
164
					$type == 'html_iframe'
165
				);
166
				/**
167
				 * Truncation
168
				 */
169
				if ($format !== null) {
170
					/**
171
					 * After this `$format[0]` will contain length to truncation and `$format[1]` if exists - ending
172
					 */
173
					$format   = explode(':', $format);
174
					$argument = truncate($argument, $format[0], isset($format[1]) ? $format[1] : '...', true);
175
				}
176
				break;
177
			case 'set':
178
				$allowed_arguments = explode(',', $format);
179
				if (!in_array($argument, $allowed_arguments)) {
180
					$argument = $allowed_arguments[0];
181
				}
182
				break;
183
			case 'json':
184
				$argument = _json_encode($argument);
185
				break;
186
		}
187
		return $argument;
188
	}
189
	/**
190
	 * @return bool
191
	 */
192
	private function is_multilingual () {
193
		return isset($this->data_model_ml_group) && $this->data_model_ml_group;
194
	}
195
	/**
196
	 * @return bool
197
	 */
198
	private function with_files_support () {
199
		return isset($this->data_model_files_tag_prefix) && $this->data_model_files_tag_prefix;
200
	}
201
	/**
202
	 * @param int|string     $id
203
	 * @param int[]|string[] $data_before
204
	 * @param int[]|string[] $data_after
205
	 */
206
	private function find_update_files_tags ($id, $data_before, $data_after) {
207
		if (!$this->with_files_support()) {
208
			return;
209
		}
210
		$prefix = $this->data_model_files_tag_prefix;
211
		$clang  = Language::instance()->clang;
212
		$this->update_files_tags(
213
			"$prefix/$id/$clang",
214
			$this->find_urls($data_before ?: []),
215
			$this->find_urls($data_after ?: [])
216
		);
217
	}
218
	/**
219
	 * Find URLs (any actually) in attributes values (wrapped with `"`, other quotes are not supported) or if field itself is URL
220
	 *
221
	 * @param array $data
222
	 *
223
	 * @return string[]
224
	 */
225
	protected function find_urls ($data) {
226
		/**
227
		 * At first we search URLs among attributes values, then whether some field looks like URL itself, and lastly do recursive scan
228
		 */
229
		return array_merge(
230
			preg_match_all('/"((http[s]?:)?\/\/.+)"/Uims', $this->recursive_implode(' ', $data), $files)
231
				? array_unique($files[1])
232
				: [],
233
			array_filter(
234
				$data,
235
				function ($data) {
236
					return !is_array($data) && preg_match('/^(http[s]?:)?\/\/.+$/Uims', $data);
237
				}
238
			),
239
			$data ? call_user_func_array(
240
				'array_merge',
241
				array_map(
242
					function ($data) {
243
						return is_array($data) ? $this->find_urls($data) : [];
244
					},
245
					array_values($data)
246
				)
247
			) : []
248
		);
249
	}
250
	/**
251
	 * @param string $glue
252
	 * @param array  $pieces
253
	 *
254
	 * @return string
255
	 */
256
	protected function recursive_implode ($glue, $pieces) {
257
		foreach ($pieces as &$p) {
258
			if (is_array($p)) {
259
				$p = $this->recursive_implode($glue, $p);
260
			}
261
		}
262
		return implode($glue, $pieces);
263
	}
264
	/**
265
	 * @param string   $tag
266
	 * @param string[] $old_files
267
	 * @param string[] $new_files
268
	 */
269
	protected function update_files_tags ($tag, $old_files, $new_files) {
270
		if ($old_files || $new_files) {
271
			foreach (array_diff($old_files, $new_files) as $file) {
272
				Event::instance()->fire(
273
					'System/upload_files/del_tag',
274
					[
275
						'tag' => $tag,
276
						'url' => $file
277
					]
278
				);
279
			}
280
			foreach (array_diff($new_files, $old_files) as $file) {
281
				Event::instance()->fire(
282
					'System/upload_files/add_tag',
283
					[
284
						'tag' => $tag,
285
						'url' => $file
286
					]
287
				);
288
			}
289
		}
290
	}
291
	/**
292
	 * @param int|string $id
293
	 */
294
	private function delete_files_tags ($id) {
295
		if (!$this->with_files_support()) {
296
			return;
297
		}
298
		Event::instance()->fire(
299
			'System/upload_files/del_tag',
300
			[
301
				'tag' => "$this->data_model_files_tag_prefix/$id%"
302
			]
303
		);
304
	}
305
}
306