Forum


Replies: 6   Views: 1288
How to selectively add to multimerge
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  · 29-10-2020 - 22:03

Hello,

We have phpdocx Premium license and I'm trying to see if there is a way to handle when a doc may or may not exist and then add to the array and also in the correct order.The issue is there will always be a body doc that gets created, then there may or may not be a cover doc and there may or may not be a back cover doc.  I've been testing a few ways and seem to get errors if I try to set an empty array for the 2 optional docs or even just an empty string as placeholder so it doesn't error.  below is some psuedo code.

Essentially, is there a way to do a final output createDocx from an already assembled merged array of docs?

I thought it would be ideal to handle the insertion inside a statement that already does the test for whether or not we need the file/page.  i use array_push and tried array_unshift but that just gives an error about zip extraction or similar. then i tried to use if statements to either merge or not merge but the createdocx() just pulls whatever was the last doc created in-stream it seems. 

Is there a way to either do the merge selectively and then just create a doc from the already merged array of docs or a way to add docs in a specific order?  i looked at mergeDocxAt but that creates an output doc and I only want the output doc at the very end of testing for all needed docs/building the array.  If i test for only one or the other and do a ton of if/else i still could potentially not have the correct order/array needed. 
Thank you for any help or suggestions! 
Renee

      if (!empty($ending_doc_file)) {
          $docx = new CreateDocxFromTemplate($doc_file);
          $last_doc = $docx->createDocx();
          array_push($doc_arr, $last_doc); //this way the last doc is already in the array but the problem is now i can't output this merged array as the output doc I guess.
      }
      
      if (!empty($first_doc_file)) {
          $docx = new CreateDocxFromTemplate($another_file);
          $first_doc = $docx->createDocx(); // create file
      }

      CreateDocx::$returnDocxStructure = false;

      $merge = new MultiMerge();

      if ( (!empty($first_doc)) || (!empty($last_doc)) ) {
        $merge = new MultiMerge();
        $merge->mergeDocx($first_doc, $doc_arr, $final_path.$doc_title, array());
      } else {
        $docx->CreateDocx($final_path.$doc_title); //if I default to this, the potential “last doc†is missing because it’s not in the doc-array above.
      }

  

Posted by admin  · 30-10-2020 - 09:34

Hello,

Using a Premium license, mergeDocx allows DOCX files and DOCXStructure objects as inputs, and the first parameter is always a single document (file or DOCXStructure) and the second parameter is an array of the documents to be merge (file or DOCXStructure).

This is the signature:

public function mergeDocx($firstDocument, $documentArray, $finalDocument, $options)

