Generate a matrix from an excel worksheet [ExcelBundle-Symfony] - php

I made the follow function for get a matrix from a worksheet.
private function getMatrixFromSheet($worksheet){
foreach ($worksheet->getRowIterator() as $row) {
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
foreach ($cellIterator as $cell) {
if (!is_null($cell)) {
$matrix[$cell->getRow()][$cell->getColumn()] = $cell->getCalculatedValue();
}
}
}
return $matrix;
}
In this way if I want to access to the second row, second col cell, I can do $matrix[2]['B'].
I need to be able to access to the matrix indexes both as number in order to access to the previous cell as $matrix[2][2].
Is there a way to do this?
I try to convert $cell->getColumn() result in a integer, but unfortunately (int) "a" == 0

If you just want a numeric value for the column, then PHPExcel provides a static helper method to do precisely that
$columnIndex = PHPExcel_Cell::columnIndexFromString($cell->getColumn());
Which returns 1 for column A, 2 for column B, 27 for column AA, 28 for column AB, etc
However, you might find that you can simplify your function completely, because PHPExcel also provides the toArray() method to do exactly what you've written this method for:
private function getMatrixFromSheet($worksheet){
return $worksheet->toArray();
}
Note that row/column offsets for the matrix returned by toArray() are 0-based

Related

How to fetch one single row of an excel sheet with Box\Spout

I'm trying out Box\Spout and rewriting some code which was formerly using PHPExcel. Iterating through rows is clear but in a few cases I need to address directly one specific row. I cannot find this in the documentation.
Something like:
$row = $sheet->getRow(8);
You can't access rows directly. If you need the 8th row, you'll need to read the first 8 rows... This is because Spout does not load the entire spreadsheet in memory so it reads the data row by row.
However, you can do something like this:
foreach ($reader->getSheetIterator() as $sheet) {
foreach ($sheet->getRowIterator() as $rowIndex => $row) {
if ($rowIndex !== 8) {
continue;
}
// do something with row 8
}
}
Try using the iterator's key() method:
$it = $sheet->getRowIterator();
$row = $it->key(8);

Read merged cells PhPExcel

