Forum


Replies: 5   Views: 2842
Merging documents at same time as another mixes output
Topic closed:
Please note this is an old forum thread. Information in this post may be out-to-date and/or erroneous.
Every phpdocx version includes new features and improvements. Previously unsupported features may have been added to newer releases, or past issues may have been corrected.
We encourage you to download the current phpdocx version and check the Documentation available.

Posted by cem  · 15-06-2017 - 18:13

Hi,

We have version 7 Premium edition.  Right now I'm trying to merge many templates together and it works fine if I am the only one using it.  However, once someone else tries to run the documents, our info gets mixed up and merged into the output.  I have tested and confirmed this happens.

For example, if I'm running invoices for one customer, and another employee is running it for a different customer, the output has the two different customers filled in the variables if we are literally running at the same exact time. 

I believe it's because of the temp files.  There seems to be only one path and if we both run it, we overwrite each others temp files.  Is there any way to change this behavior?  A different method of merging and producing output?  A way for createDocx() to add something unique and only merge those set of docs? 

We do have sessions, so maybe I can use each person's session to create a path and overwrite whatever is the default path (which also seems random as it's not taking my set temp_path)?  But then every employee will need their own path.  If that is what we must do, I can set that up, but hoping there is a known solution or alternative method for merging.

Here's a sample code, this is the basics of what I'm running. 

// using in-memory temp files

require_once '../../../classes/CreateDocx.inc';

// array of the DOCX to be merged
$doc_arr = array();

$variables = array(
    'field1' => 'test text',
    'field2' => 'more test text'
);

// first DOCX
$docx = new CreateDocxFromTemplate('file1.docx');
$docx->setTemplateSymbol('^');
$docx->replaceVariableByText($variables);
$docx->createDocx('output_1.docx');

array_push($doc_arr, 'output_1.docx');

// second DOCX
$docx = new CreateDocxFromTemplate('file2.docx');
$docx->setTemplateSymbol('^');
$docx->replaceVariableByText($variables);
$docx->createDocx('output_2.docx');

array_push($doc_arr, 'output_2.docx');

// third DOCX
$docx = new CreateDocxFromTemplate('file3.docx');
$docx->setTemplateSymbol('^');
$docx->replaceVariableByText($variables);
$docx->createDocx('output_3.docx');

array_push($doc_arr, 'output_3.docx');

$merge = new MultiMerge();
$merge->mergeDocx('coversheet.docx', $doc_arr, 'output_merged_1.docx', array());

 

Posted by admin  · 15-06-2017 - 19:01

Hello,

Since phpdocx 6.5, the library doesn't create temp files (except when generating charts, that create unique temp files for charts).

We think the problem may come from the names you are using to generate the DOCX to be merged. If you use the same file name for the DOCX to be merged or the merge DOCX outputs, then a race condition may happen. We recommend using unique file names to avoid this issue:

$fileName1 = uniqid('document', true) . 'docx';
$docx->createDocx($fileName1);

array_push($doc_arr, $fileName1)

Other solution, as you are using a Premium license, is to do in-memory merging to avoid creating DOCX files and get a better performance. This sample is included in phpdocx 7 (examples/DocxUtilities/mergeDocx/sample2_php file):

<?php

require_once 'classes/MultiMerge.inc';

CreateDocx::$returnDocxStructure = true;

// create the first document to be merged and return it as DOCX structure
$docx_a = new CreateDocx();

$text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, ' .
    'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut ' .
    'enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut' .
    'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit ' .
    'in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ' .
    'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui ' .
    'officia deserunt mollit anim id est laborum.';

$paragraphOptions = array(
    'bold' => true,
    'font' => 'Arial'
);

$docx_a->addText($text, $paragraphOptions);

$document1 = $docx_a->createDocx();

// create the second document to be merged and return it as DOCX structure
$docx_b = new CreateDocx();

$text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, ' .
    'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut ' .
    'enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut' .
    'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit ' .
    'in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ' .
    'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui ' .
    'officia deserunt mollit anim id est laborum.';

$paragraphOptions = array(
    'font' => 'Arial'
);

$docx_b->addText($text, $paragraphOptions);

$footnote = new WordFragment($docx_b, 'document');
$footnote->addFootnote(
  array(
    'textDocument' => 'footnote',
    'textFootnote' => 'The footnote we want to insert.',
    'referenceMark' => array('b' => 'on'),
  )
);

