Issues (4)

src/CsvLoader/CsvLoader.php (3 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jtotty\CsvLoader;
6
7
use Jtotty\Steps\CheckGroupOptionsStep;
8
use Jtotty\Steps\CheckPupilDobStep;
9
use Jtotty\Steps\CheckPupilGenderStep;
10
use Jtotty\Steps\CheckPupilNamesStep;
11
use Port\Csv\CsvReader;
12
use Port\Csv\CsvWriter;
13
use Port\Steps\Step\FilterStep;
14
use Port\Steps\Step\MappingStep;
15
use Port\Steps\Step\ValueConverterStep;
16
use Port\Steps\StepAggregator as Workflow;
17
use Port\ValueConverter\DateTimeValueConverter;
18
use Port\Writer\ArrayWriter;
19
20
class CsvLoader
21
{
22
    /**
23
     * Holds an instance of SplFileObject.
0 ignored issues
show
The type Jtotty\CsvLoader\SplFileObject was not found. Did you mean SplFileObject? If so, make sure to prefix the type with \.
Loading history...
24
     *
25
     * @var SplFileObject
26
     */
27
    private $file;
28
29
    /**
30
     * Holds an isntance of file contents as an associative array.
31
     *
32
     * @var array
33
     */
34
    private $contents = [];
35
36
    /**
37
     * Holds an instance of the column name mappings.
38
     *
39
     * @var array
40
     */
41
    private $columnMap;
42
43
    /**
44
     * Holds an instance of the \Port\Csv\CsvReader.
45
     *
46
     * @var \Port\Csv\CsvReader
47
     */
48
    private $reader;
49
50
    /**
51
     * Holds an instanec of the \Port\Steps\StepAggregator.
52
     *
53
     * @var \Port\Steps\StepAggregator
54
     */
55
    private $workflow;
56
57
    /**
58
     * Constructor method.
59
     */
60
    public function __constructor()
61
    {
62
    }
63
64
    /**
65
     * Load the file to the SplFileObject.
66
     *
67
     * @return void
68
     */
69
    public function loadFile(string $file_path)
70
    {
71
        // Set the file
72
        $this->file = new \SplFileObject($file_path);
0 ignored issues
show
Documentation Bug introduced by
It seems like new SplFileObject($file_path) of type SplFileObject is incompatible with the declared type Jtotty\CsvLoader\SplFileObject of property $file.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
73
74
        // Add file to reader
75
        $this->reader = new CsvReader($this->file);
76
        $this->reader->setHeaderRowNumber(0, CsvReader::DUPLICATE_HEADERS_INCREMENT);
77
78
        // Initialise the workflow!!
79
        $this->createWorkFlow();
80
    }
81
82
    /**
83
     * Starts the workflow to process the array data.
84
     *
85
     * @return void
86
     */
87
    public function createWorkFlow()
88
    {
89
        // Initialise Workflow
90
        $this->workflow = new Workflow($this->reader);
91
92
        // The modified array data will be saved to `$this->contents`
93
        $writer = new ArrayWriter($this->contents);
94
        $this->workflow->addWriter($writer);
95
    }
96
97
    /**
98
     * Returns the number of data rows.
99
     *
100
     * @return Integer
101
     */
102
    public function getDataCount()
103
    {
104
        return $this->reader->count();
105
    }
106
107
    /**
108
     * Returns the column headings from the uploaded csv.
109
     *
110
     * @return Array
111
     */
112
    public function getColumnHeadings()
113
    {
114
        return $this->reader->getColumnHeaders();
115
    }
116
117
    /**
118
     * Run the workflow.
119
     *
120
     * @return void
121
     */
122
    public function processData()
123
    {
124
        $this->workflow->process();
125
    }
126
127
    /**
128
     * Stop the workflow so we can unlink the file.
129
     *
130
     * @return void
131
     */
132
    public function stopWorkflow()
133
    {
134
        $this->workflow = null;
135
    }
136
137
    /**
138
     * Sets the data column mapping array.
139
     *
140
     * @return void
141
     */
142
    public function setColumnMap(array $columnMap)
143
    {
144
        $this->columnMap = $columnMap;
145
    }
146
147
    /**
148
     * Remove rows that have no data in all columns.
149
     *
150
     * @return void
151
     */
152
    public function removeEmptyRowsStep()
153
    {
154
        $columnCount = count($this->getColumnHeadings());
155
156
        $filterStep = new FilterStep();
157
        $filterStep->add(function ($input) use ($columnCount) {
158
            $blanks = 0;
159
            foreach ($input as $value) {
160
                if (empty($value)) {
161
                    ++$blanks;
162
                }
163
            }
164
165
            return $blanks !== $columnCount;
166
        });
167
168
        $this->workflow->addStep($filterStep);
169
    }
170
171
    /**
172
     * Renames the column names according to the map.
173
     *
174
     * @return void
175
     */
176
    public function mapColumnNamesStep()
177
    {
178
        // Create a mapping step
179
        $mappingStep = new MappingStep();
180
181
        // Iterate through the column names that need changing
182
        foreach ($this->columnMap as $key => $value) {
183
            $mappingStep->map('[' . $key . ']', '[' . $value . ']');
184
        }
185
186
        // Add to the workflow
187
        $this->workflow->addStep($mappingStep);
188
    }
189
190
    /**
191
     * Converts date of birth to correct format.
192
     *
193
     * @return void
194
     */
195
    public function convertDobStep()
196
    {
197
        // Format the dob string so we can convert it correctly
198
        $this->workflow->addStep(new CheckPupilDobStep());
199
200
        // Convert from input format to output
201
        $converterStep = new ValueConverterStep();
202
        $converterStep->add('[DOB]', new DateTimeValueConverter(null, 'Y-m-d'));
203
204
        $this->workflow->addStep($converterStep);
205
    }
206
207
    /**
208
     * Very specific method to check if a user has entered
209
     * the entire pupil's name into one column (Either forename or surname).
210
     */
211
    public function checkPupilNamesStep()
212
    {
213
        $this->workflow->addStep(new CheckPupilNamesStep());
214
    }
215
216
    /**
217
     * Step to check for validity of pupil gender and convert to
218
     * appropriate 'male' or 'female' string value.
219
     */
220
    public function checkPupilGenderStep()
221
    {
222
        $this->workflow->addStep(new CheckPupilGenderStep());
223
    }
224
225
    /**
226
     * Very specific method to convert the additional attributes
227
     * to.
228
     */
229
    public function checkGroupOptionValuesStep(array $options)
230
    {
231
        $this->workflow->addStep(new CheckGroupOptionsStep($options));
232
    }
233
234
    /**
235
     * Returns the content of the csv file as array.
236
     */
237
    public function getProcessedContents()
238
    {
239
        return $this->contents;
240
    }
241
242
    /**
243
     * Writes contents of an array to a csv file.
244
     */
245
    public function writeToCsv(array $data, string $filePath)
246
    {
247
        $writer = new CsvWriter(','); // Set delimiter to comma (default = semicolon)
248
        $writer->setStream(fopen($filePath, 'w'));
0 ignored issues
show
It seems like fopen($filePath, 'w') can also be of type false; however, parameter $stream of Port\Writer\AbstractStreamWriter::setStream() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

248
        $writer->setStream(/** @scrutinizer ignore-type */ fopen($filePath, 'w'));
Loading history...
249
250
        // Write column headers
251
        $columnHeaders = array_keys($data[0]);
252
        $writer->writeItem($columnHeaders);
253
254
        // Get row data and write
255
        foreach ($data as $pupil) {
256
            $writer->writeItem(array_values($pupil)); // Reset associative keys to indexes
257
        }
258
259
        $writer->finish();
260
    }
261
}
262