From a5f7309ecf4bf9daca57684e9a9378d0a6c0ff1b Mon Sep 17 00:00:00 2001 From: Sebastien Routier Date: Mon, 7 May 2018 15:47:35 -0400 Subject: [PATCH] Fix issue #430: DataGrid crashes when exporting big result set. --- src/DataGrid/DataGrid.php | 81 +++++++++++++++++++++++---------------- src/DataSet.php | 12 ++++-- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/src/DataGrid/DataGrid.php b/src/DataGrid/DataGrid.php index 5dc249ed..71bdf130 100644 --- a/src/DataGrid/DataGrid.php +++ b/src/DataGrid/DataGrid.php @@ -77,18 +77,15 @@ public function build($view = '') return $this->output; } - public function buildCSV($file = '', $timestamp = '', $sanitize = true,$del = array()) + public function buildCSV($file = '', $timestamp = '', $sanitize = true, $del = array(), $directToFile = false) { - $this->limit = null; - parent::build(); - $segments = \Request::segments(); $filename = ($file != '') ? basename($file, '.csv') : end($segments); $filename = preg_replace('/[^0-9a-z\._-]/i', '',$filename); $filename .= ($timestamp != "") ? date($timestamp).".csv" : ".csv"; - $save = (bool) strpos($file,"/"); + $save = (strpos($file,"/")===false) ? false : true; //Delimiter $delimiter = array(); @@ -113,35 +110,17 @@ public function buildCSV($file = '', $timestamp = '', $sanitize = true,$del = ar fputs($handle, $delimiter['enclosure'].implode($delimiter['enclosure'].$delimiter['delimiter'].$delimiter['enclosure'], $this->headers) .$delimiter['enclosure'].$delimiter['line_ending']); - foreach ($this->data as $tablerow) { - $row = new Row($tablerow); - - foreach ($this->columns as $column) { - - if (in_array($column->name,array("_edit"))) - continue; - - $cell = new Cell($column->name); - $value = str_replace('"', '""',str_replace(PHP_EOL, '', strip_tags($this->getCellValue($column, $tablerow, $sanitize)))); - - // Excel for Mac is pretty stupid, and will break a cell containing \r, such as user input typed on a - // old Mac. - // On the other hand, PHP will not deal with the issue for use, see for instance: - // http://stackoverflow.com/questions/12498337/php-preg-replace-replacing-line-break - // We need to normalize \r and \r\n into \n, otherwise the CSV will break on Macs - $value = preg_replace('/\r\n|\n\r|\n|\r/', "\n", $value); + $this->limit = null; + if ($save && $directToFile) { + parent::build(function ($tablerow) use ($handle, $sanitize, $delimiter) { + $this->processTableRow($tablerow, $handle, $sanitize, $delimiter); + }); + } else { + parent::build(); - $cell->value($value); - $row->add($cell); + foreach ($this->data as $tablerow) { + $this->processTableRow($tablerow, $handle, $sanitize, $delimiter); } - - if (count($this->row_callable)) { - foreach ($this->row_callable as $callable) { - $callable($row); - } - } - - fputs($handle, $delimiter['enclosure'] . implode($delimiter['enclosure'].$delimiter['delimiter'].$delimiter['enclosure'], $row->toArray()) . $delimiter['enclosure'].$delimiter['line_ending']); } fclose($handle); @@ -154,6 +133,44 @@ public function buildCSV($file = '', $timestamp = '', $sanitize = true,$del = ar } } + /** + * @param $tablerow + * @param $handle + * @param $sanitize + * @param $delimiter + */ + public function processTableRow($tablerow, $handle, $sanitize, $delimiter) + { + $row = new Row($tablerow); + + foreach ($this->columns as $column) { + + if (in_array($column->name, array("_edit"))) + continue; + + $cell = new Cell($column->name); + $value = str_replace('"', '""', str_replace(PHP_EOL, '', strip_tags($this->getCellValue($column, $tablerow, $sanitize)))); + + // Excel for Mac is pretty stupid, and will break a cell containing \r, such as user input typed on a + // old Mac. + // On the other hand, PHP will not deal with the issue for use, see for instance: + // http://stackoverflow.com/questions/12498337/php-preg-replace-replacing-line-break + // We need to normalize \r and \r\n into \n, otherwise the CSV will break on Macs + $value = preg_replace('/\r\n|\n\r|\n|\r/', "\n", $value); + + $cell->value($value); + $row->add($cell); + } + + if (count($this->row_callable)) { + foreach ($this->row_callable as $callable) { + $callable($row); + } + } + + fputs($handle, $delimiter['enclosure'] . implode($delimiter['enclosure'] . $delimiter['delimiter'] . $delimiter['enclosure'], $row->toArray()) . $delimiter['enclosure'] . $delimiter['line_ending']); + } + protected function getCellValue($column, $tablerow, $sanitize = true) { //blade diff --git a/src/DataSet.php b/src/DataSet.php index 55d021a2..85b98d4b 100644 --- a/src/DataSet.php +++ b/src/DataSet.php @@ -121,7 +121,7 @@ public function paginate($items) return $this; } - public function build() + public function build($rowproc = null) { if (is_string($this->source) && strpos(" ", $this->source) === false) { //tablename @@ -204,10 +204,14 @@ public function build() $skip++; continue; } - //gather the rows to render. + //gather the rows to render else { - $rows[$cnt] = $row; - $cnt++; + if (is_callable($rowproc)) { + $rowproc($row); + } else { + $rows[$cnt] = $row; + $cnt++; + } } // If limit is set and we are passed it, break out of loop. if (isset($limit) && ($cnt > $limit)) {