تخطَّ إلى المحتوى

إرجاع ملف PDF مُولَّد من وحدة تحكم

أنشئ ملف ⁨PDF⁩ داخل إجراء وحدة تحكم، ثم أرجِعه كاستجابة ⁨HTTP⁩. يتضمن كل تكامل لإطار عمل مساعد PdfResponse يبني كائن الاستجابة، ويضبط Content-Type: application/pdf، ويرفق ترويسات الأمان، وينقِّح اسم الملف. يشرح هذا الدليل أوضاع التسليم الثلاثة في ⁨Laravel⁩ و⁨Symfony⁩ و⁨CodeIgniter 4⁩: المعاينة المضمَّنة، وتنزيل الملف، والتسليم المتدفِّق.

تحقق من هذه المتطلبات المسبقة أولًا، حتى يكون مسار وحدة التحكم جاهزًا قبل البدء:

  • نواة ⁨NextPDF⁩ مثبَّتة.
  • تكامل واحد لإطار العمل مثبَّت، وقد اكتُشف مزوِّد الخدمة أو الحزمة أو الخدمة الخاصة به. تحقق من الاكتشاف في صفحة التثبيت الخاصة بإطار عملك قبل البدء.
  • لا يحتاج الوضع المتدفِّق إلى أي حزم إضافية. يوفر كل تكامل المتغيِّر المتدفِّق إلى جانب المتغيِّر المُخزَّن مؤقتًا.

هذا دليل إرشادي يفترض أنك تعرف بالفعل كيفية توجيه طلب إلى وحدة تحكم في إطار عملك. للحصول على أول مثال قابل للتشغيل في كل إطار عمل، اقرأ دليل البدء السريع المرتبط ضمن انظر أيضًا.

ثبِّت التكامل الخاص بإطار عملك. نفِّذ أحد الأوامر التالية:

Terminal window
composer require nextpdf/laravel
Terminal window
composer require nextpdf/symfony
Terminal window
composer require nextpdf/codeigniter

بالنسبة إلى ⁨Laravel⁩، انشر الإعداد بعد التثبيت:

Terminal window
php artisan vendor:publish --tag=nextpdf-config

يسجِّل ⁨Symfony⁩ الحزمة عبر ⁨Flex⁩، ويكتشف ⁨CodeIgniter⁩ الخدمة تلقائيًا. أكِّد الاكتشاف في صفحة تثبيت إطار عملك قبل المتابعة.

يتبع كل تكامل لإطار عمل النمط نفسه المكوَّن من ثلاثة أجزاء: احصل على مستند جديد، واكتب المحتوى في ذلك المستند، ثم مرِّره إلى مصنع PdfResponse الذي يُرجِع استجابة ⁨HTTP⁩. واجهة برمجة تطبيقات المستند (addPage() وcell() وsetFont()) هي سطح محرك النواة، وهي متطابقة عبر الإطارات. يختلف مصنع الاستجابة فقط في فئة الاستجابة التي يُرجِعها، لأن لكل إطار عمل نوع استجابة ⁨HTTP⁩ خاصًا به.

يوفِّر PdfResponse ثلاثة أوضاع للتسليم. المعاينة المضمَّنة تضبط ترويسة Content-Disposition: inline، بحيث يعرض المتصفح ملف ⁨PDF⁩ في علامة تبويب عارض. التنزيل يضبط Content-Disposition: attachment، بحيث يحفظ المتصفح الملف. التدفُّق يُصدِر متن ملف ⁨PDF⁩ في كتل ثابتة بدلًا من تخزين المستند بأكمله مؤقتًا في الذاكرة. اختره للمستندات الكبيرة عندما تكون ذروة استخدام الذاكرة أهم من معرفة Content-Length.

