Completed
Push — master ( e850f9...efb5f6 )
by Nicolaas
02:20
created

EcommerceTaskCSVToVariations::createProducts()   C

Complexity

Conditions 10
Paths 38

Size

Total Lines 48
Code Lines 35

Duplication

Lines 7
Ratio 14.58 %

Importance

Changes 0
Metric Value
dl 7
loc 48
rs 5.3454
c 0
b 0
f 0
cc 10
eloc 35
nc 38
nop 0

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
/**
5
 * allows the creation of variations from a CSV
6
 * CSV will have the following fields:
7
 * ProductTitle,
8
 * Size,
9
 * Colour,
10
 * Price
11
 * If you like to add more fields, then it is recommended that you extend this BuildTask
12
 * to your own BuildTask.
13
 *
14
 */
15
16
class EcommerceTaskCSVToVariations extends BuildTask
0 ignored issues
show
Coding Style Compatibility introduced by
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...
17
{
18
    protected $forreal = false;
19
20
    protected $title = "Create variations from a Spreadsheets (comma separated file CSV)";
21
22
    protected $description = "
23
        Does not delete any record, it only updates and adds.
24
        The minimum recommend columns are: ProductTitle (or ProductInternalItemID), Size, Colour, Price, InternalItemID.
25
        You can add ?forreal=1 to the URL to run the task for real.";
26
27
    /**
28
     * excluding base folder
29
     *
30
     * e.g. assets/files/mycsv.csv
31
     * @var String
32
     */
33
    private static $file_location = "";
0 ignored issues
show
Unused Code introduced by
The property $file_location is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
34
35
    /**
36
     * Cell entry for a price that is not available
37
     * @var String
38
     */
39
    private static $no_price_available = "POA";
0 ignored issues
show
Unused Code introduced by
The property $no_price_available is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
40
41
    /**
42
     * @var Array
43
     */
44
    private static $attribute_type_field_names = array(
0 ignored issues
show
Unused Code introduced by
The property $attribute_type_field_names is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
45
        "Size",
46
        "Colour"
47
    );
48
49
    /**
50
     * Is the CSV separated by , or ; or [tab]?
51
     */
52
    protected $csvSeparator = ",";
53
54
55
    /**
56
     * @var Boolean
57
     */
58
    protected $debug = true;
59
60
61
    /**
62
     * the original data from the CVS
63
     * @var Array
64
     */
65
    protected $csv = array();
66
67
    /**
68
     * Structure will be as follows:
69
     *
70
     *     ProductID => array(
71
     *         "Product" => $product,
72
     *         "VariationRows" => array(
73
     *             [1] => array(
74
     *                 "Data" => array(),
75
     *                 "Variation" => $variation
76
     *             )
77
     *         )
78
     *     ),
79
     *     ProductID => array(
80
     *         "Product" => $product,
81
     *         "VariationRows" => array(
82
     *             [1] => array(
83
     *                 "Data" => array(),
84
     *                 "Variation" => $variation
85
     *             ),
86
     *             [2] => array(
87
     *                 "Data" => array(),
88
     *                 "Variation" => $variation
89
     *             )
90
     *         )
91
     *     )
92
     *
93
     * @var Array
94
     */
95
    protected $data = array();
96
97
    /**
98
     * list of products without variations
99
     * @return Array
100
     */
101
    protected $soleProduct = array();
102
103
    /**
104
     * The default page of where the products are added.
105
     * @var Int
106
     */
107
    protected $defaultProductParentID = 0;
108
109
    public function getDescription()
110
    {
111
        if ($this->csvSeparator == "\t") {
112
            $this->csvSeparatorName = "[TAB]";
0 ignored issues
show
Bug introduced by
The property csvSeparatorName does not seem to exist. Did you mean csvSeparator?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
113
        } else {
114
            $this->csvSeparatorName = $this->csvSeparator;
0 ignored issues
show
Bug introduced by
The property csvSeparatorName does not seem to exist. Did you mean csvSeparator?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
115
        }
116
        return $this->description .". The file to be used is: ".$this->Config()->get("file_location").". The columns need to be separated by '".$this->csvSeparatorName."'";
0 ignored issues
show
Bug introduced by
The property csvSeparatorName does not seem to exist. Did you mean csvSeparator?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
117
    }
118
119
    /**
120
     *
121
     */
122
    public function run($request)
0 ignored issues
show
Coding Style introduced by
run uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
123
    {
124
        increase_time_limit_to(3600);
125
        increase_memory_limit_to('512M');
126
        if ($request->param("forreal") || (isset($_GET["forreal"]) && $_GET["forreal"] == 1)) {
127
            $this->forreal = true;
128
        }
129
        if ($this->forreal) {
130
            $this->reset();
131
        }
132
        $this->readFile();
133
        $this->createProducts();
134
        $this->findVariations();
135
        if ($this->forreal) {
136
            $this->createVariations();
137
            $this->getExtraDataForVariations();
138
        } else {
139
            $this->showData();
140
        }
141
    }
142
143
    /**
144
     * do more with Product
145
     * @param Product $product
146
     * @param Array $row
147
     */
148
    protected function addMoreProduct($product, $row)
0 ignored issues
show
Unused Code introduced by
The parameter $product is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $row is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
149
    {
150
        //overwrite in an extension of this task
151
    }
152
153
    /**
154
     * do more with Product that does have any variations
155
     * @param Product $product
156
     * @param Array $row
157
     */
158
    protected function addMoreProductForProductWithoutVariations($product, $row)
0 ignored issues
show
Unused Code introduced by
The parameter $product is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $row is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
159
    {
160
        //overwrite in an extension of this task
161
    }
162
163
    /**
164
     * do more with Product Variation
165
     * @param ProductAttributeType $attributeType
166
     * @param String $fieldName
167
     * @param Product $product
168
     */
169
    protected function addMoreAttributeType($attributeType, $fieldName, $product)
0 ignored issues
show
Unused Code introduced by
The parameter $attributeType is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $fieldName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $product is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
170
    {
171
        //overwrite in an extension of this task
172
    }
173
174
    /**
175
     * do more with Product Variation
176
     * @param ProductAttributeType $attributeValue
177
     * @param ProductAttributeType $attributeType
178
     * @param Product $product
179
     */
180
    protected function addMoreToAttributeValue($attributeValue, $attributeType, $product)
0 ignored issues
show
Unused Code introduced by
The parameter $attributeValue is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $attributeType is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $product is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
181
    {
182
        //overwrite in an extension of this task
183
    }
184
185
    /**
186
     * do more with Product Variation
187
     * @param ProductVariation $variation
188
     * @param Array $variationData
189
     * @param Product $product
190
     */
191
    protected function addMoreToVariation($variation, $variationData, $product)
0 ignored issues
show
Unused Code introduced by
The parameter $variation is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $variationData is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $product is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
192
    {
193
        //overwrite in an extension of this task
194
    }
195
196
197
    protected function reset()
198
    {
199
        //to do...
200
    }
201
202
    protected function readFile()
203
    {
204
        echo "================================================ READING FILE ================================================";
205
        $this->alterationMessage("<h3>".$this->getDescription()."</h3>", "created");
206
        $rowCount = 1;
207
        $rows = array();
208
        $fileLocation = $this->config()->get("file_location");
209
        $this->alterationMessage("$fileLocation is the file we are reading", "created");
210
        if (($handle = fopen($fileLocation, "r")) !== false) {
211
            while (($data = fgetcsv($handle, 100000, $this->csvSeparator)) !== false) {
212
                $rows[] = $data;
213
                $rowCount++;
214
            }
215
            fclose($handle);
216
        }
217
        //$rows = str_getcsv(file_get_contents(, ",", '"');
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
218
219
        $header = array_shift($rows);
220
221
        $this->csv = array();
222
        $rowCount = 1;
223
        foreach ($rows as $row) {
224
            if (count($header) != count($row)) {
225
                $this->alterationMessage("I am trying to merge ".implode(", ", $header)." with ".implode(", ", $row)." but the column count does not match!", "deleted");
226
                die("STOPPED");
0 ignored issues
show
Coding Style Compatibility introduced by
The method readFile() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
227
            }
228
            $this->csv[] = array_combine($header, $row);
229
            $rowCount++;
230
        }
231
        //data fixes
232
        foreach ($this->csv as $key => $row) {
233
            if (!isset($row["ProductTitle"])) {
234
                $this->csv[$key]["ProductTitle"] = "";
235
            }
236
            if (!isset($row["ProductInternalItemID"])) {
237
                $this->csv[$key]["ProductInternalItemID"] = $row["ProductTitle"];
238
            }
239
        }
240
        $this->alterationMessage("Imported ".count($this->csv)." rows with ".count($header)." cells each");
241
        $this->alterationMessage("Fields are: ".implode("<br /> - ............ ", $header));
242
        $this->alterationMessage("================================================", "show");
243
    }
244
245
    /**
246
     *
247
     *
248
     */
249
    protected function createProducts()
250
    {
251
        $this->alterationMessage("================================================ CREATING PRODUCTS ================================================", "show");
252
        $productsCompleted = array();
253
        foreach ($this->csv as $row) {
254
            if (!isset($productsCompleted[$row["ProductTitle"]])) {
255
                $filterArray = array(
256
                    "Title" => $row["ProductTitle"],
257
                    "InternalItemID" => $row["ProductInternalItemID"]
258
                );
259
                $product = DataObject::get_one(
260
                   'ProductPage', 
261
                    $filterArray
262
                );
263
                if ($product && $product->ParentID) {
264
                    $this->defaultProductParentID = $product->ParentID;
265
                } elseif (!$this->defaultProductParentID) {
266
                    $this->defaultProductParentID = DataObject::get_one('ProductGroup')->ID;
267
                }
268
                if (!$product) {
269
                    $product = ProductPage::create($filterArray);
270
                    $product->MenuTitle = $row["ProductTitle"];
271
272
                    $this->alterationMessage("Creating Product: ".$row["ProductTitle"], "created");
273
                } else {
274
                    $this->alterationMessage("Product: ".$row["ProductTitle"]." already exists");
275
                }
276
                if (!$product->ParentID) {
277
                    $product->ParentID = $this->defaultProductParentID;
278
                }
279
                $product->Title = $row["ProductTitle"];
280
                $product->InternalItemID = $row["ProductInternalItemID"];
281 View Code Duplication
                if ($this->forreal) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282
                    $this->addMoreProduct($product, $row);
283
                    $product->write("Stage");
284
                    if ($product->IsPublished()) {
285
                        $product->Publish('Stage', 'Live');
286
                    }
287
                }
288
                $productsCompleted[$row["ProductTitle"]] = $product->ID;
289
                $this->data[$product->ID] = array(
290
                    "Product" => $product,
291
                    "VariationRows" => array()
292
                );
293
            }
294
        }
295
        $this->alterationMessage("================================================", "show");
296
    }
