read formula from other sheet with phpexcel - php

Trying to maintain some old dusty code, I am facing a problem with phpexcel in a import symfony command.
It seems that the library cannot calculate the formula correctly which is linked to another sheet of the same document as the active sheet.
The error I get is :
[PHPExcel_Calculation_Exception]
Price Template Map!B2 -> Invalid cell coordinate A
My code is :
try {
$inputFileType = \PHPExcel_IOFactory::identify($filePath);
$objReader = \PHPExcel_IOFactory::createReader($inputFileType);
$objReader->setLoadSheetsOnly(array($this->getListName(), 'Template Info'));
$objReader->setIncludeCharts(true);
$objPHPExcel = $objReader->load($filePath);
} catch (\Exception $e) {
throw new \Exception("Invalid file");
}
$sheet = $objPHPExcel->getActiveSheet();
$highestRow = $sheet->getHighestRow();
$highestColumn = $sheet->getHighestColumn();
$fieldsNumber = array();
$filterData = array();
$templateIdList = array();
for ($i = 1; $i <= $highestRow; $i++) {
$rowData = $sheet->rangeToArray('A' . $i . ':' . $highestColumn . $i, null, true, false);
$rowData = $rowData[0];
var_dump($rowData);
}
The first line with the headers is read correctly, but the rest is not.
My formula is :
=VLOOKUP(A18,'Template Info'!A:C,3,FALSE)"
Do not hesitate to ask me more informations if you need it !
Thank you all in advance :) !

The problem is the column reference: the PHPExcel Calculation Engine supports range references (even to other worksheets), but not row or column references
so
=VLOOKUP(A18,'Template Info'!A1:C100,3,FALSE)
would be valid, but
=VLOOKUP(A18,'Template Info'!A:C,3,FALSE)
can't be calculated

Use the getCalculatedValue() function.

Related

Import Excel file to MySQL using PHP Yii2

I just want to import data from excel (xls, xlsx) to mySql db in Yii2 using PHPExcel.
This is my code in Controller:
$modelFile ->file = $firstName. '_' .$middleName. '_' .date('Y-m-d'). '_' .$file ->getBaseName(). "." .$file ->getExtension();
$objPHPExcel = new \PHPExcel();
$inputFiles = fopen("../file/".$modelFile ->file, "r");
try {
$inputFileType = \PHPExcel_IOFactory::identify($inputFiles);
$objReader = \PHPExcel_IOFactory::createReader($inputFileType);
$objPHPExcel = $objReader ->load($inputFiles);
} catch (Exception $ex) {
die('Error');
}
$sheet = $objPHPExcel ->getSheet(0);
$highestRow = $sheet ->getHighestRow();
$highestColumn = $sheet ->getHighestColumn();
//$row is start 2 because first row assigned for heading.
for ($row = 2; $row <= $highestRow; ++$row) {
$rowData = $sheet ->rangeToArray('A'.$row. ':' .$highestColumn.$row, NULL, TRUE, FALSE);
//save to branch table.
$modelHeader = new FakturOut();
$modelDetail = new FakturOutDetail();
$modelHeader ->name = $rowData[0][0];
$modelHeader ->age = $rowData[0][1];
$modelHeader ->address = $rowData[0][2];
$modelHeader ->academic_id = $rowData[0][3];
$modelHeader ->mother_name = $rowData[0][4];
$modelHeader ->father_Name = $rowData[0][5];
$modelHeader ->gender = $rowData[0][6];
$modelHeader ->height = $rowData[0][7];
$modelHeader ->weight= $rowData[0][8];
$modelHeader ->save();
}
And then the browser return an error notification like pathinfo() expects parameter 1 to be string, resource given. Please help to solve this error.
in your code you have used
$inputFileType =\PHPExcel_IOFactory::identify($inputFiles);
which is used to identify the valid excel file and it expects the parameter as filename.
I can see in your code, you have passed $inputFiles as a parameter to identify method, which is not a file name but resource handler. and identify method expects it to be string (file name).
This is the reason you are getting error.
Note : fopen() returns a file pointer resource on success, or FALSE on error.
$inputFiles = fopen("../file/" . $modelFile->file, "r");

phpexcel reads long numbers as boolean

