Passed
Push — master ( a28c56...4904ea )
by Aimeos
05:05
created

Base::apply()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 30
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
dl 0
loc 30
rs 8.8333
cc 7
nc 12
nop 2
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2021-2024
6
 * @package MShop
7
 * @subpackage Rule
8
 */
9
10
11
namespace Aimeos\MShop\Rule\Manager;
12
13
14
/**
15
 * Abstract class for rule managers.
16
 *
17
 * @package MShop
18
 * @subpackage Service
19
 */
20
abstract class Base
21
	extends \Aimeos\MShop\Common\Manager\Base
22
{
23
	private array $rules = [];
24
25
26
	/**
27
	 * Applies the rules for modifying items dynamically
28
	 *
29
	 * @param \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface $items Item or list of items
30
	 * @param string $type Type of rules to apply to the items (e.g. "basket" or "catalog")
31
	 * @return \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface Modified item or list of items
32
	 */
33
	public function apply( $items, string $type = 'catalog' )
34
	{
35
		if( !isset( $this->rules[$type] ) )
36
		{
37
			$this->rules[$type] = [];
38
			$manager = $this->object();
39
40
			$filter = $manager->filter( true )->add( ['rule.type' => $type] )
41
				->order( 'rule.position' )->slice( 0, 10000 );
42
43
			foreach( $manager->search( $filter ) as $id => $ruleItem ) {
44
				$this->rules[$type][$id] = $manager->getProvider( $ruleItem, $type );
45
			}
46
		}
47
48
		foreach( map( $items ) as $item )
49
		{
50
			foreach( $this->rules[$type] as $rule )
51
			{
52
				// Selection products are handled by rule providers
53
				$articleIds = $item->getType() === 'select' ? $item->getRefItems( 'product', null, 'default' )->keys() : [];
54
				$this->apply( $item->getRefItems( 'product' )->except( $articleIds ), $type );
55
56
				if( $rule->apply( $item ) ) {
57
					break;
58
				}
59
			}
60
		}
61
62
		return $items;
63
	}
64
65
66
	/**
67
	 * Returns the rule provider which is responsible for the rule item.
68
	 *
69
	 * @param \Aimeos\MShop\Rule\Item\Iface $item Rule item object
70
	 * @param string $type Rule type code
71
	 * @return \Aimeos\MShop\Rule\Provider\Iface Returns the decoratad rule provider object
72
	 * @throws \LogicException If provider couldn't be found
73
	 */
74
	public function getProvider( \Aimeos\MShop\Rule\Item\Iface $item, string $type ) : \Aimeos\MShop\Rule\Provider\Iface
75
	{
76
		$type = ucwords( $type );
77
		$context = $this->context();
78
		$names = explode( ',', $item->getProvider() );
79
80
		if( ctype_alnum( $type ) === false ) {
81
			throw new \LogicException( sprintf( 'Invalid characters in type name "%1$s"', $type ), 400 );
82
		}
83
84
		if( ( $provider = array_shift( $names ) ) === null ) {
85
			throw new \LogicException( sprintf( 'Provider in "%1$s" not available', $item->getProvider() ), 400 );
86
		}
87
88
		if( ctype_alnum( $provider ) === false ) {
89
			throw new \LogicException( sprintf( 'Invalid characters in provider name "%1$s"', $provider ), 400 );
90
		}
91
92
		$classname = '\Aimeos\MShop\Rule\Provider\\' . $type . '\\' . $provider;
93
		$interface = \Aimeos\MShop\Rule\Provider\Factory\Iface::class;
94
95
		$provider = \Aimeos\Utils::create( $classname, [$context, $item], $interface );
96
		$provider = $this->addRuleDecorators( $item, $provider, $names, $type );
97
98
		return $provider->setObject( $provider );
99
	}
100
101
102
	/**
103
	 *
104
	 * @param \Aimeos\MShop\Rule\Item\Iface $ruleItem Rule item object
105
	 * @param \Aimeos\MShop\Rule\Provider\Iface $provider Rule provider object
106
	 * @param array $names List of decorator names that should be wrapped around the rule provider object
107
	 * @param string $type Rule type code
108
	 * @return \Aimeos\MShop\Rule\Provider\Iface Rule provider object
109
	 */
110
	protected function addRuleDecorators( \Aimeos\MShop\Rule\Item\Iface $ruleItem,
111
		\Aimeos\MShop\Rule\Provider\Iface $provider, array $names, string $type ) : \Aimeos\MShop\Rule\Provider\Iface
112
	{
113
		$context = $this->context();
114
		$classprefix = '\Aimeos\MShop\Rule\Provider\\' . $type . '\Decorator\\';
115
116
		foreach( $names as $name )
117
		{
118
			if( ctype_alnum( $name ) === false )
119
			{
120
				$msg = $this->context()->translate( 'mshop', 'Invalid characters in class name "%1$s"' );
121
				throw new \Aimeos\MShop\Rule\Exception( sprintf( $msg, $name ), 400 );
122
			}
123
124
			$classname = $classprefix . $name;
125
			$interface = $classprefix . 'Iface';
126
127
			$provider = \Aimeos\Utils::create( $classname, [$context, $ruleItem, $provider], $interface );
128
		}
129
130
		return $provider;
131
	}
132
}
133