احصل على المستند عبر مسار التحليل المعتاد لإطار عملك:

  • ⁨Laravel⁩ — حلِّل NextPDF\Contracts\DocumentFactoryInterface من الحاوية باستخدام app(...) واستدعِ create()، الذي يُرجِع NextPDF\Core\Document جديدًا، وهو النوع الملموس الذي تقبله مصانع PdfResponse.
  • ⁨Symfony⁩ — احقن NextPDF\Symfony\Service\PdfFactory واستدعِ create()، الذي يُرجِع NextPDF\Core\Document جديدًا مع تطبيق الإعدادات الافتراضية المُهيَّأة للمستند مسبقًا.
  • ⁨CodeIgniter 4⁩ — حلِّل مكتبة Pdf عبر Services::pdf() (أو المساعد pdf())، أو احصل على مستند مجرَّد عبر pdf_document().
الجانب المعني⁨Laravel⁩⁨Symfony⁩⁨CodeIgniter 4⁩
مستند جديدapp(DocumentFactoryInterface::class)->create()PdfFactory::create()pdf_document() / Services::pdf()->document()
استجابة مضمَّنةPdfResponse::inline($doc, $name)PdfResponse::inline($doc, $name)$pdf->inline($name) / PdfResponse::inline($doc, $name)
استجابة تنزيلPdfResponse::download($doc, $name)PdfResponse::download($doc, $name)$pdf->download($name) / PdfResponse::download($doc, $name)
مضمَّن متدفِّقPdfResponse::streamInline($doc, $name)PdfResponse::streamInline($doc, $name)PdfResponse::streamInline($doc, $name)
تنزيل متدفِّقPdfResponse::streamDownload($doc, $name)PdfResponse::streamDownload($doc, $name)PdfResponse::streamDownload($doc, $name)
النوع المُرجَعIlluminate\Http\Response (متدفِّق: StreamedResponse)Symfony\Component\HttpFoundation\Response (متدفِّق: StreamedResponse)CodeIgniter\HTTP\DownloadResponse

يوجد PdfResponse الخاص بـ ⁨Laravel⁩ في NextPDF\Laravel\Http\PdfResponse، والخاص بـ ⁨Symfony⁩ في NextPDF\Symfony\Http\PdfResponse، والخاص بـ ⁨CodeIgniter⁩ في NextPDF\CodeIgniter\Http\PdfResponse. توثِّق صفحة الأمان والعمليات الخاصة بكل تكامل سلوك الاستجابة الكامل لتلك الحزمة: مجموعة الترويسات، وقواعد الترتيب، وتنقيح اسم الملف. تجد هذه الصفحات مرتبطة ضمن انظر أيضًا.

فيما يلي الحد الأدنى لإجراء التنزيل في كل إطار عمل. تستخدم استدعاءات المستند سطح النواة نفسه، ولا يتغير إلا قالب وحدة التحكم.

Laravel: app/Http/Controllers/ReportController.php
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use NextPDF\Contracts\DocumentFactoryInterface;
use NextPDF\Laravel\Http\PdfResponse;
final class ReportController extends Controller
{
public function download(): Response
{
$document = app(DocumentFactoryInterface::class)->create();
$document->addPage();
$document->cell(0, 10, 'Monthly report', newLine: true);
return PdfResponse::download($document, 'report.pdf');
}
}
Symfony: src/Controller/ReportController.php
<?php
declare(strict_types=1);
namespace App\Controller;
use NextPDF\Symfony\Http\PdfResponse;
use NextPDF\Symfony\Service\PdfFactory;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class ReportController
{
#[Route('/report', name: 'report_pdf')]
public function download(PdfFactory $pdf): Response
{
$document = $pdf->create();
$document->addPage();
$document->cell(0, 10, 'Monthly report', newLine: true);
return PdfResponse::download($document, 'report.pdf');
}
}
CodeIgniter 4: app/Controllers/ReportController.php
<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\DownloadResponse;
use NextPDF\CodeIgniter\Config\Services;
final class ReportController extends BaseController
{
public function download(): DownloadResponse
{
$pdf = Services::pdf();
$pdf->document()->addPage();
$pdf->document()->cell(0, 10, 'Monthly report');
return $pdf->download('report.pdf');
}
}