I'm using Symfony2.3.4, PHP5.6.3 and PHPExcel 1.8.0.
When I tried to read an excel file it works OK for almost all cells.
If the cell contains a very large number, when I read it and show the value in an html view it outputs false.
I tried to use a custom value binder like Mark Baker instructed here but I couldn't make it work, it just comes as a boolean right from the beginning.
IMPORTANT:
The excels I'm trying to load in the html are downloaded(generated) from another site and I noticed when you try to open them with Microsoft Excel, it first prompts you with a warning window telling the user that the FILE EXTENSION AND THE FILE FORMAT DO NOT MATCH, although if you choose to open it anyway, it opens fine.
I think that's what's causing the problem, I'm almost sure(I can't contact the guys who implemented the other site's download function) they did something like this:
$objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, $ext == 'xlsx' ?
'Excel5' : 'Excel2007');
when they should have done something like this:
$objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, $ext == 'xls' ?
'Excel5' : 'Excel2007');
making the EXTENSION and the FORMAT match, as instructed in the PHPExcel's docs.
If you need any specific clarification please ask.
My code to load the file into the html:
public function uploadAction() {
$request = $this->getRequest();
$form = $this->createFormBuilder()
->add('file', 'file')
->getForm();
if ($request->getMethod() == 'POST'){
$form->submit($request);
$file = $form['file'];
$file->getData()->move(
'uploads', $form['file']->getData()->getClientOriginalName());
$ext = pathinfo($file->getData()->getClientOriginalName(), PATHINFO_EXTENSION);
$name = pathinfo($file->getData()->getClientOriginalName(), PATHINFO_BASENAME);
//$objReader = \PHPExcel_IOFactory::createReader('xlsx' == $ext ? 'Excel2007' : 'Excel5');
$objReader = \PHPExcel_IOFactory::createReaderForFile('uploads/' . $name);
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load('uploads/' . $name);
$activeSheet = $objPHPExcel->getActiveSheet();
$rowIter = $activeSheet->getRowIterator();
foreach ($rowIter as $key => $row) {
$columns = array();
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
foreach ($cellIterator as $cell)
$columns[] = $cell->getCalculatedValue();
}
}
}
NOTE: I really don't know the difference between:
$objReader = \PHPExcel_IOFactory::createReader('xlsx' == $ext ? 'Excel2007' : 'Excel5');
and
$objReader = \PHPExcel_IOFactory::createReaderForFile('uploads/' . $name);
I DO know I can't use the first because of the problem I described above about the files being ill-generated and so. If I try to use it, the browser goes:
The filename uploads/<name>.xls is not recognised as an OLE file.
Can anyone point me to a workaround, because it's now me on the hook and I'm supposed to make it work somehow. Maybe there's nothing wrong with the files and it's me doing something wrong. Please help, this is causing me problems with dates too but one step at a time.
EDIT:
This is but the read function in OLERead.php.
I was browsing it and var_dump-ing all I could get my hands on.
As you can see there are two var_dumps in the code below, those output:
string '<div>
' (length=8)
string '��ࡱ�' (length=8)
Which doesn't happen when I try it with a regular .xls file created manually:
string '��ࡱ�' (length=8)
string '��ࡱ�' (length=8)
I guessed you could use this better than me if it helps at all. Thanks again.
public function read($sFileName) {
// Check if file exists and is readable
if (!is_readable($sFileName)) {
throw new PHPExcel_Reader_Exception("Could not open " . $sFileName . " for reading! File does not exist, or it is not readable.");
}
// Get the file identifier
// Don't bother reading the whole file until we know it's a valid OLE file
$this->data = file_get_contents($sFileName, FALSE, NULL, 0, 8);
////VAR_DUMPSSSSSSSSSSSS
var_dump($this->data);
var_dump(self::IDENTIFIER_OLE);
die();
// Check OLE identifier
if ($this->data != self::IDENTIFIER_OLE) {
throw new PHPExcel_Reader_Exception('The filename ' . $sFileName . ' is not recognised as an OLE file');
}
// Get the file data
$this->data = file_get_contents($sFileName);
// Total number of sectors used for the SAT
$this->numBigBlockDepotBlocks = self::_GetInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
// SecID of the first sector of the directory stream
$this->rootStartBlock = self::_GetInt4d($this->data, self::ROOT_START_BLOCK_POS);
// SecID of the first sector of the SSAT (or -2 if not extant)
$this->sbdStartBlock = self::_GetInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
// SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
$this->extensionBlock = self::_GetInt4d($this->data, self::EXTENSION_BLOCK_POS);
// Total number of sectors used by MSAT
$this->numExtensionBlocks = self::_GetInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
$bigBlockDepotBlocks = array();
$pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
$bbdBlocks = $this->numBigBlockDepotBlocks;
if ($this->numExtensionBlocks != 0) {
$bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4;
}
for ($i = 0; $i < $bbdBlocks; ++$i) {
$bigBlockDepotBlocks[$i] = self::_GetInt4d($this->data, $pos);
$pos += 4;
}
for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
$pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
$blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
$bigBlockDepotBlocks[$i] = self::_GetInt4d($this->data, $pos);
$pos += 4;
}
$bbdBlocks += $blocksToRead;
if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
$this->extensionBlock = self::_GetInt4d($this->data, $pos);
}
}
$pos = 0;
$this->bigBlockChain = '';
$bbs = self::BIG_BLOCK_SIZE / 4;
for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
$pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
$this->bigBlockChain .= substr($this->data, $pos, 4 * $bbs);
$pos += 4 * $bbs;
}
$pos = 0;
$sbdBlock = $this->sbdStartBlock;
$this->smallBlockChain = '';
while ($sbdBlock != -2) {
$pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
$this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
$pos += 4 * $bbs;
$sbdBlock = self::_GetInt4d($this->bigBlockChain, $sbdBlock * 4);
}
// read the directory stream
$block = $this->rootStartBlock;
$this->entry = $this->_readData($block);
$this->_readPropertySets();
}
The difference between
$objReader = \PHPExcel_IOFactory::createReader('xlsx' == $ext ? 'Excel2007' : 'Excel5');
and
$objReader = \PHPExcel_IOFactory::createReaderForFile('uploads/' . $name);
The first is trusting that the extension is correct for the actual format of the file, that a file with an extension of .xlsx really is an OfficeOpenXML-format file or an extension of .xls really is a BIFF-format file, and then telling PHPExcel to use the appropriate reader.
This isn't normally a problem unless it isn't (for example) just HTML markup in a file with an .xls or .xlsx extension.... then you're selecting the wrong Reader for the actual format of the file; and this is what MS Excel itself is telling you with its message that "FILE EXTENSION AND THE FILE FORMAT DO NOT MATCH"
The second is using PHPExcel's identify() method to work out what format the file really is (irrespective of what it claims to be based on a false extension), and then selecting the appropriate Reader for that format.
EDIT
Unsure exactly how large your large numbers are, but I'll take a look at the HTML Reader and see if I can identify why it should be giving a boolean false instead of an actual numeric value

