66use Illuminate \Support \Collection ;
77use Illuminate \Support \Str ;
88use InvalidArgumentException ;
9+ use OpenSpout \Common \Entity \Cell ;
910use OpenSpout \Common \Entity \Row ;
1011use OpenSpout \Common \Entity \Style \Style ;
11- use OpenSpout \Writer \Common \Creator \WriterEntityFactory ;
12- use OpenSpout \Writer \XLSX \Writer ;
1312
1413/**
1514 * Trait Exportable.
2019 */
2120trait Exportable
2221{
23- /**
24- * @var Style
25- */
26- private $ header_style ;
27- private $ rows_style ;
28-
29- /**
30- * @param \OpenSpout\Reader\ReaderInterface|\OpenSpout\Writer\WriterInterface $reader_or_writer
31- *
32- * @return mixed
33- */
34- abstract protected function setOptions (&$ reader_or_writer );
22+ private ?Style $ header_style = null ;
23+ private ?Style $ rows_style = null ;
3524
3625 /**
3726 * @param string $path
3827 * @param callable|null $callback
3928 *
40- * @throws \OpenSpout\Common\Exception\InvalidArgumentException
4129 * @throws \OpenSpout\Common\Exception\UnsupportedTypeException
4230 * @throws \OpenSpout\Writer\Exception\WriterNotOpenedException
4331 * @throws \OpenSpout\Common\Exception\IOException
32+ * @throws \OpenSpout\Common\Exception\InvalidArgumentException
4433 *
4534 * @return string
4635 */
47- public function export ($ path , callable $ callback = null )
36+ public function export (string $ path , callable $ callback = null )
4837 {
4938 self ::exportOrDownload ($ path , 'openToFile ' , $ callback );
5039
@@ -55,10 +44,10 @@ public function export($path, callable $callback = null)
5544 * @param $path
5645 * @param callable|null $callback
5746 *
58- * @throws \OpenSpout\Common\Exception\InvalidArgumentException
5947 * @throws \OpenSpout\Common\Exception\UnsupportedTypeException
6048 * @throws \OpenSpout\Writer\Exception\WriterNotOpenedException
6149 * @throws \OpenSpout\Common\Exception\IOException
50+ * @throws \OpenSpout\Common\Exception\InvalidArgumentException
6251 *
6352 * @return \Symfony\Component\HttpFoundation\StreamedResponse|string
6453 */
@@ -74,6 +63,37 @@ public function download($path, callable $callback = null)
7463 return '' ;
7564 }
7665
66+ /**
67+ * @param Style $style
68+ *
69+ * @return Exportable
70+ */
71+ public function headerStyle (Style $ style )
72+ {
73+ $ this ->header_style = $ style ;
74+
75+ return $ this ;
76+ }
77+
78+ /**
79+ * @param Style $style
80+ *
81+ * @return Exportable
82+ */
83+ public function rowsStyle (Style $ style )
84+ {
85+ $ this ->rows_style = $ style ;
86+
87+ return $ this ;
88+ }
89+
90+ /**
91+ * @param \OpenSpout\Reader\ReaderInterface|\OpenSpout\Writer\WriterInterface $reader_or_writer
92+ *
93+ * @return mixed
94+ */
95+ abstract protected function setOptions (&$ reader_or_writer );
96+
7797 /**
7898 * @param $path
7999 * @param string $function
@@ -85,27 +105,15 @@ public function download($path, callable $callback = null)
85105 * @throws \OpenSpout\Writer\Exception\WriterNotOpenedException
86106 * @throws \OpenSpout\Common\Exception\SpoutException
87107 */
88- private function exportOrDownload ($ path , $ function , callable $ callback = null )
108+ private function exportOrDownload ($ path , string $ function , callable $ callback = null )
89109 {
90- if (Str::endsWith ($ path , 'csv ' )) {
91- $ options = new \OpenSpout \Writer \CSV \Options ();
92- $ writer = new \OpenSpout \Writer \CSV \Writer ($ options );
93- } elseif (Str::endsWith ($ path , 'ods ' )) {
94- $ options = new \OpenSpout \Writer \ODS \Options ();
95- $ writer = new \OpenSpout \Writer \ODS \Writer ($ options );
96- } else {
97- $ options = new \OpenSpout \Writer \XLSX \Options ();
98- $ writer = new \OpenSpout \Writer \XLSX \Writer ($ options );
99- }
100-
101- $ this ->setOptions ($ options );
102- /* @var \OpenSpout\Writer\WriterInterface $writer */
110+ /* @var \OpenSpout\Writer\WriterInterface $writer */
111+ $ writer = $ this ->prepareWriter ($ path );
112+ $ this ->setOptions ($ writer );
103113 $ writer ->$ function ($ path );
104114
105- $ has_sheets = ($ writer instanceof \OpenSpout \Writer \XLSX \Writer || $ writer instanceof \OpenSpout \Writer \ODS \Writer);
106-
107115 // It can export one sheet (Collection) or N sheets (SheetCollection)
108- $ data = $ this ->transpose ? $ this -> transposeData () : ( $ this -> data instanceof SheetCollection ? $ this -> data : collect ([ $ this -> data ]) );
116+ $ data = $ this ->prepareDataForExport ( );
109117
110118 foreach ($ data as $ key => $ collection ) {
111119 if ($ collection instanceof Collection) {
@@ -120,19 +128,48 @@ private function exportOrDownload($path, $function, callable $callback = null)
120128 if (is_string ($ key )) {
121129 $ writer ->getCurrentSheet ()->setName ($ key );
122130 }
123- if ($ has_sheets && $ data ->keys ()->last () !== $ key ) {
131+ if ($ this -> hasSheets ( $ writer ) && $ data ->keys ()->last () !== $ key ) {
124132 $ writer ->addNewSheetAndMakeItCurrent ();
125133 }
126134 }
127135 $ writer ->close ();
128136 }
129137
138+ private function prepareWriter ($ path ): \OpenSpout \Writer \WriterInterface
139+ {
140+ if (Str::endsWith ($ path , 'csv ' )) {
141+ $ writer = new \OpenSpout \Writer \CSV \Writer (new \OpenSpout \Writer \CSV \Options ());
142+ } elseif (Str::endsWith ($ path , 'ods ' )) {
143+ $ writer = new \OpenSpout \Writer \ODS \Writer (new \OpenSpout \Writer \ODS \Options ());
144+ } else {
145+ $ writer = new \OpenSpout \Writer \XLSX \Writer (new \OpenSpout \Writer \XLSX \Options ());
146+ }
147+
148+ return $ writer ;
149+ }
150+
151+ private function hasSheets (\OpenSpout \Writer \WriterInterface $ writer ): bool
152+ {
153+ return $ writer instanceof \OpenSpout \Writer \XLSX \Writer || $ writer instanceof \OpenSpout \Writer \ODS \Writer;
154+ }
155+
156+ private function prepareDataForExport (): SheetCollection |array |Generator |Collection |null
157+ {
158+ return $ this ->transpose
159+ ? $ this ->transposeData ()
160+ : (
161+ $ this ->data instanceof SheetCollection
162+ ? $ this ->data
163+ : collect ([$ this ->data ])
164+ );
165+ }
166+
130167 /**
131168 * Transpose data from rows to columns.
132169 *
133170 * @return SheetCollection
134171 */
135- private function transposeData ()
172+ private function transposeData (): SheetCollection
136173 {
137174 $ data = $ this ->data instanceof SheetCollection ? $ this ->data : collect ([$ this ->data ]);
138175 $ transposedData = [];
@@ -164,39 +201,22 @@ private function writeRowsFromCollection($writer, Collection $collection, ?calla
164201 return $ callback ($ value );
165202 });
166203 }
204+
167205 // Prepare collection (i.e remove non-string)
168- $ this ->prepareCollection ($ collection );
206+ // and transform into collection of row cells collections
207+ //$this->transformCollection($collection);
208+
169209 // Add header row.
170210 if ($ this ->with_header ) {
171- $ this ->writeHeader ($ writer , $ collection ->first ());
172- }
173-
174- // createRowFromArray works only with arrays
175- if (!is_array ($ collection ->first ())) {
176- $ collection = $ collection ->map (function ($ value ) {
177- return $ value ->toArray ();
178- });
211+ $ this ->writeHeader ($ writer , $ this ->transformRow ($ collection ->first ()));
179212 }
180213
181- // is_array($first_row) ? $first_row : $first_row->toArray())
182- $ all_rows = $ collection ->map (function ($ value ) {
183- return Row::fromValues ($ value );
184- })->toArray ();
185- if ($ this ->rows_style ) {
186- $ this ->addRowsWithStyle ($ writer , $ all_rows , $ this ->rows_style );
187- } else {
188- $ writer ->addRows ($ all_rows );
189- }
190- }
191-
192- private function addRowsWithStyle ($ writer , $ all_rows , $ rows_style )
193- {
194- $ styled_rows = [];
195- // Style rows one by one
196- foreach ($ all_rows as $ row ) {
197- $ styled_rows [] = Row::fromValues ($ row ->toArray (), $ rows_style );
198- }
199- $ writer ->addRows ($ styled_rows );
214+ // Add rows
215+ $ writer ->addRows (
216+ $ collection ->map (function ($ row ) {
217+ return $ this ->createRow ($ this ->transformRow ($ row ), $ this ->rows_style );
218+ })->toArray ()
219+ );
200220 }
201221
202222 private function writeRowsFromGenerator ($ writer , Generator $ generator , ?callable $ callback = null )
@@ -208,14 +228,17 @@ private function writeRowsFromGenerator($writer, Generator $generator, ?callable
208228 }
209229
210230 // Prepare row (i.e remove non-string)
211- $ item = $ this ->transformRow ($ item );
231+ // and transform to collection of Cells
232+ $ row_cells = $ this ->transformRow ($ item );
212233
213234 // Add header row.
214235 if ($ this ->with_header && $ key === 0 ) {
215- $ this ->writeHeader ($ writer , $ item );
236+ $ this ->writeHeader ($ writer , $ row_cells );
216237 }
217238 // Write rows (one by one).
218- $ writer ->addRow (Row::fromValues ($ item ->toArray (), $ this ->rows_style ));
239+ $ writer ->addRow (
240+ $ this ->createRow ($ row_cells , $ this ->rows_style )
241+ );
219242 }
220243 }
221244
@@ -229,82 +252,42 @@ private function writeRowsFromArray($writer, array $array, ?callable $callback =
229252 }
230253 }
231254
232- private function writeHeader ($ writer , $ first_row )
255+ private function writeHeader ($ writer , array $ first_row )
233256 {
234- if ($ first_row === null ) {
235- return ;
236- }
237-
238- $ keys = array_keys (is_array ($ first_row ) ? $ first_row : $ first_row ->toArray ());
239- $ writer ->addRow (Row::fromValues ($ keys ));
240- // $writer->addRow(WriterEntityFactory::createRowFromArray($keys, $this->header_style));
257+ $ writer ->addRow (
258+ $ this ->createRow ($ this ->transformRow (array_keys ($ first_row )), $ this ->header_style )
259+ );
241260 }
242261
243262 /**
244- * Prepare collection by removing non string if required.
245- */
246- protected function prepareCollection (Collection $ collection )
247- {
248- $ need_conversion = false ;
249- $ first_row = $ collection ->first ();
250-
251- if (!$ first_row ) {
252- return ;
253- }
254-
255- foreach ($ first_row as $ item ) {
256- if (!is_string ($ item )) {
257- $ need_conversion = true ;
258- }
259- }
260- if ($ need_conversion ) {
261- $ this ->transform ($ collection );
262- }
263- }
264-
265- /**
266- * Transform the collection.
263+ * Transform one row (i.e remove non-string).
264+ * into array of Cells.
267265 */
268- private function transform ( Collection $ collection )
266+ private function transformRow ( $ data ): array
269267 {
270- $ collection ->transform (function ($ data ) {
271- return $ this ->transformRow ($ data );
272- });
268+ return collect ($ data )
269+ ->filter (function ($ value ) {
270+ return $ this ->isValidValue ($ value );
271+ })->map (function ($ value ) {
272+ return $ this ->createCell ($ value , null );
273+ })->toArray ();
273274 }
274275
275- /**
276- * Transform one row (i.e remove non-string).
277- */
278- private function transformRow ($ data )
276+ private function isValidValue ($ value ): bool
279277 {
280- return collect ($ data )->map (function ($ value ) {
281- return is_null ($ value ) ? (string ) $ value : $ value ;
282- })->filter (function ($ value ) {
283- return is_string ($ value ) || is_int ($ value ) || is_float ($ value );
284- });
278+ return is_string ($ value ) || is_int ($ value ) || is_float ($ value )
279+ || is_null ($ value ) || $ value instanceof Cell;
285280 }
286281
287- /**
288- * @param Style $style
289- *
290- * @return Exportable
291- */
292- public function headerStyle (Style $ style )
282+ private function createCell ($ value , ?Style $ style ): Cell
293283 {
294- $ this -> header_style = $ style ;
295-
296- return $ this ;
284+ return ( $ value instanceof Cell)
285+ ? $ value
286+ : Cell:: fromValue ( $ value , $ style ) ;
297287 }
298288
299- /**
300- * @param Style $style
301- *
302- * @return Exportable
303- */
304- public function rowsStyle (Style $ style )
289+ private function createRow (array $ row_cells , ?Style $ row_style ): Row
305290 {
306- $ this ->rows_style = $ style ;
307-
308- return $ this ;
291+ return new Row ($ row_cells , $ row_style );
309292 }
310293}
0 commit comments