للمعاينة في المتصفح بدلًا من التنزيل، استبدل استدعاء download(...) بـ inline(...) في ⁨Laravel⁩ و⁨Symfony⁩، أو بـ $pdf->inline('report.pdf') في ⁨CodeIgniter⁩. يتغير الترتيب إلى inline، وتبقى كل ترويسة أخرى كما هي.

يحقن إجراء الإنتاج تبعياته، ويلتقط أكثر الاستثناءات تحديدًا مما يوثِّقه التكامل، ويسجِّل فئة الفشل دون تسريب أثر التتبع، ويُرجِع خطأ ⁨HTTP⁩ محدَّدًا. يستخدم المثال أدناه حقن المُنشئ في ⁨Laravel⁩. تتبع المكافِئات في ⁨Symfony⁩ و⁨CodeIgniter⁩ النمط نفسه وتظهر في صفحة الاستخدام الإنتاجي الخاصة بكل تكامل.

Laravel: app/Http/Controllers/InvoiceController.php
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use NextPDF\Contracts\DocumentFactoryInterface;
use NextPDF\Laravel\Http\PdfResponse;
use Psr\Log\LoggerInterface;
use Throwable;
final class InvoiceController extends Controller
{
public function __construct(
private readonly DocumentFactoryInterface $documents,
private readonly LoggerInterface $logger,
) {}
public function show(int $invoiceId): Response
{
try {
$document = $this->documents->create();
$document->addPage();
$document->cell(0, 10, "Invoice #{$invoiceId}", newLine: true);
return PdfResponse::download(
$document,
"invoice-{$invoiceId}.pdf",
);
} catch (Throwable $exception) {
// Log the exception class, never the message or a stack trace,
// so internal detail does not leak into the log sink.
$this->logger->error('Invoice PDF generation failed', [
'invoice_id' => $invoiceId,
'exception' => $exception::class,
]);
return new Response('Could not generate the invoice PDF.', 500);
}
}
}

احقن DocumentFactoryInterface واستدعِ create() في كل إجراء. يُرجِع هذا NextPDF\Core\Document جديدًا، وهو النوع الملموس الذي تقبله مصانع PdfResponse الخاصة بـ ⁨Laravel⁩. تحليل مستند جديد لكل طلب يبقي المصنع قابلًا للاستبدال في الاختبارات. لا تُعِد استخدام مثيل وحدة تحكم واحد لمستندَين غير مرتبطَين داخل عملية عامل واحدة طويلة التشغيل.

بالنسبة إلى المستندات الكبيرة جدًا، استبدل المصنع المُخزَّن مؤقتًا بمصنع متدفِّق لتقييد ذروة استخدام الذاكرة. يُرجِع المتغيِّر المتدفِّق StreamedResponse (في ⁨Laravel⁩ و⁨Symfony⁩) ويُصدِر المتن في كتل ثابتة. ويحذف Content-Length عمدًا، لذلك لا ترى أشرطة تقدُّم التنزيل ولا الوكلاء الحساسون للطول حجمًا معروفًا. فضِّل download() / inline() المُخزَّنَين مؤقتًا للاستجابات الصغيرة الحساسة لزمن الوصول.