PHPExcel write rows as is , without any calculation / styling

Is there a way to tell PHPExcel to just write rows supplied from an array, without doing any calculation / apply styling / any other thing it does while writing OR when using fromArray ?
Need this for performance.
$inputFileName = 'client_files/sample.xlsx';
$objPHPExcel = PHPExcel_IOFactory::load($inputFileName);
$objPHPExcel->getSheet(0)->setCellValue('D2', '#' . $user . ' followers');
$objPHPExcel->getSheet(0)->fromArray(
$followersData,
NULL,
'A5'
);
$objWriter = new PHPExcel_Writer_Excel2007($objPHPExcel);
$objWriter->setPreCalculateFormulas(false);
$objWriter->save(FINAL_FOLDER . '/' . $line[0] . '.xlsx');
Memory consumption isn't an issue. But the above is just taking too much time (2 minutes with 2700 rows)
the ->save() call takes 93 seconds. The ->fromArray() takes 53 seconds
Also is there any other wayy faster Excel library that allows loading existing xlsx and then writing to it ?
Thanks
You can try using Spout. If you don't care about styling/calculation, it should solve your performance problem (it takes only a few seconds).
Something along these lines should work:
$inputFileName = 'client_files/sample.xlsx';
$reader = ReaderFactory::create(Type::XLSX);
$reader->open($inputFileName);
$outputFileName = FINAL_FOLDER . '/' . $line[0] . '.xlsx';
$writer = WriterFactory::create(Type::XLSX);
$writer->openToFile($outputFileName);
$reader->nextSheet();
$rowCount = 0;
while ($reader->hasNextRow()) {
$row = $reader->nextRow();
if ($rowCount === 1) {
$row[1] = '#' . $user . ' followers';
}
$followersDataForCurrentRow = $followersData[$rowCount];
$columnIndexStart = 4; // To add stuff in the 5th column
foreach ($followersDataForCurrentRow as $followerValue) {
$row[$columnIndexStart] = $followerValue;
$columnIndexStart++;
}
$writer->addRow($row);
$rowCount++;
}
$reader->close();
$writer->close();
I did a bunch of things that resulted in wayyyy faster performance.
ran the script outside the IDE
set memory limit to 3GB
Used a different version of PHP
Fixed memory leak
$objPHPExcel->disconnectWorksheets() ;
unset($objPHPExcel) ;
I am not sure what solved the issue..

PHPExcel toArray skip first header row

