Completed
Push — master ( f874d3...34d177 )
by Aimeos
08:55
created

ProductFreeOptions::addPrices()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 10
nop 4
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017
6
 * @package MShop
7
 * @subpackage Plugin
8
 */
9
10
11
namespace Aimeos\MShop\Plugin\Provider\Order;
12
13
14
/**
15
 * Configurable free product options
16
 *
17
 * @package MShop
18
 * @subpackage Plugin
19
 */
20
class ProductFreeOptions
21
	extends \Aimeos\MShop\Plugin\Provider\Factory\Base
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces between "Base" and comma; 1 found
Loading history...
22
	implements \Aimeos\MShop\Plugin\Provider\Factory\Iface
23
{
24
	/**
25
	 * Subscribes itself to a publisher
26
	 *
27
	 * @param \Aimeos\MW\Observer\Publisher\Iface $p Object implementing publisher interface
28
	 */
29
	public function register( \Aimeos\MW\Observer\Publisher\Iface $p )
30
	{
31
		$p->addListener( $this->getObject(), 'addProduct.after' );
32
		$p->addListener( $this->getObject(), 'editProduct.after' );
33
	}
34
35
36
	/**
37
	 * Receives a notification from a publisher object
38
	 *
39
	 * @param \Aimeos\MW\Observer\Publisher\Iface $order Shop basket instance implementing publisher interface
40
	 * @param string $action Name of the action to listen for
41
	 * @param mixed $value Object or value changed in publisher
42
	 */
43
	public function update( \Aimeos\MW\Observer\Publisher\Iface $order, $action, $value = null )
44
	{
45
		if( !( $order instanceof \Aimeos\MShop\Order\Item\Base\Iface ) )
46
		{
47
			$msg = $this->getContext()->getI18n()->dt( 'mshop', 'Object is not of required type "%1$s"' );
48
			throw new \Aimeos\MShop\Plugin\Exception( sprintf( $msg, '\Aimeos\MShop\Order\Item\Base\Product\Iface' ) );
49
		}
50
51
		$attrQtys = $attrTypes = [];
52
		$context = $this->getContext();
53
54
		$prodManager = \Aimeos\MShop\Factory::createManager( $context, 'product' );
55
		$prodItem = $prodManager->getItem( $value->getProductId(), ['price'] );
56
		$prodConf = $prodItem->getConfig();
57
58
59
		foreach( $value->getAttributes( 'config' ) as $attr )
60
		{
61
			$attrQtys[$attr->getAttributeId()] = $attr->getQuantity();
62
			$attrTypes[] = $attr->getCode();
63
		}
64
65
		if( array_intersect( $attrTypes, array_keys( $prodConf ) ) === [] ) {
66
			return true;
67
		}
68
69
70
		$priceManager = \Aimeos\MShop\Factory::createManager( $context, 'price' );
71
72
		$prices = $prodItem->getRefItems( 'price', 'default', 'default' );
73
		$priceItem = $priceManager->getLowestPrice( $prices, $value->getQuantity() );
74
75
		foreach( $this->getAttributeMap( array_keys( $attrQtys ) ) as $type => $list )
76
		{
77
			if( isset( $prodConf[$type] ) )
78
			{
79
				$list = $this->sortByPrice( $list, $attrQtys );
80
				$priceItem = $this->addPrices( $priceItem, $list, $attrQtys, (int) $prodConf[$type] );
81
			}
82
			else
83
			{
84
				$priceItem = $this->addPrices( $priceItem, $list, $attrQtys, 0 );
85
			}
86
		}
87
88
		$value->setPrice( $priceItem );
89
90
		return true;
91
	}
92
93
94
	/**
95
	 * Adds the prices of the attribute items without the given amount of free items
96
	 *
97
	 * @param \Aimeos\MShop\Price\Item\Iface $price Product price item
98
	 * @param array $attrItems Associative list of attribute IDs as keys and items with prices as values
99
	 * @param array $quantities Associative list of attribute IDs as keys and their quantities as values
100
	 * @param integer Number of free items
101
	 * @param \Aimeos\MShop\Price\Item\Iface Price item with attribute prices added
102
	 */
103
	protected function addPrices( \Aimeos\MShop\Price\Item\Iface $price, array $attrItems, array $quantities, $free )
104
	{
105
		$priceManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'price' );
106
107
		foreach( $attrItems as $attrId => $attrItem )
108
		{
109
			if( ( $prices = $attrItem->getRefItems( 'price', 'default', 'default' ) ) !== [] )
110
			{
111
				$qty = ( isset( $quantities[$attrId] ) ? $quantities[$attrId] : 0 );
112
				$priceItem = $priceManager->getLowestPrice( $prices, $qty );
113
114
				$qty = ( $qty >= $free ? $qty - $free : 0 );
115
				$free = ( $free >= $qty ? $free - $qty : 0 );
116
117
				$price->addItem( $priceItem, $qty );
118
			}
119
		}
120
121
		return $price;
122
	}
123
124
125
	/**
126
	 * Returns the attribute items including the prices for the given IDs
127
	 *
128
	 * @param array $ids List of attribute IDs
129
	 * @return array Associative List of attribute type and ID as keys and \Aimeos\MShop\Attribute\Item\Iface as values
130
	 */
131
	protected function getAttributeMap( array $ids )
132
	{
133
		$attrMap = [];
134
		$attrManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'attribute' );
135
136
		$search = $attrManager->createSearch();
137
		$search->setConditions( $search->compare( '==', 'attribute.id', $ids ) );
138
		$search->setSlice( 0, 0x7fffffff );
139
140
		foreach( $attrManager->searchItems( $search, ['price'] ) as $attrId => $attrItem ) {
141
			$attrMap[$attrItem->getType()][$attrId] = $attrItem;
142
		}
143
144
		return $attrMap;
145
	}
146
147
148
	/**
149
	 * Sorts the given attribute items by their price (lowest first)
150
	 *
151
	 * @param \Aimeos\MShop\Attribute\Item\Iface[] $attrItems Associative list of attribute IDs as keys and items as values
152
	 * @param array $attrQtys Associative list of attribute IDs as keys and their quantities as values
153
	 * @return \Aimeos\MShop\Attribute\Item\Iface[] Sorted associative list of attribute IDs as keys and items as values
154
	 */
155
	protected function sortByPrice( array $attrItems, array $attrQtys )
156
	{
157
		$priceManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'price' );
158
159
		$sortFcn = function( $a, $b ) use( $priceManager, $attrQtys )
160
		{
161
			if( ( $pricesA = $a->getRefItems( 'price', 'default', 'default' ) ) === [] ) {
162
				return 1;
163
			}
164
165
			if( ( $pricesB = $b->getRefItems( 'price', 'default', 'default' ) ) === [] ) {
166
				return -1;
167
			}
168
169
			$qty = ( isset( $attrQtys[$a->getId()] ) ? $attrQtys[$a->getId()] : 0 );
170
			$p1 = $priceManager->getLowestPrice( $pricesA, $qty );
171
172
			$qty = ( isset( $attrQtys[$b->getId()] ) ? $attrQtys[$b->getId()] : 0 );
173
			$p2 = $priceManager->getLowestPrice( $pricesB, $qty );
174
175
			if( $p1->getValue() < $p2->getValue() ) {
176
				return -1;
177
			} elseif( $p1->getValue() > $p2->getValue() ) {
178
				return 1;
179
			}
180
181
			return 0;
182
		};
183
184
		uasort( $attrItems, $sortFcn );
185
186
		return $attrItems;
187
	}
188
}
189