55use PhpOffice \PhpSpreadsheet \Calculation \Calculation ;
66use PhpOffice \PhpSpreadsheet \Cell \Coordinate ;
77use PhpOffice \PhpSpreadsheet \Cell \DataType ;
8+ use PhpOffice \PhpSpreadsheet \Worksheet \AutoFilter ;
89use PhpOffice \PhpSpreadsheet \Worksheet \Worksheet ;
910
1011class ReferenceHelper
@@ -358,8 +359,12 @@ protected function adjustRowDimensions(Worksheet $worksheet, $beforeCellAddress,
358359 * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
359360 * @param Worksheet $worksheet The worksheet that we're editing
360361 */
361- public function insertNewBefore ($ beforeCellAddress , $ numberOfColumns , $ numberOfRows , Worksheet $ worksheet ): void
362- {
362+ public function insertNewBefore (
363+ string $ beforeCellAddress ,
364+ int $ numberOfColumns ,
365+ int $ numberOfRows ,
366+ Worksheet $ worksheet
367+ ): void {
363368 $ remove = ($ numberOfColumns < 0 || $ numberOfRows < 0 );
364369 $ allCoordinates = $ worksheet ->getCoordinates ();
365370
@@ -372,30 +377,12 @@ public function insertNewBefore($beforeCellAddress, $numberOfColumns, $numberOfR
372377
373378 // 1. Clear column strips if we are removing columns
374379 if ($ numberOfColumns < 0 && $ beforeColumn - 2 + $ numberOfColumns > 0 ) {
375- for ($ i = 1 ; $ i <= $ highestRow - 1 ; ++$ i ) {
376- for ($ j = $ beforeColumn - 1 + $ numberOfColumns ; $ j <= $ beforeColumn - 2 ; ++$ j ) {
377- $ coordinate = Coordinate::stringFromColumnIndex ($ j + 1 ) . $ i ;
378- $ worksheet ->removeConditionalStyles ($ coordinate );
379- if ($ worksheet ->cellExists ($ coordinate )) {
380- $ worksheet ->getCell ($ coordinate )->setValueExplicit ('' , DataType::TYPE_NULL );
381- $ worksheet ->getCell ($ coordinate )->setXfIndex (0 );
382- }
383- }
384- }
380+ $ this ->clearColumnStrips ($ highestRow , $ beforeColumn , $ numberOfColumns , $ worksheet );
385381 }
386382
387383 // 2. Clear row strips if we are removing rows
388384 if ($ numberOfRows < 0 && $ beforeRow - 1 + $ numberOfRows > 0 ) {
389- for ($ i = $ beforeColumn - 1 ; $ i <= Coordinate::columnIndexFromString ($ highestColumn ) - 1 ; ++$ i ) {
390- for ($ j = $ beforeRow + $ numberOfRows ; $ j <= $ beforeRow - 1 ; ++$ j ) {
391- $ coordinate = Coordinate::stringFromColumnIndex ($ i + 1 ) . $ j ;
392- $ worksheet ->removeConditionalStyles ($ coordinate );
393- if ($ worksheet ->cellExists ($ coordinate )) {
394- $ worksheet ->getCell ($ coordinate )->setValueExplicit ('' , DataType::TYPE_NULL );
395- $ worksheet ->getCell ($ coordinate )->setXfIndex (0 );
396- }
397- }
398- }
385+ $ this ->clearRowStrips ($ highestColumn , $ beforeColumn , $ beforeRow , $ numberOfRows , $ worksheet );
399386 }
400387
401388 // Find missing coordinates. This is important when inserting column before the last column
@@ -466,47 +453,11 @@ function ($coordinate) use ($allCoordinates) {
466453 $ highestRow = $ worksheet ->getHighestRow ();
467454
468455 if ($ numberOfColumns > 0 && $ beforeColumn - 2 > 0 ) {
469- for ($ i = $ beforeRow ; $ i <= $ highestRow - 1 ; ++$ i ) {
470- // Style
471- $ coordinate = Coordinate::stringFromColumnIndex ($ beforeColumn - 1 ) . $ i ;
472- if ($ worksheet ->cellExists ($ coordinate )) {
473- $ xfIndex = $ worksheet ->getCell ($ coordinate )->getXfIndex ();
474- $ conditionalStyles = $ worksheet ->conditionalStylesExists ($ coordinate ) ?
475- $ worksheet ->getConditionalStyles ($ coordinate ) : false ;
476- for ($ j = $ beforeColumn ; $ j <= $ beforeColumn - 1 + $ numberOfColumns ; ++$ j ) {
477- $ worksheet ->getCellByColumnAndRow ($ j , $ i )->setXfIndex ($ xfIndex );
478- if ($ conditionalStyles ) {
479- $ cloned = [];
480- foreach ($ conditionalStyles as $ conditionalStyle ) {
481- $ cloned [] = clone $ conditionalStyle ;
482- }
483- $ worksheet ->setConditionalStyles (Coordinate::stringFromColumnIndex ($ j ) . $ i , $ cloned );
484- }
485- }
486- }
487- }
456+ $ this ->duplicateStylesByColumn ($ worksheet , $ beforeColumn , $ beforeRow , $ highestRow , $ numberOfColumns );
488457 }
489458
490459 if ($ numberOfRows > 0 && $ beforeRow - 1 > 0 ) {
491- for ($ i = $ beforeColumn ; $ i <= Coordinate::columnIndexFromString ($ highestColumn ); ++$ i ) {
492- // Style
493- $ coordinate = Coordinate::stringFromColumnIndex ($ i ) . ($ beforeRow - 1 );
494- if ($ worksheet ->cellExists ($ coordinate )) {
495- $ xfIndex = $ worksheet ->getCell ($ coordinate )->getXfIndex ();
496- $ conditionalStyles = $ worksheet ->conditionalStylesExists ($ coordinate ) ?
497- $ worksheet ->getConditionalStyles ($ coordinate ) : false ;
498- for ($ j = $ beforeRow ; $ j <= $ beforeRow - 1 + $ numberOfRows ; ++$ j ) {
499- $ worksheet ->getCell (Coordinate::stringFromColumnIndex ($ i ) . $ j )->setXfIndex ($ xfIndex );
500- if ($ conditionalStyles ) {
501- $ cloned = [];
502- foreach ($ conditionalStyles as $ conditionalStyle ) {
503- $ cloned [] = clone $ conditionalStyle ;
504- }
505- $ worksheet ->setConditionalStyles (Coordinate::stringFromColumnIndex ($ i ) . $ j , $ cloned );
506- }
507- }
508- }
509- }
460+ $ this ->duplicateStylesByRow ($ worksheet , $ beforeColumn , $ beforeRow , $ highestColumn , $ numberOfRows );
510461 }
511462
512463 // Update worksheet: column dimensions
@@ -534,59 +485,7 @@ function ($coordinate) use ($allCoordinates) {
534485 $ this ->adjustProtectedCells ($ worksheet , $ beforeCellAddress , $ numberOfColumns , $ numberOfRows );
535486
536487 // Update worksheet: autofilter
537- $ autoFilter = $ worksheet ->getAutoFilter ();
538- $ autoFilterRange = $ autoFilter ->getRange ();
539- if (!empty ($ autoFilterRange )) {
540- if ($ numberOfColumns != 0 ) {
541- $ autoFilterColumns = $ autoFilter ->getColumns ();
542- if (count ($ autoFilterColumns ) > 0 ) {
543- $ column = '' ;
544- $ row = 0 ;
545- sscanf ($ beforeCellAddress , '%[A-Z]%d ' , $ column , $ row );
546- $ columnIndex = Coordinate::columnIndexFromString ($ column );
547- [$ rangeStart , $ rangeEnd ] = Coordinate::rangeBoundaries ($ autoFilterRange );
548- if ($ columnIndex <= $ rangeEnd [0 ]) {
549- if ($ numberOfColumns < 0 ) {
550- // If we're actually deleting any columns that fall within the autofilter range,
551- // then we delete any rules for those columns
552- $ deleteColumn = $ columnIndex + $ numberOfColumns - 1 ;
553- $ deleteCount = abs ($ numberOfColumns );
554- for ($ i = 1 ; $ i <= $ deleteCount ; ++$ i ) {
555- if (isset ($ autoFilterColumns [Coordinate::stringFromColumnIndex ($ deleteColumn + 1 )])) {
556- $ autoFilter ->clearColumn (Coordinate::stringFromColumnIndex ($ deleteColumn + 1 ));
557- }
558- ++$ deleteColumn ;
559- }
560- }
561- $ startCol = ($ columnIndex > $ rangeStart [0 ]) ? $ columnIndex : $ rangeStart [0 ];
562-
563- // Shuffle columns in autofilter range
564- if ($ numberOfColumns > 0 ) {
565- $ startColRef = $ startCol ;
566- $ endColRef = $ rangeEnd [0 ];
567- $ toColRef = $ rangeEnd [0 ] + $ numberOfColumns ;
568-
569- do {
570- $ autoFilter ->shiftColumn (Coordinate::stringFromColumnIndex ($ endColRef ), Coordinate::stringFromColumnIndex ($ toColRef ));
571- --$ endColRef ;
572- --$ toColRef ;
573- } while ($ startColRef <= $ endColRef );
574- } else {
575- // For delete, we shuffle from beginning to end to avoid overwriting
576- $ startColID = Coordinate::stringFromColumnIndex ($ startCol );
577- $ toColID = Coordinate::stringFromColumnIndex ($ startCol + $ numberOfColumns );
578- $ endColID = Coordinate::stringFromColumnIndex ($ rangeEnd [0 ] + 1 );
579- do {
580- $ autoFilter ->shiftColumn ($ startColID , $ toColID );
581- ++$ startColID ;
582- ++$ toColID ;
583- } while ($ startColID != $ endColID );
584- }
585- }
586- }
587- }
588- $ worksheet ->setAutoFilter ($ this ->updateCellReference ($ autoFilterRange , $ beforeCellAddress , $ numberOfColumns , $ numberOfRows ));
589- }
488+ $ this ->adjustAutoFilter ($ worksheet , $ beforeCellAddress , $ numberOfColumns , $ numberOfRows );
590489
591490 // Update worksheet: freeze pane
592491 if ($ worksheet ->getFreezePane ()) {
@@ -601,7 +500,9 @@ function ($coordinate) use ($allCoordinates) {
601500
602501 // Page setup
603502 if ($ worksheet ->getPageSetup ()->isPrintAreaSet ()) {
604- $ worksheet ->getPageSetup ()->setPrintArea ($ this ->updateCellReference ($ worksheet ->getPageSetup ()->getPrintArea (), $ beforeCellAddress , $ numberOfColumns , $ numberOfRows ));
503+ $ worksheet ->getPageSetup ()->setPrintArea (
504+ $ this ->updateCellReference ($ worksheet ->getPageSetup ()->getPrintArea (), $ beforeCellAddress , $ numberOfColumns , $ numberOfRows )
505+ );
605506 }
606507
607508 // Update worksheet: drawings
@@ -948,7 +849,7 @@ public function updateNamedFormulas(Spreadsheet $spreadsheet, $oldName = '', $ne
948849 foreach ($ spreadsheet ->getWorksheetIterator () as $ sheet ) {
949850 foreach ($ sheet ->getCoordinates (false ) as $ coordinate ) {
950851 $ cell = $ sheet ->getCell ($ coordinate );
951- if (($ cell !== null ) && ($ cell ->getDataType () == DataType::TYPE_FORMULA )) {
852+ if (($ cell !== null ) && ($ cell ->getDataType () === DataType::TYPE_FORMULA )) {
952853 $ formula = $ cell ->getValue ();
953854 if (strpos ($ formula , $ oldName ) !== false ) {
954855 $ formula = str_replace ("' " . $ oldName . "'! " , "' " . $ newName . "'! " , $ formula );
@@ -1038,6 +939,162 @@ private function updateSingleCellReference($cellReference = 'A1', $beforeCellAdd
1038939 return $ newColumn . $ newRow ;
1039940 }
1040941
942+ private function clearColumnStrips (int $ highestRow , int $ beforeColumn , int $ numberOfColumns , Worksheet $ worksheet ): void
943+ {
944+ for ($ i = 1 ; $ i <= $ highestRow - 1 ; ++$ i ) {
945+ for ($ j = $ beforeColumn - 1 + $ numberOfColumns ; $ j <= $ beforeColumn - 2 ; ++$ j ) {
946+ $ coordinate = Coordinate::stringFromColumnIndex ($ j + 1 ) . $ i ;
947+ $ worksheet ->removeConditionalStyles ($ coordinate );
948+ if ($ worksheet ->cellExists ($ coordinate )) {
949+ $ worksheet ->getCell ($ coordinate )->setValueExplicit ('' , DataType::TYPE_NULL );
950+ $ worksheet ->getCell ($ coordinate )->setXfIndex (0 );
951+ }
952+ }
953+ }
954+ }
955+
956+ private function clearRowStrips (string $ highestColumn , int $ beforeColumn , int $ beforeRow , int $ numberOfRows , Worksheet $ worksheet ): void
957+ {
958+ $ lastColumnIndex = Coordinate::columnIndexFromString ($ highestColumn ) - 1 ;
959+
960+ for ($ i = $ beforeColumn - 1 ; $ i <= $ lastColumnIndex ; ++$ i ) {
961+ for ($ j = $ beforeRow + $ numberOfRows ; $ j <= $ beforeRow - 1 ; ++$ j ) {
962+ $ coordinate = Coordinate::stringFromColumnIndex ($ i + 1 ) . $ j ;
963+ $ worksheet ->removeConditionalStyles ($ coordinate );
964+ if ($ worksheet ->cellExists ($ coordinate )) {
965+ $ worksheet ->getCell ($ coordinate )->setValueExplicit ('' , DataType::TYPE_NULL );
966+ $ worksheet ->getCell ($ coordinate )->setXfIndex (0 );
967+ }
968+ }
969+ }
970+ }
971+
972+ private function adjustAutoFilter (Worksheet $ worksheet , string $ beforeCellAddress , int $ numberOfColumns , int $ numberOfRows ): void
973+ {
974+ $ autoFilter = $ worksheet ->getAutoFilter ();
975+ $ autoFilterRange = $ autoFilter ->getRange ();
976+ if (!empty ($ autoFilterRange )) {
977+ if ($ numberOfColumns !== 0 ) {
978+ $ autoFilterColumns = $ autoFilter ->getColumns ();
979+ if (count ($ autoFilterColumns ) > 0 ) {
980+ $ column = '' ;
981+ $ row = 0 ;
982+ sscanf ($ beforeCellAddress , '%[A-Z]%d ' , $ column , $ row );
983+ $ columnIndex = Coordinate::columnIndexFromString ($ column );
984+ [$ rangeStart , $ rangeEnd ] = Coordinate::rangeBoundaries ($ autoFilterRange );
985+ if ($ columnIndex <= $ rangeEnd [0 ]) {
986+ if ($ numberOfColumns < 0 ) {
987+ $ this ->adjustAutoFilterDeleteRules ($ columnIndex , $ numberOfColumns , $ autoFilterColumns , $ autoFilter );
988+ }
989+ $ startCol = ($ columnIndex > $ rangeStart [0 ]) ? $ columnIndex : $ rangeStart [0 ];
990+
991+ // Shuffle columns in autofilter range
992+ if ($ numberOfColumns > 0 ) {
993+ $ this ->adjustAutoFilterInsert ($ startCol , $ numberOfColumns , $ rangeEnd [0 ], $ autoFilter );
994+ } else {
995+ $ this ->adjustAutoFilterDelete ($ startCol , $ numberOfColumns , $ rangeEnd [0 ], $ autoFilter );
996+ }
997+ }
998+ }
999+ }
1000+
1001+ $ worksheet ->setAutoFilter (
1002+ $ this ->updateCellReference ($ autoFilterRange , $ beforeCellAddress , $ numberOfColumns , $ numberOfRows )
1003+ );
1004+ }
1005+ }
1006+
1007+ private function adjustAutoFilterDeleteRules (int $ columnIndex , int $ numberOfColumns , array $ autoFilterColumns , AutoFilter $ autoFilter ): void
1008+ {
1009+ // If we're actually deleting any columns that fall within the autofilter range,
1010+ // then we delete any rules for those columns
1011+ $ deleteColumn = $ columnIndex + $ numberOfColumns - 1 ;
1012+ $ deleteCount = abs ($ numberOfColumns );
1013+
1014+ for ($ i = 1 ; $ i <= $ deleteCount ; ++$ i ) {
1015+ $ columnName = Coordinate::stringFromColumnIndex ($ deleteColumn + 1 );
1016+ if (isset ($ autoFilterColumns [$ columnName ])) {
1017+ $ autoFilter ->clearColumn ($ columnName );
1018+ }
1019+ ++$ deleteColumn ;
1020+ }
1021+ }
1022+
1023+ private function adjustAutoFilterInsert (int $ startCol , int $ numberOfColumns , int $ rangeEnd , AutoFilter $ autoFilter ): void
1024+ {
1025+ $ startColRef = $ startCol ;
1026+ $ endColRef = $ rangeEnd ;
1027+ $ toColRef = $ rangeEnd + $ numberOfColumns ;
1028+
1029+ do {
1030+ $ autoFilter ->shiftColumn (Coordinate::stringFromColumnIndex ($ endColRef ), Coordinate::stringFromColumnIndex ($ toColRef ));
1031+ --$ endColRef ;
1032+ --$ toColRef ;
1033+ } while ($ startColRef <= $ endColRef );
1034+ }
1035+
1036+ private function adjustAutoFilterDelete (int $ startCol , int $ numberOfColumns , int $ rangeEnd , AutoFilter $ autoFilter ): void
1037+ {
1038+ // For delete, we shuffle from beginning to end to avoid overwriting
1039+ $ startColID = Coordinate::stringFromColumnIndex ($ startCol );
1040+ $ toColID = Coordinate::stringFromColumnIndex ($ startCol + $ numberOfColumns );
1041+ $ endColID = Coordinate::stringFromColumnIndex ($ rangeEnd + 1 );
1042+
1043+ do {
1044+ $ autoFilter ->shiftColumn ($ startColID , $ toColID );
1045+ ++$ startColID ;
1046+ ++$ toColID ;
1047+ } while ($ startColID !== $ endColID );
1048+ }
1049+
1050+ private function duplicateStylesByColumn (Worksheet $ worksheet , int $ beforeColumn , int $ beforeRow , int $ highestRow , int $ numberOfColumns ): void
1051+ {
1052+ $ beforeColumnName = Coordinate::stringFromColumnIndex ($ beforeColumn - 1 );
1053+ for ($ i = $ beforeRow ; $ i <= $ highestRow - 1 ; ++$ i ) {
1054+ // Style
1055+ $ coordinate = $ beforeColumnName . $ i ;
1056+ if ($ worksheet ->cellExists ($ coordinate )) {
1057+ $ xfIndex = $ worksheet ->getCell ($ coordinate )->getXfIndex ();
1058+ $ conditionalStyles = $ worksheet ->conditionalStylesExists ($ coordinate ) ?
1059+ $ worksheet ->getConditionalStyles ($ coordinate ) : false ;
1060+ for ($ j = $ beforeColumn ; $ j <= $ beforeColumn - 1 + $ numberOfColumns ; ++$ j ) {
1061+ $ worksheet ->getCellByColumnAndRow ($ j , $ i )->setXfIndex ($ xfIndex );
1062+ if ($ conditionalStyles ) {
1063+ $ cloned = [];
1064+ foreach ($ conditionalStyles as $ conditionalStyle ) {
1065+ $ cloned [] = clone $ conditionalStyle ;
1066+ }
1067+ $ worksheet ->setConditionalStyles (Coordinate::stringFromColumnIndex ($ j ) . $ i , $ cloned );
1068+ }
1069+ }
1070+ }
1071+ }
1072+ }
1073+
1074+ private function duplicateStylesByRow (Worksheet $ worksheet , int $ beforeColumn , int $ beforeRow , string $ highestColumn , int $ numberOfRows ): void
1075+ {
1076+ $ highestColumnIndex = Coordinate::columnIndexFromString ($ highestColumn );
1077+ for ($ i = $ beforeColumn ; $ i <= $ highestColumnIndex ; ++$ i ) {
1078+ // Style
1079+ $ coordinate = Coordinate::stringFromColumnIndex ($ i ) . ($ beforeRow - 1 );
1080+ if ($ worksheet ->cellExists ($ coordinate )) {
1081+ $ xfIndex = $ worksheet ->getCell ($ coordinate )->getXfIndex ();
1082+ $ conditionalStyles = $ worksheet ->conditionalStylesExists ($ coordinate ) ?
1083+ $ worksheet ->getConditionalStyles ($ coordinate ) : false ;
1084+ for ($ j = $ beforeRow ; $ j <= $ beforeRow - 1 + $ numberOfRows ; ++$ j ) {
1085+ $ worksheet ->getCell (Coordinate::stringFromColumnIndex ($ i ) . $ j )->setXfIndex ($ xfIndex );
1086+ if ($ conditionalStyles ) {
1087+ $ cloned = [];
1088+ foreach ($ conditionalStyles as $ conditionalStyle ) {
1089+ $ cloned [] = clone $ conditionalStyle ;
1090+ }
1091+ $ worksheet ->setConditionalStyles (Coordinate::stringFromColumnIndex ($ i ) . $ j , $ cloned );
1092+ }
1093+ }
1094+ }
1095+ }
1096+ }
1097+
10411098 /**
10421099 * __clone implementation. Cloning should not be allowed in a Singleton!
10431100 */
0 commit comments