How To Create Custom Packages In Laravel 12 A Complete Step By Step Guide

image
image
image
image
image
image
image
image
How to Create Custom Packages in Laravel 12: A Complete Step-by-Step Guide

How to Create Custom Packages in Laravel 12: A Complete Step-by-Step Guide

Learn how to build, test and publish your own custom Laravel 12 packages with this developer-friendly tutorial. Master package development to enhance code reusability and contribute to the Laravel ecosystem. Follow our expert step-by-step process from initial setup to Packagist submission.


What You'll Learn

  1. Setting up a package development environment
  2. Creating the package structure
  3. Implementing service providers
  4. Adding controllers, models, and migrations
  5. Publishing assets and configurations
  6. Testing your package
  7. Submitting to Packagist'


Prerequisites

  1. PHP 8.2 or higher
  2. Composer installed
  3. Basic knowledge of Laravel framework
  4. Laravel 12 project for testing


Step 1: Set Up Your Development Environment

First, create a new Laravel 12 project where you'll develop and test your package:

composer create-project laravel/laravel laravel-package-dev
cd laravel-package-dev


Note: Ok, Now In the below part I am using CodeHunger as the packages list and Learning as the Package name, for example, the directory structure will be like this packages/YourName/yourpackage/src , then in below example it will be packages/CodeHunger/Learning/src



Step 2: Create Package Structure

Laravel packages follow a specific directory structure. Create this structure in your project's root directory:

mkdir -p packages/CodeHunger/Learning/src


Now, Create the below folder under your src Folder:


packages/
└── CodeHunger/
└── Learning/
└── src/
├── Console/
│ └── Commands/
├── Config/
├── Controllers/
├── Database/
│ ├── migrations/
│ └── seeders/
│ └── LearningSeeder.php
├── Http/
│ ├── Controllers/
│ └── Middleware/
├── Models/
│ └── Learning.php
├── Providers/
├── Resources/
│ ├── views/
│ └── lang/
├── Routes/
└── Support/




Step 3: Create Service Provider

The service provider is the entry point of your package. Create a file named LearningServiceProvider.php in the src directory:

<?php

namespace CodeHunger\Learning\Provider;

use Illuminate\Support\ServiceProvider;

class LearningServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Load routes
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');

// Load migrations
$this->loadMigrationsFrom(__DIR__ . '/../Database/migrations');
// Load views with a namespace
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'learning');


// Optional: Publish assets or configs (currently commented)
$this->vendorPublish();
}

public function register(): void
{
// Bind classes or services into the container here if needed
}

protected function vendorPublish(): void
{
// Example for asset publishing (uncomment and customize if needed)
/*
$this->publishes([
__DIR__ . '/../resources/assets/js' => public_path('vendor/learning/js'),
], 'learning-assets');
*/
}
}


Step 4: Add the package path to the composer.json in your autoload json array and in the extra array


"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"CodeHunger\\Learning\\": "packages/CodeHunger/Learning/src/"
}
},

