Panduan lengkap untuk deploy aplikasi Laravel ke hosting gratis InfinityFree dengan metode yang telah terbukti stabil dan aman.
Pastikan kamu sudah memiliki:
- β Laravel versi 8, 9, 10 atau max laravel 11
- β Aplikasi berjalan normal di lokal
- β Akun InfinityFree aktif dengan domain/subdomain
- β FTP Client (FileZilla) atau akses File Manager
- β Composer terinstall di lokal
Struktur ini memastikan keamanan optimal di shared hosting dengan memisahkan file publik dan privat:
htdocs/
βββ index.php β Entry point (dari public/)
βββ .htaccess β URL rewriting rules
βββ assets/ β Isi dari folder public/ Laravel (css, js, images)
β βββ css/
β βββ js/
β βββ images/
βββ laravel/ β Core Laravel application
βββ app/
βββ bootstrap/
βββ config/
βββ database/
βββ resources/
βββ routes/
βββ storage/
βββ vendor/ β Dependencies (file terbesar)
βββ artisan
βββ .env
βββ composer.json
InfinityFree memiliki keterbatasan environment yang mempengaruhi deployment Laravel:
- No Composer CLI: Tidak bisa jalankan
composer installdi server - PHP Extensions: Terbatas pada extension bawaan, tidak bisa install custom
- File Structure: Path public harus disesuaikan dengan struktur hosting
- Bootstrap Method: Laravel 11+ menggunakan method baru yang tidak kompatibel
- CLI Commands: Artisan commands terbatas atau tidak berfungsi
| Laravel Version | Bootstrap Method | InfinityFree Status | Action Required |
|---|---|---|---|
| Laravel 10.x | Classic (new Application) |
β Compatible | Ready to deploy |
| Laravel 11.x | Modern (Application::configure) |
Modify bootstrap file | |
| Laravel 12.x | Modern only | β Problematic | Complex workaround |
Laravel 11 menggunakan bootstrap method baru di bootstrap/app.php:
<?php
use Illuminate\Foundation\Application;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
//
})
->withExceptions(function (Exceptions $exceptions) {
//
})
->create();Ganti dengan bootstrap method klasik (Laravel 10 style):
<?php
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
return $app;Pastikan file-file berikut ada (copy dari Laravel 10 project):
app/Http/Kernel.phpapp/Console/Kernel.phpapp/Exceptions/Handler.php
Note
Laravel 11 Compatibility Reminder
Saat menggunakan gaya klasik di bootstrap/app.php, Laravel akan memanggil:
app/Http/Kernel.phpapp/Console/Kernel.phpapp/Exceptions/Handler.php
File ini sudah tersedia default di Laravel 11, jadi tidak perlu copy dari Laravel 10.
Cukup pastikan file tersebut tidak terhapus.
- Breeze: v1.19.x (latest compatible)
- Tailwind: 3.2.x - 3.3.x
- Installation:
composer require laravel/breeze:^1.19
- Breeze: v2.x
- Tailwind: 3.4.x
- Installation:
composer require laravel/breeze:^2.0
- Breeze: v3.x
- Tailwind: 4.x (beta)
- Installation:
composer require laravel/breeze:^3.0
- Backup original
bootstrap/app.php - Replace dengan classic bootstrap method
- Copy required Kernel files dari Laravel 10
- Test local setelah modifikasi
Tested With: Laravel 10.48, 11.23 | Breeze 1.19, 2.1 | Tailwind 3.3, 3.4
# Optimasi aplikasi untuk production
composer install --no-dev --optimize-autoloader
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan key:generate --show
β οΈ Penting: SimpanAPP_KEYyang dihasilkan untuk file.envhosting.
<?php
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
// Maintenance mode check
if (file_exists($maintenance = __DIR__.'/laravel/storage/framework/maintenance.php')) {
require $maintenance;
}
// Autoloader
require __DIR__.'/laravel/vendor/autoload.php';
// Bootstrap application
$app = require_once __DIR__.'/laravel/bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
$request = Request::capture()
)->send();
$kernel->terminate($request, $response);<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// Fix public path untuk InfinityFree
$this->app->bind('path.public', function() {
return base_path('../');
});
}
public function boot()
{
// Force HTTPS jika diperlukan
if (env('FORCE_HTTPS', false)) {
\URL::forceScheme('https');
}
}
}Karena folder public/ Laravel dipindah ke htdocs/assets/, kita perlu helper untuk path yang benar.
Buat file app/helpers.php:
<?php
if (!function_exists('public_asset')) {
function public_asset($path)
{
// Untuk akses file di htdocs/assets/
return url('/assets') . '/' . ltrim($path, '/');
}
}Daftarkan di composer.json:
{
"autoload": {
"files": [
"app/helpers.php"
],
"psr-4": {
"App\\": "app/"
}
}
}
β οΈ Batasan: InfinityFree memiliki limit upload 8MB per file. Foldervendor/harus diupload bertahap.
Step 1: Upload File Utama Dulu
- Isi
htdocs/: Uploadindex.phpdan.htaccess(yang sudah diedit) dari folderpublic/lokalmu kehtdocs/ - Upload assets: Pindahkan isi folder
public/(css, js, images) ke folderassets/dihtdocs/ - Buat folder
laravel/: Di dalamhtdocs/, buat folder baru bernamalaravel - Upload folder Laravel:
htdocs/laravel/
βββ app/ β Upload pertama
βββ bootstrap/ β Upload kedua
βββ config/ β Upload ketiga
βββ database/ β Upload keempat
βββ resources/ β Upload kelima
βββ routes/ β Upload keenam
βββ storage/ β Upload ketujuh
βββ .env β Upload kedelapan
βββ artisan β Upload kesembilan
βββ composer.json β Upload kesepuluh
βββ file lainnya... β Upload kesebelas
Step 2: Upload Vendor (Pilih Metode)
# Upload satu per satu folder vendor/
vendor/
βββ symfony/ β Upload pertama (terbesar)
βββ laravel/ β Upload kedua
βββ composer/ β Upload ketiga
βββ ... β Lanjutkan bertahap# Di lokal, split vendor menjadi bagian kecil
cd vendor/
tar -czf ../vendor-part1.tar.gz symfony/ laravel/
tar -czf ../vendor-part2.tar.gz composer/ psr/ monolog/Upload file .tar.gz lalu extract via File Manager cPanel.
Di lokal:
# Kompres folder vendor menjadi zip
cd your-laravel-project/
zip -r vendor.zip vendor/Upload ke hosting:
- Upload file
vendor.zipke folderhtdocs/ - Buat file
extract.phpdihtdocs/dengan kode berikut:
<?php
$zip = new ZipArchive;
$path = __DIR__ . '/laravel/vendor'; // direktori tujuan ekstrak
$namaZip = 'test.zip';
if (!is_dir($path)) {
mkdir($path, 0755, true);
}
if ($zip->open($namaZip) === TRUE) {
$zip->extractTo($path);
$zip->close();
echo "Vendor extracted to laravel/vendor successfully β
.<br>";
// β
Pastikan nama file yang dihapus sama dengan yang dibuka
if (unlink($namaZip)) {
echo "$namaZip deleted.";
} else {
echo "Failed to delete $namaZip.";
}
} else {
echo "Failed to open $namaZip.";
}
?>- Akses
http://yourdomain.page.gd/extract.phpvia browser - Tunggu proses extract selesai
- Hapus file
extract.phpsetelah selesai untuk keamanan
- File
vendor.zipmungkin besar (20-50MB), pastikan koneksi stabil - Proses extract bisa memakan waktu 2-5 menit
- Selalu hapus
extract.phpsetelah digunakan
Transfer Settings:
β Concurrent transfers: 1
β Maximum transfers: 1
β Timeout: 60 seconds
β Passive mode: enabled
β Transfer mode: Binary
Edit .env di htdocs/laravel/:
APP_NAME="Your App Name"
APP_ENV=production
APP_DEBUG=false
APP_URL=http://yourdomain.page.gd
APP_KEY=base64:your_generated_key_here
# Database credentials dari InfinityFree
DB_CONNECTION=mysql
DB_HOST=sqlXXX.page.gd
DB_PORT=3306
DB_DATABASE=epiz_xxxxxxxx_dbname
DB_USERNAME=epiz_xxxxxxxx
DB_PASSWORD=your_db_password
# Session & Cache
SESSION_DRIVER=file
CACHE_DRIVER=file
QUEUE_CONNECTION=database
# Disable untuk hosting gratis
BROADCAST_DRIVER=log
MAIL_MAILER=log
# Timezone
APP_TIMEZONE=Asia/Makassar
# Optional
FORCE_HTTPS=falseFile .htaccess di htdocs/:
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Front Controller
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
# Security Headers
<IfModule mod_headers.c>
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
</IfModule>
# Disable Directory Browsing
Options -Indexes
# Protect Sensitive Files
<FilesMatch "\.(env|log|htaccess)$">
Order Allow,Deny
Deny from all
</FilesMatch>- Buat database di cPanel InfinityFree
- Import file SQL via phpMyAdmin
- Jalankan migrasi jika menggunakan session database:
# Di lokal
php artisan session:table
php artisan cache:table
php artisan migrateBuat route test di routes/web.php:
Route::get('/test-db', function () {
try {
\DB::connection()->getPdo();
return "β
Database connection successful!";
} catch (\Exception $e) {
return "β Database failed: " . $e->getMessage();
}
});Akses: http://yourdomain.page.gd/test-db
| Masalah | Gejala | Solusi |
|---|---|---|
| Error 500 / Halaman Putih | Server Error, halaman kosong | β’ Aktifkan APP_DEBUG=true sementaraβ’ Periksa path di index.phpβ’ Cek permission storage/ (755)β’ Validasi syntax .env |
| Asset (CSS/JS) Tidak Load | Style tidak muncul, 404 error | β’ Gunakan helper public_asset()β’ Pastikan folder assets/ di htdocs/β’ Cek console browser untuk error |
| Session Error | Login logout terus, session hilang | β’ Pastikan tabel sessions adaβ’ Set SESSION_DRIVER=databaseβ’ Cek permission storage/framework/sessions/ |
| Database Connection Failed | Error koneksi database | β’ Verifikasi kredensial di .envβ’ Pastikan database sudah dibuat β’ Test dengan route /test-db |
| 403 Forbidden | Akses ditolak | β’ Periksa permission folder β’ Cek file .htaccessβ’ Pastikan index.php ada |
| Mixed Content Error | HTTPS/HTTP conflict | β’ Set FORCE_HTTPS=trueβ’ Update semua URL ke HTTPS |
| Upload Gagal/Timeout | Upload terputus, file rusak | β’ Upload bertahap untuk vendor/β’ Set FileZilla 1 concurrent transfer β’ Upload di jam sepi (malam) |
| Vendor Tidak Lengkap | Class not found error | β’ Cek semua package di vendor/β’ Gunakan metode kompresi β’ Verifikasi autoload.php |
Penyebab: InfinityFree tidak mengenali folder public/ Laravel karena struktur direktori diubah.
Gejala:
ErrorException: file_get_contents(public/css/app.css): failed to open stream
Solusi:
// Di AppServiceProvider.php method register()
$this->app->bind('path.public', function () {
return base_path('../');
});Checklist tambahan:
- β
File
composer.jsonada dihtdocs/laravel/ - β
Hapus semua cache di
bootstrap/cache/ - β
Package
barryvdh/laravel-dompdfterupload lengkap
Penyebab: File composer.json belum terupload ke hosting.
Solusi: Upload file composer.json ke folder htdocs/laravel/
Penyebab: Error resolve asset saat path public tidak cocok.
Solusi Alternatif:
// Ganti dari:
$pdf = Pdf::loadView('tabungan.pdf', compact(...));
// Menjadi:
$view = view('tabungan.pdf', compact(...))->render();
$pdf = Pdf::loadHtml($view);Penyebab: Helper asset() mengarah ke path Laravel asli.
Solusi: Gunakan helper kustom:
<!-- Ganti dari: -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<!-- Menjadi: -->
<link href="{{ public_asset('assets/css/app.css') }}" rel="stylesheet">Penyebab: File hasil build tidak dikenali struktur hosting.
Solusi:
- Pastikan file hasil
npm run buildterupload kehtdocs/build/ - Gunakan path manual:
<link rel="stylesheet" href="{{ asset('build/assets/app-Bu5eBVKL.css') }}">
<script src="{{ asset('build/assets/app-CLAht3ih.js') }}" defer></script>π‘ Tip: Nama file hash berubah setiap build. Cek nama file yang tepat di folder
build/assets/
Dokumentasi Lengkap: 3 Metode Penanganan Upload File di Laravel untuk Shared Hosting (khususnya InfinityFree)
Versi: 1.2 Tanggal: 23 Agustus 2025 Proyek: BlackFile
Dokumen ini adalah panduan teknis yang membandingkan tiga metode untuk menangani upload file pada aplikasi Laravel di lingkungan shared hosting dengan struktur direktori non-standar. Tujuannya adalah untuk memberikan pemahaman mendalam tentang setiap pendekatan, mulai dari yang paling sederhana hingga yang paling tangguh dan portabel.
Pada banyak platform shared hosting (contoh: InfinityFree), struktur direktori untuk keamanan memisahkan document root (folder yang bisa diakses web) dari file inti aplikasi.
Struktur Direktori Tipikal:
/htdocs/ <-- Document Root (Akses Web)
|-- uploads/ <-- Folder tujuan file upload kita
|-- index.php
|-- laravel_core/ <-- Direktori inti Laravel (Tidak bisa diakses web)
| |-- app/
| |-- public/ <-- Folder public asli Laravel (TIDAK BISA DIAKSES)
| |-- ...
Masalahnya adalah helper public_path() dari Laravel akan selalu menunjuk ke /htdocs/laravel_core/public/, bukan ke /htdocs/. Ini menyebabkan semua file yang diunggah tidak dapat diakses melalui browser, sehingga menghasilkan Error 404 Not Found.
Ini adalah solusi paling langsung yang menggunakan variabel superglobal PHP untuk mendapatkan path absolut ke htdocs.
-
Kelebihan:
- Sederhana & Lugas: Logikanya sangat mudah dipahami.
- Andal di Lingkungan Target:
$_SERVER['DOCUMENT_ROOT']hampir selalu diatur dengan benar oleh server web seperti Apache.
-
Kekurangan:
- Tidak Portabel: Kode ini akan rusak di lingkungan development lokal standar, karena
DOCUMENT_ROOTdi lokal biasanya menunjuk ke.../project/public. - Bukan "The Laravel Way": Menggunakan variabel superglobal secara langsung menghindari abstraksi yang disediakan oleh framework, membuat kode lebih sulit diuji (testing).
- Tidak Portabel: Kode ini akan rusak di lingkungan development lokal standar, karena
// Di dalam Controller
public function uploadFile(Request $request)
{
if ($request->hasFile('avatar')) {
$documentRoot = $_SERVER['DOCUMENT_ROOT'];
$imageName = time() . '.' . $request->avatar->extension();
$destinationPath = $documentRoot . '/uploads/avatars'; // Menargetkan /htdocs/uploads/avatars
$request->avatar->move($destinationPath, $imageName);
$user->avatar = '/uploads/avatars/' . $imageName; // Simpan path relatif URL
$user->save();
}
}Pendekatan ini memanfaatkan helper base_path() dari Laravel untuk menemukan direktori induknya, yang dalam kasus shared hosting ini adalah htdocs.
-
Kelebihan:
- Lebih Konsisten: Bergantung pada struktur internal Laravel, bukan pada konfigurasi server web.
- Tanpa Variabel Global: Menghindari penggunaan langsung
$_SERVER, yang dianggap praktik lebih bersih.
-
Kekurangan:
- Juga Tidak Portabel: Sama seperti Pendekatan #1, logika ini akan gagal total di lingkungan development lokal, karena
dirname(base_path())akan menunjuk ke direktori di atas folder proyek Anda.
- Juga Tidak Portabel: Sama seperti Pendekatan #1, logika ini akan gagal total di lingkungan development lokal, karena
// Di dalam Controller
public function uploadFile(Request $request)
{
if ($request->hasFile('avatar')) {
// base_path() -> /htdocs/laravel_core
// dirname(base_path()) -> /htdocs
$realPublicPath = dirname(base_path());
$imageName = time() . '.' . $request->avatar->extension();
$destinationPath = $realPublicPath . '/uploads/avatars';
$request->avatar->move($destinationPath, $imageName);
$user->avatar = '/uploads/avatars/' . $imageName;
$user->save();
}
}Ini adalah solusi paling tangguh dan profesional. Kode ini menggabungkan kekuatan public_path() (untuk lokal) dan dirname(base_path()) (untuk produksi) dengan membuatnya mampu mendeteksi di mana ia sedang berjalan.
-
Kelebihan:
- Sangat Portabel: Satu basis kode bekerja secara konsisten di lingkungan lokal dan produksi tanpa perlu diubah.
- Sesuai Praktik Terbaik Laravel: Menggunakan helper inti Laravel dan logika kondisional berdasarkan environment.
- Dapat Diandalkan & Mudah Dipelihara: Tidak ada lagi kejutan saat melakukan deployment.
-
Kekurangan:
- Memerlukan disiplin untuk memastikan variabel
APP_ENVdi file.envdiatur dengan benar (localuntuk lokal,productionuntuk server).
- Memerlukan disiplin untuk memastikan variabel
- Di file
.envlokal Anda:APP_ENV=local - Di file
.envserver produksi:APP_ENV=production
Pastikan variabel APP_ENV diatur dengan benar di setiap lingkungan agar logika deteksi di atas berjalan.
// File: app/Http/Controllers/PrototypeController.php
public function store(Request $request)
{
// ... (Validasi request) ...
// Deteksi lingkungan untuk menentukan path upload yang benar.
$realPublicPath = app()->environment('production')
? dirname(base_path()) // Untuk server shared hosting
: public_path(); // Untuk development lokal
// Handle upload (contoh: cover_image)
if ($request->hasFile('cover_image')) {
$image = $request->file('cover_image');
$imageName = time() . '.' . $image->getClientOriginalExtension();
// Gunakan path yang benar sesuai lingkungan
$image->move($realPublicPath . '/uploads/prototypes', $imageName);
// Simpan path RELATIF ke database (selalu sama)
$validatedData['cover_image_path'] = 'uploads/prototypes/' . $imageName;
}
// ...
Prototype::create($validatedData);
// ...
}
// Logika yang sama diterapkan untuk metode update() dan destroy() saat mengakses file.Dengan pendekatan ini, logika di sisi view tetap sangat bersih dan standar, karena path yang tersimpan di database selalu relatif terhadap document root yang benar.
@if($prototype->cover_image_path)
{{-- Cek apakah path adalah URL eksternal atau file lokal --}}
@if(Illuminate\Support\Str::startsWith($prototype->cover_image_path, 'http'))
<img src="{{ $prototype->cover_image_path }}" alt="Cover Image">
@else
{{-- Helper 'asset()' akan bekerja sempurna di kedua lingkungan --}}
<img src="{{ asset($prototype->cover_image_path) }}" alt="Cover Image">
@endif
@endif| Metode | Portabilitas (Lokal & Produksi) | Kepatuhan Laravel | Rekomendasi |
|---|---|---|---|
$_SERVER['DOCUMENT_ROOT'] |
Rendah (Hanya Produksi) | Rendah | Untuk perbaikan cepat & sementara. |
dirname(base_path()) |
Rendah (Hanya Produksi) | Sedang | Mirip dengan di atas, sedikit lebih bersih. |
| Deteksi Lingkungan | Tinggi (Universal) | Tinggi | Sangat Direkomendasikan untuk semua proyek. |
Untuk proyek BlackFile, Pendekatan #3 adalah standar yang harus diikuti untuk memastikan aplikasi dapat dikembangkan di lokal dan di-deploy ke shared hosting tanpa masalah.
Dengan mengikuti panduan ini, fungsionalitas upload file akan menjadi tangguh, aman, dan sepenuhnya portabel antara lingkungan development dan produksi.
Buatkan saya method `store` dan `update` untuk sebuah `PrototypeController` di Laravel 10.
**Konteks & Aturan:**
1. **Model:** `Prototype`
2. **Upload File:** Ada dua jenis file yang bisa di-upload: `cover_image` dan `icon`.
3. **Opsi Ganda:** Setiap file memiliki dua opsi input:
* Upload file (dari input `name="cover_image"` dan `name="icon"`).
* Link eksternal (dari input `name="cover_image_url"` dan `name="icon_url"`).
4. **Prioritas:** Jika kedua opsi (file dan URL) diisi, prioritaskan **upload file**.
5. **Path Portabel (Wajib):** Logika upload harus portabel antara environment `local` dan `production` (shared hosting).
* Di **produksi**, path tujuan adalah `dirname(base_path())`.
* Di **lokal**, path tujuan adalah `public_path()`.
* Gunakan `app()->environment('production')` untuk deteksi.
6. **Penyimpanan Database:** Path yang disimpan di database harus **relatif** (contoh: `uploads/prototypes/namafile.png`). Kolom di database bernama `cover_image_path` dan `icon_path`.
7. **Logika Update:** Saat `update`, jika file lokal baru di-upload atau diganti dengan URL, file lokal yang lama harus **dihapus** dari server.
**Tugas:**
Buatkan kode PHP lengkap untuk `public function store(Request $request)` dan `public function update(Request $request, Prototype $prototype)` yang menerapkan semua aturan di atas, termasuk validasi, pemindahan file, penghapusan file lama (di `update`), dan me-return redirect dengan pesan sukses.
Buat file extract.php di htdocs/ untuk ekstrak vendor yang sudah di-zip:
<?php
$zip = new ZipArchive;
$path = __DIR__ . '/laravel/vendor'; // direktori tujuan ekstrak
$namaZip = 'test.zip';
if (!is_dir($path)) {
mkdir($path, 0755, true);
}
if ($zip->open($namaZip) === TRUE) {
$zip->extractTo($path);
$zip->close();
echo "Vendor extracted to laravel/vendor successfully β
.<br>";
// β
Pastikan nama file yang dihapus sama dengan yang dibuka
if (unlink($namaZip)) {
echo "$namaZip deleted.";
} else {
echo "Failed to delete $namaZip.";
}
} else {
echo "Failed to open $namaZip.";
}
?>Cara pakai:
- Zip folder
vendor/di lokal jadivendor.zip - Upload
vendor.zipkehtdocs/ - Upload
extract.phpkehtdocs/ - Akses
http://yourdomain.page.gd/extract.php - Tunggu proses selesai
- Hapus
extract.phpuntuk keamanan
Daftarkan package PDF generator:
<?php
return [
'providers' => [
// Provider lainnya...
Barryvdh\DomPDF\ServiceProvider::class,
],
'aliases' => [
// Alias lainnya...
'Pdf' => Barryvdh\DomPDF\Facade\Pdf::class,
],
];<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register()
{
// Perbaiki path public untuk InfinityFree
$this->app->bind('path.public', function () {
return base_path('../');
});
}
/**
* Bootstrap any application services.
*/
public function boot()
{
// Paksa HTTPS jika diperlukan (opsional)
if (env('FORCE_HTTPS', false)) {
\URL::forceScheme('https');
}
}
}Contoh controller untuk generate PDF tanpa error path:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View; // β
Tambahkan ini
use Barryvdh\DomPDF\Facade\Pdf;
use Dompdf\Dompdf; // β
Tambahkan ini juga
use Dompdf\Options; // β
Ini penting buat konfigurasi DomPDF
class ExampleController extends Controller
{
public function exportPdf(Request $request)
{
$data = $this->getFilteredQuery($request)->get();
$totalPemasukan = $data->where('kategoriJenis.jenis', 'Pemasukan')->sum('nominal');
$totalPengeluaran = $data->where('kategoriJenis.jenis', 'Pengeluaran')->sum('nominal');
$saldoAkhir = $totalPemasukan - $totalPengeluaran;
$filters = $request->only(['tanggal_mulai', 'tanggal_selesai']);
// Render manual untuk menghindari error path
$view = view('laporan.pdf', compact(
'data',
'totalPemasukan',
'totalPengeluaran',
'saldoAkhir',
'filters'
))->render();
$pdf = Pdf::loadHtml($view);
return $pdf->download('laporan-' . date('d-m-Y') . '.pdf');
}
// METHOD BARU UNTUK EXPORT PDF (OPSI KEDUA)
public function exportPdf(Request $request)
{
// Ambil data
$data = $this->getFilteredQuery($request)->get();
$totalPemasukan = $data->where('kategoriJenis.jenis', 'Pemasukan')->sum('nominal');
$totalPengeluaran = $data->where('kategoriJenis.jenis', 'Pengeluaran')->sum('nominal');
$saldoAkhir = $totalPemasukan - $totalPengeluaran;
$filters = $request->only(['tanggal_mulai', 'tanggal_selesai']);
// Render HTML view ke string
$html = View::make('tabungan.pdf', compact(
'data',
'totalPemasukan',
'totalPengeluaran',
'saldoAkhir',
'filters'
))->render();
// Setup DomPDF
$options = new Options();
$options->set('isRemoteEnabled', true); // aktifkan load asset dari URL
$dompdf = new Dompdf($options);
$dompdf->loadHtml($html);
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
// Output sebagai file download
return response($dompdf->output(), 200)
->header('Content-Type', 'application/pdf')
->header('Content-Disposition', 'attachment; filename="laporan-tabungan-' . date('d-m-Y') . '.pdf"');
}
}<?php
/**
* Generate asset URL untuk InfinityFree hosting
*/
if (!function_exists('public_asset')) {
function public_asset($path)
{
return url('/') . '/' . ltrim($path, '/');
}
}
/**
* Check if app in production
*/
if (!function_exists('is_production')) {
function is_production()
{
return app()->environment('production');
}
}
/**
* Generate versioned asset untuk cache busting
*/
if (!function_exists('versioned_asset')) {
function versioned_asset($path)
{
$timestamp = filemtime(public_path($path));
return public_asset($path) . '?v=' . $timestamp;
}
}{
"name": "laravel/laravel",
"type": "project",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"require": {
"php": "^7.4|^8.0",
"laravel/framework": "^8.75"
},
"autoload": {
"files": [
"app/helpers.php"
],
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"scripts": {
"post-autoload-dump": [
"@php artisan package:discover --ansi"
]
}
}<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
@if(is_production())
<!-- Production: gunakan public_asset helper -->
<link href="{{ public_asset('assets/css/app.css') }}" rel="stylesheet">
<link href="{{ public_asset('assets/css/bootstrap.min.css') }}" rel="stylesheet">
@else
<!-- Development: gunakan asset helper biasa -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<link href="{{ asset('css/bootstrap.min.css') }}" rel="stylesheet">
@endif
</head>
<body>
<div id="app">
@yield('content')
</div>
<!-- Scripts -->
@if(is_production())
<script src="{{ public_asset('assets/js/app.js') }}"></script>
<script src="{{ public_asset('assets/js/bootstrap.bundle.min.js') }}"></script>
@else
<script src="{{ asset('js/app.js') }}"></script>
<script src="{{ asset('js/bootstrap.bundle.min.js') }}"></script>
@endif
@stack('scripts')
</body>
</html><?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
*/
Route::get('/', function () {
return view('welcome');
});
// Route untuk testing database connection
Route::get('/test-db', function () {
try {
\DB::connection()->getPdo();
$dbName = \DB::connection()->getDatabaseName();
return "β
Database connection successful!<br>Database: {$dbName}";
} catch (\Exception $e) {
return "β Database connection failed: " . $e->getMessage();
}
});
// Route untuk testing file permissions
Route::get('/test-storage', function () {
$storagePath = storage_path('app');
$isWritable = is_writable($storagePath);
return $isWritable
? "β
Storage directory is writable"
: "β Storage directory is not writable";
});
// Route untuk testing helper functions
Route::get('/test-helpers', function () {
return [
'public_asset' => public_asset('css/app.css'),
'is_production' => is_production(),
'app_url' => config('app.url'),
'public_path' => public_path(),
];
});# Di lokal sebelum upload
php artisan config:cache
php artisan route:cache
php artisan view:cache
npm run production # Jika pakai Laravel Mix- π Selalu gunakan
APP_DEBUG=falsedi production - π Jangan expose file
.env - π Update Laravel ke versi terbaru
- π Gunakan HTTPS jika tersedia
- β° Upload di jam 00:00-06:00 WIB
- π‘ Gunakan koneksi kabel untuk stabilitas
- π¦ Compress asset sebelum upload
- πΎ Backup sebelum deploy
Q: Berapa lama waktu upload ke InfinityFree? A: File Laravel dasar: 5-10 menit, Vendor lengkap: 30-60 menit
Q: Apakah bisa pakai Laravel 11? A: Ya, tapi perlu penyesuaian struktur folder bootstrap yang baru
Q: Bagaimana jika upload vendor gagal terus? A: Coba upload di jam sepi atau gunakan metode kompresi, atau gunakan script extract.php untuk file zip
Q: Bisa pakai database selain MySQL? A: InfinityFree hanya support MySQL/MariaDB
Q: Bagaimana cara update aplikasi? A: Upload file yang berubah saja, jangan upload ulang vendor jika tidak perlu
Q: Apakah file extract.php aman? A: Pastikan hapus file extract.php setelah digunakan untuk mencegah akses yang tidak diinginkan
Jika mengalami kendala:
- Periksa Troubleshooting di atas
- Cek error log di cPanel InfinityFree
- Buat issue di repository ini
- Join komunitas Laravel Indonesia
Last Updated: [Date]
Panduan ini gratis digunakan untuk keperluan edukasi dan komersial.
π‘ Pro Tip: Bookmark panduan ini untuk referensi deploy selanjutnya!
Dibuat dengan β€οΈ untuk komunitas Laravel Indonesia