Phpword: Variables in blocks

Created on 30 May 2018  路  8Comments  路  Source: PHPOffice/PHPWord

This is:

  • [ ] a bug report
  • [x] a feature request

Expected Behavior

Being able to create a block with an array of variables (aka foreach in templating).

In the docx "template":

Guests:
${CLONEME}
Forname: ${FORENAME}
Lastname: ${LASTNAME}
${/CLONEME}

In the code:

$templateProcessor->cloneBlock('CLONEME', [
['FORENAME' => 'John', 'LASTNAME' => 'Donut'],
['FORENAME' => 'Cat', 'LASTNAME' => 'Stefano']
]);

I want to start a discussion about this before I might implement it and send a PR.

Most helpful comment

When using setValue, you use a third parameter and set it to 1

            $templateProcessor->setValue(
                'FORENAME', $forename, 1
            );

That way it will only replace the first value it finds. So first you use cloneBlock and clone the blocks, and after that run your foreach and only replace the first variable at a time.

All 8 comments

Why don't you just loop through the array and set the values?

Well this way I can have the formatting and text in the template and fill the data in the code. I don't see how this would be possible. Especially with two times the same macro FORENAME and two times LASTNAME.
The result of the code above is:

Forname: John
Lastname: Donut
Forname: Cat
Lastname: Stefano

How would you achieve that with a foreach loop?

When using setValue, you use a third parameter and set it to 1

            $templateProcessor->setValue(
                'FORENAME', $forename, 1
            );

That way it will only replace the first value it finds. So first you use cloneBlock and clone the blocks, and after that run your foreach and only replace the first variable at a time.

If anybody else is wondering how to do this:

        $repeatdata = [
            ['FORENAME' => 'John', 'LASTNAME' => 'Donut'],
            ['FORENAME' => 'Cat', 'LASTNAME' => 'Stefano']
        ];
        $templateProcessor->cloneBlock('CLONEME', count($repeatdata));

        foreach ($repeatdata as $repeatitem){
            foreach ($repeatitem as $search => $replace){
                $templateProcessor->setValue($search, $replace, 1);
            }
        }

Thanks @rkorebrits! This is way cleaner.

Perfect, that's pretty much how I use it also (although without the second foreach, but assign one by one). Also prefer it like this without preparing the data as it's cleaner to have conditional logic in the foreach imho.

Please close the ticket if resolved :-)

I found two edge-cases where the solution above does not work (but with my PR):

  1. If you have two Blocks with the same (named) macros inside:
${MYCAT}
Name: ${NAME}
${/MYCAT}
${MYDOG}
Name: ${NAME}
${/MYDOG}

This only works accidentally if the data is in the correct order (CAT before DOG). Otherwise Cat will be replaced with dogs name since the block context is missing,

  1. If you have a macro (with the same name) twice in the Block:
${MYCAT}
Her name is "${NAME}". ${NAME} is the best cat in the world,
${/MYCAT}

Obviously only the first one is being replaced.

  1. Is a good point, 1. Is just bad (ambiguous) naming imho. Rather call it CAT_NAME and DOG_NAME

In a path syntax the CAT.NAME and DOG.NAME makes sense. Yes, the second is worse and I don't even have a workaround for that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Joel-James picture Joel-James  路  3Comments

cedrictailly picture cedrictailly  路  5Comments

Ryuzakix3 picture Ryuzakix3  路  6Comments

agang235 picture agang235  路  3Comments

jaberu picture jaberu  路  3Comments