Traits::deleteListItems()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 11
rs 10
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2018-2025
6
 * @package MShop
7
 * @subpackage Common
8
 */
9
10
11
namespace Aimeos\MShop\Common\Item\ListsRef;
12
13
14
/**
15
 * Common trait for items containing list items
16
 *
17
 * @package MShop
18
 * @subpackage Common
19
 */
20
trait Traits
21
{
22
	private array $listItems = [];
23
	private array $listRmItems = [];
24
	private array $listMap = [];
25
	private int $listMax = 0;
26
27
28
	/**
29
	 * Creates a deep clone of all objects
30
	 */
31
	public function __clone()
32
	{
33
		parent::__clone();
34
35
		foreach( $this->listItems as $domain => $list )
36
		{
37
			foreach( $list as $id => $item ) {
38
				$this->listItems[$domain][$id] = clone $item;
39
			}
40
		}
41
42
		foreach( $this->listRmItems as $key => $item ) {
43
			$this->listRmItems[$key] = clone $item;
44
		}
45
	}
46
47
48
	/**
49
	 * Returns the unique ID of the item.
50
	 *
51
	 * @return string|null ID of the item
52
	 */
53
	abstract public function getId() : ?string;
54
55
56
	/**
57
	 * Returns the item type
58
	 *
59
	 * @return string Item type, subtypes are separated by slashes
60
	 */
61
	abstract public function getResourceType() : string;
62
63
64
	/**
65
	 * Registers a custom macro that has access to the class properties if called non-static
66
	 *
67
	 * @param string $name Macro name
68
	 * @param \Closure|null $function Anonymous function
69
	 * @return \Closure|null Registered function
70
	 */
71
	abstract public static function macro( string $name, ?\Closure $function = null ) : ?\Closure;
72
73
74
	/**
75
	 * Sets the modified flag of the object.
76
	 *
77
	 * @return \Aimeos\MShop\Common\Item\Iface Item for chaining method calls
78
	 */
79
	abstract public function setModified() : \Aimeos\MShop\Common\Item\Iface;
80
81
82
	/**
83
	 * Adds a new or overwrite an existing list item which references the given domain item (created if it doesn't exist)
84
	 *
85
	 * @param string $domain Name of the domain (e.g. media, text, etc.)
86
	 * @param \Aimeos\MShop\Common\Item\Lists\Iface $listItem List item referencing the new domain item
87
	 * @param \Aimeos\MShop\Common\Item\Iface|null $refItem New item added to the given domain or null if no item should be referenced
88
	 * @return \Aimeos\MShop\Common\Item\ListsRef\Iface Self object for method chaining
89
	 */
90
	public function addListItem( string $domain, \Aimeos\MShop\Common\Item\Lists\Iface $listItem, ?\Aimeos\MShop\Common\Item\Iface $refItem = null ) : \Aimeos\MShop\Common\Item\ListsRef\Iface
91
	{
92
		$id = '_' . $this->listMax++;
93
94
		if( $refItem !== null )
95
		{
96
			if( $refItem instanceof \Aimeos\MShop\Common\Item\Domain\Iface && !$refItem->getDomain() ) {
97
				$refItem->setDomain( $this->getResourceType() );
98
			}
99
100
			$listItem->setRefItem( $refItem )->setRefId( $refItem->getId() ?: $id );
101
		}
102
103
		$id = $listItem->getId() ?: $id;
104
105
		unset( $this->listItems[$domain][$id] ); // append at the end
106
		$this->listItems[$domain][$id] = $listItem->setDomain( $domain );
107
108
		if( isset( $this->listMap[$domain] ) )
109
		{
110
			unset( $this->listMap[$domain][$listItem->getType()][$listItem->getRefId()] ); // append at the end
111
			$this->listMap[$domain][$listItem->getType()][$listItem->getRefId()] = $listItem;
112
		}
113
114
		return $this;
115
	}
116
117
118
	/**
119
	 * Removes a list item which references the given domain item (removed as well if it exists)
120
	 *
121
	 * @param string $domain Name of the domain (e.g. media, text, etc.)
122
	 * @param \Aimeos\MShop\Common\Item\Lists\Iface $listItem List item referencing the domain item
123
	 * @param \Aimeos\MShop\Common\Item\Iface|null $refItem Existing item removed from the given domain or null if item shouldn't be removed
124
	 * @return \Aimeos\MShop\Common\Item\ListsRef\Iface Self object for method chaining
125
	 */
126
	public function deleteListItem( string $domain, \Aimeos\MShop\Common\Item\Lists\Iface $listItem, ?\Aimeos\MShop\Common\Item\Iface $refItem = null ) : \Aimeos\MShop\Common\Item\ListsRef\Iface
127
	{
128
		if( ( $key = array_search( $listItem, $this->listItems[$domain] ?? [], true ) ) !== false ) {
129
			$this->listRmItems[] = $listItem->setRefItem( $refItem );
130
131
			unset( $this->listMap[$domain][$listItem->getType()][$listItem->getRefId()] );
132
			unset( $this->listItems[$domain][$key] );
133
		}
134
135
		return $this;
136
	}
137
138
139
	/**
140
	 * Removes a list of list items which references their domain items (removed as well if it exists)
141
	 *
142
	 * @param \Aimeos\MShop\Common\Item\Lists\Iface[] $items Existing list items
143
	 * @param bool $all True to delete referenced items as well, false for list items only
144
	 * @return \Aimeos\MShop\Common\Item\ListsRef\Iface Self object for method chaining
145
	 * @throws \Aimeos\MShop\Exception If an item isn't a list item or isn't found
146
	 */
147
	public function deleteListItems( iterable $items, bool $all = false ) : \Aimeos\MShop\Common\Item\ListsRef\Iface
148
	{
149
		map( $items )->implements( \Aimeos\MShop\Common\Item\Lists\Iface::class, true );
150
151
		foreach( $items as $item )
152
		{
153
			$refItem = $all ? $item->getRefItem() : null;
154
			$this->deleteListItem( $item->getDomain(), $item, $refItem );
155
		}
156
157
		return $this;
158
	}
159
160
161
	/**
162
	 * Returns the domains for which items are available
163
	 *
164
	 * @return string[] List of domain names
165
	 */
166
	public function getDomains() : array
167
	{
168
		return array_keys( $this->listItems );
169
	}
170
171
172
	/**
173
	 * Returns the deleted list items which include the domain items if available
174
	 *
175
	 * @param string|null $domain Domain name to get the deleted list items for
176
	 * @return \Aimeos\Map Associative list of domains as keys list items as values or list items only
177
	 */
178
	public function getListItemsDeleted( ?string $domain = null ) : \Aimeos\Map
179
	{
180
		if( $domain ) {
181
			return map( $this->listRmItems )->filter( fn( $item ) => $item->getDomain() === $domain );
182
		}
183
184
		return map( $this->listRmItems );
185
	}
186
187
188
	/**
189
	 * Returns the list item for the given reference ID, domain and list type
190
	 *
191
	 * @param string $domain Name of the domain (e.g. product, text, etc.)
192
	 * @param string $listtype Name of the list item type
193
	 * @param string $refId Unique ID of the referenced item
194
	 * @param bool $active True to return only active items, false to return all
195
	 * @return \Aimeos\MShop\Common\Item\Lists\Iface|null Matching list item or null if none
196
	 */
197
	public function getListItem( string $domain, string $listtype, string $refId, bool $active = true ) : ?\Aimeos\MShop\Common\Item\Lists\Iface
198
	{
199
		if( !isset( $this->listMap[$domain] ) && isset( $this->listItems[$domain] ) )
200
		{
201
			$map = [];
202
203
			foreach( $this->listItems[$domain] as $listItem ) {
204
				$map[$listItem->getType()][$listItem->getRefId()] = $listItem;
205
			}
206
207
			$this->listMap[$domain] = $map;
208
		}
209
210
		if( isset( $this->listMap[$domain][$listtype][$refId] ) )
211
		{
212
			$listItem = $this->listMap[$domain][$listtype][$refId];
213
214
			if( $active && !$listItem->isAvailable() ) {
215
				return null;
216
			}
217
218
			return $listItem;
219
		}
220
221
		return null;
222
	}
223
224
225
	/**
226
	 * Returns the list items attached, optionally filtered by domain and list type.
227
	 *
228
	 * The reference parameter in search() must have been set accordingly
229
	 * to the requested domain to get the items. Otherwise, no items will be
230
	 * returned by this method.
231
	 *
232
	 * @param array|string|null $domain Name/Names of the domain (e.g. product, text, etc.) or null for all
233
	 * @param array|string|null $listtype Name/Names of the list item type or null for all
234
	 * @param array|string|null $type Name/Names of the item type or null for all
235
	 * @param bool $active True to return only active items, false to return all
236
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Common\Item\Lists\Iface
237
	 */
238
	public function getListItems( $domain = null, $listtype = null, $type = null, bool $active = true ) : \Aimeos\Map
239
	{
240
		$result = [];
241
		$fcn = static::macro( 'listFilter' );
242
243
		$iface = \Aimeos\MShop\Common\Item\TypeRef\Iface::class;
244
		$listTypes = is_array( $listtype ) ? $listtype : [$listtype];
245
		$domains = is_array( $domain ) ? $domain : [$domain];
246
		$types = is_array( $type ) ? $type : [$type];
247
248
		foreach( $this->listItems as $dname => $list )
249
		{
250
			if( $domain && !in_array( $dname, $domains ) ) {
251
				continue;
252
			}
253
254
			$set = [];
255
256
			foreach( $list as $id => $item )
257
			{
258
				$refItem = $item->getRefItem();
259
260
				if( $type && !( $refItem && $refItem instanceof $iface && in_array( $refItem->getType(), $types ) ) ) {
261
					continue;
262
				}
263
264
				if( $listtype && !in_array( $item->getType(), $listTypes ) ) {
265
					continue;
266
				}
267
268
				if( $active && !$item->isAvailable() ) {
269
					continue;
270
				}
271
272
				$set[$id] = $item;
273
			}
274
275
			$result = array_replace( $result, $fcn ? $fcn( $set ) : $set );
276
		}
277
278
		return map( $result );
279
	}
280
281
282
	/**
283
	 * Returns the product, text, etc. items filtered by domain and optionally by type and list type.
284
	 *
285
	 * The reference parameter in search() must have been set accordingly
286
	 * to the requested domain to get the items. Otherwise, no items will be
287
	 * returned by this method.
288
	 *
289
	 * @param array|string|null $domain Name/Names of the domain (e.g. product, text, etc.) or null for all
290
	 * @param array|string|null $type Name/Names of the item type or null for all
291
	 * @param array|string|null $listtype Name/Names of the list item type or null for all
292
	 * @param bool $active True to return only active items, false to return all
293
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Common\Item\Iface
294
	 */
295
	public function getRefItems( $domain = null, $type = null, $listtype = null, bool $active = true ) : \Aimeos\Map
296
	{
297
		$list = [];
298
299
		foreach( $this->getListItems( $domain, $listtype, $type, $active ) as $listItem )
300
		{
301
			if( ( $refItem = $listItem->getRefItem() ) && ( !$active || $refItem->isAvailable() ) ) {
302
				$list[$listItem->getDomain()][$listItem->getRefId()] = $refItem;
303
			}
304
		}
305
306
		if( is_array( $domain ) ) {
307
			return map( $list )->only( $domain );
308
		}
309
310
		if( $domain ) {
311
			return map( $list[$domain] ?? [] );
312
		}
313
314
		return map( $list );
315
	}
316
317
318
	/**
319
	 * Returns the label of the item.
320
	 * This method should be implemented in the derived class if a label column is available.
321
	 *
322
	 * @return string Label of the item
323
	 */
324
	public function getLabel() : string
325
	{
326
		return '';
327
	}
328
329
330
	/**
331
	 * Returns the localized text type of the item or the internal label if no name is available.
332
	 *
333
	 * @param string $type Text type to be returned
334
	 * @param string|null $langId Two letter ISO Language code of the text
335
	 * @return string Specified text type or label of the item
336
	 */
337
	public function getName( string $type = 'name', ?string $langId = null ) : string
338
	{
339
		foreach( $this->getRefItems( 'text', $type ) as $textItem )
340
		{
341
			if( $textItem->getLanguageId() === $langId || $langId === null ) {
342
				return $textItem->getContent();
343
			}
344
		}
345
346
		return $this->getLabel();
347
	}
348
349
350
	/**
351
	 * Initializes the list items in the trait
352
	 *
353
	 * @param array $listItems Two dimensional associative list of domain / ID / list items that implement \Aimeos\MShop\Common\Item\Lists\Iface
354
	 */
355
	protected function initListItems( array $listItems )
356
	{
357
		$this->listMax = count( $listItems );
358
359
		foreach( $listItems as $id => $listItem ) {
360
			$this->listItems[$listItem->getDomain()][$id] = $listItem;
361
		}
362
	}
363
}
364