Laravel: streamed download for a large report
$document = $this->documents->create();
// ... emit content onto $document ...
return PdfResponse::streamDownload($document, 'annual-report.pdf');
  • مستند جديد لكل استدعاء. في التكاملات الثلاثة جميعها، المستند هو ناتج مصنع جديد لكل عملية تحليل. لا تخزِّن مستندًا مُحلَّلًا مؤقتًا بين مستندات منطقية مختلفة، أو بين الطلبات في عامل طويل التشغيل. وإلا فستنتقل حالة المحتوى القديمة إلى ما يليها.
  • اسم ملف فارغ. يؤدي اسم الملف الفارغ المُمرَّر إلى مصنع PdfResponse إلى الرجوع إلى اسم افتراضي (document.pdf) بدلًا من إنتاج ترتيب فارغ. مرِّر اسم ملف صريحًا وذا معنى.
  • أسماء الملفات غير ⁨ASCII⁩. تضيف استجابة ⁨Laravel⁩ معلَمة ⁨RFC 5987⁩ filename*= تلقائيًا للأسماء غير ⁨ASCII⁩، وتستخدم معلَمة اسم الملف العادية لأسماء ⁨ASCII⁩. لا تُرمِّز اسم الملف يدويًا بنفسك.
  • الاستجابات المتدفِّقة خلف وكيل يُخزِّن مؤقتًا. الوكيل الذي يخزِّن المتن الكامل مؤقتًا يلغي فائدة الذاكرة من التدفُّق. هيِّئ الوكيل لتدفُّق استجابات ⁨PDF⁩، أو استخدم استجابة مُخزَّنة مؤقتًا في ذلك المسار.
  • رد نداء ⁨Symfony⁩ المتدفِّق. يُرجِع متغيِّر ⁨Symfony⁩ المتدفِّق StreamedResponse، ويفرغ رد ندائه المخرجات. لا تكتب إلى متن الاستجابة بنفسك بعد إعادته.

يحجب التوليد المتزامن داخل وحدة التحكم الطلب طوال مدة بناء ملف ⁨PDF⁩ بالكامل. يبقى المستند المؤلَّف من صفحة واحدة عادةً ضمن ميزانية الطلب المعتادة بهامش جيد. بالنسبة إلى المخرجات متعددة الصفحات أو الدُّفعية، انقل التوليد بعيدًا عن مؤشر ترابط الطلب باستخدام مهمة في الطابور — انظر توليد ملف ⁨PDF⁩ في مهمة بالطابور. تقلِّل المتغيِّرات المتدفِّقة ذروة استخدام الذاكرة للمستندات الكبيرة على حساب عدم معرفة Content-Length. اخترها عندما تكون الذاكرة هي القيد ولا يكون شريط التقدُّم مطلوبًا.

  • تطبِّق مصانع PdfResponse مجموعة ثابتة من ترويسات تقوية الاستجابة وتنقِّح اسم ملف التنزيل في كل تكامل. لا تضِف هذه الترويسات بنفسك.
  • لا تُدرِج أبدًا مدخلات المستخدم غير المُتحقَّق منها مباشرةً في اسم ملف تمرِّره إلى المصنع. مرِّر قيمة تتحكم فيها، ودَع المصنع ينقِّحها كطبقة ثانية.
  • في كتلة ⁨catch⁩، سجِّل فئة الاستثناء ومُعرِّف الارتباط، لا رسالة الاستثناء ولا أثر التتبع. أثر التتبع الخام في مَصرِف السجلات يُعد تسريبًا للمعلومات.
  • لا تكتب أبدًا كتلة catch فارغة. كل مثال هنا يسجِّل ويُرجِع استجابة خطأ محدَّدة.

توثِّق صفحة الأمان والعمليات الخاصة بكل تكامل نموذج التهديد لذلك التكامل: مجموعة الترويسات، وقواعد تنقيح اسم الملف، ومدة ربط المستند.

لا يقدِّم هذا الدليل أي ادعاء معياري بشأن المعايير. كل استدعاء لواجهة برمجة التطبيقات معروض هنا هو السطح العام المُتحقَّق منه للتكامل المُسمَّى، وقد تم التحقق منه مقابل صفحات البدء السريع والاستخدام الإنتاجي لكل حزمة. توثِّق صفحات الاستخدام الإنتاجي الأصلية المرتبطة ضمن انظر أيضًا دلالات الترويسة وسلوك ربط الحاوية اللذَين تعتمد عليهما التكاملات، إلى جانب استشهادات ⁨PSR⁩ الخاصة بها. تعيد صفحة كتاب الطبخ هذه ذكر الاستخدام وتؤجِّل الاستشهادات المعيارية إلى تلك الصفحات.