Passed
Push — master ( fd14ef...e3fcf4 )
by
unknown
02:45
created

CountryPrice_EcommerceCountry   F

Complexity

Total Complexity 58

Size/Duplication

Total Lines 379
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 19

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 19
dl 0
loc 379
rs 2.4812
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
B updateCMSFields() 0 68 5
A get_real_countries_list() 0 5 1
A get_sibling_countries() 0 21 2
A get_distributor_country() 0 10 3
A get_distributor_primary_country() 0 11 3
F get_real_country() 0 123 32
A get_backup_country() 0 11 3
A hasDistributor() 0 9 3
A requireDefaultRecords() 0 17 4
A ComputedLanguageAndCountryCode() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like CountryPrice_EcommerceCountry often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CountryPrice_EcommerceCountry, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Adds fields to individual countries.
5
 *
6
 */
7
8
class CountryPrice_EcommerceCountry extends DataExtension
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...
9
{
10
    private static $db = array(
0 ignored issues
show
Unused Code introduced by
The property $db 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...
11
        'IsBackupCountry' => 'Boolean',
12
        'FAQContent' => 'HTMLText',
13
        'TopBarMessage' => 'Varchar(255)',
14
        'DeliveryCostNote' => 'Varchar(255)',
15
        'ShippingEstimation' => 'Varchar(255)',
16
        'ReturnInformation' => 'Varchar(255)',
17
        'ProductNotAvailableNote' => 'HTMLText',
18
        'LanguageAndCountryCode' => 'Varchar(20)'
19
    );
20
21
    private static $has_one = array(
0 ignored issues
show
Unused Code introduced by
The property $has_one 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...
22
        'Distributor' => 'Distributor',
23
        'EcommerceCurrency' => 'EcommerceCurrency',
24
        'AlwaysTheSameAs' => 'EcommerceCountry'
25
    );
26
27
    private static $has_many = array(
0 ignored issues
show
Unused Code introduced by
The property $has_many 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...
28
        'ParentFor' => 'EcommerceCountry'
29
    );
30
31
    private static $searchable_fields = array(
0 ignored issues
show
Unused Code introduced by
The property $searchable_fields 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...
32
        "AlwaysTheSameAsID" => true,
33
        "IsBackupCountry" => "ExactMatchFilter"
34
    );
35
36
    private static $default_sort = array(
0 ignored issues
show
Unused Code introduced by
The property $default_sort 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...
37
        'Name' => 'DESC'
38
    );
39
40
41
    private static $indexes = array(
0 ignored issues
show
Unused Code introduced by
The property $indexes 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...
42
        "IsBackupCountry" => true
43
    );
44
45
    private static $casting = array(
0 ignored issues
show
Unused Code introduced by
The property $casting 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...
46
        "LanguageAndCountryCode" => 'Varchar'
47
    );
48
49
    public function updateCMSFields(FieldList $fields)
50
    {
51
        $fields->addFieldToTab(
52
            "Root.ParentCountry",
53
            DropdownField::create(
54
                'AlwaysTheSameAsID',
55
                'Parent Country',
56
                array('' => '--- PLEASE SELECT ---') + EcommerceCountry::get()->filter(array("AlwaysTheSameAsID" => 0))->exclude(array("ID" => $this->owner->ID))->map("ID", "Name")->toArray()
57
            )
58
        );
59
        if ($this->owner->AlwaysTheSameAsID) {
60
            $removeByNameArray = array(
61
                'IsBackupCountry',
62
                'DoNotAllowSales',
63
                'FAQContent',
64
                'TopBarMessage',
65
                'DeliveryCostNote',
66
                'ShippingEstimation',
67
                'ReturnInformation',
68
                'ProductNotAvailableNote',
69
                'DistributorID',
70
                'EcommerceCurrencyID',
71
                'ParentFor',
72
                'Regions'
73
            );
74
            foreach ($removeByNameArray as $removeByNameField) {
75
                $fields->removeByName(
76
                    $removeByNameField
77
                );
78
            }
79
        } else {
80
            $fields->addFieldToTab('Root.Messages', TextField::create('TopBarMessage', 'Top Bar Message')->setRightTitle("also see the site config for default messages"));
81
            if ($this->owner->DistributorID) {
82
                $FAQContentField = new HtmlEditorField('FAQContent', 'Content');
83
                $FAQContentField->setRows(7);
84
                $FAQContentField->setColumns(7);
85
                $fields->addFieldToTab('Root.FAQPage', $FAQContentField);
86
            } else {
87
                $fields->addFieldToTab(
88
                    'Root.FAQPage',
89
                    new LiteralField(
90
                        "FAQPageExplanation",
91
                        "<p class=\"message warning\">FAQ information can only be added to the main country for a ". _t('Distributor.SINGULAR_NAME', 'Distributor') ."</p>"
92
                    )
93
                );
94
            }
95
96
            $distributors = Distributor::get()
97
                ->filter(array("IsDefault" => 0));
98
            $distributors = $distributors->count() ? $distributors->map('ID', 'Name')->toArray() : array();
99
            $distributors = array('' => '--- PLEASE SELECT ---') + $distributors;
100
            $fields->addFieldToTab(
101
                'Root.Main',
102
                DropdownField::create('DistributorID', _t('Distributor.SINGULAR_NAME', 'Distributor'), array(0 => "-- Not Selected --") + $distributors),
103
                "DoNotAllowSales"
104
            );
105
106
            $fields->addFieldToTab(
107
                "Root.Testing",
108
                new LiteralField("LogInAsThisCountry", "<h3><a href=\"/whoami/setmycountry/".$this->owner->Code."/?countryfortestingonly=".$this->owner->Code."\">place an order as a person from ".$this->owner->Title."</a></h3>")
109
            );
110
111
            $fields->addFieldToTab(
112
                "Root.Currency",
113
                $fields->dataFieldByName("EcommerceCurrencyID")
114
            );
115
        }
116
    }
117
118
    public static function get_real_countries_list()
119
    {
120
        return EcommerceCountry::get()
121
            ->filter(array('DoNotAllowSales' => 0, 'AlwaysTheSameAsID' => 0));
122
    }
123
124
    /**
125
     *
126
     *
127
     * @param  [type] $countryCode [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
128
     * @return DataList
129
     */
130
    public static function get_sibling_countries($countryCode = null)
131
    {
132
        $countryObject = self::get_real_country($countryCode);
133
        if ($countryObject->AlwaysTheSameAsID) {
134
            return EcommerceCountry::get()
135
                ->filterAny(
136
                    array(
137
                        'AlwaysTheSameAsID' => array($countryObject->AlwaysTheSameAsID),
138
                        "ID" => array($countryObject->AlwaysTheSameAsID, $countryObject->ID)
139
                    )
140
                );
141
        } else {
142
            return EcommerceCountry::get()
143
                ->filterAny(
144
                    array(
145
                        'AlwaysTheSameAsID' => $countryObject->ID,
146
                        'ID' => $countryObject->ID
147
                    )
148
                );
149
        }
150
    }
151
152
    /**
153
     * checks if the country has a distributor
154
     * and returns it.  If not, returns the defaulf country.
155
     *
156
     * @return EcommerceCountry
0 ignored issues
show
Documentation introduced by
Should the return type not be EcommerceCountry|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
157
     *
158
     */
159
    public static function get_distributor_country($countryCode = null)
160
    {
161
        $countryObject = CountryPrice_EcommerceCountry::get_real_country($countryCode);
162
        if ($countryObject && $countryObject->hasDistributor()) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
163
            //do nothing ...
164
        } else {
165
            $countryObject = self::get_backup_country();
166
        }
167
        return $countryObject;
168
    }