297
298
299
    protected function findVariations()
300
    {
301
        $this->alterationMessage("================================================ FINDING VARIATIONS ================================================", "show");
302
        foreach ($this->data as $productKey => $data) {
303
            $product = $data["Product"];
304
            $title = $product->Title;
305
            $internalItemID = $product->InternalItemID;
306
            foreach ($this->csv as $key => $row) {
307
                if (strtolower(trim($title)) == strtolower(trim($row["ProductTitle"])) || strtolower(trim($internalItemID)) == strtolower(trim($row["ProductInternalItemID"]))) {
308
                    $this->data[$product->ID]["VariationRows"][$key] = array(
309
                        "Data" => $row,
310
                        "Variation" => null
311
                    );
312
                }
313
            }
314
            if (count($this->data[$product->ID]["VariationRows"]) < 2) {
315
                $varData = array_shift($this->data[$product->ID]["VariationRows"]);
316
                $varDataRow = $varData["Data"];
317
                $this->addFieldToObject($product, $data, "Price", "");
318
                $this->addFieldToObject($product, $data, "InternalItemID", "");
319 View Code Duplication
                if ($this->forreal) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320
                    $this->addMoreProductForProductWithoutVariations($product, $varDataRow);
321
                    $product->write("Stage");
322
                    if ($product->IsPublished()) {
323
                        $product->Publish('Stage', 'Live');
324
                    }
325
                }
326
                $this->soleProduct[$product->ID] = $product->Title.", ID: ".$product->ID;
327
                unset($this->data[$productKey]);
328
                $this->alterationMessage("Removing data for ".$product->Title." because there is only ONE variation. ", "deleted");
329
            } else {
330
                $this->alterationMessage("Found ".count($this->data[$product->ID]["VariationRows"])." Variations for ".$product->Title);
331
            }
332
        }
