Provedores de Serviços
Os provedores de serviços são o local central de toda a inicialização da aplicação PivotPHP. Sua própria aplicação, bem como todos os serviços principais do PivotPHP, são inicializados através de provedores de serviços.
Introdução
Os provedores de serviços são o ponto de conexão entre seu pacote e o PivotPHP. Um provedor de serviços é responsável por vincular coisas ao contêiner de serviços do PivotPHP e informar ao PivotPHP onde carregar recursos do pacote, como views, configurações e arquivos de localização.
Escrevendo Provedores de Serviços
Todos os provedores de serviços estendem a classe PivotPHP\Core\Core\ServiceProvider
e contêm dois métodos: register
e boot
.
Estrutura Básica
namespace App\Providers;
use PivotPHP\Core\Core\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Registra qualquer serviço da aplicação.
*/
public function register(): void
{
// Registra vinculações no contêiner
}
/**
* Inicializa qualquer serviço da aplicação.
*/
public function boot(): void
{
// Inicializa sua aplicação
}
}
O Método Register
Dentro do método register
, você deve apenas vincular coisas ao contêiner de serviços. Você nunca deve tentar registrar ouvintes de eventos, rotas ou qualquer outra funcionalidade dentro do método register
.
public function register(): void
{
// Vinculação simples
$this->app->bind('mailer', function ($app) {
return new Mailer($app->make('config')->get('mail'));
});
// Vinculação singleton
$this->app->singleton(ConnectionInterface::class, function ($app) {
return new DatabaseConnection(
$app->make('config')->get('database')
);
});
// Vinculação de instância
$this->app->instance('api.client', new ApiClient(
$_ENV['API_KEY']
));
}
O Método Boot
O método boot
é chamado depois que todos os outros provedores de serviços foram registrados, o que significa que você tem acesso a todos os outros serviços registrados pelo framework.
public function boot(): void
{
// Registra ouvintes de eventos
Event::listen(UserRegistered::class, SendWelcomeEmail::class);
// Registra middleware
$this->app->middleware(RateLimitMiddleware::class);
// Publica configuração
$this->publishes([
__DIR__.'/../config/services.php' => config_path('services.php'),
], 'config');
// Carrega rotas
$this->loadRoutesFrom(__DIR__.'/../routes/api.php');
// Carrega views
$this->loadViewsFrom(__DIR__.'/../resources/views', 'package');
}
Registrando Provedores
Na Configuração
Registre seus provedores de serviços no arquivo de configuração config/app.php
:
'providers' => [
/*
* Provedores de Serviços do Framework PivotPHP...
*/
PivotPHP\Foundation\Providers\FoundationServiceProvider::class,
PivotPHP\Routing\RoutingServiceProvider::class,
PivotPHP\Session\SessionServiceProvider::class,
/*
* Provedores de Serviços da Aplicação...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
Provedores Adiados
Se seu provedor está apenas registrando vinculações no contêiner de serviços, você pode escolher adiar seu registro até que uma das vinculações registradas seja realmente necessária:
namespace App\Providers;
use PivotPHP\Core\Core\DeferredServiceProvider;
use App\Services\ImageProcessor;
class ImageServiceProvider extends DeferredServiceProvider
{
/**
* Registra qualquer serviço da aplicação.
*/
public function register(): void
{
$this->app->singleton(ImageProcessor::class, function ($app) {
return new ImageProcessor(
$app->make('filesystem'),
$app->make('config')->get('images')
);
});
}
/**
* Obtém os serviços fornecidos pelo provedor.
*/
public function provides(): array
{
return [ImageProcessor::class];
}
}
Exemplos Comuns de Provedores de Serviços
Provedor de Serviços de Rotas
namespace App\Providers;
use PivotPHP\Core\Core\ServiceProvider;
use PivotPHP\Routing\Router;
class RouteServiceProvider extends ServiceProvider
{
/**
* O caminho para a rota "home" da sua aplicação.
*/
public const HOME = '/dashboard';
/**
* Define suas vinculações de modelo de rota, filtros de padrão, etc.
*/
public function boot(): void
{
$this->configureRateLimiting();
$this->defineRoutes();
}
/**
* Define as rotas para a aplicação.
*/
protected function defineRoutes(): void
{
$router = $this->app->make(Router::class);
// Rotas da API
$router->group([
'prefix' => 'api',
'middleware' => ['api', 'throttle:60,1'],
], function ($router) {
require base_path('routes/api.php');
});
// Rotas Web
$router->group([
'middleware' => ['web'],
], function ($router) {
require base_path('routes/web.php');
});
}
/**
* Configura os limitadores de taxa para a aplicação.
*/
protected function configureRateLimiting(): void
{
RateLimiter::for('api', function ($request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('login', function ($request) {
return Limit::perMinute(5)->by($request->ip());
});
}
}
Provedor de Serviços de Autenticação
namespace App\Providers;
use PivotPHP\Core\Core\ServiceProvider;
use App\Services\Auth\JwtGuard;
use App\Services\Auth\UserProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Registra qualquer serviço de autenticação/autorização.
*/
public function register(): void
{
// Registra provedor de usuário personalizado
$this->app->singleton('auth.provider', function ($app) {
return new UserProvider(
$app->make('hash'),
$app->make(UserRepository::class)
);
});
// Registra guarda personalizado
$this->app->singleton('auth.guard', function ($app) {
return new JwtGuard(
$app->make('auth.provider'),
$app->make('request'),
$app->make('config')->get('auth.jwt')
);
});
}
/**
* Inicializa qualquer serviço de autenticação/autorização.
*/
public function boot(): void
{
// Registra portões de autorização
Gate::define('update-post', function ($user, $post) {
return $user->id === $post->user_id;
});
Gate::define('admin', function ($user) {
return $user->role === 'admin';
});
// Registra políticas
Gate::policy(Post::class, PostPolicy::class);
Gate::policy(Comment::class, CommentPolicy::class);
}
}
Provedor de Serviços de Eventos
namespace App\Providers;
use PivotPHP\Core\Core\ServiceProvider;
use PivotPHP\Events\Dispatcher;
class EventServiceProvider extends ServiceProvider
{
/**
* Os mapeamentos de ouvintes de eventos para a aplicação.
*/
protected array $listen = [
UserRegistered::class => [
SendEmailVerificationNotification::class,
LogUserRegistration::class,
UpdateUserStatistics::class,
],
OrderPlaced::class => [
ProcessPayment::class,
SendOrderConfirmation::class,
UpdateInventory::class,
NotifyWarehouse::class,
],
PaymentFailed::class => [
NotifyCustomerOfFailure::class,
LogFailedPayment::class,
RevertOrderStatus::class,
],
];
/**
* Os assinantes a serem registrados.
*/
protected array $subscribe = [
UserEventSubscriber::class,
PaymentEventSubscriber::class,
];
/**
* Registra qualquer evento para sua aplicação.
*/
public function boot(): void
{
parent::boot();
// Registra eventos de modelo
User::observe(UserObserver::class);
Post::observe(PostObserver::class);
// Registra eventos personalizados
Event::listen('cache.cleared', function () {
Log::info('Cache da aplicação foi limpo');
});
}
/**
* Determina se eventos e ouvintes devem ser automaticamente descobertos.
*/
public function shouldDiscoverEvents(): bool
{
return true;
}
/**
* Obtém os diretórios de ouvintes que devem ser usados para descobrir eventos.
*/
protected function discoverEventsWithin(): array
{
return [
$this->app->path('Listeners'),
$this->app->path('Observers'),
];
}
}
Provedor de Serviços de Transmissão
namespace App\Providers;
use PivotPHP\Core\Core\ServiceProvider;
use PivotPHP\Broadcasting\BroadcastManager;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Inicializa qualquer serviço da aplicação.
*/
public function boot(): void
{
$this->registerChannels();
// Registra rotas de transmissão
require base_path('routes/channels.php');
}
/**
* Registra canais de transmissão.
*/
protected function registerChannels(): void
{
// Autorização de canal privado
Broadcast::channel('user.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
// Autorização de canal de presença
Broadcast::channel('chat.{roomId}', function ($user, $roomId) {
if ($user->canJoinRoom($roomId)) {
return [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar_url,
];
}
});
// Transmissão de modelo
Broadcast::channel('App.Models.Order.{order}', function ($user, Order $order) {
return $user->id === $order->user_id;
});
}
}
Provedor de Serviços de View
namespace App\Providers;
use PivotPHP\Core\Core\ServiceProvider;
use PivotPHP\View\View;
class ViewServiceProvider extends ServiceProvider
{
/**
* Registra qualquer serviço da aplicação.
*/
public function register(): void
{
//
}
/**
* Inicializa qualquer serviço da aplicação.
*/
public function boot(): void
{
// Registra compositores de view
View::composer('profile', ProfileComposer::class);
View::composer(['dashboard', 'analytics'], function ($view) {
$view->with('stats', app(StatsService::class)->getStats());
});
// Registra criadores de view
View::creator('notifications', function ($view) {
$view->with('notifications', auth()->user()->unreadNotifications);
});
// Compartilha dados com todas as views
View::share('appName', config('app.name'));
View::share('currentYear', date('Y'));
// Registra diretivas personalizadas
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('Y-m-d H:i:s'); ?>";
});
Blade::if('env', function ($environment) {
return app()->environment($environment);
});
}
}
Provedores de Serviços de Pacotes
Ao criar pacotes, use provedores de serviços para registrar recursos do pacote:
namespace YourPackage\Providers;
use PivotPHP\Core\Core\ServiceProvider;
class PackageServiceProvider extends ServiceProvider
{
/**
* Inicializa os serviços da aplicação.
*/
public function boot(): void
{
// Carrega rotas do pacote
$this->loadRoutesFrom(__DIR__.'/../routes.php');
// Carrega views do pacote
$this->loadViewsFrom(__DIR__.'/../resources/views', 'package');
// Carrega traduções do pacote
$this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'package');
// Carrega migrações do pacote
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
// Publica recursos do pacote
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/../config/package.php' => config_path('package.php'),
], 'config');
$this->publishes([
__DIR__.'/../resources/views' => resource_path('views/vendor/package'),
], 'views');
$this->publishes([
__DIR__.'/../resources/assets' => public_path('vendor/package'),
], 'assets');
}
// Registra comandos
$this->commands([
InstallCommand::class,
PublishCommand::class,
]);
}
/**
* Registra os serviços da aplicação.
*/
public function register(): void
{
// Mescla configuração do pacote
$this->mergeConfigFrom(
__DIR__.'/../config/package.php', 'package'
);
// Registra serviços do pacote
$this->app->singleton('package', function ($app) {
return new Package($app['config']['package']);
});
}
}
Testando Provedores de Serviços
namespace Tests\Unit\Providers;
use Tests\TestCase;
use App\Providers\CustomServiceProvider;
use App\Services\CustomService;
class CustomServiceProviderTest extends TestCase
{
public function test_service_is_registered()
{
$this->assertInstanceOf(
CustomService::class,
$this->app->make(CustomService::class)
);
}
public function test_bindings_are_correct()
{
$this->app->register(CustomServiceProvider::class);
$this->assertTrue($this->app->bound('custom.service'));
$this->assertSame(
$this->app->make('custom.service'),
$this->app->make('custom.service')
);
}
public function test_configuration_is_published()
{
$provider = new CustomServiceProvider($this->app);
$this->assertArrayHasKey('config', $provider->pathsToPublish());
}
}
Melhores Práticas
- Mantenha provedores focados: Cada provedor deve ter uma única responsabilidade
- Use register apenas para vinculações: Não acesse outros serviços no register
- Adie quando possível: Use provedores adiados para melhor performance
- Documente dependências: Indique claramente quais serviços seu provedor requer
- Teste provedores: Escreva testes para garantir que os provedores funcionem corretamente
- Use configuração: Permita que usuários configurem seu serviço através de arquivos de configuração
- Siga convenções: Use padrões de nomenclatura e organização padrão
- Evite operações pesadas: Não execute operações caras nos provedores