You can generate an array of the DOCX to be merged (Advanced licenses can't use in-memory DOCX, only Premium licenses):

require_once 'classes/MultiMerge.php';

// enable in-memory DOCX
CreateDocx::$returnDocxStructure = true;

// first document
$docx_a = new CreateDocx();

$text = 'Text 1';
$paragraphOptions = array('bold' => true, 'font' => 'Arial');
$docx_a->addText($text, $paragraphOptions);

$document1 = $docx_a->createDocx();

// second document
$docx_b = new CreateDocx();

$text = 'Text 2';
$paragraphOptions = array('font' => 'Arial');
$docx_b->addText($text, $paragraphOptions);

$document2 = $docx_b->createDocx();

// third document
$docx_c = new CreateDocx();

$text = 'Text 3';
$docx_c->addText($text);

$document3 = $docx_c->createDocx();

// array of the DOCX to be merged
$array_docx = array();
array_push($array_docx, $document2);
array_push($array_docx, $document3);

// disable in-memory DOCX
CreateDocx::$returnDocxStructure = false;

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

Or, maybe this is is the easiest solution for your case, you can call mergeDocx for each DOCX to be merged:

require_once 'classes/MultiMerge.php';

// enable in-memory DOCX
CreateDocx::$returnDocxStructure = true;

$merge = new MultiMerge();
  
// first document
$docx_a = new CreateDocx();

$text = 'Text 1';
$paragraphOptions = array('bold' => true, 'font' => 'Arial');
$docx_a->addText($text, $paragraphOptions);

$document1 = $docx_a->createDocx();

// second document
$docx_b = new CreateDocx();

$text = 'Text 2';
$paragraphOptions = array('font' => 'Arial');
$docx_b->addText($text, $paragraphOptions);

$document2 = $docx_b->createDocx();

$document1 = $merge->mergeDocx($document1, array($document2), null, array());

// third document
$docx_c = new CreateDocx();

$text = 'Text 3';
$docx_c->addText($text);

$document3 = $docx_c->createDocx();

$document1 = $merge->mergeDocx($document1, array($document3), null, array());

// disable in-memory DOCX
CreateDocx::$returnDocxStructure = false;

$document1->saveDocx('output.docx');

In both cases you'll get the same output, but maybe the second approach is the easiest one as you can merge DOCX when needed instead of creating a temp array.

Regards.

Posted by cem  · 30-10-2020 - 13:59

Hi,

I'll take a look at the saveDocx() method, that might help.  The main issue was i was hoping for something like an insertDocx where you could insert a doc into either the very beginning or the end.  The problem is we have cover pages and back covers, but not always.  I suppose I'll just play with a temp array like i already have and test for the numerous conditions where a front cover or back cover exists or not, then try and get the merge to put it in the right spot. 

I'm still not sure what unshift() doesn't work tho.  that would solve the problem of moving a doc to the first position of the array. 

Thanks

Posted by cem  · 30-10-2020 - 19:34

I just tried the second example using merge as needed to try and control the position of the documents using conditional statements.  I get Fatal error: Call to a member function saveDocx() on null.

i'm sure there is something simple i am missing with this. is there a way to simply have conditional statements on each possible document (such as my example), merge as needed and the save that merged doc to the correct path?  I can't seem to pass path parameters to saveDocx() and I'm not seeing any documentation for that.

it seems like you can only do the merge w/o a conditional and the saveDocX() doesn't work with the final merged document.

Also, even just copying the example and testing it gave a lot of errors about zip archive, null, etc.  I don't know if we need to set something in our config/ini but we can't just merge on the fly it seems.  There is always an error unless we use only one merge and then the process is complete - the doc gets created even if other objects were created and attempting to merge.  You can only ever have one mergedocx it seems.

 

Thanks

Posted by admin  · 30-10-2020 - 20:06

Hello,

Both samples posted in our previous reply have been fully tested with phpdocx 9.5 and phpdocx 10 and they work correctly. Please run them standalone without changing anything but the path to MultiMerge.php; please note that although our previous codes use in-memory DOCX, they also work with DOCX files.

If after changing the second sample you get Fatal error: Call to a member function saveDocx() on null is because you are not using the correct variable to call saveDocx (a null variable instead of a correct one) or in-memory DOCX (CreateDocx::$returnDocxStructure) is not set as true when doing the merge, so no DOCXStructure object is returned or you are using an old Premium version.

You can use any target path when calling saveDocx in a DOCXStructure, for example:

$document1->saveDocx('../output_2.docx');

About your question, saveDocx works with DOCXStructure objects (https://www.phpdocx.com/documentation/cookbook/in-memory-docx-documents) and you just need to set the path to the output DOCX. CreateDocx and CreateDocxFromTemplate objects use the createDocx method to save the DOCX (that call internally saveDocx of DOCXStructure).

About it seems like you can only do the merge w/o a conditional, of course you can use mergeDocx inside a conditional, but you need to generate an array that contains valid (existing) DOCX files or DOCXStructure objects. For example, from your code:

if ( (!empty($first_doc)) || (!empty($last_doc)) ) {
  $merge = new MultiMerge();
  $merge->mergeDocx($first_doc, $doc_arr, $final_path.$doc_title, array());
}

As you are using an OR (||) condition, if $last_doc is not empty but $first_doc is empty the condition is true, so mergeDocx is called with an empty variable ($first_doc), so the method will return an error because $first_doc isn't a valid ZIP file (or DOCXStructure object).

If you get a ZIP error is because you are trying to merge not existing DOCX files (or not valid DOCXStructure objects).

We recommend you to generate valid conditions checking that all inputs used in mergeDocx are valid DOCX files (Advanced and Premium licenses) or DOCXStructure objects (Premium licenses).

Regards.

Posted by cem  · 03-11-2020 - 21:55

Ah, versions 9.5 and 10.  I'm sorry, I failed to mention that we are/were using 9.0.  It definitely does not work in 9.0 (or this specific test didn't).  I updated to 9.5 just to test the sample method 2 and it worked just fine!  Sorry again, i know how important versioning is, i didn't realize i hadn't mentioned it.

If it helps anyone else, using a tweak to the sample code where $document1 is actually updated conditionally and the new object (doc3 or doc2 or whatever) is actually used first and $document1 is the array to add to it does work.

Basically since the order is vital and we don't always have those documents/objects, updating $document1 to be used in front OR as the array (in back position) does seem to work.  I've added the tweaked sample to show the difference using $document3 as the front page which does solve our issue of positioning within the array of documents. And there are other ways to do it i'm sure using the merge() method.  the empty temporary array method also would work and it's what we were doing before but everything got pushed to the back doing that. 

Thank you again for your help and patience!  this seems to have completely solved a problem with positioning the pages we've had since we first signed up for version 7.0!  It's great that it is now so flexible!

require_once 'classes/MultiMerge.php';

// enable in-memory DOCX
CreateDocx::$returnDocxStructure = true;

$merge = new MultiMerge();
  
// first document
$docx_a = new CreateDocx();

$text = 'Text 1';
$paragraphOptions = array('bold' => true, 'font' => 'Arial');
$docx_a->addText($text, $paragraphOptions);

$document1 = $docx_a->createDocx();

// second document
$docx_b = new CreateDocx();

$text = 'Text 2';
$paragraphOptions = array('font' => 'Arial');
$docx_b->addText($text, $paragraphOptions);

$document2 = $docx_b->createDocx();

$document1 = $merge->mergeDocx($document1, array($document2), null, array());

// third document - this is actually the FRONT COVER- First page
$docx_c = new CreateDocx();

$text = 'Text 3';
$docx_c->addText($text);

$document3 = $docx_c->createDocx();

// below is the key part - merge the object you want in front and your built array will work.
$document1 = $merge->mergeDocx($document3, array($document1), null, array());

// disable in-memory DOCX
CreateDocx::$returnDocxStructure = false;

$document1->saveDocx('output.docx');

 

Posted by admin  · 04-11-2020 - 07:12

Hello,

Merging in-memory DOCX documents was added in phpdocx 9.5 (https://www.phpdocx.com/news/post/phpdocx-v9-5-release-notes/219), previous versions don't include this feature. Also please note that you can run a similar code but using DOCX files instead of in-memory documents if needed (or even mixing them).

Regards.