333
        $this->alterationMessage("================================================", "show");
334
    }
335
336
    protected function showData()
337
    {
338
        echo "<h2>Variation Summary</h2>";
339
        foreach ($this->data as $productKey => $value) {
340
            if (isset($value["Product"]) && $value["Product"]) {
341
                $this->data[$productKey]["Product"] = $value["Product"]->Title.", ID: ".$value["Product"]->ID;
342
            } else {
343
                $this->data[$productKey]["Product"] = "Not found";
344
            }
345
            $this->alterationMessage($this->data[$productKey]["Product"].", variations: ".count($this->data[$productKey]["VariationRows"]), "created");
346
        }
347
        echo "<h2>Products without variations</h2>";
348
        foreach ($this->soleProduct as $productKey => $value) {
349
            $this->alterationMessage($value, "created");
350
        }
351
        echo "<h2>Variation data</h2>";
352
        echo "<pre>";
353
        print_r($this->data);
354
        echo "</pre>";
355
        echo "<h2>CSV Data</h2>";
356
        echo "<pre>";
357
        print_r($this->csv);
358
        echo "</pre>";
359
        die("====================================================== STOPPED - add ?forreal=1 to run for real. ======================================");
0 ignored issues
show
Coding Style Compatibility introduced by
The method showData() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
360
    }
