Completed
Push — master ( 33a95e...daccbb )
by Aimeos
09:31
created

Standard::getListProducts()   C

Complexity

Conditions 11
Paths 17

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 33
rs 5.2653
cc 11
eloc 18
nc 17
nop 2

How to fix   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, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2014
6
 * @copyright Aimeos (aimeos.org), 2015
7
 * @package Controller
8
 * @subpackage Customer
9
 */
10
11
12
namespace Aimeos\Controller\Jobs\Customer\Email\Watch;
13
14
15
/**
16
 * Product notification e-mail job controller.
17
 *
18
 * @package Controller
19
 * @subpackage Customer
20
 */
21
class Standard
22
	extends \Aimeos\Controller\Jobs\Base
23
	implements \Aimeos\Controller\Jobs\Iface
24
{
25
	private $client;
26
	private $warehouses;
27
28
29
	/**
30
	 * Returns the localized name of the job.
31
	 *
32
	 * @return string Name of the job
33
	 */
34
	public function getName()
35
	{
36
		return $this->getContext()->getI18n()->dt( 'controller/jobs', 'Product notification e-mails' );
37
	}
38
39
40
	/**
41
	 * Returns the localized description of the job.
42
	 *
43
	 * @return string Description of the job
44
	 */
45
	public function getDescription()
46
	{
47
		return $this->getContext()->getI18n()->dt( 'controller/jobs', 'Sends e-mails for watched products' );
48
	}
49
50
51
	/**
52
	 * Executes the job.
53
	 *
54
	 * @throws \Aimeos\Controller\Jobs\Exception If an error occurs
55
	 */
56
	public function run()
57
	{
58
		$langIds = array();
59
		$context = $this->getContext();
60
		$typeId = $this->getListTypeItem( 'watch' )->getId();
61
62
		$localeManager = \Aimeos\MShop\Factory::createManager( $context, 'locale' );
63
		$custManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
64
65
		$localeItems = $localeManager->searchItems( $localeManager->createSearch() );
66
67
		foreach( $localeItems as $localeItem )
68
		{
69
			$langId = $localeItem->getLanguageId();
70
71
			if( isset( $langIds[$langId] ) ) {
72
				continue;
73
			}
74
75
			$langIds[$langId] = true;
76
			// fetch language specific text and media items for products
77
			$context->getLocale()->setLanguageId( $langId );
78
79
			$search = $custManager->createSearch( true );
80
			$expr = array(
81
				$search->compare( '==', 'customer.languageid', $langId ),
82
				$search->compare( '==', 'customer.lists.typeid', $typeId ),
83
				$search->compare( '==', 'customer.lists.domain', 'product' ),
84
				$search->getConditions(),
85
			);
86
			$search->setConditions( $search->combine( '&&', $expr ) );
87
			$search->setSortations( array( $search->sort( '+', 'customer.id' ) ) );
88
89
			$start = 0;
90
91
			do
92
			{
93
				$customers = $custManager->searchItems( $search );
94
95
				$this->execute( $context, $customers, $typeId );
96
97
				$count = count( $customers );
98
				$start += $count;
99
				$search->setSlice( $start );
100
			}
101
			while( $count >= $search->getSliceSize() );
102
		}
103
	}
104
105
106
	/**
107
	 * Sends product notifications for the given customers in their language
108
	 *
109
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context item object
110
	 * @param array $customers List of customer items implementing \Aimeos\MShop\Customer\Item\Iface
111
	 * @param string $listTypeId Customer list type ID
112
	 */
113
	protected function execute( \Aimeos\MShop\Context\Item\Iface $context, array $customers, $listTypeId )
114
	{
115
		$prodIds = $custIds = array();
116
		$whItem = $this->getWarehouseItem( 'default' );
117
		$listManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
118
		$listItems = $this->getListItems( $context, array_keys( $customers ), $listTypeId );
119
120
		foreach( $listItems as $id => $listItem )
121
		{
122
			$refId = $listItem->getRefId();
123
			$custIds[ $listItem->getParentId() ][$id] = $refId;
124
			$prodIds[$refId] = $refId;
125
		}
126
127
		$date = date( 'Y-m-d H:i:s' );
128
		$products = $this->getProducts( $context, $prodIds, $whItem->getId() );
129
130
		foreach( $custIds as $custId => $list )
131
		{
132
			$custListItems = $listIds = array();
133
134
			foreach( $list as $listId => $prodId )
135
			{
136
				$listItem = $listItems[$listId];
137
138
				if( $listItem->getDateEnd() < $date ) {
139
					$listIds[] = $listId;
140
				}
141
142
				$custListItems[$listId] = $listItems[$listId];
143
			}
144
145
			try
146
			{
147
				$custProducts = $this->getListProducts( $custListItems, $products );
148
149
				if( !empty( $custProducts ) )
150
				{
151
					$this->sendMail( $context, $customers[$custId]->getPaymentAddress(), $custProducts );
152
					$listIds += array_keys( $custProducts );
153
				}
154
			}
155
			catch( \Exception $e )
156
			{
157
				$str = 'Error while trying to send product notification e-mail for customer ID "%1$s": %2$s';
158
				$msg = sprintf( $str, $custId, $e->getMessage() );
159
				$context->getLogger()->log( $msg );
160
			}
161
162
			$listManager->deleteItems( $listIds );
163
		}
164
	}
165
166
167
	/**
168
	 * Returns the product notification e-mail client
169
	 *
170
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context item object
171
	 * @return \Aimeos\Client\Html\Iface Product notification e-mail client
172
	 */
173
	protected function getClient( \Aimeos\MShop\Context\Item\Iface $context )
174
	{
175
		if( !isset( $this->client ) )
176
		{
177
			$templatePaths = $this->getAimeos()->getCustomPaths( 'client/html' );
178
			$this->client = \Aimeos\Client\Html\Email\Watch\Factory::createClient( $context, $templatePaths );
179
		}
180
181
		return $this->client;
182
	}
183
184
185
	/**
186
	 * Returns the list items for the given customer IDs and list type ID
187
	 *
188
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context item object
189
	 * @param array $custIds List of customer IDs
190
	 * @param string $listTypeId Customer list type ID
191
	 * @return array List of customer list items implementing \Aimeos\MShop\Common\Item\Lists\Iface
192
	 */
193
	protected function getListItems( \Aimeos\MShop\Context\Item\Iface $context, array $custIds, $listTypeId )
194
	{
195
		$listManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
196
197
		$search = $listManager->createSearch();
198
		$expr = array(
199
			$search->compare( '==', 'customer.lists.parentid', $custIds ),
200
			$search->compare( '==', 'customer.lists.typeid', $listTypeId ),
201
			$search->compare( '==', 'customer.lists.domain', 'product' ),
202
		);
203
		$search->setConditions( $search->combine( '&&', $expr ) );
204
		$search->setSlice( 0, 0x7fffffff );
205
206
		return $listManager->searchItems( $search );
207
	}
208
209
210
	/**
211
	 * Returns a filtered list of products for which a notification should be sent
212
	 *
213
	 * @param array $listItems List of customer list items implementing \Aimeos\MShop\Common\Item\Lists\Iface
214
	 * @param array $products List of product items implementing \Aimeos\MShop\Product\Item\Iface
215
	 * @return array Multi-dimensional associative list of list IDs as key and product / price item maps as values
216
	 */
217
	protected function getListProducts( array $listItems, array $products )
218
	{
219
		$result = array();
220
		$priceManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'price' );
221
222
		foreach( $listItems as $id => $listItem )
223
		{
224
			try
225
			{
226
				$refId = $listItem->getRefId();
227
				$config = $listItem->getConfig();
228
229
				if( isset( $products[$refId] ) )
230
				{
231
					$prices = $products[$refId]->getRefItems( 'price', 'default', 'default' );
232
					$currencyId = ( isset( $config['currency'] ) ? $config['currency'] : null );
233
234
					$price = $priceManager->getLowestPrice( $prices, 1, $currencyId );
235
236
					if( isset( $config['stock'] ) && $config['stock'] == 1 ||
237
						isset( $config['price'] ) && $config['price'] == 1 &&
238
						isset( $config['pricevalue'] ) && $config['pricevalue'] > $price->getValue()
239
					) {
240
						$result[$id]['item'] = $products[$refId];
241
						$result[$id]['price'] = $price;
242
					}
243
				}
244
			}
245
			catch( \Exception $e ) { ; } // no price available
246
		}
247
248
		return $result;
249
	}