I'm uploading an excel file to website and process it for database use.
I'm using toArray() function to get all rows in a php array.
But I want to skip the first row ( header title row). The rest of rows will be stored in array.
How can I skip the first row.
Note : I can't use rangeToArray() function since there is not fixed range to get rows into array. It is dynamic. All i want is get all rows except first.
Eko answers half the problem, you can use rangeToArray(); but you don't need to use a loop at all:
$highestRow = $sheet->getHighestRow();
$highestColumn = $sheet->getHighestColumn();
$sheetData = $sheet->rangeToArray(
'A2:' . $highestColumn . $highestRow,
NULL,TRUE,FALSE
);
Alternatively, use toArray() and then just unset the first element from the returned array
You can achieve that using array_shift this:
$toArray = $worksheet->toArray()
array_shift($toArray);
I create a function to read an excel file using PHPExcel like this below :
function Read_Excel($fname=null,$isheet=0,$irow=1,$icol='A'){
$inputFileName = $fname;
try {
$inputFileType = PHPExcel_IOFactory::identify($inputFileName);
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
$objPHPExcel = $objReader->load($inputFileName);
} catch(Exception $e) {
die('Error loading file "'.pathinfo($inputFileName,PATHINFO_BASENAME).'": '.$e->getMessage());
}
$sheet = $objPHPExcel->getSheet(intval($isheet));
$highestRow = $sheet->getHighestRow();
$highestColumn = $sheet->getHighestColumn();
for ($row = intval($irow); $row <= $highestRow; $row++){
// Read a row of data into an array
$rowData = $sheet->rangeToArray($icol . $row . ':' . $highestColumn . $row,NULL,TRUE,FALSE);
$rec_tbl[] = $rowData[0];
}
return $rec_tbl;
}
you just need to change the $irow=1 in the function parameters to get a row you wanted.

PHPExcel how to use data from reader

I'm trying to understand how to get data from cells in PHPExcel but I have no idea. I read all documentation and I made this:
<?php
include('PHPExcel/Classes/PHPExcel/Reader/Excel2007.php');
class MyReadFilter implements PHPExcel_Reader_IReadFilter
{
public function readCell($column, $row, $worksheetName = '') {
// Read title row and rows 20 - 30
if ($row == 1 || ($row >= 20 && $row <= 30)) {
return true;
}
return false;
}
}
$objReader = new PHPExcel_Reader_Excel2007();
$objReader->setReadFilter( new MyReadFilter() );
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load("sample_mymails222.xlsx");
print_r($objPHPExcel);
?>
print_r show very big array.
I think there is some functions to get data from cell in $objPHPExcel.
How to do it?
Thanx!
For anyone else who wants a more helpful answer, following the code in the example above, here is a quick snippet that will parse the active worksheet into a multi-dimensional array (this example ignores the ReadFilter that the OP created).
$data = array();
$worksheet = $objPHPExcel->getActiveSheet();
foreach ($worksheet->getRowIterator() as $row) {
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(false);
foreach ($cellIterator as $cell) {
$data[$cell->getRow()][$cell->getColumn()] = $cell->getValue();
}
}
var_dump($data);
BTW The reason I say "more helpful answer" is that I also had a hard time figuring out the PHPExcel documentation (which consists of 6 separate documents and no real API-like reference) the first time around.
The OP says he "read all the docs", but I am guessing that, like me, he read through the file called PHPExcel User Documentation - Reading Spreadsheet Files.doc which explains how to read in a spreadsheet, but does not cover using the contents.
To find that out, you have to dig into PHPExcel developer documentation.doc and get down to part 4 (which is where I found the example above).
Yup, my fault for not being thorough, but clearly this is a recurring issue. :-)
for reading excel file you can use this
require_once('PHPExcel.php');
$input_file_type = PHPExcel_IOFactory::identify($excel_file);
$obj_reader = PHPExcel_IOFactory::createReader($input_file_type);
$obj_reader->setReadDataOnly(true);
$objPHPExcel = $obj_reader->load($excel_file);
$objWorksheet = $objPHPExcel->setActiveSheetIndex(0);
$highest_row = $objWorksheet->getHighestRow();
$highest_col = $objWorksheet->getHighestColumn();
//$highest_col_index = PHPExcel_Cell::columnIndexFromString($highest_col);
// start $row from 2, if you want to skip header
for ($counter = 2; $counter <= $highest_row; $counter++)
{
$row = $objWorksheet->rangeToArray('A'.$counter.':'.$highest_col.$counter);
$row = reset($row);
}
Please read the documentation chapter 4.5 (included in the PHPExcel download package)

Categories