sunnysideup /
silverstripe-ecommerce_combo_product
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * A combination product combines several products into one new product. |
||
| 4 | * |
||
| 5 | * The way this works is that the combo product links to zero or more products (many many relationship) |
||
| 6 | * |
||
| 7 | * When you add the combo product, the individual products are added. A modifier adds the "discount" for the combo. |
||
| 8 | * It also deletes all the products as soon as you delete one. |
||
| 9 | * |
||
| 10 | * There are two restrictions to keep in mind: |
||
| 11 | * (a) it only applies to products and not all buyables. |
||
| 12 | * (b) all products need to return true for canPurchase. This means that if the product is available as part of a combo, it should also be available by itself. |
||
| 13 | * |
||
| 14 | * We use the sort order for the order attribute to group it... |
||
| 15 | * |
||
| 16 | * @package: ecommerce |
||
| 17 | * @sub-package: products |
||
| 18 | * |
||
| 19 | **/ |
||
| 20 | |||
| 21 | |||
| 22 | class CombinationProduct extends Product |
||
|
0 ignored issues
–
show
|
|||
| 23 | { |
||
| 24 | private static $db = array( |
||
|
0 ignored issues
–
show
|
|||
| 25 | 'NewPrice' => 'Currency' |
||
| 26 | ); |
||
| 27 | |||
| 28 | private static $many_many = array( |
||
|
0 ignored issues
–
show
|
|||
| 29 | 'IncludedProducts' => 'Product' |
||
| 30 | ); |
||
| 31 | |||
| 32 | private static $searchable_fields = array( |
||
|
0 ignored issues
–
show
|
|||
| 33 | 'ID', |
||
| 34 | 'Title', |
||
| 35 | 'InternalItemID', |
||
| 36 | 'Price', |
||
| 37 | 'ListOfProducts' |
||
| 38 | ); |
||
| 39 | |||
| 40 | private static $casting = array( |
||
|
0 ignored issues
–
show
|
|||
| 41 | "OriginalPrice" => "Currency" |
||
| 42 | ); |
||
| 43 | |||
| 44 | private static $singular_name = "Combination Product"; |
||
|
0 ignored issues
–
show
|
|||
| 45 | public function i18n_singular_name() |
||
| 46 | { |
||
| 47 | return _t("CombinationProduct.COMBINATIONPRODUCT", "Combination Product"); |
||
| 48 | } |
||
| 49 | |||
| 50 | private static $plural_name = "Combination Products"; |
||
|
0 ignored issues
–
show
|
|||
| 51 | public function i18n_plural_name() |
||
| 52 | { |
||
| 53 | return _t("CombinationProduct.COMBINATIONPRODUCTS", "Combination Products"); |
||
| 54 | } |
||
| 55 | |||
| 56 | private static $default_parent = 'ProductGroup'; |
||
|
0 ignored issues
–
show
|
|||
| 57 | |||
| 58 | private static $default_sort = '"Title" ASC'; |
||
|
0 ignored issues
–
show
|
|||
| 59 | |||
| 60 | private static $icon = 'ecommerce_combo_product/images/icons/CombinationProduct'; |
||
|
0 ignored issues
–
show
|
|||
| 61 | |||
| 62 | public function getCMSFields() |
||
|
0 ignored issues
–
show
The return type could not be reliably inferred; please add a
@return annotation.
Our type inference engine in quite powerful, but sometimes the code does not
provide enough clues to go by. In these cases we request you to add a Loading history...
|
|||
| 63 | { |
||
| 64 | $fields = parent::getCMSFields(); |
||
| 65 | $fields->addFieldToTab("Root.Components", $this->getIncludedProductsFormField()); |
||
| 66 | $fields->replaceField("Price", new ReadOnlyField("Price", "Full Price")); |
||
| 67 | $fields->addFieldToTab("Root.Details", new NumericField("NewPrice", "New Price"), "Price"); |
||
| 68 | $fields->addFieldToTab("Root.Details", new ReadOnlyField("Savings", "Savings", $this->getPrice() - $this->NewPrice), "Price"); |
||
| 69 | return $fields; |
||
| 70 | } |
||
| 71 | |||
| 72 | |||
| 73 | /** |
||
| 74 | *@return TreeMultiselectField |
||
| 75 | **/ |
||
| 76 | protected function getIncludedProductsFormField() |
||
| 77 | { |
||
| 78 | $field = new TreeMultiselectField( |
||
| 79 | $name = "IncludedProducts", |
||
| 80 | $title = "Included Products", |
||
| 81 | $sourceObject = "SiteTree", |
||
| 82 | $keyField = "ID", |
||
| 83 | $labelField = "MenuTitle" |
||
| 84 | ); |
||
| 85 | $filter = create_function('$obj', 'return ( ( $obj InstanceOf Product || $obj InstanceOf ProductGroup) && ($obj->ID != '.$this->ID.'));'); |
||
|
0 ignored issues
–
show
The use of
create_function is highly discouraged, better use a closure.
// Instead of
$function = create_function('$a, $b', 'return $a + $b');
// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
|
|||
| 86 | $field->setFilterFunction($filter); |
||
| 87 | return $field; |
||
| 88 | } |
||
| 89 | |||
| 90 | /** |
||
| 91 | * Conditions for whether a product can be purchased. |
||
| 92 | * |
||
| 93 | * If it has the checkbox for 'Allow this product to be purchased', |
||
| 94 | * as well as having a price, it can be purchased. Otherwise a user |
||
| 95 | * can't buy it. |
||
| 96 | * |
||
| 97 | * Other conditions may be added by decorating with the canPurcahse function |
||
| 98 | * |
||
| 99 | * @return boolean |
||
| 100 | */ |
||
| 101 | public function canPurchase(Member $member = null, $checkPrice = true) |
||
| 102 | { |
||
| 103 | if ($includedProducts = $this->IncludedProducts()) { |
||
| 104 | if ($includedProducts->count()) { |
||
| 105 | foreach ($includedProducts as $includedProduct) { |
||
| 106 | if (!$includedProduct->canPurchase($member)) { |
||
| 107 | return false; |
||
| 108 | } |
||
| 109 | } |
||
| 110 | return parent::canPurchase($member); |
||
| 111 | } |
||
| 112 | } |
||
| 113 | return false; |
||
| 114 | } |
||
| 115 | |||
| 116 | public function classNameForOrderItem() |
||
| 117 | { |
||
| 118 | return "CombinationProduct_OrderItem"; |
||
| 119 | } |
||
| 120 | |||
| 121 | |||
| 122 | /** |
||
| 123 | * |
||
| 124 | * |
||
| 125 | * |
||
| 126 | */ |
||
| 127 | public function getPrice() |
||
| 128 | { |
||
| 129 | if ($includedProducts = $this->IncludedProducts()) { |
||
| 130 | $originalPrice = 0; |
||
| 131 | if ($includedProducts && $includedProducts->count()) { |
||
| 132 | foreach ($includedProducts as $includedProduct) { |
||
| 133 | $originalPrice += $includedProduct->CalculatedPrice(); |
||
| 134 | } |
||
| 135 | } |
||
| 136 | } |
||
| 137 | return $originalPrice; |
||
|
0 ignored issues
–
show
The variable
$originalPrice does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
Loading history...
|
|||
| 138 | } |
||
| 139 | |||
| 140 | public function getCalculatedPrice() |
||
|
0 ignored issues
–
show
The return type could not be reliably inferred; please add a
@return annotation.
Our type inference engine in quite powerful, but sometimes the code does not
provide enough clues to go by. In these cases we request you to add a Loading history...
|
|||
| 141 | { |
||
| 142 | return $this->getField("NewPrice"); |
||
| 143 | } |
||
| 144 | |||
| 145 | /** |
||
| 146 | * remove any non-products from the list. |
||
| 147 | * |
||
| 148 | */ |
||
| 149 | public function onBeforeWrite() |
||
| 150 | { |
||
| 151 | parent::onBeforeWrite(); |
||
| 152 | $includedProducts = $this->IncludedProducts(); |
||
| 153 | if ($includedProducts) { |
||
| 154 | foreach ($includedProducts as $includedProduct) { |
||
| 155 | if (!$includedProduct instanceof Product) { |
||
| 156 | $includedProducts->remove($includedProduct); |
||
| 157 | } |
||
| 158 | } |
||
| 159 | } |
||
| 160 | $this->Price = $this->NewPrice; |
||
| 161 | } |
||
| 162 | } |
||
| 163 | |||
| 164 | class CombinationProduct_Controller extends Product_Controller |
||
|
0 ignored issues
–
show
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.
You can fix this by adding a namespace to your class: namespace YourVendor;
class YourClass { }
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries. Loading history...
|
|||
| 165 | { |
||
| 166 | public function init() |
||
| 167 | { |
||
| 168 | parent::init(); |
||
| 169 | Requirements::themedCSS("CombinationProduct", "ecommerce_combo_product"); |
||
| 170 | } |
||
| 171 | } |
||
| 172 | |||
| 173 | |||
| 174 | class CombinationProduct_OrderItem extends Product_OrderItem |
||
|
0 ignored issues
–
show
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.
You can fix this by adding a namespace to your class: namespace YourVendor;
class YourClass { }
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries. Loading history...
|
|||
| 175 | { |
||
| 176 | |||
| 177 | |||
| 178 | //add a deletion system |
||
| 179 | |||
| 180 | public function onBeforeDelete() |
||
| 181 | { |
||
| 182 | parent::onBeforeDelete(); |
||
| 183 | $includedProductsOrderItems = IncludedProduct_OrderItem::get() |
||
| 184 | ->filter(array("ParentOrderItemID" => $this->ID, "OrderID" => $this->Order()->ID)); |
||
| 185 | if ($includedProductsOrderItems->count()) { |
||
| 186 | foreach ($includedProductsOrderItems as $includedProductsOrderItem) { |
||
| 187 | $includedProductsOrderItem->delete(); |
||
| 188 | $includedProductsOrderItem->destroy(); |
||
| 189 | } |
||
| 190 | } |
||
| 191 | } |
||
| 192 | |||
| 193 | public function TableSubTitle() |
||
| 194 | { |
||
| 195 | $buyable = $this->Buyable(); |
||
| 196 | $includedProducts = $buyable->IncludedProducts(); |
||
| 197 | $titleArray = array(); |
||
| 198 | if ($includedProducts) { |
||
| 199 | foreach ($includedProducts as $includedProduct) { |
||
| 200 | $titleArray[] = $includedProduct->MenuTitle; |
||
| 201 | } |
||
| 202 | } |
||
| 203 | if (count($titleArray)) { |
||
| 204 | return _t("CombinationProduct.INCLUDES", "Includes").": ".implode(", ", $titleArray)."."; |
||
| 205 | } |
||
| 206 | } |
||
| 207 | |||
| 208 | public function onBeforeWrite() |
||
| 209 | { |
||
| 210 | parent::onBeforeWrite(); |
||
| 211 | $this->Sort = $this->Buyable()->ID; |
||
|
0 ignored issues
–
show
The property
Sort does not exist on object<CombinationProduct_OrderItem>. Since you implemented __set, maybe consider adding a @property annotation.
Since your code implements the magic setter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
Since the property has write access only, you can use the @property-write annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. Loading history...
|
|||
| 212 | } |
||
| 213 | } |
||
| 214 | |||
| 215 | class IncludedProduct_OrderItem extends Product_OrderItem |
||
|
0 ignored issues
–
show
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.
You can fix this by adding a namespace to your class: namespace YourVendor;
class YourClass { }
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries. Loading history...
|
|||
| 216 | { |
||
| 217 | private static $has_one = array( |
||
|
0 ignored issues
–
show
|
|||
| 218 | "ParentOrderItem" => "CombinationProduct_OrderItem" |
||
| 219 | ); |
||
| 220 | |||
| 221 | public function LiveCalculatedTotal() |
||
| 222 | { |
||
| 223 | return 0; |
||
| 224 | } |
||
| 225 | |||
| 226 | public function Total($recalculate = false) |
||
| 227 | { |
||
| 228 | return $this->getTotal($recalculate); |
||
| 229 | } |
||
| 230 | |||
| 231 | public function getTotal($recalculate = false) |
||
| 232 | { |
||
| 233 | return 0; |
||
| 234 | } |
||
| 235 | |||
| 236 | public function TableSubTitle() |
||
| 237 | { |
||
| 238 | Requirements::themedCSS("CombinationProductModifier", "ecommerce_combo_product"); |
||
| 239 | return _t("CombinationProduct.PARTOF", "Part of").": ".$this->ParentOrderItem()->TableTitle()."."; |
||
|
0 ignored issues
–
show
The method
ParentOrderItem does not exist on object<IncludedProduct_OrderItem>? Since you implemented __call, maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
Loading history...
|
|||
| 240 | } |
||
| 241 | |||
| 242 | public function onBeforeWrite() |
||
| 243 | { |
||
| 244 | parent::onBeforeWrite(); |
||
| 245 | if ($parentOrderItem = $this->ParentOrderItem()) { |
||
|
0 ignored issues
–
show
The method
ParentOrderItem does not exist on object<IncludedProduct_OrderItem>? Since you implemented __call, maybe consider adding a @method annotation.
If you implement This is often the case, when class ParentClass {
private $data = array();
public function __call($method, array $args) {
if (0 === strpos($method, 'get')) {
return $this->data[strtolower(substr($method, 3))];
}
throw new \LogicException(sprintf('Unsupported method: %s', $method));
}
}
/**
* If this class knows which fields exist, you can specify the methods here:
*
* @method string getName()
*/
class SomeClass extends ParentClass { }
Loading history...
|
|||
| 246 | if ($buyable = $parentOrderItem->Buyable()) { |
||
| 247 | $this->Sort = $buyable->ID + 1; |
||
|
0 ignored issues
–
show
The property
Sort does not exist on object<IncludedProduct_OrderItem>. Since you implemented __set, maybe consider adding a @property annotation.
Since your code implements the magic setter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
Since the property has write access only, you can use the @property-write annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. Loading history...
|
|||
| 248 | } |
||
| 249 | } |
||
| 250 | } |
||
| 251 | |||
| 252 | public function RemoveLink() |
||
| 253 | { |
||
| 254 | return ""; |
||
| 255 | } |
||
| 256 | |||
| 257 | |||
| 258 | public function RemoveAllLink() |
||
| 259 | { |
||
| 260 | return ""; |
||
| 261 | } |
||
| 262 | |||
| 263 | public function QuantityField() |
||
| 264 | { |
||
| 265 | return new ReadonlyField("Quantity", "", $this->Quantity); |
||
|
0 ignored issues
–
show
The property
Quantity does not exist on object<IncludedProduct_OrderItem>. Since you implemented __get, maybe consider adding a @property annotation.
Since your code implements the magic getter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
If the property has read access only, you can use the @property-read annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. Loading history...
|
|||
| 266 | } |
||
| 267 | |||
| 268 | |||
| 269 | public function onBeforeDelete() |
||
| 270 | { |
||
| 271 | parent::onBeforeDelete(); |
||
| 272 | CartResponse::set_force_reload(); |
||
| 273 | } |
||
| 274 | } |
||
| 275 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.