Updated article? - How to create a Laravel Package
Hey Chris!
What I've been doing lately is to use the Spatie Laravel Package toolkit repo:
But in general, here is a guide on how to do this from scratch:
Prerequisites:
- PHP 8.2 or higher
- Composer
- Git
- Basic knowledge of Laravel and PHP
- A code editor (e.g., Visual Studio Code, PhpStorm)
Step 1 — Setting Up the Development Environment
First, ensure you have the necessary tools installed:
-
Install PHP 8.2 or higher:
sudo apt update sudo apt install php8.2 php8.2-cli php8.2-mbstring php8.2-curl php8.2-mysql php8.2-xml
-
Install Composer:
curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer
-
Install Git:
sudo apt install git
To test your package, create a new Laravel project:
composer create-project laravel/laravel example-app
cd example-app
Step 2 — Creating the Package Structure
Choose a name for your package following the vendor-name/package-name
convention. We'll use acme/blog-package
.
Create the package directory structure:
mkdir -p ~/packages/acme/blog-package/{src,config,database,resources,tests}
cd ~/packages/acme/blog-package
This creates the following structure:
acme/blog-package/
├── src/
├── config/
├── database/
├── resources/
└── tests/
Step 3 — Initializing the Package
Initialize Composer for your package:
composer init
Follow the prompts to set up your composer.json
. Here's a detailed example:
{
"name": "acme/blog-package",
"description": "A comprehensive blog package for Laravel",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {
"Acme\\BlogPackage\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Acme\\BlogPackage\\Tests\\": "tests/"
}
},
"authors": [
{
"name": "Your Name",
"email": "[email protected]"
}
],
"require": {
"php": "^8.2",
"illuminate/support": "^11.0"
},
"require-dev": {
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.0"
},
"extra": {
"laravel": {
"providers": [
"Acme\\BlogPackage\\BlogPackageServiceProvider"
]
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
After creating the composer.json
, run:
composer update
Step 4 — Creating the Service Provider
Create a new file src/BlogPackageServiceProvider.php
:
<?php
namespace Acme\BlogPackage;
use Illuminate\Support\ServiceProvider;
class BlogPackageServiceProvider extends ServiceProvider
{
public function register()
{
$this->mergeConfigFrom(__DIR__.'/../config/blog-package.php', 'blog-package');
}
public function boot()
{
// Publish configuration
$this->publishes([
__DIR__.'/../config/blog-package.php' => config_path('blog-package.php'),
], 'config');
// Load migrations
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
// Load routes
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
// Load views
$this->loadViewsFrom(__DIR__.'/../resources/views', 'blog-package');
}
}
Step 5 — Developing Package Features
Let's add core functionality to our blog package:
- Create a Post model in
src/Models/Post.php
:
<?php
namespace Acme\BlogPackage\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['title', 'content'];
}
- Create a PostController in
src/Http/Controllers/PostController.php
:
<?php
namespace Acme\BlogPackage\Http\Controllers;
use Acme\BlogPackage\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
$posts = Post::all();
return view('blog-package::posts.index', compact('posts'));
}
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|max:255',
'content' => 'required',
]);
Post::create($validatedData);
return redirect()->route('posts.index');
}
// Add other CRUD methods as needed
}
- Create routes in
routes/web.php
:
<?php
use Acme\BlogPackage\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
Route::group(['middleware' => ['web']], function () {
Route::resource('posts', PostController::class);
});
Step 6 — Adding Configuration
Create a config file config/blog-package.php
:
<?php
return [
'posts_table' => 'posts',
'posts_per_page' => 10,
'admin_email' => '[email protected]',
];
Step 7 — Creating Database Migrations
Create a migration for the posts table:
mkdir -p database/migrations
touch database/migrations/2024_07_22_000000_create_posts_table.php
Edit the migration file:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostsTable extends Migration
{
public function up()
{
Schema::create(config('blog-package.posts_table'), function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists(config('blog-package.posts_table'));
}
}
Step 8 — Creating Views
Create views for your package:
mkdir -p resources/views/posts
touch resources/views/posts/index.blade.php
Edit resources/views/posts/index.blade.php
:
@extends('layouts.app')
@section('content')
<h1>Blog Posts</h1>
@foreach($posts as $post)
<div>
<h2>{{ $post->title }}</h2>
<p>{{ Str::limit($post->content, 100) }}</p>
</div>
@endforeach
@endsection
Step 9 — Writing Tests
Create a test file tests/Feature/PostTest.php
:
<?php
namespace Acme\BlogPackage\Tests\Feature;
use Acme\BlogPackage\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Orchestra\Testbench\TestCase;
class PostTest extends TestCase
{
use RefreshDatabase;
protected function getPackageProviders($app)
{
return ['Acme\BlogPackage\BlogPackageServiceProvider'];
}
/** @test */
public function it_can_create_a_post()
{
$post = Post::create([
'title' => 'Test Post',
'content' => 'This is a test post.',
]);
$this->assertDatabaseHas('posts', [
'title' => 'Test Post',
'content' => 'This is a test post.',
]);
}
// Add more tests as needed
}
To run tests:
./vendor/bin/phpunit
Step 10 — Documentation
Create a comprehensive README.md
file in your package root. Include:
- Package description
- Installation instructions
- Configuration options
- Usage examples
- Testing instructions
- Contribution guidelines
- License information
Step 11 — Publishing Your Package
-
Initialize a Git repository:
git init git add . git commit -m "Initial commit"
-
Create a GitHub repository and push your code:
git remote add origin https://github.com/yourusername/blog-package.git git push -u origin main
-
Tag a release:
git tag 1.0.0 git push --tags
-
Submit your package to Packagist:
- Sign up for a Packagist account
- Click "Submit Package"
- Enter your GitHub repository URL
Step 12 — Using Your Package
To use your package in a Laravel project:
-
Add it to
composer.json
:"require": { "acme/blog-package": "^1.0" }
-
Run
composer update
-
Publish the package assets:
php artisan vendor:publish --provider="Acme\BlogPackage\BlogPackageServiceProvider"
-
Run migrations:
php artisan migrate
-
Use the package features in your Laravel application.
Conclusion
You've now created a comprehensive Laravel package with models, controllers, views, migrations, and tests. Remember to:
- Keep your package well-documented
- Write thorough tests
- Follow Laravel's coding standards
- Regularly update your package for new Laravel versions















