Traits::saveListItems()   F
last analyzed

Complexity

Conditions 11
Paths 648

Size

Total Lines 70
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 31
c 1
b 0
f 0
nc 648
nop 3
dl 0
loc 70
rs 3.6388

How to fix   Long Method    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
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2024
6
 * @package MShop
7
 * @subpackage Common
8
 */
9
10
11
namespace Aimeos\MShop\Common\Manager\ListsRef;
12
13
14
/**
15
 * Trait for managers working with referenced list items
16
 *
17
 * @package MShop
18
 * @subpackage Common
19
 */
20
trait Traits
21
{
22
	/**
23
	 * Creates a new lists item object
24
	 *
25
	 * @param array $values Values the item should be initialized with
26
	 * @return \Aimeos\MShop\Common\Item\Lists\Iface New list items object
27
	 */
28
	public function createListItem( array $values = [] ) : \Aimeos\MShop\Common\Item\Lists\Iface
29
	{
30
		return $this->object()->getSubManager( 'lists' )->create( $values );
31
	}
32
33
34
	/**
35
	 * Creates a new item for the specific manager.
36
	 *
37
	 * @param array $values Associative list of key/value pairs
38
	 * @param \Aimeos\MShop\Common\Item\Lists\Iface[] $listItems List of list items
39
	 * @param \Aimeos\MShop\Common\Item\Iface[] $refItems List of referenced items
40
	 * @return \Aimeos\MShop\Common\Item\Iface New item
41
	 */
42
	abstract protected function createItemBase( array $values = [], array $listItems = [],
43
		array $refItems = [] ) : \Aimeos\MShop\Common\Item\Iface;
44
45
46
	/**
47
	 * Applies the filters for the item type to the item
48
	 *
49
	 * @param object $item Item to apply the filter to
50
	 * @return object|null Object if the item should be used, null if not
51
	 */
52
	abstract protected function applyFilter( $item );
53
54
55
	/**
56
	 * Returns the context object.
57
	 *
58
	 * @return \Aimeos\MShop\ContextIface Context object
59
	 */
60
	abstract protected function context() : \Aimeos\MShop\ContextIface;
61
62
63
	/**
64
	 * Creates a new extension manager in the domain.
65
	 *
66
	 * @param string $domain Name of the domain (product, text, media, etc.)
67
	 * @param string|null $name Name of the implementation, will be from configuration (or Standard) if null
68
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager extending the domain functionality
69
	 */
70
	abstract public function getSubManager( string $domain, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface;
71
72
73
	/**
74
	 * Creates the items with address item, list items and referenced items.
75
	 *
76
	 * @param array $map Associative list of IDs as keys and the associative array of values
77
	 * @param string[] $domains List of domains to fetch list items and referenced items for
78
	 * @param string $prefix Domain prefix
79
	 * @param array $local Associative list of IDs as keys and the associative array of items as values
80
	 * @param array $local2 Associative list of IDs as keys and the associative array of items as values
81
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Common\Item\Iface with ids as keys
82
	 */
83
	protected function buildItems( array $map, array $domains, string $prefix, array $local = [], array $local2 = [] ) : \Aimeos\Map
84
	{
85
		$items = $listItemMap = $refItemMap = [];
86
87
		foreach( $this->getListItems( array_keys( $map ), $domains, $prefix ) as $id => $listItem )
88
		{
89
			$domain = $listItem->getDomain();
90
			$parentid = $listItem->getParentId();
91
92
			$listItemMap[$parentid][$domain][$id] = $listItem;
93
94
			if( $refItem = $listItem->getRefItem() ) {
95
				$refItemMap[$parentid][$domain][$listItem->getRefId()] = $refItem;
96
			}
97
		}
98
99
		foreach( $map as $id => $values )
100
		{
101
			$localItems = $local[$id] ?? [];
102
			$localItems2 = $local2[$id] ?? [];
103
			$refItems = $refItemMap[$id] ?? [];
104
			$listItems = $listItemMap[$id] ?? [];
105
106
			if( $item = $this->applyFilter( $this->createItemBase( $values, $listItems, $refItems, $localItems, $localItems2 ) ) ) {
0 ignored issues
show
Unused Code introduced by
The call to Aimeos\MShop\Common\Mana...raits::createItemBase() has too many arguments starting with $localItems. ( Ignorable by Annotation )

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

106
			if( $item = $this->applyFilter( $this->/** @scrutinizer ignore-call */ createItemBase( $values, $listItems, $refItems, $localItems, $localItems2 ) ) ) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
107
				$items[$id] = $item;
108
			}
109
		}
110
111
		return map( $items );
112
	}
113
114
115
	/**
116
	 * Removes the items referenced by the given list items.
117
	 *
118
	 * @param \Aimeos\MShop\Common\Item\ListsRef\Iface[]|\Aimeos\Map|array $items List of items with deleted list items
119
	 * @return \Aimeos\MShop\Common\Manager\ListsRef\Iface Manager object for method chaining
120
	 */
121
	protected function deleteRefItems( $items ) : \Aimeos\MShop\Common\Manager\ListsRef\Iface
122
	{
123
		if( ( $items = map( $items ) )->isEmpty() ) {
124
			return $this;
125
		}
126
127
		$map = [];
128
129
		foreach( $items as $item )
130
		{
131
			if( $item instanceof \Aimeos\MShop\Common\Item\ListsRef\Iface )
132
			{
133
				foreach( $item->getListItemsDeleted() as $listItem )
134
				{
135
					if( $listItem->getRefItem() ) {
136
						$map[$listItem->getDomain()][] = $listItem->getRefId();
137
					}
138
				}
139
			}
140
		}
141
142
		foreach( $map as $domain => $ids ) {
143
			\Aimeos\MShop::create( $this->context(), $domain )->begin()->delete( $ids )->commit();
144
		}
145
146
		return $this;
147
	}
148
149
150
	/**
151
	 * Returns the list items that belong to the given IDs.
152
	 *
153
	 * @param string[] $ids List of IDs
154
	 * @param string[] $domains List of domain names whose referenced items should be attached
155
	 * @param string $prefix Domain prefix
156
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Common\Item\Lists\Iface with IDs as keys
157
	 */
158
	protected function getListItems( array $ids, array $domains, string $prefix ) : \Aimeos\Map
159
	{
160
		if( empty( $domains ) ) {
161
			return map();
162
		}
163
164
		$manager = $this->object()->getSubManager( 'lists' );
165
		$search = $manager->filter()->slice( 0, 0x7fffffff )->order( [
166
			$prefix . '.lists.parentid',
167
			$prefix . '.lists.domain',
168
			$prefix . '.lists.type'
169
		] );
170
171
		$list = [];
172
		$len = strlen( $prefix );
173
		$expr = [$search->compare( '==', $prefix . '.lists.parentid', $ids )];
174
175
		foreach( $domains as $key => $domain )
176
		{
177
			if( is_array( $domain ) )
178
			{
179
				$key = !strncmp( $key, $prefix . '/', $len + 1 ) ? [$key, substr( $key, $len + 1 )] : $key; // remove prefix
180
181
				$list[] = $search->and( [
182
					$search->compare( '==', $prefix . '.lists.domain', $key ),
183
					$search->compare( '==', $prefix . '.lists.type', $domain ),
184
				] );
185
			}
186
			else
187
			{
188
				$domain = !strncmp( $domain, $prefix . '/', $len + 1 ) ? [$domain, substr( $domain, $len + 1 )] : $domain; // remove prefix
189
				$list[] = $search->compare( '==', $prefix . '.lists.domain', $domain );
190
			}
191
		}
192
193
		$expr[] = $search->or( $list );
194
		$search->setConditions( $search->and( $expr ) );
195
196
		return $manager->search( $search, $domains );
197
	}
198
199
200
	/**
201
	 * Returns the outmost decorator of the decorator stack
202
	 *
203
	 * @return \Aimeos\MShop\Common\Manager\Iface Outmost decorator object
204
	 */
205
	abstract protected function object() : \Aimeos\MShop\Common\Manager\Iface;
206
207
208
	/**
209
	 * Returns the referenced items for the given IDs.
210
	 *
211
	 * @param array $refIdMap Associative list of domain/ref-ID/parent-item-ID key/value pairs
212
	 * @param string[] $domains List of domain names whose referenced items should be attached
213
	 * @return array Associative list of parent-item-ID/domain/items key/value pairs
214
	 */
215
	protected function getRefItems( array $refIdMap, array $domains ) : array
216
	{
217
		$items = [];
218
219
		foreach( $refIdMap as $domain => $list )
220
		{
221
			$manager = \Aimeos\MShop::create( $this->context(), $domain );
222
223
			if( ( $attr = current( $manager->getSearchAttributes() ) ) === false )
224
			{
225
				$msg = sprintf( 'No search configuration available for domain "%1$s"', $domain );
226
				throw new \Aimeos\MShop\Exception( $msg );
227
			}
228
229
			$search = $manager->filter()->slice( 0, count( $list ) )
230
				->add( [$attr->getCode() => array_keys( $list )] );
231
232
			foreach( $manager->search( $search, $domains ) as $id => $item )
233
			{
234
				foreach( $list[$id] as $parentId ) {
235
					$items[$parentId][$domain][$id] = $item;
236
				}
237
			}
238
		}
239
240
		return $items;
241
	}
242
243
244
	/**
245
	 * Adds new, updates existing and deletes removed list items and referenced items if available
246
	 *
247
	 * @param \Aimeos\MShop\Common\Item\ListsRef\Iface $item Item with referenced items
248
	 * @param string $domain Domain of the calling manager
249
	 * @param bool $fetch True if the new ID should be returned in the item
250
	 * @return \Aimeos\MShop\Common\Item\ListsRef\Iface $item with updated referenced items
251
	 */
252
	protected function saveListItems( \Aimeos\MShop\Common\Item\ListsRef\Iface $item, string $domain,
253
		bool $fetch = true ) : \Aimeos\MShop\Common\Item\ListsRef\Iface
254
	{
255
		$context = $this->context();
256
		$rmListItems = $rmItems = $refManager = [];
257
		$listManager = $this->object()->getSubManager( 'lists' );
258
259
260
		foreach( $item->getListItemsDeleted() as $listItem )
261
		{
262
			$rmListItems[] = $listItem;
263
264
			if( ( $refItem = $listItem->getRefItem() ) !== null ) {
265
				$rmItems[$listItem->getDomain()][] = $refItem->getId();
266
			}
267
		}
268
269
270
		try
271
		{
272
			foreach( $rmItems as $refDomain => $list )
273
			{
274
				$refManager[$refDomain] = \Aimeos\MShop::create( $context, $refDomain );
275
				$refManager[$refDomain]->begin();
276
277
				$refManager[$refDomain]->delete( $list );
278
			}
279
280
			$listManager->delete( $rmListItems );
281
282
283
			foreach( $item->getListItems( null, null, null, false ) as $listItem )
284
			{
285
				$refDomain = $listItem->getDomain();
286
287
				if( ( $refItem = $listItem->getRefItem() ) !== null )
288
				{
289
					if( !isset( $refManager[$refDomain] ) )
290
					{
291
						$refManager[$refDomain] = \Aimeos\MShop::create( $context, $refDomain );
292
						$refManager[$refDomain]->begin();
293
					}
294
295
					$refItem = $refManager[$refDomain]->save( $refItem );
296
					$listItem->setRefId( $refItem->getId() );
297
				}
298
299
				if( $listItem->getParentId() != $item->getId() ) {
300
					$listItem->setId( null ); // create new list item if copied
301
				}
302
303
				$listManager->save( $listItem->setParentId( $item->getId() ), $fetch );
304
				// @todo update list item in $item
305
			}
306
307
308
			foreach( $refManager as $manager ) {
309
				$manager->commit();
310
			}
311
		}
312
		catch( \Exception $e )
313
		{
314
			foreach( $refManager as $manager ) {
315
				$manager->rollback();
316
			}
317
318
			throw $e;
319
		}
320
321
		return $item;
322
	}
323
}
324