361
362
    protected function createVariations()
363
    {
364
        $this->alterationMessage("================================================ CREATING VARIATIONS ================================================", "show");
365
        foreach ($this->data as $data) {
366
            $types = array();
367
            $values = array();
368
            $product = $data["Product"];
369
            $arrayForCreation = array();
370
            $variationFilter = array();
0 ignored issues
show
Unused Code introduced by
$variationFilter is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
371
            $this->alterationMessage("<h1>Working out variations for ".$product->Title."</h1>");
372
            //create attribute types for one product
373
            $this->alterationMessage("....Creating attribute types");
374
            foreach ($this->Config()->get("attribute_type_field_names") as $fieldKey => $fieldName) {
375
                $startMessage = "........Checking field $fieldName";
376
                $attributeTypeName = trim($data["Product"]->Title)."_".$fieldName;
377
                $filterArray = array("Name" => $attributeTypeName);
378
                $type = DataObject::get_one('ProductAttributeType', $filterArray);
379
                if (!$type) {
380
                    $this->alterationMessage($startMessage." ... creating new attribute type: ".$attributeTypeName, "created");
381
                    $type = new ProductAttributeType($filterArray);
382
                    $type->Label = $attributeTypeName;
0 ignored issues
show
Documentation introduced by
The property Label does not exist on object<ProductAttributeType>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?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...
383
                    $type->Sort = $fieldKey;
0 ignored issues
show
Documentation introduced by
The property Sort does not exist on object<ProductAttributeType>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?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...
384
                } else {
385
                    $this->alterationMessage($startMessage." ... 	found existing attribute type: ".$attributeTypeName);
386
                }
387
                $this->addMoreAttributeType($type, $fieldName, $product);
0 ignored issues
show
Compatibility introduced by
$type of type object<DataObject> is not a sub-type of object<ProductAttributeType>. It seems like you assume a child class of the class DataObject to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
388
                $type->write();
389
                $types[$fieldName] = $type;
390
                $product->VariationAttributes()->add($type);
391
            }
392
            //go through each variation to make the values
393
            $this->alterationMessage("....Creating attribute values");
394
            foreach ($data["VariationRows"] as $key => $row) {
395
                //go through each value
396
                foreach ($this->Config()->get("attribute_type_field_names") as $fieldName) {
397
                    if (!isset($row["Data"][$fieldName])) {
398
                        $this->alterationMessage("ERROR; $fieldName not set at all....", "deleted");
399
                        continue;
400
                    } elseif (!trim($row["Data"][$fieldName])) {
401
                        $this->alterationMessage("skipping $fieldName as there are no entries...");
402
                        continue;
403
                    }
404
                    $startMessage = "........Checking field $fieldName";
405
                    //create attribute value
406
                    $attributeValueName = $row["Data"][$fieldName];
407
                    $filterArray = array("Code" => $attributeValueName, "TypeID" => $types[$fieldName]->ID);
408
                    $value = DataObject::get_one('ProductAttributeValue', $filterArray);
409
                    if (!$value) {
410
                        $this->alterationMessage($startMessage."............creating new attribute value:  <strong>".$attributeValueName."</strong> for ".$types[$fieldName]->Name, "created");
411
                        $value = ProductAttributeValue::create($filterArray);
412
                        $value->Code = $attributeValueName;
0 ignored issues
show
Documentation introduced by
The property Code does not exist on object<ProductAttributeValue>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?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...
413
                        $value->Value = $attributeValueName;
0 ignored issues
show
Documentation introduced by
The property Value does not exist on object<ProductAttributeValue>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?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...
414
                    } else {
415
                        $this->alterationMessage($startMessage."............found existing attribute value: <strong>".$attributeValueName."</strong> for ".$types[$fieldName]->Name);
416
                    }
417
                    $this->addMoreAttributeType($value, $types[$fieldName], $product);
0 ignored issues
show
Compatibility introduced by
$value of type object<DataObject> is not a sub-type of object<ProductAttributeType>. It seems like you assume a child class of the class DataObject to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
418
                    $value->write();
419
                    $values[$fieldName] = $value;
420
421
                    //add at arrays for creation...
422
                    if (!isset($arrayForCreation[$types[$fieldName]->ID])) {
423
                        $arrayForCreation[$types[$fieldName]->ID] = array();
424
                    }
425
                    $arrayForCreation[$types[$fieldName]->ID][] = $value->ID;
426
                    if (!isset($variationFilters[$key])) {
427
                        $variationFilters[$key] = array();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$variationFilters was never initialized. Although not strictly required by PHP, it is generally a good practice to add $variationFilters = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
428
                    }
429
                    $variationFilters[$key][$types[$fieldName]->ID] = $value->ID;
0 ignored issues
show
Bug introduced by
The variable $variationFilters 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

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
430
                }
431
            }
432
            //remove attribute types without values... (i.e. product only has size of colour)
433
            foreach ($product->VariationAttributes() as $productTypeToBeDeleted) {
434
                if ($productTypeToBeDeleted->Values()->count() == 0) {
435
                    $this->alterationMessage("....deleting attribute type with no values: ".$productTypeToBeDeleted->Title);
436
                    $product->VariationAttributes()->remove($productTypeToBeDeleted);
437
                }
438
            }
439
            $this->alterationMessage("....Creating Variations ///");
440
            //$this->alterationMessage("....Creating Variations From: ".print_r(array_walk($arrayForCreation, array($this, 'implodeWalk'))));
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
441
            //generate variations
442
            $variationAttributeValuesPerVariation = array();
443
            foreach ($arrayForCreation as $typeID => $variationEntry) {
444
                foreach ($variationEntry as $positionOfVariation => $attributeValueID) {
445
                    $variationAttributeValuesPerVariation[$positionOfVariation][$typeID] = $attributeValueID;
446
                }
447
            }
448
449
            foreach ($variationAttributeValuesPerVariation as $variationAttributes) {
450
                $variation = $product->getVariationByAttributes($variationAttributes);
451
                if ($variation instanceof ProductVariation) {
452
                    $this->alterationMessage(".... Variation " . $variation->FullName . " Already Exists ///");
0 ignored issues
show
Documentation introduced by
The property FullName does not exist on object<ProductVariation>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?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...
453
                } else {
454
                    //2. if not, create variation with attributes
455
                    $className = $product->getClassNameOfVariations();
456
                    $newVariation = new $className(
457
                        array(
458
                            'ProductID' => $product->ID,
459
                            'Price' => $product->Price
460
                        )
461
                    );
462
                    $newVariation->setSaveParentProduct(false);
463
                    $newVariation->write();
464
                    $newVariation->AttributeValues()->addMany($variationAttributes);
465
                    $this->alterationMessage(".... Variation " . $newVariation->FullName . " created ///", "created");
466
                }
467
            }
468
469
            //find variations and add to VariationsRows
470
            foreach ($data["VariationRows"] as $key => $row) {
471
                $variation = $product->getVariationByAttributes($variationFilters[$key]);
472
                if ($variation instanceof ProductVariation) {
473
                    $this->alterationMessage("........Created variation, ".$variation->getTitle());
474
                    $this->data[$product->ID]["VariationRows"][$key]["Variation"] = $variation;
475
                } else {
476
                    $this->alterationMessage("........Could not find variation", "deleted");
477
                }
478
            }
479
        }
480
        $this->alterationMessage("================================================", "show");
481
    }
