1

I have a template file that resides under app/design/frontend//theme/Magento_Sales/templates/order/email_invoice_totals.phtml

As I understand, the business logic should not reside in those template files. Below is, I have the code for that phtml file.

<?php

use Magento\Framework\Escaper;
use Magento\Sales\Block\Order\Totals;

/** @var Totals $block */
/** @var Escaper $escaper */
$index = 0;
$totalsData = $block->getTotals();
function arrayElementPositionChange($key, $new_index, $array)
{
    if($new_index < 0) return;
    if($new_index >= count($array)) return;
    if(!array_key_exists($key, $array)) return;

    $ret = array();
    $ind = 0;
    foreach($array as $k => $v)
    {
        if($new_index == $ind)
        {
            $ret[$key] = $array[$key];
            $ind++;
        }
        if($k != $key)
        {
            $ret[$k] = $v;
            $ind++;
        }
    }
    if($new_index == $ind)
        $ret[$key] = $array[$key];
    return $ret;
}
$totalsData = arrayElementPositionChange('tax', 1, $totalsData) ? array_move('tax', 1, $totalsData) : $block->getTotals();

?>
<?php foreach ($totalsData as $_code => $_total): ?>
    <?php $index = $index+1;  ?>
    <?php if ($_total->getBlockName()): ?>
        <?=
            dd($block->getChildHtml($_total->getBlockName()));
            $htmlBlock = $block->getTotals($_total->getBlockName(), false);
        ?>
    <?php else:?>
        <tr class="<?php if ($_code == "grand_total"): ?> <?= "grand-total"?><?php elseif ($_code === "subtotal_excl"):?> <?= "subtotal-excl" ?> <?php else:?> <?= $escaper->escapeHtmlAttr($_code);?><?php endif; ?>">
            <th class=<?= $index == count($block->getTotals()) ? "tax-history" : "row-history"?> <?= /* @noEscape */ $block->getLabelProperties() ?> scope="row">
                <?= $escaper->escapeHtml($_total->getLabel()) ?>
            </th>
            <td class=<?= $index == count($block->getTotals()) ? "total-sum-history" : "price-history"?> <?= /* @noEscape */ $block->getValueProperties() ?> data-th="<?= $escaper->escapeHtmlAttr($_total->getLabel()) ?>">
                <span class="total">&euro; <?= number_format($_total['value'], 2, '.', ''); ?></span>
            </td>
        </tr>

    <?php endif; ?>
<?php endforeach?>




As seen, I have a function called arrayElementPositionChange() but, it should not be inside the template file. So, the question is, where should I add this business logic? and then use it in my phtml file?

One more question is that, I have

$block->getChildHtml($_total->getBlockName(), false);

Here, the child html for is getting fetched. How can I find the child html's phtml file and manipulate it? I want to make some changes inside that html that is returned using getChildHtml method.

2 Answers 2

0
  1. Question 1.
    There are 2 cases:
  • Case 1: If you already use a custom block class which extend Magento\Sales\Block\Order\Totals block class for your phtml template file, you should add your business logic in that block.
  • Case 2: If you use default block (Magento\Sales\Block\Order\Totals) for your phtml template file, you should use a ViewModel class.

Assume you vendor name is Vendor and your module name is Module.

In your layout xml file which declare Magento_Sales::order/email_invoice_totals.phtml, inject the ViewModel class.
For example:

<block class="Magento\Sales\Block\Order\Invoice\Totals" name="email_invoice_totals" template="Magento_Sales::order/email_invoice_totals.phtml">
    <arguments>
        <!-- other arguments -->
        <argument name="view_model" xsi:type="object">Vendor\Module\ViewModel\ElementPositionChange</argument>
    </arguments>
</block>

Create ViewModel class:
File path: app/code/Vendor/Module/ViewModel/ElementPositionChange.php

<?php

namespace Vendor\Module\ViewModel;

use Magento\Framework\View\Element\Block\ArgumentInterface;

class ElementPositionChange implements ArgumentInterface
{
    public function getTotalsWithElementPositionChange($key, $newIndex, $totalsData)
    {
        if ($newIndex < 0) return;
        if ($newIndex >= count($totalsData)) return;
        if (!array_key_exists($key, $totalsData)) return;

        $result = [];
        $ind = 0;
        foreach ($totalsData as $k => $v) {
            if ($newIndex == $ind) {
                $result[$key] = $totalsData[$key];
                $ind++;
            }
            if ($k != $key) {
                $result[$k] = $v;
                $ind++;
            }
        }
        if ($newIndex == $ind)
            $result[$key] = $totalsData[$key];
        return $result;
    }
}

Then in your phtml template, replace

$totalsData = arrayElementPositionChange('tax', 1, $totalsData) ? array_move('tax', 1, $totalsData) : $block->getTotals();

with the code below:

$totalsData = $block->getViewModel()->getTotalsWithElementPositionChange('tax', 1, $totalsData) ? array_move('tax', 1, $totalsData) : $block->getTotals();

  1. Question 2.
    Assume you using Magento\Sales\Block\Order\Totals block class for your phtml template file. To find the child html's phtml file of that block you should go to layout files which assigned Magento\Sales\Block\Order\Totals class to your phtml template file, you will see the template attribute in child blocks which declare block node.

For example, the block in the below code has tax block as child and it declare template file is Magento_Tax::order/tax.phtml.

<block class="Magento\Sales\Block\Order\Totals" name="order_totals" template="Magento_Sales::order/totals.phtml">
    <arguments>
        <argument name="label_properties" xsi:type="string">colspan="4" class="mark"</argument>
        <argument name="value_properties" xsi:type="string">class="amount"</argument>
    </arguments>
    <block class="Magento\Tax\Block\Sales\Order\Tax" name="tax" template="Magento_Tax::order/tax.phtml"/>
</block>

Some cases, multiple layout files declares child blocks for a parent block. You can run this command from your Magento root to know which files reference to the parent block:

grep -r --include \*.xml "Magento\\\Sales\\\Block\\\Order\\\Totals" app vendor
1

ViewModels is what you are looking for.

Please check the official documentation, if you can't solve it please add a comment.

Check view models at adobe.com

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.