514 lines
14 KiB
Markdown
514 lines
14 KiB
Markdown
# ExcelRowHandler Service Guide
|
|
|
|
## Overview
|
|
|
|
The `ExcelRowHandler` service is a modern, object-oriented replacement for the `addRowInTable` helper function. It provides a clean and maintainable way to add formatted rows to Excel sheets in the Register Creator system.
|
|
|
|
## Location
|
|
|
|
- **Service Class**: `App\Services\RegisterCreator\ExcelRowHandler`
|
|
- **Integration**: `App\Services\RegisterCreator\DocumentProcessors\AbstractDocumentProcessor`
|
|
|
|
## Features
|
|
|
|
- ✅ **Duplicate Detection**: Automatically prevents duplicate report entries
|
|
- ✅ **File Management**: Handles PDF and XLSX file copying with smart overwrite logic
|
|
- ✅ **Page Counting**: Automatically counts PDF pages using pdftk
|
|
- ✅ **Excel Formatting**: Preserves styles, formulas, merged cells, and row heights
|
|
- ✅ **Comprehensive Logging**: Provides detailed logging with emoji indicators
|
|
- ✅ **Statistics Tracking**: Tracks file types and successful operations
|
|
- ✅ **Type Safety**: Full PHP type hints for better IDE support and error prevention
|
|
|
|
## Architecture
|
|
|
|
### Service Class: ExcelRowHandler
|
|
|
|
The main service class handles all row addition operations:
|
|
|
|
```php
|
|
use App\Services\RegisterCreator\ExcelRowHandler;
|
|
|
|
$handler = new ExcelRowHandler();
|
|
|
|
$newRow = $handler->addRow(
|
|
$search, // Array of file paths to search
|
|
$selectDocument, // Document information array
|
|
$fullFolder, // Target folder path
|
|
$lineNumber, // Line identifier
|
|
$documentDate, // Document date
|
|
$rowNo, // Row sequence number
|
|
$sheet, // PhpSpreadsheet Worksheet object
|
|
$currentRow, // Current row position
|
|
$override // Whether to overwrite existing files
|
|
);
|
|
```
|
|
|
|
### Integration with AbstractDocumentProcessor
|
|
|
|
The service is integrated into `AbstractDocumentProcessor` for easy use in all document processors:
|
|
|
|
```php
|
|
protected function addRowToExcel(
|
|
array $search,
|
|
string $lineNumber,
|
|
string $documentDate,
|
|
int $rowNo
|
|
): int
|
|
```
|
|
|
|
## Usage in Document Processors
|
|
|
|
### Basic Usage Example
|
|
|
|
Here's how to use the service in a document processor:
|
|
|
|
```php
|
|
class MyDocumentProcessor extends AbstractDocumentProcessor
|
|
{
|
|
public function process(
|
|
array $weldLogData,
|
|
array $document,
|
|
Worksheet $sheet,
|
|
int &$currentRow,
|
|
array $settings
|
|
): int {
|
|
// Initialize processor (this creates the ExcelRowHandler instance)
|
|
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
|
|
|
// Get line identifier
|
|
$lineIdentifier = $weldLogData[$this->registerColumnBased];
|
|
|
|
// Search for files
|
|
$search = $this->searchFiles("{$document['path']}/*{$lineIdentifier}*.pdf");
|
|
|
|
// Get document date
|
|
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
|
$documentDate = $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
|
|
|
// Add row to Excel using the service
|
|
$newRow = $this->addRowToExcel(
|
|
$search,
|
|
$lineIdentifier,
|
|
$documentDate,
|
|
1 // Row number in sequence
|
|
);
|
|
|
|
if ($newRow > $currentRow) {
|
|
$currentRow = $newRow;
|
|
}
|
|
|
|
return $currentRow;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Real Example: DrawingsProcessor
|
|
|
|
```php
|
|
class DrawingsProcessor extends AbstractDocumentProcessor
|
|
{
|
|
public function process(
|
|
array $weldLogData,
|
|
array $document,
|
|
Worksheet $sheet,
|
|
int &$currentRow,
|
|
array $settings
|
|
): int {
|
|
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
|
|
|
$this->log("Processing Drawings");
|
|
|
|
$lineIdentifier = $weldLogData[$this->registerColumnBased];
|
|
$normalized = $this->normalizeSearchTerm($lineIdentifier);
|
|
$search = $this->searchFiles("{$document['path']}/*{$normalized}*.pdf");
|
|
|
|
$placeholderReplacer = new \App\Services\RegisterCreator\PlaceholderReplacer();
|
|
$documentDate = $placeholderReplacer->getLatestDate($weldLogData, $this->registerColumnBased);
|
|
|
|
// Use the new ExcelRowHandler service method
|
|
$newRow = $this->addRowToExcel(
|
|
$search,
|
|
$lineIdentifier,
|
|
$documentDate,
|
|
1
|
|
);
|
|
|
|
if ($newRow > $currentRow) {
|
|
$currentRow = $newRow;
|
|
}
|
|
|
|
return $currentRow;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Method Parameters
|
|
|
|
### addRow() Method
|
|
|
|
| Parameter | Type | Description |
|
|
|-----------|------|-------------|
|
|
| `$search` | `array` | Array of file paths to search for |
|
|
| `$selectDocument` | `array` | Document information containing title, path, type, order, etc. |
|
|
| `$fullFolder` | `string` | Target folder path (e.g., "storage/documents/project/line/") |
|
|
| `$lineNumber` | `string` | Line number or identifier |
|
|
| `$documentDate` | `string` | Document date |
|
|
| `$rowNo` | `int` | Row sequence number |
|
|
| `$sheet` | `Worksheet` | PhpSpreadsheet Worksheet object (passed by reference) |
|
|
| `$currentRow` | `int` | Current row position in Excel |
|
|
| `$override` | `bool` | Whether to overwrite existing files (default: false) |
|
|
|
|
**Returns**: `int` - Next row position
|
|
|
|
### addRowToExcel() Helper Method
|
|
|
|
| Parameter | Type | Description |
|
|
|-----------|------|-------------|
|
|
| `$search` | `array` | Array of file paths to search for |
|
|
| `$lineNumber` | `string` | Line number or identifier |
|
|
| `$documentDate` | `string` | Document date |
|
|
| `$rowNo` | `int` | Row sequence number |
|
|
|
|
**Returns**: `int` - Next row position
|
|
|
|
## Document Information Structure
|
|
|
|
The `$selectDocument` array should contain:
|
|
|
|
```php
|
|
[
|
|
'title1' => 'Main title',
|
|
'title2' => 'NDT Report Number',
|
|
'title3' => 'PDF Document Title',
|
|
'title4' => 'NDT Register Title',
|
|
'path' => 'storage/documents/...',
|
|
'type' => 'drawing|template|...',
|
|
'order' => 0, // Display order
|
|
'file_name' => 'Custom file name (optional)',
|
|
'incoming_control_description' => 'Description (optional)'
|
|
]
|
|
```
|
|
|
|
## Excel Template Placeholders
|
|
|
|
The service replaces the following placeholders in the Excel template:
|
|
|
|
| Placeholder | Description |
|
|
|-------------|-------------|
|
|
| `{rowNo}` | Row sequence number |
|
|
| `{type_order_no}` | Document type order number (e.g., 1 for QA, 2 for WDB) |
|
|
| `{sub_number}` | Sub-number within document type (1, 2, 3...) |
|
|
| `{full_number}` | Combined number in format "type_order_no.sub_number" (e.g., "1.1", "1.2", "3.1") |
|
|
| `{rowTitle}` | Document title (processed) |
|
|
| `{documentReportNumber}` | Line number/identifier |
|
|
| `{documentDate}` | Formatted document date |
|
|
| `{constructor}` | Contractor name |
|
|
| `{pageCount}` | Number of pages in PDF |
|
|
| `{pageIndex}` | Page range (e.g., "1-3") |
|
|
| `{title1}` | Document title 1 |
|
|
| `{ndt_report_no}` | NDT report number (title2) |
|
|
| `{pdf_document_title}` | PDF document title (title3) |
|
|
| `{ndt_register_title}` | NDT register title (title4) |
|
|
|
|
## Log Format
|
|
|
|
The service generates structured logs with emoji indicators:
|
|
|
|
```
|
|
📋 1. Document Title - LINE001 | 1 file(s)
|
|
-- filename.pdf
|
|
📁 File: copied
|
|
|
|
📋 2. Another Document - LINE002 | 2 file(s)
|
|
-- document1.pdf
|
|
-- document2.pdf
|
|
📁 File: updated
|
|
📊 XLSX: copied
|
|
|
|
❌ 3. Missing Document - LINE003 | NO FILES FOUND
|
|
Search paths:
|
|
-- expected_filename.pdf
|
|
|
|
🔄 DUPLICATE: Report Title - LINE001
|
|
```
|
|
|
|
### Log Indicators
|
|
|
|
- 📋 = Document processed
|
|
- ✅ = Success
|
|
- ❌ = Error or not found
|
|
- 🔄 = Duplicate (skipped)
|
|
- 📁 = PDF file operation
|
|
- 📊 = XLSX file operation
|
|
- ❓ = Warning or missing optional file
|
|
|
|
### File Copy Status
|
|
|
|
- `copied` = New file copied
|
|
- `updated` = File content changed, updated
|
|
- `overwritten` = File forcefully overwritten (override=true)
|
|
- `same content, skipped` = File exists with same content, no action needed
|
|
|
|
## File Management
|
|
|
|
### PDF Files
|
|
|
|
All PDF files are automatically:
|
|
1. Validated for existence
|
|
2. Page counted using pdftk
|
|
3. Copied to target location
|
|
4. Named according to order and title
|
|
|
|
### XLSX Files (Templates)
|
|
|
|
For documents with type="template":
|
|
1. Searches for corresponding .xlsx file
|
|
2. Copies alongside PDF if found
|
|
3. Logs result (found/not found)
|
|
|
|
### Smart Copy Logic
|
|
|
|
The service uses MD5 hash comparison to avoid unnecessary file operations:
|
|
- If file exists and content is identical → skip
|
|
- If file exists and content differs → update
|
|
- If override flag is true → always overwrite
|
|
|
|
## Statistics
|
|
|
|
The service tracks statistics that can be accessed statically:
|
|
|
|
```php
|
|
// Get file type statistics
|
|
$stats = ExcelRowHandler::getFileTypeStats();
|
|
// Returns: ['pdf' => 150, 'xlsx' => 25, ...]
|
|
|
|
// Get successful operations count
|
|
$count = ExcelRowHandler::getSuccessfulOperations();
|
|
// Returns: 150
|
|
|
|
// Reset statistics
|
|
ExcelRowHandler::resetStatistics();
|
|
```
|
|
|
|
## Duplicate Prevention
|
|
|
|
The service automatically prevents duplicate entries based on:
|
|
- Document title (title2)
|
|
- Line number
|
|
|
|
If a duplicate is detected:
|
|
1. Row is skipped
|
|
2. Duplicate log is written
|
|
3. Original row position is returned (no increment)
|
|
|
|
## Error Handling
|
|
|
|
The service handles errors gracefully:
|
|
|
|
```php
|
|
try {
|
|
$newRow = $this->addRowToExcel($search, $lineNumber, $documentDate, 1);
|
|
} catch (\Throwable $th) {
|
|
// Error is logged automatically
|
|
// Exception is re-thrown for upstream handling
|
|
}
|
|
```
|
|
|
|
### Error Logging
|
|
|
|
All errors are logged to:
|
|
1. Laravel log (with context)
|
|
2. Project-specific log.txt file
|
|
3. Console output
|
|
|
|
## Cache Dependencies
|
|
|
|
The service relies on cached values:
|
|
|
|
| Cache Key | Description |
|
|
|-----------|-------------|
|
|
| `rc_contractor` | Contractor name |
|
|
| `rc_firstPage` | First page number |
|
|
| `rc_lastPage` | Last page number |
|
|
| `rc_template_row` | Template row number |
|
|
|
|
These are managed by `RegisterCreatorService`.
|
|
|
|
## Migration from addRowInTable Helper
|
|
|
|
### Old Way (Helper Function)
|
|
|
|
```php
|
|
$newRow = addRowInTable(
|
|
$search,
|
|
$document,
|
|
$this->getFullFolder(),
|
|
$lineIdentifier,
|
|
$documentDate,
|
|
1,
|
|
$this->sheet,
|
|
$currentRow,
|
|
$this->settings['override'] ?? true
|
|
);
|
|
```
|
|
|
|
### New Way (Service Method)
|
|
|
|
```php
|
|
$newRow = $this->addRowToExcel(
|
|
$search,
|
|
$lineIdentifier,
|
|
$documentDate,
|
|
1
|
|
);
|
|
```
|
|
|
|
### Benefits of Migration
|
|
|
|
1. **Cleaner Code**: Less parameters, more intuitive
|
|
2. **Better Testability**: Service can be mocked and tested
|
|
3. **Type Safety**: Full type hints
|
|
4. **Object-Oriented**: Follows SOLID principles
|
|
5. **Easier Maintenance**: All logic in one place
|
|
6. **Better IDE Support**: Autocomplete and hints
|
|
|
|
## Best Practices
|
|
|
|
### 1. Always Initialize
|
|
|
|
```php
|
|
$this->initialize($weldLogData, $document, $sheet, $currentRow, $settings);
|
|
```
|
|
|
|
This creates the ExcelRowHandler instance and sets up necessary properties.
|
|
|
|
### 2. Update Current Row
|
|
|
|
```php
|
|
$newRow = $this->addRowToExcel(...);
|
|
|
|
if ($newRow > $currentRow) {
|
|
$currentRow = $newRow;
|
|
}
|
|
```
|
|
|
|
Always check if row was actually added before updating position.
|
|
|
|
### 3. Handle Search Results
|
|
|
|
```php
|
|
$search = $this->searchFiles("{$document['path']}/*{$normalized}*.pdf");
|
|
|
|
if (empty($search)) {
|
|
$this->log("No files found for pattern", "warning");
|
|
return $currentRow;
|
|
}
|
|
```
|
|
|
|
Check if files exist before calling addRowToExcel.
|
|
|
|
### 4. Use Normalized Search Terms
|
|
|
|
```php
|
|
$normalized = $this->normalizeSearchTerm($lineIdentifier);
|
|
$search = $this->searchFiles("{$document['path']}/*{$normalized}*.pdf");
|
|
```
|
|
|
|
Normalize search terms for better file matching.
|
|
|
|
### 5. Log Processing Steps
|
|
|
|
```php
|
|
$this->log("Processing Drawings");
|
|
$newRow = $this->addRowToExcel(...);
|
|
$this->log("Drawings processed successfully");
|
|
```
|
|
|
|
Use the log() method for consistent logging.
|
|
|
|
## Troubleshooting
|
|
|
|
### Files Not Found
|
|
|
|
1. Check file paths in search array
|
|
2. Verify storage permissions
|
|
3. Check if files exist in expected location
|
|
4. Review log.txt for exact search paths
|
|
|
|
### Duplicate Detection Issues
|
|
|
|
1. Check title2 field consistency
|
|
2. Verify line number format
|
|
3. Reset statistics if testing: `ExcelRowHandler::resetStatistics()`
|
|
|
|
### Page Count Issues
|
|
|
|
1. Ensure pdftk is installed: `which pdftk`
|
|
2. Check LANG environment variable
|
|
3. Verify PDF file is not corrupted
|
|
|
|
### Excel Formatting Issues
|
|
|
|
1. Verify template row is set correctly
|
|
2. Check placeholder names match exactly
|
|
3. Ensure template has proper merged cells
|
|
|
|
## Performance Considerations
|
|
|
|
1. **File Operations**: Uses Laravel Storage for efficiency
|
|
2. **MD5 Comparison**: Only calculates when necessary
|
|
3. **Static Properties**: Shared across instances for statistics
|
|
4. **Lazy Loading**: PDFs only opened when needed
|
|
|
|
## Testing
|
|
|
|
Example test structure:
|
|
|
|
```php
|
|
use Tests\TestCase;
|
|
use App\Services\RegisterCreator\ExcelRowHandler;
|
|
|
|
class ExcelRowHandlerTest extends TestCase
|
|
{
|
|
public function test_adds_row_successfully()
|
|
{
|
|
$handler = new ExcelRowHandler();
|
|
|
|
// Setup test data
|
|
$search = ['storage/documents/test.pdf'];
|
|
$document = ['title2' => 'Test', 'order' => 0, ...];
|
|
|
|
// Execute
|
|
$newRow = $handler->addRow(
|
|
$search,
|
|
$document,
|
|
'storage/documents/test/',
|
|
'LINE001',
|
|
'2024-01-01',
|
|
1,
|
|
$this->mockSheet,
|
|
10,
|
|
false
|
|
);
|
|
|
|
// Assert
|
|
$this->assertEquals(11, $newRow);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Related Documentation
|
|
|
|
- [Register Creator Guide](./register-creator-guide.md)
|
|
- [Save Trigger System Guide](./save-trigger-system-guide.md)
|
|
- [Document Processors](./document-processors-guide.md)
|
|
|
|
## Version History
|
|
|
|
- **v1.0** (2024-10): Initial release
|
|
- Migrated from addRowInTable helper function
|
|
- Added full type hints
|
|
- Improved error handling
|
|
- Added statistics tracking
|
|
|