$textFragment = new WordFragment($docx_b, 'document');

$text = array();
$text[] = array('text' => 'Other text ');
$text[] = $footnote;

$paragraphOptions = array(
  'textAlign' => 'center',
  'bold' => true,
);
$textFragment->addText($text, $paragraphOptions);

$htmlFragment = new WordFragment($docx_b, 'document');

$htmlFragmentString = new WordFragment($docx_b, 'document');
$htmlFragmentString->embedHtml('<p style="font-family: verdana; font-size: 11px">HTML tags <b>bold</b></p>');

$textHtml = array();
$textHtml[] = $htmlFragmentString;

$htmlFragment->addText($textHtml);

$valuesTable = array(
  array(
    $textFragment,
  ),
  array(
    '2',
  ),
  array(
    $htmlFragment,
  ),
);

$docx_b->addTable($valuesTable);

$document2 = $docx_b->createDocx();

CreateDocx::$returnDocxStructure = false;

$merge = new MultiMerge();
$merge->mergeDocx($document1, array($document2), 'example_merge_docx_2.docx', array());

Please use an unique file name as DOCX output when calling mergeDocx. If you need to return the merged document as DOCX stream instead of generating the file, you can use this option:

https://www.phpdocx.com/api-documentation/performance/zip-stream-docx-with-PHP

You can use the in-memory features with DOCX created from scratch and from templates.

Regards.

Posted by cem  · 15-06-2017 - 20:08

Hi,

Thank you for the prompt response!  I will try both the unique file name and the in-memory option and let you know the results. 

I have noticed though, that the default on our installation does produce temp files.  Not the .tmp files but actual results of the createDocx() for each document.  Then it takes those and seems to merge them.  It's creating only one temp .docx per each createDocx() and puts it in our Public folder path.  I never set anything to output to that path so I'm not sure why they go there.  The final merged doc goes in a specific path I set so that part works fine.  maybe it's something in the config files?  I didn't touch those but perhaps something defaults to the Public folder (i'm using IIS as a local web server). 

I'm a little unclear on the stream option as I'm not familiar with what it actually is/does.  I'll look at the documentation and experiment with that as well. 

 

Thank you and I'll see if those suggestions work!

Posted by admin  · 16-06-2017 - 06:13

Hello,

Yes, when you call createDocx, it creates a DOCX; this is how phpdocx generates and saves the documents (that's not a temp file but a final file you can storage, download...).

If your script generates more than one DOCX in the same path with the same name, then you need to set unique file names to avoid overwritting the DOCX outputs.

You can set unique file names with any phpdocx license or with the Premium license you can use the in-memory approach to don't generate files in the file system.

Regards.

Posted by cem  · 16-06-2017 - 16:44

Hi,

I tried the in-memory approach and so far I haven't been able to cross my work with any other employee.  It does seem to work!  We've tried generating as close to the same exact time as possible and the documents seem to retain only our own work, not any data mixing from someone else.  No .docx files are generated other than the final merged doc, which is great.  So using that approach, we will just always assign each document object to a variable, then build our array from those variables?  I just want to make sure I understand the fundamentals when doing it this way.

One question that might be obvious but I'm not clear on: is this using the workstation memory or the server memory? 

Thanks again for your excellent help!

Posted by admin  · 16-06-2017 - 18:44

Hello,

Yes, using the mergeDocx you can merge from two to any DOCX documents:

$merge = new MultiMerge(); $merge->mergeDocx($document1, array($document2, $document_3, $document_4, $document_5), 'example_merge_docx_2.docx', array());

And as you are using a Premium license, you can do it using in-memory documents (without generating in the fs the DOCX to be merged), as the sample from our previous update does. To use in-memory DOCX, you just need to enable the $returnDocxStructure flag:

CreateDocx::$returnDocxStructure = true;

And the DOCX is not created but returned when you call the createDocx method:

$document1 = $docx_a->createDocx();

You can generate an keep in variables as many documents as needed to be storaged, merged, serialized...

Do not forget to disable the flag after you have finished creating documents:

CreateDocx::$returnDocxStructure = false;

It uses the memory of the server where PHP runs, this is the web server.

Regards.