169
170
    /**
171
    * checks if the country has a distributor
172
    * and returns the primary country for the distributor.
173
    * If not, returns the defaulf country.
174
     *
175
     * @return EcommerceCountry
176
     *
177
     */
178
    public static function get_distributor_primary_country($countryCode = null)
179
    {
180
        $countryObject = CountryPrice_EcommerceCountry::get_real_country($countryCode);
181
        if ($countryObject && $countryObject->hasDistributor()) {
182
            return $countryObject->Distributor()->PrimaryCountry();
183
            //do nothing ...
184
        } else {
185
            $countryObject = self::get_backup_country();
186
        }
187
        return $countryObject;
188
    }
189
190
    private static $_get_real_country_cache = array();
191
192
    /**
193
     * returns the 'always the same as' (parent) country if necessary
194
     * @param  EcommerceCountry | string | int   (optional)  $country
195
     *
196
     * @return EcommerceCountry
197
     */
198
    public static function get_real_country($country = null)
0 ignored issues
show
Coding Style introduced by
get_real_country 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...
199
    {
200
201
        if($country && $country instanceof EcommerceCountry) {
202
            $cacheKey = $country->Code;
203
        } elseif($country) {
204
            $cacheKey = $country;
205
        } else {
206
            $cacheKey = 'notprovided';
207
        }
208
        if(isset(self::$_get_real_country_cache[$cacheKey]) && self::$_get_real_country_cache[$cacheKey]) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
209
210
        } else {
211
            //save original - just in case...
212
            $originalCountry = $country;
213
214
            //no country provided
215
            if (! $country) {
216
                $param = Config::inst()->get('CountryPrice_Translation', 'locale_get_parameter');
217
218
                // 1. CHECK FROM URL
219
                $urlCountryCode = null;
220
                if (isset($_GET[$param])) {
221
                    $urlCountryCode = Convert::raw2sql(preg_replace("/[^A-Z]+/", "", strtoupper($_GET[$param])));
222
                }
223
224
                // 2. CHECK WHAT THE SYSTEM THINKS THE COUNTRY CHOULD BE
225
226
                //now we check it from order / session ....
227
                $order = ShoppingCart::current_order();
228
                if($order && $order->exists()) {
229
                    Session::clear('temporary_country_order_store');
230
                    $countryCode = $order->getCountry();
231
                } else {
232
                    $countryCode = Session::get('temporary_country_order_store');
233
                }
234
235
                //if we still dont have a country then we use the standard e-commerce methods ...
236
                if(! $countryCode) {
237
                    $countryCode = EcommerceCountry::get_country();
238
                }
239
240
                //lets make our object!
241
                if($countryCode) {
242
                    $country = DataObject::get_one('EcommerceCountry', ['Code' => $countryCode]);
243
                }
244
245
                if($country && $country instanceof EcommerceCountry) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
246
                    //do nothing
247
                } else {
248
                    $country = null;
249
                }
250
                //IF THE COUNTRY DOES NOT MATCH THE URL COUNTRY THEN THE URL WINS!!!!
251
                if($urlCountryCode) {
252
                    if (
253
                            ($country && $country->Code !== $urlCountryCode)
254
                        ||
255
                            ! $country
256
257
                    ){
258
                        $country = DataObject::get_one('EcommerceCountry', ['Code' => $urlCountryCode]);
259
                        if($country) {
260
                            //change country Object
261
                            //reset everything ...
262
                            CountryPrices_ChangeCountryController::set_new_country($country);
263
264
                            // return self::get_real_country($country);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
265
                        } else {
266
                            return $this->redirect('404-country-not-found');
0 ignored issues
show
Bug introduced by
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The method redirect() does not seem to exist on object<CountryPrice_EcommerceCountry>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
267
                        }
268
                    } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
269
270
                    }
271
                }
