SearchableOrderReport::getExportQuery()   F
last analyzed

Complexity

Conditions 11
Paths 240

Size

Total Lines 42
Code Lines 28

Duplication

Lines 12
Ratio 28.57 %

Importance

Changes 0
Metric Value
dl 12
loc 42
rs 3.9999
c 0
b 0
f 0
cc 11
eloc 28
nc 240
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
 * @package ecommercextras
5
 * @author nicolaas[at]sunnysideup.co.nz
6
 */
7
class SearchableOrderReport extends SalesReport
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...
8
{
9
    protected $title = 'Searchable Orders';
10
11
    protected $description = 'Search all orders';
12
13
    protected static $default_from_time = "11:00 am";
14
    public static function set_default_from_time($v)
15
    {
16
        self::$default_from_time = $v;
17
    }
18
    public static function get_default_from_time()
19
    {
20
        return self::$default_from_time;
21
    }
22
    public static function get_default_from_time_as_full_date_time()
23
    {
24
        return date("Y-m-d", time()) . " " . date("H:i", strtotime(self::get_default_from_time()));
25
    }
26
    protected static $default_until_time = "10:00 pm";
27
    public static function set_default_until_time($v)
28
    {
29
        self::$default_until_time = $v;
30
    }
31
    public static function get_default_until_time()
32
    {
33
        return self::$default_until_time;
34
    }
35
    public static function get_default_until_time_as_full_date_time()
36
    {
37
        return date("Y-m-d", time()) . " " . date("H:i", strtotime(self::get_default_until_time()));
38
    }
39
40
    public function parameterFields()
41
    {
42
        //$fields = parent::getCMSFields();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
43
        $fields =new FieldSet();
44
        $stats[] = "Count: ".$this->statistic("count");
0 ignored issues
show
Coding Style Comprehensibility introduced by
$stats was never initialized. Although not strictly required by PHP, it is generally a good practice to add $stats = 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...
45
        $stats[] = "Sum: ".$this->currencyFormat($this->statistic("sum"));
46
        $stats[] = "Avg: ".$this->currencyFormat($this->statistic("avg"));
47
        $stats[] = "Min: ".$this->currencyFormat($this->statistic("min"));
48
        $stats[] = "Max: ".$this->currencyFormat($this->statistic("max"));
49
        if ($this->statistic("count") > 3) {
50
            $fields->push(new LiteralField("stats", '<h2>Payment Statistics</h2><ul><li>'.implode('</li><li>', $stats).'</li></ul>'));
51
        }
52 View Code Duplication
        if ($humanWhere = Session::get("SearchableOrderReport.humanWhere")) {
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...
53
            $fields->push(new LiteralField("humanWhere", "<p>Current Search: ".$humanWhere."</p>"), "ReportDescription");
54
            $fields->removeByName("ReportDescription");
55
            $fields->push(new FormAction('clearSearch', 'Clear Search'));
0 ignored issues
show
Documentation introduced by
'clearSearch' is of type string, but the function expects a object<The>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
56
        }
57
        $fields->push(new CheckboxSetField("Status", "Order Status", OrderDecorator::get_order_status_options()));
0 ignored issues
show
Documentation introduced by
'Status' is of type string, but the function expects a object<The>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
58
        $fields->push(new NumericField("OrderID", "Order ID"));
59
        $fields->push(new DateField("From", "From..."));
60
        $fields->push(new TimeField("FromTime", "Start time...", self::get_default_from_time_as_full_date_time(), "H:i a"));
0 ignored issues
show
Unused Code introduced by
The call to TimeField::__construct() has too many arguments starting with 'H:i a'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
61
        $fields->push(new DateField("Until", "Until..."));
62
        $fields->push(new TimeField("UntilTime", "End time...", self::get_default_until_time_as_full_date_time(), "H:i a"));
0 ignored issues
show
Unused Code introduced by
The call to TimeField::__construct() has too many arguments starting with 'H:i a'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
63
        $fields->push(new TextField("Email", "Email"));
64
        $fields->push(new TextField("FirstName", "First Name"));
65
        $fields->push(new TextField("Surname", "Surname"));
66
        $fields->push(new NumericField("HasMinimumPayment", "Has Minimum Payment of ..."));
67
        $fields->push(new NumericField("HasMaximumPayment", "Has Maximum Payment of ..."));
68
        $fields->push(new FormAction('doSearch', 'Apply Search'));
0 ignored issues
show
Documentation introduced by
'doSearch' is of type string, but the function expects a object<The>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
69
        $fields->push(new LiteralField('doExport', '<a href="SalesReport_Handler/fullsalesexport/">export all details (do a search first to limit results)</a>'));
70
        return $fields;
71
    }
72
73
    public function processform()
0 ignored issues
show
Coding Style introduced by
processform uses the super-global variable $_REQUEST 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...
74
    {
75
        $bt = defined('DB::USE_ANSI_SQL') ? "\"" : "`";
76
        $where = array();
77
        $having = array();
78
        $humanWhere = array();
79
        foreach ($_REQUEST as $key => $value) {
80
            $value = Convert::raw2sql($value);
81
            if ($value) {
82
                switch ($key) {
83
                    case "OrderID":
84
                        $where[] = " {$bt}Order{$bt}.{$bt}ID{$bt} = ".intval($value);
85
                        $humanWhere[] = ' OrderID equals '.intval($value);
86
                        break;
87 View Code Duplication
                    case "From":
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...
88
                        $d = new Date("date");
89
                        $d->setValue($value);
90
                        $t = new Time("time");
91
                        $cleanTime = trim(preg_replace('/([ap]m)/', "", Convert::raw2sql($_REQUEST["FromTime"])));
92
                        $t->setValue($cleanTime); //
93
                        $exactTime = strtotime($d->format("Y-m-d")." ".$t->Nice24());
94
                        $where[] = " UNIX_TIMESTAMP({$bt}Order{$bt}.{$bt}Created{$bt}) >= '".$exactTime."'";
95
                        $humanWhere[] = ' Order on or after '.Date("r", $exactTime);//r = Example: Thu, 21 Dec 2000 16:01:07 +0200 // also consider: l jS \of F Y H:i Z(e)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
96
                        break;
97 View Code Duplication
                    case "Until":
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...
98
                        $d = new Date("date");
99
                        $d->setValue($value);
100
                        $t = new Time("time");
101
                        $cleanTime = trim(preg_replace('/([ap]m)/', "", Convert::raw2sql($_REQUEST["FromTime"])));
102
                        $t->setValue($cleanTime); //
103
                        $exactTime = strtotime($d->format("Y-m-d")." ".$t->Nice24());
104
                        $where[] = " UNIX_TIMESTAMP({$bt}Order{$bt}.{$bt}Created{$bt}) <= '".$exactTime."'";
105
                        $humanWhere[] = ' Order before or on '.Date("r", $exactTime);//r = Example: Thu, 21 Dec 2000 16:01:07 +0200 // also consider: l jS \of F Y H:i Z(e)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
106
                        break;
107 View Code Duplication
                    case "Email":
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...
108
                        $where[] = " {$bt}Member{$bt}.{$bt}Email{$bt} = '".$value."'";
109
                        $humanWhere[] = ' Customer Email equals "'.$value.'"';
110
                        break;
111 View Code Duplication
                    case "FirstName":
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...
112
                        $where[] = " {$bt}Member{$bt}.{$bt}FirstName{$bt} LIKE '%".$value."%'";
113
                        $humanWhere[] = ' Customer First Name equals '.$value.'"';
114
                        break;
115 View Code Duplication
                    case "Surname":
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...
116
                        $where[] = " {$bt}Member{$bt}.{$bt}Surname{$bt} LIKE '%".$value."%'";
117
                        $humanWhere[] = ' Customer Surname equals "'.$value.'"';
118
                        break;
119
                    case "Status":
120
                        $subWhere = array();
121
                        foreach ($value as $item) {
122
                            $subWhere[] = " {$bt}Order{$bt}.{$bt}Status{$bt} = '".$item."'";
123
                            $humanWhere[] = ' Order Status equals "'.$item.'"';
124
                        }
125
                        if (count($subWhere)) {
126
                            $where[] = implode(" OR ", $subWhere);
127
                        }
128
                        break;
129 View Code Duplication
                    case "HasMinimumPayment":
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...
130
                        $having[] = ' RealPayments > '.intval($value);
131
                        $humanWhere[] = ' Real Payment of at least '.$this->currencyFormat($value);
132
                        break;
133 View Code Duplication
                    case "HasMaximumPayment":
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...
134
                        $having[] = ' RealPayments < '.intval($value);
135
                        $humanWhere[] = ' Real Payment of no more than '.$this->currencyFormat($value);
136
                        break;
137
                    //this has been included for SearchableProductSalesReport
138
                    case "Product":
139
                        $where[] = " IF(ProductVariationsForVariations.Title IS NOT NULL, CONCAT(ProductSiteTreeForVariations.Title,' : ', ProductVariationsForVariations.Title), IF(SiteTreeForProducts.Title IS NOT NULL, SiteTreeForProducts.Title, OrderAttribute.ClassName)) LIKE '%".$value."%'";
140
                        $humanWhere[] = ' Product includes the phrase '.$value.'"';
141
                        break;
142
143
                    default:
144
                     break;
145
                }
146
            }
147
        }
148
        return $this->saveProcessedForm($having, $where, $humanWhere);
149
    }
150
151
    protected function saveProcessedForm($having, $where, $humanWhere)
152
    {
153
        Session::set("SearchableOrderReport.having", implode(" AND ", $having));
154
        Session::set("SearchableOrderReport.where", implode(" AND", $where));
155
        Session::set("SearchableOrderReport.humanWhere", implode(", ", $humanWhere));
156
        return "ok";
157
    }
158
159
    public function getReportField()
160
    {
161
        $report = parent::getReportField();
162
        $report->setCustomCsvQuery($this->getExportQuery());
163
        return $report;
164
    }
165
166
    public function getCustomQuery()
0 ignored issues
show
Documentation introduced by
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 @return annotation as described here.

Loading history...
167
    {
168
        $bt = defined('DB::USE_ANSI_SQL') ? "\"" : "`";
169
            //buildSQL($filter = "", $sort = "", $limit = "", $join = "", $restrictClasses = true, $having = "")
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% 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...
170
        $where = Session::get("SearchableOrderReport.where");
171
        if (trim($where)) {
172
            $where = " ( $where ) AND ";
173
        }
174
        $where .= "({$bt}Payment{$bt}.{$bt}Status{$bt} = 'Success' OR {$bt}Payment{$bt}.{$bt}Status{$bt} = 'Pending' OR  {$bt}Payment{$bt}.{$bt}Status{$bt} IS NULL)";
175
        $query = singleton('Order')->buildSQL(
176
            $where,
177
            $sort = "{$bt}Order{$bt}.{$bt}Created{$bt} DESC",
178
            $limit = "",
179
            $join = "
180
				INNER JOIN {$bt}Member{$bt} ON {$bt}Member{$bt}.{$bt}ID{$bt} = {$bt}Order{$bt}.{$bt}MemberID{$bt}
181
				LEFT JOIN Payment ON {$bt}Payment{$bt}.{$bt}OrderID{$bt} = {$bt}Order{$bt}.{$bt}ID{$bt}
182
			"
183
        );
184
        $query->select[] = "SUM({$bt}Payment{$bt}.{$bt}Amount{$bt}) RealPayments";
185
        if ($having = Session::get("SearchableOrderReport.having")) {
186
            $query->having($having);
187
        }
188
        $query->groupby("{$bt}Order{$bt}.{$bt}ID{$bt}");
189
        return $query;
190
    }
191
192
    public function getExportFields()
193
    {
194
        return array(
195
            "OrderSummary" => "Order Details",
196
            "RealPayments" => "Payments",
197
            "Payment.Message" => "Reference",
198
            "Member.FirstName" => "Customer first name",
199
            "Member.Surname" => "Customer last name",
200
            "Member.HomePhone" => "Customer home phone",
201
            "Member.MobilePhone" => "Customer mobile phone",
202
            "Member.Email" => "Customer phone",
203
            "Member.Address" => "Customer address 1",
204
            "Member.AddressLine2" => "Customer address 2",
205
            "Member.City" => "Customer City",
206
        );
207
    }
208
209
    public function getExportQuery()
0 ignored issues
show
Documentation introduced by
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 @return annotation as described here.

Loading history...
210
    {
211
        $bt = defined('DB::USE_ANSI_SQL') ? "\"" : "`";
212
        //buildSQL($filter = "", $sort = "", $limit = "", $join = "", $restrictClasses = true, $having = "")
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% 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...
213
        $where = Session::get("SearchableOrderReport.where");
214
        if (trim($where)) {
215
            $where = " ( $where ) AND ";
216
        }
217
        $where .= "({$bt}Payment{$bt}.{$bt}Status{$bt} = 'Success' OR {$bt}Payment{$bt}.{$bt}Status{$bt} = 'Pending' OR  {$bt}Payment{$bt}.{$bt}Status{$bt} IS NULL)";
218
        $query = singleton('Order')->buildSQL(
219
            $where,
220
            $sort = "{$bt}Order{$bt}.{$bt}Created{$bt} DESC",
221
            $limit = "",
222
            $join = "
223
				INNER JOIN {$bt}Member{$bt} ON {$bt}Member{$bt}.{$bt}ID{$bt} = {$bt}Order{$bt}.{$bt}MemberID{$bt}
224
				LEFT JOIN Payment ON {$bt}Payment{$bt}.{$bt}OrderID{$bt} = {$bt}Order{$bt}.{$bt}ID{$bt}
225
			"//
226
        );
227
        $fieldArray = $this->getExportFields();
228 View Code Duplication
        if (is_array($fieldArray)) {
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...
229
            if (count($fieldArray)) {
230
                foreach ($fieldArray as $key => $field) {
231
                    $query->select[] = $key;
232
                }
233
            }
234
        }
235
        foreach ($query->select as $key=>$value) {
236
            if ($value == "RealPayments") {
237
                $query->select[$key] = "SUM(IF(Payment.Status = 'Success',{$bt}Payment{$bt}.{$bt}Amount{$bt}, 0)) RealPayments";
238
            }
239
        }
240 View Code Duplication
        foreach ($query->select as $key=>$value) {
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...
241
            if ($value == "OrderSummary") {
242
                $query->select[$key] = "CONCAT({$bt}Order{$bt}.{$bt}ID{$bt}, ' :: ', {$bt}Order{$bt}.{$bt}Created{$bt}, ' :: ', {$bt}Order{$bt}.{$bt}Status{$bt}) AS OrderSummary";
243
            }
244
        }
245
        $query->groupby("{$bt}Order{$bt}.{$bt}ID{$bt}");
246
        if ($having = Session::get("SearchableOrderReport.having")) {
247
            $query->having($having);
248
        }
249
        return $query;
250
    }
251
252
    protected function currencyFormat($v)
0 ignored issues
show
Documentation introduced by
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 @return annotation as described here.

Loading history...
253
    {
254
        $c = new Currency("currency");
0 ignored issues
show
Deprecated Code introduced by
The class Currency has been deprecated with message: 2.5 Use Money class

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
255
        $c->setValue($v);
256
        return $c->Nice();
257
    }
258
}
259