Транзакции (TransactionManager)
Транзакции позволяют сделать снимок состояния документа, отрендерить контент спекулятивно, а затем решить, сохранить результат или отклонить. Это основной механизм для логики компоновки «попробовать-и-разместить».
Обзор API
| Метод | Описание | Возвращает |
|---|---|---|
startTransaction() | Сделать снимок текущего состояния документа | static |
commitTransaction() | Отбросить снимок и сохранить все изменения | static |
rollbackTransaction() | Восстановить документ до состояния снимка | static |
Все три метода возвращают static для fluent-цепочки.
Как это работает
При вызове startTransaction() TransactionManager сохраняет полную копию текущего состояния документа — позицию курсора, количество страниц, буферы контента и внутренние счётчики. Затем вы рендерите контент как обычно.
- Commit отбрасывает сохранённый снимок. Отрендеренный контент остаётся в документе.
- Rollback заменяет текущее состояние сохранённым снимком. Всё, отрендеренное после
startTransaction(), удаляется.
Базовый пример
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();
}Сценарии использования
Размещение контента в оставшемся пространстве
Наиболее распространённый сценарий — проверка, помещается ли контент на текущей странице, перед его фиксацией:
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();
}
}Измерение высоты контента
Используйте транзакцию для измерения вертикального пространства, которое займёт контент, без его фактического размещения:
$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(). - Не выполняйте файловый ввод-вывод и не отправляйте вывод внутри блока транзакции — только мутации документа могут быть откачены.
- Предпочитайте измерение малых секций, а не оборачивание всей генерации документа в транзакцию.