272
            }
273
274
275
            //MAKE SURE WE HAVE AN OBJECT
276
            //get the Object
277
            if ($country instanceof EcommerceCountry) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
278
                //do nothing
279
            } elseif (is_numeric($country) && intval($country) == $country) {
280
                $country = EcommerceCountry::get()->byID($country);
281
            } elseif (is_string($country)) {
282
                $country = strtoupper($country);
283
                $country = EcommerceCountry::get_country_object(false, $country);
284
            }
285
286
287
            //LOOK FOR REPLACEMENT COUNTRIES
288
            //substitute (always the same as) check ....
289
            if ($country && $country instanceof EcommerceCountry) {
290
                if ($country->AlwaysTheSameAsID) {
291
                    $realCountry = $country->AlwaysTheSameAs();
292
                    if ($realCountry && $realCountry->exists()) {
293
                        $country = $realCountry;
294
                    }
295
                }
296
            } else {
297
                //last chance ... do this only once ...
298
                $countryCode = EcommerceCountry::get_country_default();
299
                if ($countryCode && !$originalCountry) {
300
                    $country = self::get_real_country($countryCode);
301
                }
302
            }
303
304
            //FINAL BOARDING CALL!
305
            //surely we have one now???
306
            if ($country && $country instanceof EcommerceCountry) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
307
                //do nothing
308
            } else {
309
                //final backup....
310
                $country = EcommerceCountry::get()->first();
311
            }
312
313
            //set to cache ...
314
            self::$_get_real_country_cache[$cacheKey] = $country;
315
316
        }
317
318
        return self::$_get_real_country_cache[$cacheKey];
319
320
    }
321
322
    /**
323
     *
324
     * @return EcommerceCountry
0 ignored issues
show
Documentation introduced by
Should the return type not be EcommerceCountry|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
325
     */
326
    public static function get_backup_country()
327
    {
328
        $obj = EcommerceCountry::get()->filter(array("IsBackupCountry" => true))->first();
329
        if (! $obj) {
330
            $obj = EcommerceCountry::get()->filter(array("Code" => EcommerceConfig::get('EcommerceCountry', 'default_country_code')))->first();
331
            if (! $obj) {
332
                $obj = EcommerceCountry::get()->first();
333
            }
334
        }
335
        return $obj;
336
    }
337
338
339
    /**
340
     *
341
     *
342
     * @return boolean
343
     */
344
    public function hasDistributor()
345
    {
346
        $countryObject = CountryPrice_EcommerceCountry::get_real_country($this->owner);
347
348
        return
349
            $countryObject->DistributorID &&
350
            $countryObject->Distributor() &&
351
            $countryObject->Distributor()->exists();
352
    }
353
354
355
    /**
356
     * make sure there is always a backup country ...
357
     */
358
    public function requireDefaultRecords()
359
    {
360
        $backupCountry = EcommerceCountry::get()->filter(array("IsBackupCountry" => 1))->first();
361
        if (!$backupCountry) {
362
            $backupCountry = self::get_backup_country();
363
            if ($backupCountry) {
364
                $backupCountry->IsBackupCountry = true;
365
                $backupCountry->write();
366
            }
367
        }
368
        if ($backupCountry) {
369
            DB::query("UPDATE EcommerceCountry SET IsBackupCountry = 0 WHERE EcommerceCountry.ID <> ".$backupCountry->ID);
370
            DB::alteration_message("Creating back-up country");
371
        } else {
372
            DB::alteration_message("Back-up country has not been set", "deleted");
373
        }
374
    }
375
376
    /**
377
     * @return string
378
     */
379
    public function ComputedLanguageAndCountryCode()
380
    {
381
        if ($this->owner->LanguageAndCountryCode) {
382
            return $this->owner->LanguageAndCountryCode;
383
        }
384
        return strtolower('en-'.$this->owner->Code);
385
    }
386
}
387