Completed
Push — master ( f09455...266dce )
by Song
02:34
created

CsvExporter::filename()   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 1
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;
6
use Encore\Admin\Grid\Column;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Collection;
10
use Illuminate\Support\Str;
11
12
class CsvExporter extends AbstractExporter
13
{
14
    /**
15
     * @var string
16
     */
17
    protected $filename;
18
19
    /**
20
     * @var \Closure
21
     */
22
    protected $callback;
23
24
    /**
25
     * @var array
26
     */
27
    protected $exceptColumns;
28
29
    /**
30
     * @var array
31
     */
32
    protected $onlyColumns;
33
34
    /**
35
     * @var []\Closure
36
     */
37
    protected $columnCallbacks;
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
     * @return $this
52
     */
53
    public function filename(string $filename = ''): self
54
    {
55
        $this->filename = $filename;
56
57
        return $this;
58
    }
59
60
    /**
61
     * @param \Closure $closure
62
     */
63
    public function setCallback(\Closure $closure): self
64
    {
65
        $this->callback = $closure;
66
67
        return $this;
68
    }
69
70
    /**
71
     * @param array $columns
72
     * @return $this
73
     */
74
    public function except(array $columns = []): self
75
    {
76
        $this->exceptColumns = $columns;
77
78
        return $this;
79
    }
80
81
    /**
82
     * @param array $columns
83
     * @return $this
84
     */
85
    public function only(array $columns = []): self
86
    {
87
        $this->onlyColumns = $columns;
88
89
        return $this;
90
    }
91
92
    /**
93
     * @param array $columns
94
     * @return $this
95
     */
96
    public function originalValue($columns = []): self
97
    {
98
        $this->columnUseOriginalValue = $columns;
99
100
        return $this;
101
    }
102
103
    /**
104
     * @param string $name
105
     * @param \Closure $callback
106
     * @return $this
107
     */
108
    public function column(string $name, \Closure $callback): self
109
    {
110
        $this->columnCallbacks[$name] = $callback;
111
112
        return $this;
113
    }
114
115
    /**
116
     * Get download response headers.
117
     *
118
     * @return array
119
     */
120
    protected function getHeaders()
121
    {
122
        if (!$this->filename) {
123
            $this->filename = $this->getTable();
124
        }
125
126
        return [
127
            'Content-Encoding'    => 'UTF-8',
128
            'Content-Type'        => 'text/csv;charset=UTF-8',
129
            'Content-Disposition' => "attachment;filename=\"{$this->filename}.csv\"",
130
        ];
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function export()
137
    {
138
        if ($this->callback) {
139
            call_user_func($this->callback, $this);
140
        }
141
142
        $response = function () {
143
            $handle = fopen('php://output', 'w');
144
            $titles = [];
145
146
            $this->chunk(function ($collection) use ($handle, &$titles) {
147
148
                Column::setOriginalGridModels($collection);
149
150
                $original = $current = $collection->toArray();
151
152
                $this->grid->getColumns()->map(function (Column $column) use (&$current) {
153
                    $current                   = $column->fill($current);
154
                    $this->grid->columnNames[] = $column->getName();
155
                });
156
157
                // Write title
158
                if (empty($titles)) {
159
                    fputcsv($handle, $titles = $this->getVisiableTitles());
160
                }
161
162
                // Write rows
163
                foreach ($current as $index => $record) {
164
                    fputcsv($handle, $this->getVisiableFields($record, $original[$index]));
165
                }
166
            });
167
            fclose($handle);
168
        };
169
170
        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...
171
172
        exit;
173
    }
174
175
    /**
176
     * @return array
177
     */
178
    protected function getVisiableTitles()
179
    {
180
        $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...
181
            ->mapWithKeys(function (Column $column) {
182
                return [$column->getName() => $column->getLabel()];
183
            });
184
185
        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...
186
            $titles = $titles->only($this->onlyColumns);
187
        }
188
189
        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...
190
            $titles = $titles->except($this->exceptColumns);
191
        }
192
193
        $this->visibleColumns = $titles->keys();
194
195
        return $titles->values()->toArray();
196
    }
197
198
    /**
199
     * @param array $value
200
     * @param array $original
201
     * @return array
202
     */
203
    public function getVisiableFields(array $value, array $original): array
204
    {
205
        $fields = [];
206
207
        foreach ($this->visibleColumns as $column) {
208
            $fields[] = $this->getColumnValue(
209
                $column,
210
                data_get($value, $column),
211
                data_get($original, $column)
212
            );
213
        }
214
215
        return $fields;
216
    }
217
218
    /**
219
     * @param string $column
220
     * @param mixed $value
221
     * @param mixed $original
222
     * @return mixed
223
     */
224
    protected function getColumnValue(string $column, $value, $original)
225
    {
226
        if (!empty($this->columnUseOriginalValue)
227
            && in_array($column, $this->columnUseOriginalValue)) {
228
            return $original;
229
        }
230
231
        if (isset($this->columnCallbacks[$column])) {
232
            return $this->columnCallbacks[$column]($value, $original);
233
        }
234
235
        return $value;
236
    }
237
}
238