482
483
    protected function getExtraDataForVariations()
484
    {
485
        $this->alterationMessage("================================================ ADDING EXTRA DATA ================================================", "show");
486
        foreach ($this->data as $productData) {
487
            $product = $productData["Product"];
488
            $this->alterationMessage("<h1>Adding extra data for ".$product->Title." with ".(count($productData["VariationRows"]))."</h1>"." Variations");
489
            foreach ($productData["VariationRows"] as $key => $row) {
490
                $variation = $row["Variation"];
491
                $variationData = $row["Data"];
492
                if ($variation instanceof ProductVariation) {
493
                    $this->alterationMessage("<h3>....Updating ".$variation->getTitle()."</h3>", "show");
494
                    if (isset($variationData["Price"])) {
495
                        if ($price = floatval($variationData["Price"]) - 0) {
496
                            if (floatval($variation->Price) != floatval($price)) {
0 ignored issues
show
Documentation introduced by
The property Price does not exist on object<ProductVariation>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?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...
497
                                $this->alterationMessage("........Price = ".$price, "created");
498
                                $variation->Price = $price;
0 ignored issues
show
Documentation introduced by
The property Price does not exist on object<ProductVariation>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?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...
499
                            }
500
                        } else {
501
                            $this->alterationMessage("........NO Price", "deleted");
502
                        }
503
                    } else {
504
                        $this->alterationMessage("........NO Price field", "deleted");
505
                    }
506
                    $this->addFieldToObject($variation, $variationData, "Price", "");
507
                    $this->addFieldToObject($variation, $variationData, "InternalItemID", "");
508
                    $this->addMoreToVariation($variation, $variationData, $product);
509
                    $variation->write();
510
                } else {
511
                    $this->alterationMessage("....Could not find variation for ".print_r($row), "deleted");
512
                }
513
            }
514
        }
515
        $this->alterationMessage("================================================", "show");
516
    }