"extra": {
"laravel": {
"providers": [
"CodeHunger\\Learning\\Provider\\LearningServiceProvider"
]
}


Full Composer.json code will looks like the below one


{
 "$schema": "https://getcomposer.org/schema.json",
 "name": "laravel/laravel",
 "type": "project",
 "description": "The skeleton application for the Laravel framework.",
 "keywords": ["laravel", "framework"],
 "license": "MIT",
 "require": {
  "php": "^8.2",
  "laravel/framework": "^12.0",
  "laravel/tinker": "^2.10.1"
 },
 "require-dev": {
  "fakerphp/faker": "^1.23",
  "laravel/pail": "^1.2.2",
  "laravel/pint": "^1.13",
  "laravel/sail": "^1.41",
  "mockery/mockery": "^1.6",
  "nunomaduro/collision": "^8.6",
  "phpunit/phpunit": "^11.5.3"
 },
 "autoload": {
  "psr-4": {
   "App\\": "app/",
   "Database\\Factories\\": "database/factories/",
   "Database\\Seeders\\": "database/seeders/",
   "CodeHunger\\Learning\\": "packages/CodeHunger/Learning/src/"
  }
 },
 "autoload-dev": {
  "psr-4": {
   "Tests\\": "tests/"
  }
 },
 "scripts": {
  "post-autoload-dump": [
   "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
   "@php artisan package:discover --ansi"
  ],
  "post-update-cmd": [
   "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
  ],
  "post-root-package-install": [
   "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
  ],
  "post-create-project-cmd": [
   "@php artisan key:generate --ansi",
   "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
   "@php artisan migrate --graceful --ansi"
  ],
  "dev": [
   "Composer\\Config::disableProcessTimeout",
   "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
  ]
 },
 "extra": {
   "laravel": {
    "providers": [
    "CodeHunger\\Learning\\Provider\\LearningServiceProvider"
    ]
  }
 },
 "config": {
  "optimize-autoloader": true,
  "preferred-install": "dist",
  "sort-packages": true,
  "allow-plugins": {
   "pestphp/pest-plugin": true,
   "php-http/discovery": true
  }
 },
 "minimum-stability": "stable",
 "prefer-stable": true
}


Step 5: Add your service provider path at App/Providers


<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
  /**
   * Register any application services.
   */
  public function register(): void
  {
     $this->app->register(\CodeHunger\Learning\Provider\LearningServiceProvider::class);
  }

  /**
   * Bootstrap any application services.
   */
  public function boot(): void
  {
    //
  }
}



Step 6: Add Routes

Create a routes directory and add routes for your package, only run the below command If you haven't created the routes folder

mkdir -p routes

Create routes/web.php:

<?php

use Illuminate\Support\Facades\Route;
use CodeHunger\Learning\Http\Controllers\LearningController;


Route::group(['prefix' => 'learning',], function () {
Route::get('/', [LearningController::class, 'index'])->name('learning.index');
});


Step 6: Create Controllers

Create a controller directory structure and add a controller:


bash
mkdir -p src/Http/Controllers

Create src/Http/Controllers/YourController.php:


<?php

namespace CodeHunger\Learning\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;

class LearningController extends Controller
{
public function index()
{
return view('learning::index');
}
}


Step 7: Add Views

Create a views directory for your package:

mkdir -p resources/views

Create resources/views/index.blade.php:

<html>
<head>
<title>CodeHunger</title>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
<h1>Welcome to CodeHunger</h1>
</body>
</html>


Step 8: Add Database Migrations

If your package needs database tables:

mkdir -p database/migrations

Create database/migrations/0000_00_00_000000_create_your_table.php:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('learnings', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('your_table');
}
};


Step 9: Create Models

If your package works with database models:

mkdir -p src/Models

Create src/Models/YourModel.php:

namespace CodeHunger\Learning\\Models;

use Illuminate\Database\Eloquent\Model;

class Learning extends Model
{
protected $fillable = [
'name',
'description',
];
}


Step:10 Check the created route


First, run the below command in your command panel

php artisan serve


Then visit the below URL in your browser

localhost:8000


You can see the output like the below image




To assist with implementation, you can directly download the complete source code referenced in this guide from the official GitHub repository:


🔗 GitHub Repository: https://github.com/codehunger-team/custom-package


This repository includes all the necessary structure and files required to build and register a Laravel 12 custom package, as detailed in the blog. Using this codebase ensures consistency with the tutorial and allows for quicker integration into your Laravel application.


Conclusion

Creating custom packages in Laravel 12 is a powerful way to modularize your code and share functionality across projects. By following this guide, you can build well-structured, maintainable packages that integrate seamlessly with Laravel's ecosystem. As you become more experienced with package development, you'll discover more advanced techniques to enhance your packages and contribute to the Laravel community. Remember that the best packages solve specific problems elegantly while maintaining compatibility with Laravel's design principles and philosophy.