diff --git a/src/DataFixtures/TimesheetFixtures.php b/src/DataFixtures/TimesheetFixtures.php index 627bee7f..215cdd68 100644 --- a/src/DataFixtures/TimesheetFixtures.php +++ b/src/DataFixtures/TimesheetFixtures.php @@ -41,7 +41,7 @@ class TimesheetFixtures extends Fixture implements DependentFixtureInterface public const MIN_MINUTES_PER_ENTRY = 15; public const MAX_MINUTES_PER_ENTRY = 840; // 14h public const MAX_TAG_PER_ENTRY = 3; - public const MAX_DESCRIPTION_LENGTH = 50; + public const MAX_DESCRIPTION_LENGTH = 500; public const BATCH_SIZE = 100; diff --git a/src/Export/Base/AbstractSpreadsheetRenderer.php b/src/Export/Base/AbstractSpreadsheetRenderer.php index d9716e90..b2b2ed55 100644 --- a/src/Export/Base/AbstractSpreadsheetRenderer.php +++ b/src/Export/Base/AbstractSpreadsheetRenderer.php @@ -74,7 +74,10 @@ abstract class AbstractSpreadsheetRenderer 'customer' => [], 'project' => [], 'activity' => [], - 'description' => [], + 'description' => [ + 'maxWidth' => 50, + 'wrapText' => false, + ], 'exported' => [], 'tags' => [], 'hourlyRate' => [], @@ -264,8 +267,31 @@ abstract class AbstractSpreadsheetRenderer } if (isset($columns['description']) && !isset($columns['description']['render'])) { - $columns['description']['render'] = function (Worksheet $sheet, int $row, int $column, ExportItemInterface $entity) { - $sheet->setCellValueByColumnAndRow($column, $row, $entity->getDescription()); + $maxWidth = array_key_exists('maxWidth', $columns['description']) ? intval($columns['description']['maxWidth']) : null; + $wrapText = array_key_exists('wrapText', $columns['description']) ? (bool) $columns['description']['wrapText'] : false; + + // This column has a column-only formatter to set the maximum width of a column. + // It needs to be executed once, so we use this as a flag on when to skip it. + $isColumnFormatted = false; + + $columns['description']['render'] = function (Worksheet $sheet, int $row, int $column, ExportItemInterface $entity) use (&$isColumnFormatted, $maxWidth, $wrapText) { + $cell = $sheet->getCellByColumnAndRow($column, $row); + + $cell->setValue($entity->getDescription()); + + // Apply wrap text if configured + if ($wrapText) { + $cell->getStyle()->getAlignment()->setWrapText(true); + } + + // Apply max width, only needs to be once per column + if (!$isColumnFormatted) { + if (null !== $maxWidth) { + $sheet->getColumnDimensionByColumn($column) + ->setWidth($maxWidth); + } + $isColumnFormatted = true; + } }; } @@ -464,6 +490,12 @@ abstract class AbstractSpreadsheetRenderer $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); + // Set default row height to automatic, so we can specify wrap text columns later on + // without bloating the output file as we would need to store stylesheet info for every cell. + // LibreOffice is still not considering this flag, @see https://github.com/PHPOffice/PHPExcel/issues/588 + // with no solution implemented so nothing we can do about it there. + $sheet->getDefaultRowDimension()->setRowHeight(-1); + $recordsHeaderColumn = 1; $recordsHeaderRow = 1; diff --git a/src/Export/Base/XlsxRenderer.php b/src/Export/Base/XlsxRenderer.php index c06db1cb..18aa33f5 100644 --- a/src/Export/Base/XlsxRenderer.php +++ b/src/Export/Base/XlsxRenderer.php @@ -11,6 +11,7 @@ namespace App\Export\Base; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Style\Alignment; class XlsxRenderer extends AbstractSpreadsheetRenderer { @@ -39,6 +40,36 @@ class XlsxRenderer extends AbstractSpreadsheetRenderer throw new \Exception('Could not open temporary file'); } + // Store expensive calculations for later + $sheet = $spreadsheet->getActiveSheet(); + $highestRow = $sheet->getHighestRow(); + $highestColumn = $sheet->getHighestColumn(); + + // Enable auto filter for header row + $sheet->setAutoFilter('A1:' . $highestColumn . '1'); + + // Freeze first row and date & time columns for easier navigation + $sheet->freezePane('D2'); + + foreach (range('A', $highestColumn) as $column) { + // We default to a reasonable auto-width decided by the client, + // sadly ->getDefaultColumnDimension() is not supported so it needs + // to be specific about what column should be auto sized. + $col = $sheet->getColumnDimension($column); + + // If no other width is specified (which defaults to -1) + if ($col->getWidth() === -1) { + $col->setAutoSize(true); + } + } + + // Text inside cells should be top left + $sheet + ->getStyle('A2:' . $highestColumn . $highestRow) + ->getAlignment() + ->setVertical(Alignment::VERTICAL_TOP) + ->setHorizontal(Alignment::HORIZONTAL_LEFT); + $writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); $writer->save($filename);