250
251
252
	/**
253
	 * Returns the products for the given IDs which are in stock in the warehouse
254
	 *
255
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context item object
256
	 * @param array $prodIds List of product IDs
257
	 * @param string $whId Unique warehouse ID
258
	 */
259
	protected function getProducts( \Aimeos\MShop\Context\Item\Iface $context, array $prodIds, $whId )
260
	{
261
		$productManager = \Aimeos\MShop\Factory::createManager( $context, 'product' );
262
		$search = $productManager->createSearch( true );
263
		$domains = array( 'text', 'price', 'media' );
264
265
		$stockExpr = array(
266
			$search->compare( '==', 'product.stock.stocklevel', null ),
267
			$search->compare( '>', 'product.stock.stocklevel', 0 ),
268
		);
269
270
		$expr = array(
271
			$search->compare( '==', 'product.id', $prodIds ),
272
			$search->getConditions(),
273
			$search->compare( '==', 'product.stock.warehouseid', $whId ),
274
			$search->combine( '||', $stockExpr ),
275
		);
276
		$search->setConditions( $search->combine( '&&', $expr ) );
277
		$search->setSlice( 0, 0x7fffffff );
278
279
		return $productManager->searchItems( $search, $domains );
280
	}
281
282
283
	/**
284
	 * Returns the customer list type item for the given type code.
285
	 *
286
	 * @param string $code Unique code of the list type item
287
	 * @return \Aimeos\MShop\Common\Item\Type\Iface List type item
288
	 * @throws \Aimeos\Controller\Jobs\Exception If the list type item wasn't found
289
	 */
