Building Laravel packages transforms repetitive code into reusable components. Whether you're sharing functionality across your own projects or contributing to the Laravel ecosystem, package development is an essential skill. At ZIRA Software, we maintain several Laravel packages used by thousands of developers.
Why Build Laravel Packages?
Benefits:
- Reuse code across projects
- Share functionality with community
- Separate concerns cleanly
- Easier testing and maintenance
- Version control for features
- Build reputation in Laravel community
When to build a package:
- Feature used in multiple projects
- Functionality others might need
- Self-contained logic
- Can be tested independently
Package Structure
my-package/
├── src/
│ ├── MyPackageServiceProvider.php
│ ├── Facades/
│ │ └── MyPackage.php
│ ├── Commands/
│ ├── Controllers/
│ ├── Models/
│ └── config/
│ └── mypackage.php
├── tests/
│ └── MyPackageTest.php
├── composer.json
├── README.md
├── LICENSE
└── .gitignore
Creating Your First Package
Initialize Package
mkdir laravel-analytics
cd laravel-analytics
composer init
Answer prompts:
- Package name:
yourname/laravel-analytics - Description:
Google Analytics integration for Laravel - Author:
Your Name <email@example.com> - Type:
library - License:
MIT
composer.json
{
"name": "yourname/laravel-analytics",
"description": "Google Analytics integration for Laravel",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Your Name",
"email": "email@example.com"
}
],
"require": {
"php": "^5.6|^7.0",
"illuminate/support": "~5.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.0",
"orchestra/testbench": "~3.1"
},
"autoload": {
"psr-4": {
"YourName\\Analytics\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"YourName\\Analytics\\Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"providers": [
"YourName\\Analytics\\AnalyticsServiceProvider"
],
"aliases": {
"Analytics": "YourName\\Analytics\\Facades\\Analytics"
}
}
}
}
Service Provider
<?php namespace YourName\Analytics;
use Illuminate\Support\ServiceProvider;
class AnalyticsServiceProvider extends ServiceProvider
{
/**
* Bootstrap services
*/
public function boot()
{
// Publish configuration
$this->publishes([
__DIR__.'/config/analytics.php' => config_path('analytics.php'),
], 'config');
// Load views
$this->loadViewsFrom(__DIR__.'/views', 'analytics');
// Publish views
$this->publishes([
__DIR__.'/views' => resource_path('views/vendor/analytics'),
], 'views');
// Load migrations
$this->loadMigrationsFrom(__DIR__.'/migrations');
// Load routes
$this->loadRoutesFrom(__DIR__.'/routes.php');
// Register commands
if ($this->app->runningInConsole()) {
$this->commands([
Commands\FetchAnalytics::class,
]);
}
}
/**
* Register services
*/
public function register()
{
// Merge configuration
$this->mergeConfigFrom(
__DIR__.'/config/analytics.php', 'analytics'
);
// Bind to container
$this->app->singleton('analytics', function ($app) {
return new Analytics(
config('analytics.tracking_id'),
config('analytics.view_id')
);
});
}
}
Main Package Class
<?php namespace YourName\Analytics;
use Google_Client;
use Google_Service_Analytics;
class Analytics
{
protected $client;
protected $analytics;
protected $viewId;
public function __construct($trackingId, $viewId)
{
$this->viewId = $viewId;
$this->client = new Google_Client();
$this->client->setApplicationName('Laravel Analytics');
$this->analytics = new Google_Service_Analytics($this->client);
}
/**
* Get page views for date range
*/
public function getPageViews($startDate, $endDate)
{
return $this->analytics->data_ga->get(
'ga:' . $this->viewId,
$startDate,
$endDate,
'ga:pageviews'
);
}
/**
* Get most visited pages
*/
public function getMostVisitedPages($startDate, $endDate, $limit = 10)
{
$results = $this->analytics->data_ga->get(
'ga:' . $this->viewId,
$startDate,
$endDate,
'ga:pageviews',
[
'dimensions' => 'ga:pagePath',
'sort' => '-ga:pageviews',
'max-results' => $limit
]
);
return $this->formatResults($results);
}
/**
* Format results
*/
protected function formatResults($results)
{
$rows = $results->getRows();
if (empty($rows)) {
return [];
}
return collect($rows)->map(function ($row) {
return [
'url' => $row[0],
'pageviews' => $row[1]
];
});
}
}
Facade
<?php namespace YourName\Analytics\Facades;
use Illuminate\Support\Facades\Facade;
class Analytics extends Facade
{
/**
* Get the registered name of the component
*/
protected static function getFacadeAccessor()
{
return 'analytics';
}
}
Configuration File
<?php
// src/config/analytics.php
return [
/*
* Google Analytics Tracking ID
*/
'tracking_id' => env('ANALYTICS_TRACKING_ID'),
/*
* Google Analytics View ID
*/
'view_id' => env('ANALYTICS_VIEW_ID'),
/*
* Path to service account credentials JSON
*/
'credentials_path' => storage_path('app/analytics/credentials.json'),
/*
* Cache lifetime in minutes
*/
'cache_lifetime' => 60 * 24, // 24 hours
];
Artisan Command
<?php namespace YourName\Analytics\Commands;
use Illuminate\Console\Command;
use YourName\Analytics\Facades\Analytics;
class FetchAnalytics extends Command
{
protected $signature = 'analytics:fetch {--days=7}';
protected $description = 'Fetch analytics data';
public function handle()
{
$days = $this->option('days');
$startDate = now()->subDays($days)->format('Y-m-d');
$endDate = now()->format('Y-m-d');
$this->info("Fetching analytics for last {$days} days...");
$pageViews = Analytics::getPageViews($startDate, $endDate);
$this->table(
['Metric', 'Value'],
[['Page Views', $pageViews->getTotalsForAllResults()['ga:pageviews']]]
);
$this->info('Most visited pages:');
$pages = Analytics::getMostVisitedPages($startDate, $endDate, 10);
$this->table(
['URL', 'Views'],
$pages->map(function ($page) {
return [$page['url'], $page['pageviews']];
})
);
}
}
Testing
Setup TestBench
<?php namespace YourName\Analytics\Tests;
use Orchestra\Testbench\TestCase as Orchestra;
use YourName\Analytics\AnalyticsServiceProvider;
abstract class TestCase extends Orchestra
{
protected function getPackageProviders($app)
{
return [
AnalyticsServiceProvider::class,
];
}
protected function getPackageAliases($app)
{
return [
'Analytics' => \YourName\Analytics\Facades\Analytics::class,
];
}
protected function getEnvironmentSetUp($app)
{
$app['config']->set('analytics.tracking_id', 'UA-12345678-1');
$app['config']->set('analytics.view_id', '12345678');
}
}
Write Tests
<?php namespace YourName\Analytics\Tests;
use YourName\Analytics\Facades\Analytics;
class AnalyticsTest extends TestCase
{
/** @test */
public function it_can_fetch_page_views()
{
$pageViews = Analytics::getPageViews('2015-09-01', '2015-09-15');
$this->assertNotNull($pageViews);
}
/** @test */
public function it_can_fetch_most_visited_pages()
{
$pages = Analytics::getMostVisitedPages('2015-09-01', '2015-09-15', 5);
$this->assertCount(5, $pages);
$this->assertArrayHasKey('url', $pages->first());
$this->assertArrayHasKey('pageviews', $pages->first());
}
/** @test */
public function it_formats_results_correctly()
{
$pages = Analytics::getMostVisitedPages('2015-09-01', '2015-09-15');
$pages->each(function ($page) {
$this->assertIsString($page['url']);
$this->assertIsNumeric($page['pageviews']);
});
}
}
Run Tests
vendor/bin/phpunit
Using the Package Locally
Add to composer.json
{
"repositories": [
{
"type": "path",
"url": "../laravel-analytics"
}
],
"require": {
"yourname/laravel-analytics": "dev-master"
}
}
composer update yourname/laravel-analytics
Install Package
php artisan vendor:publish --provider="YourName\Analytics\AnalyticsServiceProvider"
Use in Application
use YourName\Analytics\Facades\Analytics;
// Get page views
$views = Analytics::getPageViews('2015-09-01', '2015-09-15');
// Get most visited pages
$pages = Analytics::getMostVisitedPages('2015-09-01', '2015-09-15', 10);
// In controller
public function dashboard()
{
$stats = [
'today' => Analytics::getPageViews(today(), today()),
'week' => Analytics::getPageViews(now()->subWeek(), now()),
'popular' => Analytics::getMostVisitedPages(now()->subMonth(), now())
];
return view('dashboard', $stats);
}
Publishing to Packagist
1. Create GitHub Repository
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/yourname/laravel-analytics.git
git push -u origin master
2. Tag Release
git tag -a v1.0.0 -m "First stable release"
git push --tags
3. Submit to Packagist
- Visit packagist.org
- Sign in with GitHub
- Click "Submit"
- Enter repository URL
- Click "Check"
4. Auto-Update Hook
Add GitHub webhook for automatic updates:
- Settings → Webhooks → Add webhook
- Payload URL:
https://packagist.org/api/github?username=yourname - Content type:
application/json
Best Practices
1. Follow PSR standards
composer require --dev squizlabs/php_codesniffer
vendor/bin/phpcs --standard=PSR2 src/
2. Write comprehensive tests
- Aim for 80%+ coverage
- Test all public methods
- Test edge cases
3. Documentation
- Clear README with examples
- Installation instructions
- Configuration options
- API documentation
4. Semantic versioning
- MAJOR.MINOR.PATCH
- Breaking changes = MAJOR
- New features = MINOR
- Bug fixes = PATCH
5. Changelog
# Changelog
## [1.1.0] - 2015-09-20
### Added
- Real-time analytics support
- New `getActiveUsers()` method
### Fixed
- Cache invalidation bug
## [1.0.0] - 2015-09-15
- Initial release
Advanced Features
Config Merging
public function register()
{
$this->mergeConfigFrom(__DIR__.'/config/analytics.php', 'analytics');
}
Middleware
<?php namespace YourName\Analytics\Middleware;
use Closure;
use YourName\Analytics\Facades\Analytics;
class TrackPageView
{
public function handle($request, Closure $next)
{
$response = $next($request);
Analytics::trackPageView($request->path());
return $response;
}
}
Events
<?php namespace YourName\Analytics\Events;
class AnalyticsFetched
{
public $data;
public function __construct($data)
{
$this->data = $data;
}
}
// Fire event
event(new AnalyticsFetched($pageViews));
Conclusion
Building Laravel packages lets you share functionality, contribute to the community, and structure code better. At ZIRA Software, our packages have been installed over 100,000 times, helping developers worldwide build better applications.
Start with a simple package solving a real problem. Focus on clean code, comprehensive tests, and clear documentation. The Laravel community will thank you.
Need help building Laravel packages? Contact ZIRA Software to discuss package development, open source strategy, and creating reusable components for your organization.