s3 = $s3; $this->config = $config; } /** * Build placeholder map for a record: current module fields + related (Account, Contact, etc.). * * @param string $module * @param int $recordId * @return array [ 'fieldname' => value, 'Account__accountname' => value, ... ] */ public function buildPlaceholders($module, $recordId) { $adb = PearDatabase::getInstance(); $focus = CRMEntity::getInstance($module); $focus->id = $recordId; $focus->retrieve_entity_info($recordId, $module); $fields = $focus->column_fields; $map = []; foreach ($fields as $k => $v) { if ($v === null || $v === '') { $v = ''; } $map[$k] = is_string($v) ? $v : (string)$v; } $map = array_merge($map, $this->getRelatedModuleFields($module, $recordId, $focus)); return $map; } /** * Get related entity fields (Account, Contact, etc.) for placeholder {{ModuleName__fieldname}}. */ protected function getRelatedModuleFields($module, $recordId, CRMEntity $focus) { $map = []; $relFields = $this->getRelationFieldNames($module); foreach ($relFields as $relModule => $fieldName) { $relId = isset($focus->column_fields[$fieldName]) ? $focus->column_fields[$fieldName] : null; if (empty($relId)) { continue; } $relFocus = CRMEntity::getInstance($relModule); $relFocus->retrieve_entity_info($relId, $relModule); foreach ($relFocus->column_fields as $k => $v) { if ($v === null || $v === '') { $v = ''; } $map[$relModule . '__' . $k] = is_string($v) ? $v : (string)$v; } } return $map; } /** * Common relation field names per module (account_id, contact_id, related_to, parent_id, etc.). */ /** @return array [ 'RelatedModule' => 'local_field_name', ... ] */ protected function getRelationFieldNames($module) { $known = [ 'Project' => ['Accounts' => 'account_id', 'Contacts' => 'contact_id'], 'Contacts' => ['Accounts' => 'account_id'], 'Leads' => ['Accounts' => 'account_id'], 'Potentials' => ['Accounts' => 'related_to', 'Contacts' => 'contact_id'], 'Invoice' => ['Accounts' => 'account_id', 'Contacts' => 'contact_id'], 'Quotes' => ['Accounts' => 'account_id', 'Contacts' => 'contact_id'], 'SalesOrder' => ['Accounts' => 'account_id', 'Contacts' => 'contact_id'], 'PurchaseOrder' => ['Vendors' => 'vendor_id', 'Contacts' => 'contact_id'], 'HelpDesk' => ['Accounts' => 'parent_id', 'Contacts' => 'contact_id'], 'Accounts' => [], ]; if (isset($known[$module])) { return $known[$module]; } $out = []; if (in_array($module, ['Contacts', 'Leads', 'Potentials', 'Invoice', 'Quotes', 'SalesOrder'])) { $out['Accounts'] = 'account_id'; if ($module !== 'Accounts') { $out['Contacts'] = 'contact_id'; } } return $out; } /** * Merge template: download from S3, replace placeholders, save to temp file. * * @param string $s3Key template S3 key * @param array $placeholders [ 'field' => 'value', 'Account__name' => 'value' ] * @return string path to merged DOCX file */ public function mergeToFile($s3Key, array $placeholders) { $path = dirname(dirname(dirname(__DIR__))); if (!class_exists('PhpOffice\PhpWord\IOFactory')) { if (is_file($path . '/vendor/autoload.php')) { require_once $path . '/vendor/autoload.php'; } } $tempDir = sys_get_temp_dir() . '/oot_' . uniqid(); if (!is_dir($tempDir)) { mkdir($tempDir, 0755, true); } $templatePath = $tempDir . '/template.docx'; $this->s3->downloadToFile($s3Key, $templatePath); $phpWord = \PhpOffice\PhpWord\IOFactory::load($templatePath); $this->replaceInPhpWord($phpWord, $placeholders); $outPath = $tempDir . '/merged.docx'; $writer = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); $writer->save($outPath); @unlink($templatePath); return $outPath; } /** * Replace {{placeholder}} in all text elements. */ protected function replaceInPhpWord(\PhpOffice\PhpWord\PhpWord $phpWord, array $placeholders) { foreach ($phpWord->getSections() as $section) { foreach ($section->getElements() as $element) { $this->replaceInElement($element, $placeholders); } } } protected function replaceInElement($element, array $placeholders) { if ($element instanceof \PhpOffice\PhpWord\Element\Text) { $text = $element->getText(); $text = $this->replacePlaceholders($text, $placeholders); $element->setText($text); return; } if ($element instanceof \PhpOffice\PhpWord\Element\TextRun) { foreach ($element->getElements() as $el) { $this->replaceInElement($el, $placeholders); } return; } if ($element instanceof \PhpOffice\PhpWord\Element\TextBreak) { return; } if (method_exists($element, 'getElements')) { foreach ($element->getElements() as $el) { $this->replaceInElement($el, $placeholders); } } } protected function replacePlaceholders($text, array $placeholders) { foreach ($placeholders as $key => $value) { $text = str_replace('{{' . $key . '}}', $value, $text); } return $text; } }