Skip to content

Транзакции (TransactionManager)

Транзакции позволяют сделать снимок состояния документа, отрендерить контент спекулятивно, а затем решить, сохранить результат или отклонить. Это основной механизм для логики компоновки «попробовать-и-разместить».

Обзор API

МетодОписаниеВозвращает
startTransaction()Сделать снимок текущего состояния документаstatic
commitTransaction()Отбросить снимок и сохранить все измененияstatic
rollbackTransaction()Восстановить документ до состояния снимкаstatic

Все три метода возвращают static для fluent-цепочки.

Как это работает

При вызове startTransaction() TransactionManager сохраняет полную копию текущего состояния документа — позицию курсора, количество страниц, буферы контента и внутренние счётчики. Затем вы рендерите контент как обычно.

  • Commit отбрасывает сохранённый снимок. Отрендеренный контент остаётся в документе.
  • Rollback заменяет текущее состояние сохранённым снимком. Всё, отрендеренное после startTransaction(), удаляется.

Базовый пример

php
use Yeeefang\TcpdfNext\Core\Document;

$pdf = Document::create()
    ->addPage()
    ->setFont('Helvetica', '', 12);

// Попытка разместить блок на текущей странице
$pdf->startTransaction();
$startPage = $pdf->getPage();
$pdf->multiCell(0, 6, $longText);

if ($pdf->getPage() > $startPage) {
    // Контент перешёл на следующую страницу — откат и другой подход
    $pdf->rollbackTransaction();
    $pdf->addPage();
    $pdf->multiCell(0, 6, $longText);
} else {
    // Контент помещается — сохраняем
    $pdf->commitTransaction();
}

Сценарии использования

Размещение контента в оставшемся пространстве

Наиболее распространённый сценарий — проверка, помещается ли контент на текущей странице, перед его фиксацией:

php
use Yeeefang\TcpdfNext\Core\Document;

$pdf = Document::create()
    ->addPage()
    ->setFont('Helvetica', '', 10);

foreach ($sections as $section) {
    $pdf->startTransaction();
    $startPage = $pdf->getPage();

    $pdf->setFont('Helvetica', 'B', 14)
        ->cell(0, 8, $section['title'], newLine: true)
        ->setFont('Helvetica', '', 10)
        ->multiCell(0, 5, $section['body']);

    if ($pdf->getPage() > $startPage) {
        $pdf->rollbackTransaction();
        $pdf->addPage();
        $pdf->setFont('Helvetica', 'B', 14)
            ->cell(0, 8, $section['title'], newLine: true)
            ->setFont('Helvetica', '', 10)
            ->multiCell(0, 5, $section['body']);
    } else {
        $pdf->commitTransaction();
    }
}

Измерение высоты контента

Используйте транзакцию для измерения вертикального пространства, которое займёт контент, без его фактического размещения:

php
$pdf->startTransaction();
$startY = $pdf->getY();
$pdf->multiCell(0, 5, $text);
$endY = $pdf->getY();
$height = $endY - $startY;
$pdf->rollbackTransaction();

// Теперь используйте $height для принятия решений о компоновке

Важные ограничения

Нет вложенности

Вложенные транзакции не поддерживаются. Вызов startTransaction() при уже активной транзакции вызовет исключение. Всегда фиксируйте или откатывайте перед началом новой транзакции.

Влияние на производительность

Транзакция сохраняет полный снимок состояния документа. Для документов с множеством страниц и большими буферами контента это может временно удвоить потребление памяти. Делайте блоки транзакций как можно меньше — снимок, рендер, решение, затем немедленная фиксация или откат.

Лучшие практики

  • Минимизируйте код между startTransaction() и commitTransaction() / rollbackTransaction().
  • Всегда убеждайтесь, что каждый startTransaction() имеет ровно один парный commitTransaction() или rollbackTransaction().
  • Не выполняйте файловый ввод-вывод и не отправляйте вывод внутри блока транзакции — только мутации документа могут быть откачены.
  • Предпочитайте измерение малых секций, а не оборачивание всей генерации документа в транзакцию.

Распространяется по лицензии LGPL-3.0-or-later.