517
518
    /**
519
     * adds a field to the variation
520
     * @param ProductVariation | Product $variation
521
     * @param array $variationData - the array of data
522
     * @param String $objectField - the name of the field on the variation itself
523
     * @param String $arrayField - the name of the field in the variationData
524
     *
525
     */
526
    protected function addFieldToObject($variation, $variationData, $objectField, $arrayField = "")
527
    {
528
        if (!$arrayField) {
529
            $arrayField = $objectField;
530
        }
531
        if (isset($variationData[$arrayField])) {
532
            if ($value = $variationData[$arrayField]) {
533
                if ($variation->$objectField != $value) {
534
                    $this->alterationMessage("........$objectField = ".$value, "changed");
535
                }
536
                $variation->$objectField = $value;
537
            } else {
538
                $this->alterationMessage("........NO $arrayField value", "deleted");
539
            }
540
        } else {
541
            $this->alterationMessage("........NO $arrayField field", "deleted");
542
        }
543
    }
544
545
    /*
546
     * @param string $message
547
     * @param string $style
548
     */
549
    protected function alterationMessage($message, $style = "")
550
    {
551
        if (!Director::isDev() || $style) {
552
            DB::alteration_message($message, $style);
553
            ob_start();
554
            ob_end_flush();
555
        } else {
556
            echo ".";
557
            ob_start();
558
            ob_end_flush();
559
        }
560
    }
561
}
562
563
564
class EcommerceTaskCSVToVariations_EXT extends Extension
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
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...
565
{
566
    private static $allowed_actions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $allowed_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
567
        "ecommercetaskcsvtovariations" => true
568
    );
569
570
    //NOTE THAT updateEcommerceDevMenuConfig adds to Config options
571
    //but you can als have: updateEcommerceDevMenuDebugActions
572
    public function updateEcommerceDevMenuRegularMaintenance($buildTasks)
573
    {
574
        $buildTasks[] = "ecommercetaskcsvtovariations";
575
        return $buildTasks;
576
    }
577
578
    public function ecommercetaskcsvtovariations($request)
579
    {
580
        $this->owner->runTask("ecommercetaskcsvtovariations", $request);
581
    }
582
}
583