Skip to content

Instantly share code, notes, and snippets.

@jaspal747
Last active April 11, 2023 19:41
Show Gist options
  • Save jaspal747/2bd515f9e318b0331f3ca3d2297742c5 to your computer and use it in GitHub Desktop.
Save jaspal747/2bd515f9e318b0331f3ca3d2297742c5 to your computer and use it in GitHub Desktop.
PHP Spout Helper - A helper class around php box/spout to format the header row as array keys like CSV column names. Inspired by: https://github.com/box/spout/issues/368
<?php
/**
*
* Inspired by: https://github.com/box/spout/issues/368
*
* Simple helper class for Spout - to return rows indexed by the header in the sheet
*
* Author: Jaspal Singh - https://github.com/jaspal747
* Feel free to make any edits as needed. Cheers!
*
*/
class SpoutHelper {
private $rawHeadersArray = []; //Local array to hold the Raw Headers for performance
private $formattedHeadersArray = []; //Local array to hold the Formatted Headers for performance
private $headerRowNumber; //Row number where the header col is located in the file
/**
* Initialize on a per sheet basis
* Allow users to mention which row number contains the headers
*/
public function __construct($sheet, $headerRowNumber = 1) {
$this->flushHeaders();
$this->headerRowNumber = $headerRowNumber;
$this->getFormattedHeaders($sheet);//Since this also calls the getRawHeaders, we will have both the arrays set at once
}
/**
*
* Set the rawHeadersArray by getting the raw headers from the headerRowNumber or the 1st row
* Once done, set them to a local variable for being reused later
*
*/
public function getRawHeaders($sheet) {
if (empty($this->rawHeadersArray)) {
/**
* first get column headers
*/
foreach ($sheet->getRowIterator() as $key => $row) {
if ($key == $this->headerRowNumber) {
/**
* iterate once to get the column headers
*/
$this->rawHeadersArray = $row->toArray();
break;
}
}
} else {
/**
* From local cache
*/
}
return $this->rawHeadersArray;
}
/**
*
* Set the formattedHeadersArray by getting the raw headers and the parsing them
* Once done, set them to a local variable for being reused later
*
*/
public function getFormattedHeaders($sheet) {
if (empty($this->formattedHeadersArray)) {
$this->formattedHeadersArray = $this->getRawHeaders($sheet);
/**
* Now format them
*/
foreach ($this->formattedHeadersArray as $key => $value) {
if (is_a($value, 'DateTime')) { //Somehow instanceOf does not work well with DateTime, hence using is_a -- ?
$this->formattedHeadersArray[$key] = $value->format('Y-m-d');//Since the dates in headers are avilable as DateTime Objects
} else {
$this->formattedHeadersArray[$key] = strtolower(str_replace(' ' , '_', trim($value)));
}
/**
* Add more rules here as needed
*/
}
} else {
/**
* Return from local cache
*/
}
return $this->formattedHeadersArray;
}
/**
* Return row with Raw Headers
*/
public function rowWithRawHeaders($rowArray) {
return $this->returnRowWithHeaderAsKeys($this->rawHeadersArray, $rowArray);
}
/**
* Return row with Formatted Headers
*/
public function rowWithFormattedHeaders($rowArray) {
return $this->returnRowWithHeaderAsKeys($this->formattedHeadersArray, $rowArray);
}
/**
* Set the headers to keys and row as values
*/
private function returnRowWithHeaderAsKeys($headers, $rowArray) {
$headerColCount = count($headers);
$rowColCount = count($rowArray);
$colCountDiff = $headerColCount - $rowColCount;
if ($colCountDiff > 0) {
//Pad the rowArray with empty values
$rowArray = array_pad($rowArray, $headerColCount, '');
}
return array_combine($headers, $rowArray);
}
/**
* Flush local caches before each sheet
*/
public function flushHeaders() {
$this->formattedHeadersArray = [];
$this->rawHeadersArray = [];
}
}
@junioro12
Copy link

Works great so far...thanks for this!

@melitonlazaro
Copy link

Thank you for this!

@neodisco
Copy link

Legend :)

@jaspal747
Copy link
Author

Appreciate all the kind words folks! :)

@LeBra3115
Copy link

Thank you! life saver!

@p1xel007
Copy link

p1xel007 commented Nov 5, 2021

You deserve a medal!

@pbowyer
Copy link

pbowyer commented Feb 10, 2022

A thank you from me as well! This saved me much time (and mistakes).

@p1xel007
Copy link

p1xel007 commented May 2, 2022

@jaspal747

What if I had CSV with different delimiter than comma? For example | (pipe)?

@jaspal747
Copy link
Author

@p1xel007 please refer https://opensource.box.com/spout/docs/ for setting your custom delimiter.

use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;

$reader = ReaderEntityFactory::createReaderFromFile('/path/to/file.csv');
/** All of these methods have to be called before opening the reader. */
$reader->setFieldDelimiter('|');
$reader->setFieldEnclosure('@');

The helper code I have written should work with any delimiter as it simply uses the base library to decode each row ($row->toArray()).
Regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment