Documentation Index
Fetch the complete documentation index at: https://docs.mejik.web.id/llms.txt
Use this file to discover all available pages before exploring further.
Final Code: app/Console/Commands/GenerateInvoiceFromTemplate.php
This is the complete and final code for your Artisan command. It includes logic to handle images from both URLs and local file paths.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use PhpOffice\PhpWord\TemplateProcessor;
class GenerateInvoiceFromTemplate extends Command
{
/**
* The name and signature of the console command.
* @var string
*/
protected $signature = 'invoice:generate
{--data=invoice_data.json : The path to the JSON data file}
{--template=storage/app/templates/invoice_template.docx : The path to the DOCX template file}
{--output=storage/app/invoices/invoice_output.docx : The path for the generated DOCX file}';
/**
* The console command description.
* @var string
*/
protected $description = 'Generate a DOCX invoice from a template and a JSON data file';
/**
* Execute the console command.
*/
public function handle()
{
// 1. Get file paths from options
$dataPath = $this->option('data');
$templatePath = $this->option('template');
$outputPath = $this->option('output');
// 2. Validate inputs
if (!File::exists($dataPath)) {
$this->error("Data file not found at: {$dataPath}");
return 1;
}
if (!File::exists($templatePath)) {
$this->error("Template file not found at: {$templatePath}");
return 1;
}
// 3. Load and parse JSON data
$data = json_decode(File::get($dataPath), true);
if (json_last_error() !== JSON_ERROR_NONE) {
$this->error("Invalid JSON in data file. Error: " . json_last_error_msg());
return 1;
}
try {
// 4. Load the DOCX template
$templateProcessor = new TemplateProcessor($templatePath);
// 5. Handle Image Replacement
if (isset($data['logo']) && !empty($data['logo'])) {
$localImagePath = $this->getLocalImagePath($data['logo'], $data['logo_is_url'] ?? false);
if ($localImagePath) {
$templateProcessor->setImageValue('logo', [
'path' => $localImagePath,
'width' => 150,
'ratio' => true
]);
// Clean up temporary image if it was downloaded
if ($data['logo_is_url'] ?? false) {
File::delete($localImagePath);
}
} else {
$this->warn("Could not process image for 'logo'. Removing placeholder.");
$templateProcessor->setValue('logo', ''); // Remove placeholder if image fails
}
}
// 6. Handle Conditional Block (Bill To)
if (isset($data['bill_present']) && $data['bill_present']) {
$templateProcessor->cloneBlock('if_bill', 1, true, false, [
['bill_name' => $data['bill_to']['name'] ?? ''],
['bill_addr1' => $data['bill_to']['addr1'] ?? ''],
['bill_addr2' => $data['bill_to']['addr2'] ?? ''],
['bill_phone' => $data['bill_to']['phone'] ?? ''],
['bill_email' => $data['bill_to']['email'] ?? ''],
]);
} else {
$templateProcessor->deleteBlock('if_bill');
}
// 7. Handle Simple Value Replacements
$templateProcessor->setValues([
'company_name' => $data['company_name'] ?? '',
'company_address' => $data['company_address'] ?? '',
'company_phone' => $data['company_phone'] ?? '',
'company_email' => $data['company_email'] ?? '',
'invoice_number' => $data['invoice_number'] ?? '',
'invoice_date' => $data['invoice_date'] ?? '',
'invoice_payment' => $data['invoice_payment'] ?? '',
'invoice_subtotal' => number_format($data['invoice_subtotal'] ?? 0, 2),
'company_tax_name' => $data['company_tax_name'] ?? 'Tax',
'invoice_tax' => number_format($data['invoice_tax'] ?? 0, 2),
'invoice_fees' => number_format($data['invoice_fees'] ?? 0, 2),
'invoice_total' => number_format($data['invoice_total'] ?? 0, 2),
'department' => $data['department'] ?? '',
'cc_list' => isset($data['cc_list']) ? implode(', ', $data['cc_list']) : ''
]);
// 8. Handle Table (Loop)
$items = $data['item_list'] ?? [];
if (!empty($items)) {
$templateProcessor->cloneRow('item.no', count($items));
foreach ($items as $index => $item) {
$rowNumber = $index + 1;
$templateProcessor->setValue('item.no#' . $rowNumber, $rowNumber);
$templateProcessor->setValue('item.name#' . $rowNumber, $item['name'] ?? '');
$templateProcessor->setValue('item.quantity#' . $rowNumber, $item['quantity'] ?? 0);
$templateProcessor->setValue('item.total#' . $rowNumber, number_format($item['total'] ?? 0, 2));
$templateProcessor->setValue('item.amount#' . $rowNumber, number_format($item['amount'] ?? 0, 2));
}
} else {
// If there are no items, you might want to remove the template row
$templateProcessor->cloneRow('item.no', 0);
}
// 9. Save the final document
File::ensureDirectoryExists(dirname($outputPath));
$templateProcessor->saveAs($outputPath);
$this->info("Invoice generated successfully at: {$outputPath}");
return 0;
} catch (\Exception $e) {
$this->error("An error occurred: " . $e->getMessage());
return 1;
}
}
/**
* Get the local file path for an image, downloading it if it's a URL.
*
* @param string $path
* @param bool $isUrl
* @return string|null
*/
private function getLocalImagePath(string $path, bool $isUrl): ?string
{
if ($isUrl) {
$contents = @file_get_contents($path);
if ($contents === false) {
$this->warn("Could not download image from URL: {$path}");
return null;
}
$tempPath = storage_path('app/temp/' . Str::random(16));
File::ensureDirectoryExists(dirname($tempPath));
File::put($tempPath, $contents);
return $tempPath;
}
if (File::exists($path)) {
return $path;
}
$this->warn("Local image file not found at: {$path}");
return null;
}
}
English Documentation
DOCX Invoice Generator Guide
This guide explains how to use the Laravel Artisan command to generate .docx invoices from a template and a JSON data file.
1. Setup and Installation
- Prerequisites:
- A working Laravel project.
- Composer installed.
- The
php-zip PHP extension must be enabled on your server.
- Install PHPWord:
Navigate to your project’s root and run:
composer require phpoffice/phpword
- File Structure:
- Command:
app/Console/Commands/GenerateInvoiceFromTemplate.php (place the code above here).
- Template:
storage/app/templates/invoice_template.docx
- Data:
invoice_data.json (in the project root, or specify a path).
- Output:
storage/app/invoices/ (will be created automatically).
2. Command Usage
The command signature defines how you run the command and what options are available.
Command Signature:
php artisan invoice:generate {--data=} {--template=} {--output=}
Options:
--data: Path to the JSON data file. Default: invoice_data.json.
--template: Path to the .docx template file. Default: storage/app/templates/invoice_template.docx.
--output: Path where the final .docx will be saved. Default: storage/app/invoices/invoice_output.docx.
Examples:
php artisan invoice:generate
php artisan invoice:generate --data="data/client-A.json" --output="invoices/INV-001.docx"
3. Template Tag Reference
Use these tags inside your .docx template file. Important: All formatting (bold, color, size) applied to a tag in the template will be inherited by the replacement data.
3.1. Simple Variables
Replaces a tag with a string value.
- Syntax:
${variable_name}
- Example Template:
Invoice Number: ${invoice_number}
- Example JSON:
{
"invoice_number": "INV-2023-00123"
}
3.2. Image Insertion
Replaces a tag with an image.
- Syntax:
${image_placeholder}
- Example Template: Place a
${logo} tag at the top of your document.
- Usage (from URL): The system downloads the image and places it.
{
"logo": "https://upload.wikimedia.org/wikipedia/commons/a/ab/Laravel-logo.png",
"logo_is_url": true
}
- Usage (from Local File): The system uses an image from your server’s filesystem. Use an absolute path.
{
"logo": "/var/www/my-project/storage/app/logos/company_logo.png",
"logo_is_url": false
}
3.3. Conditional Blocks
Shows or hides a block of content based on a boolean value.
${block_name}
Content to show/hide. Can contain other variables like `${some_var}`.
${/block_name}
${if_bill}
Bill to:
${bill_name}
${bill_addr1}
${/if_bill}
- Example JSON: If
bill_present is true, the block is shown and its internal tags are processed. If false or missing, the entire block is removed.
{
"bill_present": true,
"bill_to": {
"name": "Acme Corp",
"addr1": "123 Main St"
}
}
3.4. Table Row Looping
Dynamically creates table rows from an array of objects.
- Syntax: Create a table in your
.docx file with a single row meant for templating. Place tags like ${item.name} inside its cells.
- Example Template Table Row:
| No | Item | Quantity | Amount |
|---|
${item.no} | ${item.name} | ${item.quantity} | ${item.amount} |
- How it Works: The code finds the row containing
${item.no} (or any variable you choose as the anchor) and duplicates it for every object in the item_list array.
- Example JSON:
{
"item_list": [
{ "name": "Web Development", "quantity": 1, "amount": 4500.00 },
{ "name": "Monthly Support", "quantity": 5, "amount": 750.00 }
]
}
3.5. Simple Lists (from Array)
Converts a simple array of strings into a single comma-separated string.
- Syntax:
${variable_name}
- Example Template:
CC: ${cc_list}
- How it Works: The code takes the array and joins its elements with ”, ”.
- Example JSON:
{
"cc_list": ["manager@acme.corp", "project.lead@laravelsolutions.com"]
}
Dokumentasi Bahasa Indonesia
Panduan Generator Faktur DOCX
Panduan ini menjelaskan cara menggunakan perintah Artisan Laravel untuk menghasilkan faktur .docx dari sebuah templat dan file data JSON.
1. Pengaturan dan Instalasi
- Prasyarat:
- Proyek Laravel yang sudah berjalan.
- Composer sudah terinstal.
- Ekstensi PHP
php-zip harus diaktifkan di server Anda.
- Instal PHPWord:
Masuk ke direktori root proyek Anda dan jalankan:
composer require phpoffice/phpword
- Struktur File:
- Perintah (Command):
app/Console/Commands/GenerateInvoiceFromTemplate.php (letakkan kode di atas di sini).
- Templat:
storage/app/templates/invoice_template.docx
- Data:
invoice_data.json (di root proyek, atau tentukan path lain).
- Output:
storage/app/invoices/ (folder akan dibuat secara otomatis).
2. Penggunaan Perintah
Struktur perintah (command signature) mendefinisikan cara Anda menjalankan perintah dan opsi apa saja yang tersedia.
Struktur Perintah:
php artisan invoice:generate {--data=} {--template=} {--output=}
Opsi:
--data: Path ke file data JSON. Default: invoice_data.json.
--template: Path ke file templat .docx. Default: storage/app/templates/invoice_template.docx.
--output: Path tempat file .docx akhir akan disimpan. Default: storage/app/invoices/invoice_output.docx.
Contoh:
- Jalankan dengan nilai default:
php artisan invoice:generate
php artisan invoice:generate --data="data/klien-A.json" --output="faktur/INV-001.docx"
3. Referensi Tag Templat
Gunakan tag-tag ini di dalam file templat .docx Anda. Penting: Semua format (tebal, warna, ukuran) yang diterapkan pada sebuah tag di templat akan diwarisi oleh data penggantinya.
3.1. Variabel Sederhana
Mengganti sebuah tag dengan nilai string.
- Sintaks:
${nama_variabel}
- Contoh Templat:
Nomor Faktur: ${invoice_number}
- Contoh JSON:
{
"invoice_number": "INV-2023-00123"
}
3.2. Penyisipan Gambar
Mengganti sebuah tag dengan gambar.
- Sintaks:
${penanda_gambar}
- Contoh Templat: Letakkan tag
${logo} di bagian atas dokumen Anda.
- Penggunaan (dari URL): Sistem akan mengunduh gambar dan menempatkannya.
{
"logo": "https://upload.wikimedia.org/wikipedia/commons/a/ab/Laravel-logo.png",
"logo_is_url": true
}
- Penggunaan (dari File Lokal): Sistem akan menggunakan gambar dari filesystem server Anda. Gunakan path absolut.
{
"logo": "/var/www/proyek-saya/storage/app/logos/logo_perusahaan.png",
"logo_is_url": false
}
3.3. Blok Kondisional
Menampilkan atau menyembunyikan sebuah blok konten berdasarkan nilai boolean.
${nama_blok}
Konten yang akan ditampilkan/disembunyikan. Bisa berisi variabel lain seperti `${var_lain}`.
${/nama_blok}
${if_bill}
Tagihan untuk:
${bill_name}
${bill_addr1}
${/if_bill}
- Contoh JSON: Jika
bill_present bernilai true, blok akan ditampilkan dan tag di dalamnya akan diproses. Jika false atau tidak ada, seluruh blok akan dihapus.
{
"bill_present": true,
"bill_to": {
"name": "PT. Acme",
"addr1": "Jl. Utama 123"
}
}
3.4. Perulangan Baris Tabel
Membuat baris tabel secara dinamis dari sebuah array objek.
- Sintaks: Buat sebuah tabel di file
.docx Anda dengan satu baris tunggal yang ditujukan sebagai templat. Letakkan tag seperti ${item.name} di dalam sel-selnya.
- Contoh Baris Templat Tabel:
| No | Barang | Jumlah | Total |
|---|
${item.no} | ${item.name} | ${item.quantity} | ${item.amount} |
- Cara Kerja: Kode akan menemukan baris yang berisi
${item.no} (atau variabel apa pun yang Anda pilih sebagai jangkar) dan menduplikasinya untuk setiap objek di dalam array item_list.
- Contoh JSON:
{
"item_list": [
{ "name": "Jasa Web Development", "quantity": 1, "amount": 4500.00 },
{ "name": "Dukungan Bulanan", "quantity": 5, "amount": 750.00 }
]
}
3.5. Daftar Sederhana (dari Array)
Mengubah sebuah array string sederhana menjadi satu string yang dipisahkan koma.
- Sintaks:
${nama_variabel}
- Contoh Templat:
Tembusan (CC): ${cc_list}
- Cara Kerja: Kode akan mengambil array dan menggabungkan elemen-elemennya dengan ”, ”.
- Contoh JSON:
{
"cc_list": ["manager@acme.corp", "project.lead@laravelsolutions.com"]
}