Completed
Push — master ( 4ffab1...137f58 )
by Aimeos
07:59
created

Stock   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 15
c 1
b 0
f 0
lcom 1
cbo 5
dl 0
loc 122
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
B addProduct() 0 25 6
B editProduct() 0 19 5
B getStockLevel() 0 33 4
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2016
6
 * @package Controller
7
 * @subpackage Frontend
8
 */
9
10
11
namespace Aimeos\Controller\Frontend\Basket\Decorator;
12
13
14
/**
15
 * Stock level check for basket controller
16
 *
17
 * @package Controller
18
 * @subpackage Frontend
19
 */
20
class Stock
21
	extends \Aimeos\Controller\Frontend\Basket\Decorator\Base
2 ignored issues
show
Coding Style introduced by
The extends keyword must be on the same line as the class name
Loading history...
Coding Style introduced by
Expected 0 spaces between "Base" and comma; 1 found
Loading history...
22
	implements \Aimeos\Controller\Frontend\Basket\Iface, \Aimeos\Controller\Frontend\Common\Decorator\Iface
1 ignored issue
show
Coding Style introduced by
The implements keyword must be on the same line as the class name
Loading history...
23
{
24
	/**
25
	 * Adds a categorized product to the basket of the user stored in the session.
26
	 *
27
	 * @param string $prodid ID of the base product to add
28
	 * @param integer $quantity Amount of products that should by added
29
	 * @param array $options Possible options are: 'stock'=>true|false and 'variant'=>true|false
30
	 * 	The 'stock'=>false option allows adding products without being in stock.
31
	 * 	The 'variant'=>false option allows adding the selection product to the basket
32
	 * 	instead of the specific sub-product if the variant-building attribute IDs
33
	 * 	doesn't match a specific sub-product or if the attribute IDs are missing.
34
	 * @param array $variantIds List of variant-building attribute IDs that identify a specific product
35
	 * 	in a selection products
36
	 * @param array $configIds  List of attribute IDs that doesn't identify a specific product in a
37
	 * 	selection of products but are stored together with the product (e.g. for configurable products)
38
	 * @param array $hiddenIds List of attribute IDs that should be stored along with the product in the order
39
	 * @param array $customValues Associative list of attribute IDs and arbitrary values that should be stored
40
	 * 	along with the product in the order
41
	 * @param string $warehouse Unique code of the warehouse to deliver the products from
42
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If the product isn't available
43
	 */
44
	public function addProduct( $prodid, $quantity = 1, array $options = array(), array $variantIds = array(),
45
		array $configIds = array(), array $hiddenIds = array(), array $customValues = array(), $warehouse = 'default' )
46
	{
47
		$stocklevel = null;
48
49
		if( !isset( $options['stock'] ) || $options['stock'] != false ) {
50
			$stocklevel = $this->getStockLevel( $prodid, $warehouse );
51
		}
52
53
		$qty = ( $stocklevel !== null ? min( $stocklevel, $quantity ) : $quantity );
54
55
		if( $qty > 0 )
56
		{
57
			$this->getController()->addProduct(
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\Controller\Frontend\Common\Iface as the method addProduct() does only exist in the following implementations of said interface: Aimeos\Controller\Frontend\Basket\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
58
				$prodid, $qty, $options, $variantIds, $configIds, $hiddenIds, $customValues, $warehouse
59
			);
60
		}
61
62
		if( $qty < $quantity )
63
		{
64
			$product = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'product' )->getItem( $prodid );
65
			$msg = sprintf( 'There are not enough products "%1$s" in stock', $product->getName() );
66
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
67
		}
68
	}
69
70
71
	/**
72
	 * Edits the quantity of a product item in the basket.
73
	 *
74
	 * @param integer $position Position number (key) of the order product item
75
	 * @param integer $quantity New quantiy of the product item
76
	 * @param array $options Possible options are: 'stock'=>true|false
77
	 * 	The 'stock'=>false option allows adding products without being in stock.
78
	 * @param string[] $configCodes Codes of the product config attributes that should be REMOVED
79
	 */
80
	public function editProduct( $position, $quantity, array $options = array(), array $configCodes = array() )
81
	{
82
		$stocklevel = null;
83
		$product = $this->getController()->get()->getProduct( $position );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\Controller\Frontend\Common\Iface as the method get() does only exist in the following implementations of said interface: Aimeos\Controller\Frontend\Basket\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
84
85
		if( !isset( $options['stock'] ) || $options['stock'] != false ) {
86
			$stocklevel = $this->getStockLevel( $product->getProductId(), $product->getWarehouseCode() );
87
		}
88
89
		$qty = ( $stocklevel !== null ? min( $stocklevel, $quantity ) : $quantity );
90
91
		$this->getController()->editProduct( $position, $qty, $options, $configCodes );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\Controller\Frontend\Common\Iface as the method editProduct() does only exist in the following implementations of said interface: Aimeos\Controller\Frontend\Basket\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
92
93
		if( $qty < $quantity )
94
		{
95
			$msg = sprintf( 'There are not enough products "%1$s" in stock', $product->getName() );
96
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
97
		}
98
	}
99
100
101
	/**
102
	 * Returns the highest stock level for the product.
103
	 *
104
	 * @param string $prodid Unique ID of the product
105
	 * @param string $warehouse Unique code of the warehouse
106
	 * @return integer|null Number of available items in stock (null for unlimited stock)
107
	 */
108
	protected function getStockLevel( $prodid, $warehouse )
109
	{
110
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'product/stock' );
111
112
		$search = $manager->createSearch( true );
113
		$expr = array(
114
			$search->compare( '==', 'product.stock.parentid', $prodid ),
115
			$search->getConditions(),
116
			$search->compare( '==', 'product.stock.warehouse.code', $warehouse ),
117
		);
118
		$search->setConditions( $search->combine( '&&', $expr ) );
119
120
		$result = $manager->searchItems( $search );
121
122
		if( empty( $result ) )
123
		{
124
			$msg = sprintf( 'No stock for product ID "%1$s" and warehouse "%2$s" available', $prodid, $warehouse );
125
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
126
		}
127
128
		$stocklevel = null;
129
130
		foreach( $result as $item )
131
		{
132
			if( ( $stock = $item->getStockLevel() ) === null ) {
133
				return null;
134
			}
135
136
			$stocklevel = max( (int) $stocklevel, $item->getStockLevel() );
137
		}
138
139
		return $stocklevel;
140
	}
141
}
142