Artikel sebelumnya >> temukan di sini
Anda akan segera mendapatkan konfigurasi duplikat, layanan yang berantakan, dan logika kondisional yang sulit dipertahankan. Artikel ini melanjutkan tepat dari bagian terakhir yang kami tinggalkan. Kami akan membangun Pembuat Kustom — kelas khusus yang merangkum semua konfigurasi dan logika untuk jenis PDF tertentu. Pada akhirnya, pengontrol Anda akan direduksi menjadi satu panggilan ekspresif, dan setiap jenis PDF akan berada di kelasnya sendiri yang bersih dan dapat diuji.
Apa tujuan dari Pembuat Kustom?
Untuk membuat hidup lebih mudah, saat Anda menghasilkan banyak PDF. Jika Anda membuat beberapa PDF di seluruh aplikasi Anda, konfigurasi terkadang tidak dapat dibagikan di antara semuanya—kebanyakan karena perbedaan gaya.
Di sinilah pola Builder berperan. Pola Builder adalah pola desain yang membantu Anda membuat objek kompleks selangkah demi selangkah, memisahkan cara objek dibuat dari representasi akhirnya. Dalam konteks ini, ini memungkinkan Anda merangkum semua konfigurasi dan logika untuk jenis PDF tertentu ke dalam kelas pembuat khusus.
Dengan membuat Builder kustom, Anda menghindari mengacaukan layanan Anda dengan logika kondisional atau kode duplikat. Sebaliknya, setiap pembuat kustom menangani konfigurasinya sendiri dengan rapi dan efisien, sehingga membuat basis kode Anda lebih mudah dipelihara dan diperluas.
Langkah 1: Perbarui dependensi
TL;DR lihat ini melakukan
Pertama, Anda perlu memperbarui dependensi Anda untuk mendapatkan GotenbergBundle versi terbaru. Untuk akhirnya mendapatkan v1.2 yang bukan eksperimental lagi.
Ya! 🍾
composer require sensiolabs/gotenberg-bundle:1.2.*
Langkah 2: Buat Kelas pembuat khusus
TL;DR lihat ini melakukan
Buat kelas pembuat khusus yang diperluas Pembangun Abstrak dan mengimplementasikan Antarmuka Aset Pembangun.
<?php
namespace App\Pdf;
use Sensiolabs\GotenbergBundle\Builder\AbstractBuilder;
use Sensiolabs\GotenbergBundle\Builder\BuilderAssetInterface;
final class InvoicePdfBuilder extends AbstractBuilder implements BuilderAssetInterface
{
protected function getEndpoint(): string
{
// TODO: Implement getEndpoint() method.
}
public function addAsset(string $path): static
{
// TODO: Implement addAsset() method.
}
}
Semua pembuat asli memperluas AbstrakBuilder yang mendefinisikan menghasilkan metode. Ini juga menyimpan semua konfigurasi yang Anda perlukan seperti batas, lebar…dan menyiapkan payload sebelum mengirimkannya ke Gotenberg API.
Karena Anda menambahkan aset ke dalam template Twig, Anda perlu mengimplementasikan BuilderAssetInterface.
Tambahkan atribut #[WithBuilderConfiguration(‘pdf’, ‘invoice’)] di puncak kelas.
<?php
namespace App\Pdf;
use Sensiolabs\GotenbergBundle\Builder\AbstractBuilder;
use Sensiolabs\GotenbergBundle\Builder\Attributes\WithBuilderConfiguration;
use Sensiolabs\GotenbergBundle\Builder\BuilderAssetInterface;
#[WithBuilderConfiguration('pdf', 'invoice')]
final class InvoicePdfBuilder extends AbstractBuilder implements BuilderAssetInterface
{
// rest of the code
}
Atribut ini akan membantu Anda mendapatkan konfigurasi semantik untuk pembuat khusus ini. Argumen pertama adalah memasukkan jenis pembuat baru ini ke bagian ‘pdf’ atau ‘tangkapan layar’. Dan yang kedua adalah penamaan yang Anda inginkan. Mari terapkan metodenya sekarang.
Tentang metode getEndpoint, mari gunakan konstanta yang ada HtmlPdfBuilder::ENDPOINT karena titik akhir API Gotenberg sama dengan yang digunakan HtmlPdfBuilder.
Jika Anda ingin menghasilkan PDF, ciri-ciri yang tersedia adalah:
-
AssetTraitTermasuk metode untuk menambah aset. -
ContentTraitTermasuk metode untuk menambahkan bagian konten berbeda ke PDF Anda. -
CookieTraitTermasuk metode untuk menyetel, menambah, dan meneruskan cookie ke Gotenberg API. -
CustomHttpHeadersTraitTermasuk metode untuk menambahkan header ke Gotenberg API. -
EmulatedMediaTypeTraitTermasuk metode untuk meniru layar atau mencetak. -
FailOnTraitTermasuk metode untuk menyesuaikan perilaku pada kode status yang tidak valid. -
PdfPagePropertiesTraitTermasuk metode untuk menyesuaikan rendering PDF. -
PerformanceModeTraitMetode untuk tidak menunggu jaringan Chromium menganggur. -
WaitBeforeRenderingTraitTermasuk metode untuk menambahkan penundaan sebelum mengonversinya ke PDF. -
DownloadFromTraitTermasuk metode untuk menambahkan sumber daya eksternal. -
MetadataTraitTermasuk metode untuk menambahkan metadata. -
PdfFormatTraitTermasuk metode tentang format PDF. -
SplitTraitTermasuk metode untuk membagi PDF. -
WebhookTraitTermasuk metode untuk menggunakan webhook.
Dan semuanya digabungkan menjadi ChromiumPdfTrait.Jika Anda ingin membuat pembuat khusus tentang kantor, tangkapan layar… Anda dapat mengetahui semua ciri yang tersedia di Sumber GotenbergBundle di GitHub.Jadi mari kita tambahkan AssetTrait yang akan mengimplementasikan addAsset metode untuk kita. Di balik terpalnya, metode ini menyimpan bagi kita aset yang berasal dari templat Twig, atau yang ditambahkan dengan cepat.
ContentTrait adalah untuk mendapatkan kemungkinan untuk menggunakan metode sebagai header Dan content kita gunakan ke dalam controller atau footer yang dikonfigurasi ke dalam file konfigurasi sensiolabs_gotenberg.yaml.
Dan PdfPagePropertiesTrait adalah untuk semua metode tentang margin, lanskap, lebar kertas… dan penyesuaian render PDF.
<?php
namespace App\Pdf;
use Sensiolabs\GotenbergBundle\Builder\AbstractBuilder;
use Sensiolabs\GotenbergBundle\Builder\Attributes\WithBuilderConfiguration;
use Sensiolabs\GotenbergBundle\Builder\Behaviors\Chromium\AssetTrait;
use Sensiolabs\GotenbergBundle\Builder\Behaviors\Chromium\ContentTrait;
use Sensiolabs\GotenbergBundle\Builder\Behaviors\Chromium\PdfPagePropertiesTrait;
use Sensiolabs\GotenbergBundle\Builder\BuilderAssetInterface;
use Sensiolabs\GotenbergBundle\Builder\Pdf\HtmlPdfBuilder;
#[WithBuilderConfiguration('pdf', 'invoice')]
final class InvoicePdfBuilder extends AbstractBuilder implements BuilderAssetInterface
{
use AssetTrait;
use ContentTrait;
use PdfPagePropertiesTrait;
protected function getEndpoint(): string
{
return HtmlPdfBuilder::ENDPOINT;
}
}
Langkah 3: Perbarui file konfigurasi
TL: DR lihat ini melakukan
Anda hanya perlu memperbarui nama pembuatnya. Namanya adalah nama yang Anda konfigurasikan di WithBuilderConfiguration atribut.
sensiolabs_gotenberg:
http_client: 'gotenberg.client'
default_options:
pdf:
- html:
+ invoice:
footer:
template: 'footer.html.twig'
paper_width: '21cm'
paper_height: '29.7cm'
margin_top: '6cm'
margin_bottom: '2cm'
landscape: true
Langkah 4: Perbarui pengontrol
TL: DR lihat ini melakukan
Seperti yang kami lakukan untuk konfigurasi, 2 baris harus diperbarui untuk menggunakannya.
#[Route('/pdf', 'pdf')]
public function pdf(GotenbergPdfInterface $gotenbergPdf): Response
{
$invoiceData = $this->invoiceData();
- return $gotenbergPdf
- ->html()
+ return $gotenbergPdf->get(InvoicePdfBuilder::class)
->header('header.html.twig', [
'invoice' => $invoiceData['invoice'],
'client' => $invoiceData['client'],
])
->content('content.html.twig', [
'purchases' => $invoiceData['purchases'],
'invoice' => $invoiceData['invoice'],
])
->generate()
->stream()
;
}
Langkah 5: Mari kita buat ini berhasil 💪
TL: DR lihat ini melakukan
Jika Anda memiliki GotenbergBundle lebih rendah dari v1.2, Anda perlu menambahkan konfigurator.
# services.yaml
services:
App\Pdf\InvoicePdfBuilder:
configurator: '@sensiolabs_gotenberg.builder_configurator'
Atau sejak versi v1.0.1 Anda tidak perlu melakukannya sendiri. Cukup daftarkan saja di build metode Anda Kernel kelas (lihat dokumentasi kernel Symfony jika diperlukan).
Pertama, kita mengambil 'sensiolabs_gotenberg' ekstensi, maka kita hanya perlu menelepon registerBuilder metode dengan pembuat kustom FQCN sebagai argumen. Dan voila!
class Kernel extends BaseKernel
{
use MicroKernelTrait;
protected function build(ContainerBuilder $container): void
{
/** @var SensiolabsGotenbergExtension $extension */
$extension = $container->getExtension('sensiolabs_gotenberg');
$extension->registerBuilder(InvoicePdfBuilder::class);
}
}
Dan semuanya bekerja seperti pesona.
Langkah 6: Tunggu… kita lupa intinya! 🤦
TL: DR lihat ini melakukan
Wah, kami terlalu fokus dalam membuat pembuat baru sehingga kami lupa tujuan sebenarnya dari pembuat khusus: merangkum logikanya.
Saat ini, pengontrol masih mengetahui terlalu banyak — ia mengambil data faktur, meneruskannya ke header, meneruskannya ke konten… Itulah jenis tanggung jawab yang seharusnya ada di dalam InvoicePdfBuilder.
Mari kita pindahkan semua itu.
#[WithBuilderConfiguration('pdf', 'invoice')]
final class InvoicePdfBuilder extends AbstractBuilder implements BuilderAssetInterface
{
use AssetTrait;
use ContentTrait;
use PdfPagePropertiesTrait;
public function invoice(): self
{
$invoiceData = $this->invoiceData();
$this->header('header.html.twig', [
'invoice' => $invoiceData['invoice'],
'client' => $invoiceData['client'],
]);
$this->content('content.html.twig', [
'purchases' => $invoiceData['purchases'],
'invoice' => $invoiceData['invoice'],
]);
return $this;
}
protected function getEndpoint(): string
{
return HtmlPdfBuilder::ENDPOINT;
}
private function invoiceData(): array
{
$factory = Factory::create();
$allPurchases = [];
for ($i = 0; $i < 20; $i++) {
$allPurchases[] = [
'orderId' => $factory->unixTime(),
'period' => $factory->dateTimeBetween('- 1 week')->format('Y-m-d') . ' - ' . $factory->dateTime('now')->format('Y-m-d'),
'description' => $factory->sentence(),
'price' => $factory->randomFloat(2, 1),
'quantity' => $factory->randomDigitNotZero(),
'total' => $factory->randomFloat(2, 1),
];
}
return [
'invoice' => [
'id' => $factory->unixTime(),
'date' => $factory->dateTime()->format('Y-m-d'),
'due_date' => $factory->dateTime('+1 week')->format('Y-m-d'),
'sub_total' => $factory->randomFloat(2, 1),
'total' => $factory->randomFloat(2, 1),
],
'client' => [
'phone_number' => $factory->e164PhoneNumber(),
'name' => $factory->company(),
'address' => $factory->address(),
'city' => $factory->city(),
],
'purchases' => $allPurchases
];
}
}
Sekarang pengontrolnya menjadi sangat sederhana seperti yang diinginkan dalam praktik terbaik Symfony.
#[Route('/pdf', 'pdf')]
public function pdf(GotenbergPdfInterface $gotenbergPdf): Response
{
- $invoiceData = $this->invoiceData();
-
- return $gotenbergPdf->get(InvoicePdfBuilder::class)
- ->header('header.html.twig', [
- 'invoice' => $invoiceData['invoice'],
- 'client' => $invoiceData['client'],
- ])
- ->content('content.html.twig', [
- 'purchases' => $invoiceData['purchases'],
- 'invoice' => $invoiceData['invoice'],
- ])
- ->generate()
- ->stream()
- ;
+ return $gotenbergPdf->get(InvoicePdfBuilder::class)
+ ->invoice()
+ ->generate()
+ ->stream()
+ ;
}
Pengontrol tidak lagi mengetahui apa pun tentang templat atau struktur data. Itu hanya mengatakan “berikan saya PDF fakturnya” dan pembangun menangani sisanya. Itulah intinya. 🎯
Kesimpulan
Sekarang saat kamu berlari php bin/console debug:config sensiolabs_gotenbergAnda akan melihat konfigurasi pembuat kustom Anda terintegrasi dengan rapi bersama konfigurasi asli — pertanda baik bahwa semuanya terhubung dengan benar.
Lebih penting lagi, lihat apa yang telah kami capai:
-
Pengontrolnya bersih. Ia tidak lagi mengetahui apa pun tentang templat, struktur data, atau logika rendering. Itu hanya meminta PDF faktur dan mendapatkannya.
-
Logikanya dikemas.
InvoicePdfBuildermemiliki segala sesuatu yang berhubungan dengan PDF faktur: titik akhir, ciri-ciri yang dibutuhkan, templat, data. Jika tata letak faktur berubah besok, Anda tahu persis ke mana harus pergi. -
Konfigurasinya bersifat semantik. Berkat
#[WithBuilderConfiguration('pdf', 'invoice')]milikmusensiolabs_gotenberg.yamldibaca secara alami, dan Anda dapat mengonfigurasi margin, footer, atau ukuran kertas per pembuat tanpa solusi apa pun. -
Itu berskala. Membutuhkan sebuah
ReportPdfBuilder? Ikuti langkah yang sama. Setiap jenis PDF memiliki pembuatnya sendiri, blok konfigurasinya sendiri, dan tidak ada gangguan terhadap yang lain.
Ini adalah pola Builder yang terbaik dalam konteks Symfony: dapat diprediksi, dipelihara, dan mudah diperluas. Cobalah di proyek Anda berikutnya dan beri tahu kami bagaimana kelanjutannya 🚀
PakarPBN
A Private Blog Network (PBN) is a collection of websites that are controlled by a single individual or organization and used primarily to build backlinks to a “money site” in order to influence its ranking in search engines such as Google. The core idea behind a PBN is based on the importance of backlinks in Google’s ranking algorithm. Since Google views backlinks as signals of authority and trust, some website owners attempt to artificially create these signals through a controlled network of sites.
In a typical PBN setup, the owner acquires expired or aged domains that already have existing authority, backlinks, and history. These domains are rebuilt with new content and hosted separately, often using different IP addresses, hosting providers, themes, and ownership details to make them appear unrelated. Within the content published on these sites, links are strategically placed that point to the main website the owner wants to rank higher. By doing this, the owner attempts to pass link equity (also known as “link juice”) from the PBN sites to the target website.
The purpose of a PBN is to give the impression that the target website is naturally earning links from multiple independent sources. If done effectively, this can temporarily improve keyword rankings, increase organic visibility, and drive more traffic from search results.
