Completed
Push — 2 ( 619ddf...aa370d )
by Morven
01:31
created

GridFieldColumnDateFormatter::findDateFields()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.5386
c 0
b 0
f 0
cc 7
nc 5
nop 0
1
<?php
2
3
namespace SilverStripe\GridFieldAddOns;
4
5
use SilverStripe\Core\ClassInfo;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\ORM\FieldType\DBDate;
8
use SilverStripe\Forms\GridField\GridField;
9
use SilverStripe\Forms\GridField\GridFieldDataColumns;
10
use SilverStripe\Forms\GridField\GridFieldSortableHeader;
11
use SilverStripe\Forms\GridField\GridField_ColumnProvider;
12
use SilverStripe\Core\Config\Configurable;
13
14
/**
15
 * Component that converts all date columns in the existing GridField into the
16
 * chosen format (by default it uses DateTime::Nice()).
17
 */
18
class GridFieldColumnDateFormatter implements GridField_ColumnProvider
19
{
20
    use Configurable;
21
22
    /**
23
     * `GridField` we are working with
24
     *
25
     * @var GridField
26
     */
27
    protected $grid_field;
28
29
    /**
30
     * Overwrite the date format (provided by config)
31
     * for this instance
32
     *
33
     * @var string
34
     */
35
    protected $date_type;
36
37
    /**
38
     * The date formatting method to use (this corresponds
39
     * to a Date method on the Date/DateTime data type).
40
     *
41
     * @var string
42
     */
43
    private static $default_date_type = ".Nice";
44
45
    /**
46
     * Find the column/header provider for this gridfield and then augment the
47
     * columns so that any dates are re-formatted.
48
     *
49
     * @param GridField $gridField Current gridfield
0 ignored issues
show
Bug introduced by
There is no parameter named $gridField. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
50
     * @param array     $columns   List reference of all column names.
51
     * 
52
     * @return null
53
     */
54
    public function augmentColumns($grid_field, &$columns)
55
    {
56
        $this->setGridField($grid_field);
57
        $config = $grid_field->getConfig();
58
        $db = Config::inst()->get($grid_field->getModelClass(), "db");
0 ignored issues
show
Unused Code introduced by
$db 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...
59
        $fields = $this->findDateFields();
60
61
        // First setup columns
62
        foreach ($config->getComponents() as $component) {
63
            $is_header = ($component instanceof GridFieldSortableHeader);
64
            $is_columns = $this->isColumnProvider($component);
65
66
            // If we are working with a set of data columns, look for
67
            // date/datetime columns
68
            if ($is_columns && method_exists($component, "getDisplayFields")) {
69
                $display_fields = $component->getDisplayFields($grid_field);
70
                foreach ($fields as $field) {
71
                    $display_fields = $this->changeKeys(
72
                        $field["Sort"],
73
                        $field["Column"],
74
                        $display_fields
75
                    );
76
                }
77
                $component->setDisplayFields($display_fields);
78
                $columns = array_keys($display_fields);
79
            }
80
81
            // If we are working with sortable headers, look for
82
            // date/datetime columns
83
            if ($is_header && count($component->getFieldSorting()) == 0) {
84
                $sort_fields = [];
85
                foreach ($fields as $field) {
86
                    $sort_fields[$field["Column"]] = $field["Sort"];
87
                }
88
                $component->setFieldSorting($sort_fields);
89
            }
90
        }
91
    }
92
93
    /**
94
     * Create an array of fields, titles and values that we
95
     * use to setup sortable fields in the following format:
96
     *
97
     * - Title (the human readable name of the column)
98
     * - Column (the actual field used to display data)
99
     * - Sort (DB the column used to sort the data)
100
     *
101
     * @return array
102
     */
103
    protected function findDateFields()
104
    {
105
        $grid_field = $this->getGridField();
106
        $config = $grid_field->getConfig();
107
        $class = $grid_field->getModelClass();
108
        $obj = $class::singleton();
109
        $fields = [];
110
111
        // First setup columns
112
        foreach ($config->getComponents() as $component) {
113
            // If we are working with a set of data columns, look for
114
            // date/datetime columns
115
            if ($this->isColumnProvider($component) && method_exists($component, "getDisplayFields")) {                
116
                foreach ($component->getDisplayFields($grid_field) as $k => $v) {
117
                    $field = $obj->dbObject($k);
118
                    if (isset($field) && $field instanceof DBDate) {
119
                        $fields[] = [
120
                            "Title" => $v,
121
                            "Column" => $k . $this->getDateType(),
122
                            "Sort" => $k
123
                        ];
124
                    }
125
                }
126
            }
127
        }
128
129
        return $fields;
130
    }
131
132
    /**
133
     * Is the provided component a `GridField_ColumnProvider`?
134
     *
135
     * @param object $component The current component
136
     *
137
     * @return boolean
138
     */
139
    protected function isColumnProvider($component)
140
    {
141
        $class = is_object($component) ? get_class($component) : $component;
142
        return ClassInfo::classImplements(
143
            $class,
144
            GridField_ColumnProvider::class
145
        );
146
    }
147
148
    /**
149
     * Change the array keys on the provided array to the provided alternative
150
     * (thanks to: https://stackoverflow.com/a/14227644/4161644)
151
     *
152
     * @param string $original Original key
153
     * @param string $new      New key
154
     * @param array  $array    Haystack array
155
     *
156
     * @return array
157
     */
158
    protected function changeKeys($original, $new, &$array)
159
    {
160
        foreach ($array as $k => $v) {
161
            $res[$k === $original ? $new : $k] = $v;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$res was never initialized. Although not strictly required by PHP, it is generally a good practice to add $res = 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...
162
        }
163
        return $res;
0 ignored issues
show
Bug introduced by
The variable $res 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...
164
    }
165
166
    /**
167
     * Get `GridField` we are working with
168
     *
169
     * @return  GridField
170
     */
171
    public function getGridField()
172
    {
173
        return $this->grid_field;
174
    }
175
176
    /**
177
     * Set `GridField` we are working with
178
     *
179
     * @param GridField $grid_field `GridField` we are working with
180
     *
181
     * @return self
182
     */
183
    public function setGridField(GridField $grid_field)
184
    {
185
        $this->grid_field = $grid_field;
186
187
        return $this;
188
    }
189
190
    /**
191
     * Get type for this instance
192
     *
193
     * @return string
194
     */ 
195
    public function getDateType()
196
    {
197
        if (!empty($this->date_type)) {
198
            return $this->date_type;
199
        } else {
200
            return $this->config()->default_date_type;
201
        }
202
    }
203
204
    /**
205
     * Set type for this instance
206
     *
207
     * @param string $date_type for this instance
208
     *
209
     * @return self
210
     */ 
211
    public function setDateType(string $date_type)
212
    {
213
        $this->date_type = $date_type;
214
        return $this;
215
    }
216
217
    /**
218
     * Component doesn't provide columns
219
     *
220
     * @param GridField $gridField Current GridField
221
     *
222
     * @return array
223
     */
224
    public function getColumnsHandled($gridField)
225
    {
226
        return array();
227
    }
228
229
    /**
230
     * Component doesn't provide columns
231
     *
232
     * @param GridField $gridField Current GridField
233
     *
234
     * @return array
235
     */
236
    public function getColumnMetadata($gridField, $columnName)
237
    {
238
        return array();
239
    }
240
241
    /**
242
     * Component doesn't provide columns
243
     *
244
     * @param GridField $gridField Current GridField
245
     *
246
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean?

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...
247
     */
248
    public function getColumnContent($gridField, $record, $columnName)
249
    {
250
        return false;
251
    }
252
253
    /**
254
     * Component doesn't provide columns
255
     *
256
     * @param GridField $gridField Current GridField
257
     *
258
     * @return array
259
     */
260
    public function getColumnAttributes($gridField, $record, $columnName)
261
    {
262
        return array();
263
    }
264
}