Completed
Push — master ( 9c30ba...8187a2 )
by Song
14s queued 10s
created

CsvExporter::title()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Grid\Exporters;
4
5
use Encore\Admin\Grid\Column;
6
7
class CsvExporter extends AbstractExporter
8
{
9
    /**
10
     * @var string
11
     */
12
    protected $filename;
13
14
    /**
15
     * @var \Closure
16
     */
17
    protected $callback;
18
19
    /**
20
     * @var array
21
     */
22
    protected $exceptColumns;
23
24
    /**
25
     * @var array
26
     */
27
    protected $onlyColumns;
28
29
    /**
30
     * @var []\Closure
31
     */
32
    protected $columnCallbacks;
33
34
    /**
35
     * @var []\Closure
36
     */
37
    protected $titleCallbacks;
38
39
    /**
40
     * @var array
41
     */
42
    protected $visibleColumns;
43
44
    /**
45
     * @var array
46
     */
47
    protected $columnUseOriginalValue;
48
49
    /**
50
     * @param string $filename
51
     *
52
     * @return $this
53
     */
54
    public function filename(string $filename = ''): self
55
    {
56
        $this->filename = $filename;
57
58
        return $this;
59
    }
60
61
    /**
62
     * @param \Closure $closure
63
     */
64
    public function setCallback(\Closure $closure): self
65
    {
66
        $this->callback = $closure;
67
68
        return $this;
69
    }
70
71
    /**
72
     * @param array $columns
73
     *
74
     * @return $this
75
     */
76
    public function except(array $columns = []): self
77
    {
78
        $this->exceptColumns = $columns;
79
80
        return $this;
81
    }
82
83
    /**
84
     * @param array $columns
85
     *
86
     * @return $this
87
     */
88
    public function only(array $columns = []): self
89
    {
90
        $this->onlyColumns = $columns;
91
92
        return $this;
93
    }
94
95
    /**
96
     * @param array $columns
97
     *
98
     * @return $this
99
     */
100
    public function originalValue($columns = []): self
101
    {
102
        $this->columnUseOriginalValue = $columns;
103
104
        return $this;
105
    }
106
107
    /**
108
     * @param string   $name
109
     * @param \Closure $callback
110
     *
111
     * @return $this
112
     */
113
    public function column(string $name, \Closure $callback): self
114
    {
115
        $this->columnCallbacks[$name] = $callback;
116
117
        return $this;
118
    }
119
120
    /**
121
     * @param string   $name
122
     * @param \Closure $callback
123
     *
124
     * @return $this
125
     */
126
    public function title(string $name, \Closure $callback): self
127
    {
128
        $this->titleCallbacks[$name] = $callback;
129
130
        return $this;
131
    }
132
133
    /**
134
     * Get download response headers.
135
     *
136
     * @return array
137
     */
138
    protected function getHeaders()
139
    {
140
        if (!$this->filename) {
141
            $this->filename = $this->getTable();
142
        }
143
144
        return [
145
            'Content-Encoding'    => 'UTF-8',
146
            'Content-Type'        => 'text/csv;charset=UTF-8',
147
            'Content-Disposition' => "attachment;filename=\"{$this->filename}.csv\"",
148
        ];
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function export()
155
    {
156
        if ($this->callback) {
157
            call_user_func($this->callback, $this);
158
        }
159
160
        $response = function () {
161
            $handle = fopen('php://output', 'w');
162
            $titles = [];
163
164
            $this->chunk(function ($collection) use ($handle, &$titles) {
165
                Column::setOriginalGridModels($collection);
166
167
                $original = $current = $collection->toArray();
168
169
                $this->grid->getColumns()->map(function (Column $column) use (&$current) {
170
                    $current = $column->fill($current);
171
                    $this->grid->columnNames[] = $column->getName();
172
                });
173
174
                // Write title
175
                if (empty($titles)) {
176
                    fputcsv($handle, $titles = $this->getVisiableTitles());
177
                }
178
179
                // Write rows
180
                foreach ($current as $index => $record) {
181
                    fputcsv($handle, $this->getVisiableFields($record, $original[$index]));
182
                }
183
            });
184
            fclose($handle);
185
        };
186
187
        response()->stream($response, 200, $this->getHeaders())->send();
0 ignored issues
show
Bug introduced by
The method stream does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
188
189
        exit;
190
    }
191
192
    /**
193
     * @return array
194
     */
195
    protected function getVisiableTitles()
196
    {
197
        $titles = $this->grid->visibleColumns()
0 ignored issues
show
Bug introduced by
The method mapWithKeys does only exist in Illuminate\Support\Collection, but not in Encore\Admin\Grid\Concerns\CanHidesColumns.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
198
            ->mapWithKeys(function (Column $column) {
199
                $columnName = $column->getName();
200
                $columnTitle = $column->getLabel();
201
                if (isset($this->titleCallbacks[$columnName])) {
202
                    $columnTitle = $this->titleCallbacks[$columnName]($columnTitle);
203
                }
204
205
                return [$columnName => $columnTitle];
206
            });
207
208
        if ($this->onlyColumns) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->onlyColumns of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
209
            $titles = $titles->only($this->onlyColumns);
210
        }
211
212
        if ($this->exceptColumns) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->exceptColumns of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
213
            $titles = $titles->except($this->exceptColumns);
214
        }
215
216
        $this->visibleColumns = $titles->keys();
217
218
        return $titles->values()->toArray();
219
    }
220
221
    /**
222
     * @param array $value
223
     * @param array $original
224
     *
225
     * @return array
226
     */
227
    public function getVisiableFields(array $value, array $original): array
228
    {
229
        $fields = [];
230
231
        foreach ($this->visibleColumns as $column) {
232
            $fields[] = $this->getColumnValue(
233
                $column,
234
                data_get($value, $column),
235
                data_get($original, $column)
236
            );
237
        }
238
239
        return $fields;
240
    }
241
242
    /**
243
     * @param string $column
244
     * @param mixed  $value
245
     * @param mixed  $original
246
     *
247
     * @return mixed
248
     */
249
    protected function getColumnValue(string $column, $value, $original)
250
    {
251
        if (!empty($this->columnUseOriginalValue)
252
            && in_array($column, $this->columnUseOriginalValue)) {
253
            return $original;
254
        }
255
256
        if (isset($this->columnCallbacks[$column])) {
257
            return $this->columnCallbacks[$column]($value, $original);
258
        }
259
260
        return $value;
261
    }
262
}
263