jtotty /
csv-loader
| 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
Bug
introduced
by
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
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
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 |