290
	protected function getListTypeItem( $code )
291
	{
292
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'customer/lists/type' );
293
294
		$search = $manager->createSearch( true );
295
		$search->setConditions( $search->compare( '==', 'customer.lists.type.code', $code ) );
296
		$result = $manager->searchItems( $search );
297
298
		if( ( $item = reset( $result ) ) === false ) {
299
			throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'List type for domain "%1$s" and code "%2$s" not found', 'customer', $code ) );
300
		}
301
302
		return $item;
303
	}
304
305
306
	/**
307
	 * Returns the warehouse item for the given code.
308
	 *
309
	 * @param string $code Unique code of the warehouse item
310
	 * @return \Aimeos\MShop\Product\Item\Stock\Warehouse\Iface Warehouse item
311
	 * @throws \Aimeos\Controller\Jobs\Exception If the warehouse item wasn't found
312
	 */
313
	protected function getWarehouseItem( $code )
314
	{
315
		if( !isset( $this->warehouses ) )
316
		{
317
			$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'product/stock/warehouse' );
318
			$search = $manager->createSearch( true );
319
320
			$this->warehouses = array();
321
			foreach( $manager->searchItems( $search ) as $whItem ) {
322
				$this->warehouses[ $whItem->getCode() ] = $whItem;
323
			}
324
		}
325
326
		if( !isset( $this->warehouses[$code] ) ) {
327
			throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'No warehouse "%1$s" found', $code ) );
328
		}
329
330
		return $this->warehouses[$code];
331
	}
332
333
334
	/**
335
	 * Sends the notification e-mail for the given customer address and products
336
	 *
337
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context item object
338
	 * @param \Aimeos\MShop\Common\Item\Address\Iface $address Payment address of the customer
339
	 * @param array $products List of products a notification should be sent for
340
	 */
341
	protected function sendMail( \Aimeos\MShop\Context\Item\Iface $context,
342
		\Aimeos\MShop\Common\Item\Address\Iface $address, array $products )
343
	{
344
		$view = $context->getView();
345
		$view->extProducts = $products;
346
		$view->extAddressItem = $address;
347
348
		$helper = new \Aimeos\MW\View\Helper\Translate\Standard( $view, $context->getI18n( $address->getLanguageId() ) );
349
		$view->addHelper( 'translate', $helper );
350
351
		$mailer = $context->getMail();
352
		$message = $mailer->createMessage();
353
354
		$helper = new \Aimeos\MW\View\Helper\Mail\Standard( $view, $message );
355
		$view->addHelper( 'mail', $helper );
356
357
		$client = $this->getClient( $context );
358
		$client->setView( $view );
359
		$client->getHeader();
360
		$client->getBody();
361
362
		$mailer->send( $message );
363
	}
364
}
365