With the knowledge we acquired in last few articles surrounding Laravel 9 ecosystem, let’s build something concrete – a kind of full-fledged business app. So, in next few articles, we’ll gradually build an Invoicing app using tech stacks available to Laravel 9. Well…let’s start the marathon.
In this article we’ll create migrations and models for our Invoicing app. These migrations and models will lay the foundation for the upcoming articles where our Invoicing app will materialize step by step.
Laravel 9.x requires a minimum PHP version of 8.0. This tutorial assumes that you are using Linux, macOS or WSL on Windows and Node.js, Composer and PHP 8 along with required modules i.e., bcmatch, sqlite, mbstring, xml, zip, gd, mcrypt are properly installed. Also make sure that SQLite is installed as we’ll be using SQLite to keep things simple and quick; you may use MySQL or PostgreSQL as you like, however make sure the corresponding PHP module is installed too.
Using composer, enter following command in terminal to install Laravel 9;
composer create-project laravel/laravel app 9.*
Now go inside newly created app folder by executing;
cd app
If you have VS Code installed and want to use it as your code editor, you may enter following command to open this project in VS Code;
code .
Next, we’ll setup database and Laravel environment file. Here, we’re configuring SQLite database for simplicity purposes. So, execute following command inside parent folder (app) to create new SQLite database file;
touch database/database.sqlite
Then find .env config file in the parent folder, open it and amend DB_CONNECTION line as follows;
DB_CONNECTION = sqlite
Remove DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD from .env since we’re using SQLite database.
Lets create migration file by executing following command:
php artisan make:migration create_core_tables
Then replace code inside database/migrations/(timestamp)_create_core_tables.php with following:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->nullable();
$table->string('phone')->nullable();
$table->text('address')->nullable();
$table->timestamps();
});
Schema::create('items', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('unit')->nullable();
$table->text('description')->nullable();
$table->double('sale_rate', 15, 2);
$table->double('purchase_rate', 15, 2);
$table->integer('quantity');
$table->timestamps();
});
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->foreignId('customer_id')->constrained('customers')->onDelete('cascade');
$table->string('invoice_number');
$table->date('invoice_date');
$table->double('amount',15,2);
$table->text('description')->nullable();
$table->timestamps();
});
Schema::create('invoice_items', function (Blueprint $table) {
$table->increments('id');
$table->foreignId('invoice_id')->constrained('invoices')->onDelete('cascade');
$table->foreignId('item_id')->constrained('items')->onDelete('cascade');
$table->double('quantity', 7, 2);
$table->double('rate', 15, 2);
$table->double('amount', 15, 2);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('invoice_items');
Schema::dropIfExists('invoices');
Schema::dropIfExists('items');
Schema::dropIfExists('customers');
}
};
The above migration will create four required tables for our invoicing app i.e. customers, items, invoices and invoice_items. Now, enter following command in order to migrate the above migration;
php artisan migrate
Next, create the corresponding models. We can create the required models in one go by executing following command:
for name in {Customer,Invoice,InvoiceItem,Item}; do php artisan make:model "$name"; done
Now, open app/Models/Customer.php model file and change it as follows;
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model
{
use HasFactory;
protected $fillable = [
'name', 'email', 'phone', 'address'
];
public function invoices()
{
return $this->hasMany(Invoice::class);
}
}
Then, open app/Models/Item.php model file and change it as follows;
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Item extends Model
{
use HasFactory;
protected $fillable = [
'name', 'unit', 'description', 'sale_rate', 'purchase_rate', 'quantity'
];
public function invoiceItems()
{
return $this->hasMany(InvoiceItem::class);
}
}
Then, open app/Models/Invoice.php model file and change it as follows;
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
use HasFactory;
protected $fillable = [
'customer_id', 'invoice_number', 'invoice_date', 'amount', 'description'
];
public function customer()
{
return $this->belongsTo(Customer::class);
}
public function invoiceItems()
{
return $this->hasMany(InvoiceItem::class);
}
}
And then, open app/Models/InvoiceItem.php model file and change it as follows;
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class InvoiceItem extends Model
{
use HasFactory;
protected $fillable = [
'invoice_id', 'item_id', 'quantity', 'rate', 'amount'
];
public function invoice()
{
return $this->belongsTo(Invoice::class);
}
public function item()
{
return $this->belongsTo(Item::class);
}
}
Finally, let’s create some dummy data for the database. So, open database/seeders/DatabaseSeeder.php and amend it as follows;
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class DatabaseSeeder extends Seeder
{
public function run()
{
DB::table('customers')->insert(['name' => 'ABC', 'email' => 'abc@email.com', 'phone' => '1234567', 'address' => 'ABC Plaza, Z Street.']);
DB::table('customers')->insert(['name' => 'PQR', 'email' => 'qp@email.com', 'phone' => '5677865', 'address' => 'LIMP Tower, C Street.']);
DB::table('items')->insert(['name' => 'Product A', 'unit' => 'KG', 'description' => 'Description of Product A', 'sale_rate' => 1.8, 'purchase_rate' => 1.6, 'quantity' => 2000]);
DB::table('items')->insert(['name' => 'Product B', 'unit' => 'Litre', 'description' => 'Details of Product B', 'sale_rate' => 5.25, 'purchase_rate' => 4.75, 'quantity' => 950]);
DB::table('invoices')->insert(['customer_id' => 1, 'invoice_number' => 'CO/INV/2023/2/1', 'invoice_date' => '2023-01-01', 'amount' => 24.75, 'description' => 'First invoice of the year.']);
DB::table('invoice_items')->insert(['invoice_id' => 1, 'item_id' => 1, 'quantity' => 5, 'rate' => 1.8, 'amount' => 9]);
DB::table('invoice_items')->insert(['invoice_id' => 1, 'item_id' => 2, 'quantity' => 3, 'rate' => 5.25, 'amount' => 15.75]);
}
}
Then populate the database by executing the following command;
php artisan db:seed
We’re done with the models and migrations. In next article, we’ll create the actual invoice template.
6 thoughts on “Invoicing app in Laravel 9 – migrations and models”