Продвинутая разработка на Hyva для Magento
Архитектура Hyva ViewModel
ViewModel: мост между PHP и Alpine.js
В архитектуре Hyva ViewModel играет ключевую роль. Это простой PHP-класс, который служит связующим звеном между сложной логикой бэкенда Magento 2 и реактивным фронтендом на Alpine.js. Представьте его как куратора данных: он собирает, обрабатывает и подготавливает информацию на сервере, а затем передает ее в чистом и удобном виде в ваш .phtml шаблон, где ее уже подхватывает Alpine.js.
Основная задача ViewModel — отделить логику получения и подготовки данных от логики их отображения. Это делает шаблоны чище, а код — более структурированным и тестируемым.
Как подключить ViewModel
Существует два основных способа сделать ViewModel доступной в вашем шаблоне. Первый — это классический подход Magento через Layout XML. Вы объявляете ViewModel как аргумент для блока.
<referenceBlock name="your.block.name">
<arguments>
<argument name="viewModel" xsi:type="object">Vendor\Module\ViewModel\MyViewModel</argument>
</arguments>
</referenceBlock>
Затем в шаблоне вы получаете его через $block->getViewModel(). Этот способ работает, но Hyva предлагает более изящное и современное решение.
Второй, и предпочтительный, способ — это прямо в .phtml файле. Hyva анализирует PHPDoc-блок @var в начале шаблона и автоматически создает экземпляр указанного класса. Это устраняет необходимость в громоздких XML-конфигурациях для каждого ViewModel.
<?php
/** @var \Vendor\Module\ViewModel\MyViewModel $myViewModel */
/** @var \Hyva\Theme\ViewModel\Heroicons $heroicons */
?>
<!-- Теперь $myViewModel и $heroicons доступны здесь -->
Этот подход не только чище, но и позволяет легко подключать несколько ViewModel в один шаблон, например, ваш кастомный ViewModel и стандартный Heroicons для отображения иконок.
Создаем динамический компонент цены
Давайте создадим практический пример: компонент, который отображает цену товара и показывает специальную цену со скидкой по клику. Мы создадим собственный ViewModel, который будет готовить данные о ценах продукта.
Сначала создадим сам класс ViewModel. Он будет принимать объект продукта и возвращать массив с данными о ценах.
<?php
declare(strict_types=1);
namespace Vendor\Module\ViewModel;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\Pricing\Helper\Data as PricingHelper;
use Magento\Framework\View\Element\Block\ArgumentInterface;
class ProductPrice implements ArgumentInterface
{
private PricingHelper $pricingHelper;
public function __construct(PricingHelper $pricingHelper)
{
$this->pricingHelper = $pricingHelper;
}
public function getPriceData(ProductInterface $product): array
{
$finalPrice = $product->getFinalPrice();
$regularPrice = $product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue();
return [
'regularPrice' => $this->pricingHelper->currency($regularPrice, true, false),
'finalPrice' => $this->pricingHelper->currency($finalPrice, true, false),
'hasSpecialPrice' => $finalPrice < $regularPrice,
'discountPercent' => $this->calculateDiscount($regularPrice, $finalPrice)
];
}
private function calculateDiscount(float $regular, float $final): int
{
if ($regular > $final) {
return (int)round((($regular - $final) / $regular) * 100);
}
return 0;
}
}
Теперь используем этот ViewModel в нашем .phtml шаблоне. Мы внедрим его автоматически и передадим данные в Alpine.js компонент через x-data. Для безопасного преобразования PHP-массива в мы используем специальный метод hyva_json_encode. Он надежнее стандартного json_encode, так как правильно обрабатывает возможные ошибки.
<?php
/** @var \Vendor\Module\ViewModel\ProductPrice $priceViewModel */
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
?>
<div x-data="{
priceData: <?= $escaper->escapeHtml(hyva_json_encode($priceViewModel->getPriceData($product))) ?>,
showDiscount: false
}">
<div @click="showDiscount = !showDiscount" class="cursor-pointer">
<span x-text="priceData.finalPrice" class="font-bold text-lg"></span>
<template x-if="priceData.hasSpecialPrice">
<span x-text="priceData.regularPrice" class="line-through text-gray-500 ml-2"></span>
</template>
</div>
<div x-show="showDiscount && priceData.hasSpecialPrice" x-transition class="mt-2 text-green-600">
Скидка <span x-text="priceData.discountPercent"></span>%!
</div>
</div>
В этом примере вся логика форматирования цен и расчета скидки инкапсулирована в ViewModel. Шаблон остается чистым: он только передает данные и описывает, как на них должен реагировать интерфейс.
Этот подход, сочетающий мощь PHP на бэкенде и реактивность Alpine.js на фронтенде через элегантный мост в виде ViewModel, является основой философии Hyva. Он позволяет создавать сложные, динамические интерфейсы, сохраняя при этом код чистым, производительным и удобным для поддержки.
Какова основная роль ViewModel в архитектуре Hyva?
Какой метод рекомендуется использовать в Hyva для безопасной передачи данных из PHP ViewModel в компонент Alpine.js (x-data)?
Теперь вы знаете, как создавать и использовать ViewModel для эффективной передачи данных из Magento в Alpine.js.