I'm trying to read a merged cell in phpexcel, my problem is that the cell is merged on A:B:C:D:E:F ( can't argue with what they want )
$i= 0;
foreach ($worksheet->getRowIterator() as $row) {
if ($i > 10) break ;
$i ++;
$cellIterator = $row->getCellIterator();
foreach ($cellIterator as $cell) {
if($cell->getColumn()== 'A:B:C:D:E:F'){
$specification=$cell->getCalculatedValue();
var_dump($specification);die();
// some other code
always dumps null.
I've tried $cell->getColumn()== 'A' since that the cell starts at A, but dumps null as well.
I would appreciate any help.
I don't understand exactly what you're trying to do here, because getColumn() will only ever return a single column address like A or B and never anything like 'A:B:C:D:E:F'
It may be sensible to iterate only existing cells using
$cellIterator->setIterateOnlyExistingCells(true);
but there's a couple of functions that may help you with merged cells:
$cell->isInMergeRange()
will return a Boolean true/false, indicating if the cell is part of a merge range
and
$cell->isMergeRangeValueCell()
can be used to test whether a cell within a merge range is the top-left (primary) cell of that range, and will return a Boolean true/false indicating whether it holds the actual data value/type etc for that range... Note that it will return a false if the cell isn't part of any merge range

PHPExcel and subtotal

I use PHPExcel to parse XLSX files. I have found that SUBTOTAL() cells are not ignored when aggregating values with functions such as SUM(). Is there a setting or something that needs to be set to force PHPExcel to not take cells with the SUBTOTAL formula into account when summing over it? Maybe some workaround if this is not possible?
In the end I wrote a function similar to the toArray() function to parse through the Excel sheet and return previously cached values (stored in the "v" tag in the sheet xml). This prevents PHPExcel from trying to calculate cell values. Such a function was not exposed by PHPExcel. Note that it uses the calculated value in the case where the cached value is not available. Practically speaking, the cached value should always be available as the sheet will be generated by Excel, which populates the cached value field.
function getCachedSheetData($sheet)
{
/* Usage
sheet: $excel_obj->getActiveSheet()
*/
// This function is similar to the toArray function, but returns previously cached values to prevent PHPExcel from calculating any formulas
$toreturn = array();
foreach ($sheet->getRowIterator() as $row)
{
$toreturn[] = array();
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false); // This loops all cells, even if it is not set. By default, only cells that are set will be iterated.
foreach ($cellIterator as $cell)
{
if ($cell->getOldCalculatedValue() === null)
{
$toreturn[$cell->getRow()][$cell->getColumn()] = $cell->getValue();
}
else
{
$toreturn[$cell->getRow()][$cell->getColumn()] = $cell->getOldCalculatedValue();
}
}
}
return $toreturn;
}
If covering the full range of subtotals a simple workaround is to accept that all values will be SUMmed (as desired) and then also all the SUBTOTALs too - which should come to the same total anyway, so simply SUM and divide result by 2.

Reading numbers as text format with PHPExcel

I have an array that can store numbers in such as 01, 01A, 01B, 02, 2, and When I get this value using PHPExcel, it's being removed the '0's in case of 01, 02, for example. To solve this problem, I tried to format the row where these values will be stored in excel and set it as text type, just like in the following code:
$objPHPExcel->setActiveSheetIndexByName('BlocksList');
$objPHPExcel->getActiveSheet()->fromArray($blockNames, null, 'A2');
$latestBLColumn = $objPHPExcel->getActiveSheet()->getHighestDataColumn();
$column = 'A';
$row = 1;
for ($column = 'A'; $column != $latestBLColumn; $column++) {
$objPHPExcel->getActiveSheet()->getStyle($column.$row)->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_TEXT );
}
$objPHPExcel->getActiveSheet()->fromArray($blockNames, null, 'A1');
So, by doing this, I get the array with numbers like 01, 01A, 02, 02B... and I store it in the Row A2. I get the highest Column to use this value in the condition For. In this condition, I set for the Row 1 in the range A until the highest column, to be formated as text.
My template is generated, and all the numbers are in text format, but the problem is that I think when I use the "fromArray()" method, it transforms the numbers of the array before I can get it right in excel.
Do you have any idea of how can I solve this problem??
Formatting using a number format affects the way a number is displayed, not the way it is stored.
You'll have to store the numbers explicitly as strings, so you can't use fromArray().
Use setCellValueExplicit() or setCellValueExplicitByColumnAndRow() instead, passing a $pDataType argument of PHPExcel_Cell_DataType::TYPE_STRING.
EDIT
Note that you can also set styles for a range of cells, so there's no need to add the overhead of the for loop:
$range = 'A'.$row.':'.$latestBLColumn.$row;
$objPHPExcel->getActiveSheet()
->getStyle($range)
->getNumberFormat()
->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_TEXT );
EDIT #2 Using a cell binder
Create a customised cell value binder:
class PHPExcel_Cell_MyValueBinder extends PHPExcel_Cell_DefaultValueBinder
implements PHPExcel_Cell_IValueBinder
{
public function bindValue(PHPExcel_Cell $cell, $value = null)
{
// sanitize UTF-8 strings
if (is_string($value)) {
$value = PHPExcel_Shared_String::SanitizeUTF8($value);
}
// Implement your own override logic
if (is_string($value) && $value[0] == '0') {
$cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING);
return true;
}
// Not bound yet? Use default value parent...
return parent::bindValue($cell, $value);
}
}
To avoid any problems with the autoloader, create this in the /Classes/PHPExcel/Cell directory. Otherwise, give the class your own non-PHPExcel name, and ensure that it's loaded independently.
Then, before using your fromArray() call, tell PHPExcel to use your value binder instead of the default binder:
PHPExcel_Cell::setValueBinder( new PHPExcel_Cell_MyValueBinder() );
In case you need to convert the whole sheet numbers to text, you can use calculateWorksheetDimension() to get the sheet's dimensions (example: 'A1:B200' or 'A1:C150') and then use it in getStyle(), like so:
// Get sheet dimension
$sheet_dimension = $spreadsheet->getActiveSheet()->calculateWorksheetDimension();
// Apply text format to numbers
$spreadsheet->getActiveSheet()->getStyle($sheet_dimension)->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
Note: This example uses PhpSpreadsheet since it is the next version of PHPExcel.
I know this was all posted a while ago, but wanted to share something that worked for me, so you can still use ->fromArray and not have to iterate over the whole spreadsheet.
If you wrap your values in ="VALUE" it will come through as text, and not convert it, and it will not have quotes around it in your spreadsheet
$objPHPExcel
->getActiveSheet()
->getCellByColumnAndRow($col, $row)
->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING);
You can format value to text with add charater before or after value. Example add after value charater ";"
$objPHPExcel->getActiveSheet()->setCellValueByColumnAndRow(1, $row,$user->msisdn.';' );
Try this :
$objPHPExcel->getActiveSheet()->setCellValueExplicit("A1","yourvalue",PHPExcel_Cell_DataType::TYPE_STRING);
please see here setCellValueExplicit function : https://github.com/PHPOffice/PHPExcel/blob/1.8/Classes/PHPExcel/Worksheet.php

Checking if excel cell has a value

I need to dynamically generate table headings from an excel file so that the results are output in the following fashion:
Value of country1
Value of country2
Value of country4
If they contain a value, note that there is no country 3
Someone kindly helped me with the following script
// Loop the array
foreach ($vCountries as $key => $value) {
// If value is not empty
if (!empty($value)) {
// display the line
echo '<th id="'.$key.'">'.$value.'</th>'."\n";
}
}
But now I need to access and check the excel spreadsheet to check if there is a value. So somehow I need to take the following code (that's my guess by the way)
//For example, not certain if I can use range in this context in PHP
$data->sheets[0]['cells'].range[6][9].value
And stick it into this statement
if (!empty($value)) {
As I have only been doing PHP for 5 days, my brains are scrambling...
Any help?
If you're using PHP-ExcelReader, then something like this should work
$sheet = 0;
$row = 6;
$col = 9;
if (array_key_exists($row,$data->sheets[$sheet]['cells']) && array_key_exists($col,$data->sheets[$sheet]['cells'][$row])) {
// cell exists
} else {
// cell does not exist
}
That is basically the code used internally by PHP-ExcelReader
alternatively:
if(isset($data->sheets[$sheet]['cells'][$row][$col]) {
// cell exists
} else {
// cell does not exist
}
EDIT
Hopefully there aren't any merged cells in your worksheet

Categories