# Service Container - [Introduction](#introduction) - [Binding Services](#binding-services) - [Simple Bindings](#simple-bindings) - [Singletons](#singletons) - [Resolving Services](#resolving-services) - [Automatic Resolution](#automatic-resolution) - [Using Controllers](#using-controllers) ## Introduction The Magic service container is a powerful tool for managing class dependencies and performing dependency injection. Like Laravel's container, it allows you to register bindings and resolve them throughout your application. ```dart // Register a service Magic.bind('mailer', () => MailService()); // Resolve it anywhere final mailer = Magic.make('mailer'); ``` ## Binding Services ### Simple Bindings Use `Magic.bind()` to register a service. Each resolve creates a new instance: ```dart Magic.bind('api', () => ApiClient()); // Each call creates a new instance final api1 = Magic.make('api'); final api2 = Magic.make('api'); // api1 != api2 ``` ### Singletons Use `Magic.singleton()` to register a service that's instantiated only once: ```dart Magic.singleton('cache', () => CacheManager()); // Always returns the same instance final cache1 = Magic.make('cache'); final cache2 = Magic.make('cache'); // cache1 == cache2 ``` ### Binding in Service Providers The recommended way to register services is in a service provider: ```dart class AppServiceProvider extends ServiceProvider { @override void register() { // Register services app.bind('api', () => ApiClient(Config.get('api.url'))); app.singleton('cache', () => CacheManager()); } @override Future boot() async { // Perform initialization after all services are registered final cache = app.make('cache'); await cache.initialize(); } } ``` ## Resolving Services ### Using Magic.make() ```dart // With type inference final service = Magic.make('myservice'); // Without type (returns dynamic) final service = Magic.make('myservice'); ``` ### Checking Existence ```dart if (Magic.bound('api')) { final api = Magic.make('api'); } ``` ## Automatic Resolution Magic facades automatically resolve their underlying services. You don't need to manually resolve common services: ```dart // These facades auto-resolve from the container: Cache.get('key'); // Resolves CacheManager Auth.check(); // Resolves AuthManager Lang.setLocale(locale); // Resolves LocalizationService ``` ## Using Controllers Magic provides a convenient way to manage controller instances: ### findOrPut Pattern The recommended pattern for controllers is using `Magic.findOrPut()`: ```dart class UserController extends MagicController with MagicStateMixin> { // Singleton accessor static UserController get instance => Magic.findOrPut(UserController.new); // Controller logic... } // Usage anywhere in your app final controller = UserController.instance; ``` ### How It Works 1. **First call**: Creates a new controller instance and stores it 2. **Subsequent calls**: Returns the existing instance 3. **Automatic cleanup**: Controller's `onClose()` is called when disposed ### Manual Controller Management ```dart // Register a controller Magic.put(UserController()); // Find an existing controller final controller = Magic.find(); // Delete a controller Magic.delete(); ``` ### Controller Lifecycle ```dart class UserController extends MagicController { @override void onInit() { // Called when controller is first created loadUsers(); } @override void onClose() { // Called when controller is disposed // Clean up subscriptions, streams, etc. } } ``` > [!TIP] > Use the `findOrPut` pattern for controllers to ensure a single instance is used throughout your application while maintaining proper lifecycle management.