Slide 1

Slide 1 text

MASTERING the ILLUMINATE CONTAINER

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

WHAT IS THE CONTAINER?

Slide 4

Slide 4 text

WHAT IS THE CONTAINER? WHAT IS LARAVEL?

Slide 5

Slide 5 text

WHAT IS THE CONTAINER? THE CONTAINER IS THE BACKBONE OF EVERY LARAVEL APPLICATION.

Slide 6

Slide 6 text

Routing. Middleware. Requests. Constructor injection. Facades.

Slide 7

Slide 7 text

OK, IT'S ✨Magic✨... BUT WHAT DOES IT DO?

Slide 8

Slide 8 text

NAMING THE CONTAINER A container by any other name... ▸ The container ▸ Application ▸ IOC Container ▸ DI Container

Slide 9

Slide 9 text

WHAT DO "DI" and "IOC" MEAN?

Slide 10

Slide 10 text

Dependency Injection: When one object supplies ("injects") the dependencies of another object.

Slide 11

Slide 11 text

Inversion of control: Defining (once) at the framework level how to implement specific features or code paths instead of (multiple times) in the code.

Slide 12

Slide 12 text

So if the container enables DI and IOC...

Slide 13

Slide 13 text

THE CONTAINER IS THE glue

Slide 14

Slide 14 text

A QUICK INTRODUCTION TO BINDING and RESOLVING WITH THE CONTAINER

Slide 15

Slide 15 text

Getting an instance of the container: $container = app();

Slide 16

Slide 16 text

Resolving an instance out of the container: $container = app(); $logger = $container->make(Illuminate\Log\Writer::class); // or $logger = app()->make(Illuminate\Log\Writer::class); drop instance.. Or even shorter... :

Slide 17

Slide 17 text

Shortcut to resolving a instance out of the container: $logger = app(Illuminate\Log\Writer::class);

Slide 18

Slide 18 text

BUT WAIT— WHY IS THIS ANY BETTER THAN new Thing?

Slide 19

Slide 19 text

Slide 20

Slide 20 text

class MyClass { public function doThing() { echo 'abc'; } } $instance = new MyClass; $instance->doThing();

Slide 21

Slide 21 text

class MyClass { public function doThing() { echo 'abc'; } } $instance = app(MyClass::class); $instance->doThing(); At this point not actually providing any value. But...

Slide 22

Slide 22 text

WHAT ABOUT DEPENDENCIES?

Slide 23

Slide 23 text

class MyClass { protected $otherThing; public function __construct(OtherThing $otherThing) { $this->otherThing = $otherThing; } } $instance = new MyClass;

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

class MyClass { protected $otherThing; public function __construct(OtherThing $otherThing) { $this->otherThing = $otherThing; } } $instance = new MyClass(new OtherThing);

Slide 26

Slide 26 text

class MyClass { protected $otherThing; public function __construct(OtherThing $otherThing) { $this->otherThing = $otherThing; } } $instance = app(MyClass::class); // No error!

Slide 27

Slide 27 text

AUTOWIRING

Slide 28

Slide 28 text

AUTOWIRING A framework/package's ability to instantiate a class without being given explicit instructions from the user of how to instantiate that class. If a class has dependencies, an autowiring framework will use reflection to determine and provide its dependencies.

Slide 29

Slide 29 text

getConstructorDependenciesFor($key); return new $key(... $dependencies); } } It now uses reflection to figure out the constructor dependencies.

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

WHAT IF I DON'T KNOW IT'S A TURTLE? (What if the dependencies aren't typehinted?)

Slide 32

Slide 32 text

apiKey = $apiKey; } } $service = app(MyService::class);

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

MANUAL BINDING

Slide 35

Slide 35 text

Types of manual binding: ▸ bind ▸ singleton ▸ instance ▸ alias

Slide 36

Slide 36 text

BIND // Binding a class to the container app()->bind(MyService::class, function () { return new MyService('0h5oh1kn09da'); }); // Now, we can use it: $service = app(MyService::class);

Slide 37

Slide 37 text

USING $app WITHIN A BINDING CLOSURE class MyService { public function __construct(Thing $thing, Other $other, $apiKey) { ... } } app()->bind(MyService::class, function ($app) { return new MyService( $app->make(Thing::class), $app->make(Other::class), 'api1o2n34' ); });

Slide 38

Slide 38 text

BIND (AND AUTOWIRING) GENERATES NEW INSTANCES id = str_random(); } } $one = app(OnlyWantOne::class); $two = app(OnlyWantOne::class); dd($one->id, $two->id); // "metkZ7bRlSjEv8rP", "2ufdDRgcjep9DEFL"

Slide 39

Slide 39 text

BIND (AND AUTOWIRING) GENERATES NEW INSTANCES bind(OnlyWantOne::class, function () { return new OnlyWantOne; }); $one = app(OnlyWantOne::class); $two = app(OnlyWantOne::class); dd($one->id, $two->id); // "CtjkZ1b91jKf68Fq", "4B3fDmUcA5p9DnPz"

Slide 40

Slide 40 text

SINGLETON RUNS ONLY ONCE singleton(OnlyWantOne::class, function () { return new OnlyWantOne; }); $one = app(OnlyWantOne::class); $two = app(OnlyWantOne::class); dd($one->id, $two->id); // "RvSk6APVA2d8C3Yx", "RvSk6APVA2d8C3Yx"

Slide 41

Slide 41 text

INSTANCE ALWAYS RETURNS THE GIVEN INSTANCE instance(OnlyWantOne::class, $original); $one = app(OnlyWantOne::class); $two = app(OnlyWantOne::class); dd($one->id, $two->id, $original->id); // "zLgaLFnW7AD5ZIGM", "zLgaLFnW7AD5ZIGM", "zLgaLFnW7AD5ZIGM"

Slide 42

Slide 42 text

ALIAS BINDS A STRING KEY TO ANOTHER STRING KEY app()->alias(OnlyWantOne::class, 'owo'); $one = app('owo'); echo get_class($one); // OnlyWantOne

Slide 43

Slide 43 text

ALIAS FREEDOM We can really bind to any arbitrary string. Therefore interfaces are bindable too. app()->alias( PostmarkMailer::class, Mailer::class // Interface ); app()->alias( MagicService::class, 'my-favorite-interface-thing-or-whatever' // silly string );

Slide 44

Slide 44 text

LARAVEL'S SHORT ALIASES app('logger'); // Same as app(Illuminate\Log\Writer::class);

Slide 45

Slide 45 text

LARAVEL'S SHORT ALIASES (CONTINUED)

Slide 46

Slide 46 text

LARAVEL'S SHORT ALIASES (CONTINUED) Each alias is pointing to a real class instantiated to a shortcut. // Illuminate/Foundation/Application public function registerCoreContainerAliases() { $aliases = [ 'app' => [ \Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class ], // ... ]; }

Slide 47

Slide 47 text

COMMON USES of AUTOMATIC DEPENDENCY INJECTION ▸ Requesting classes from the framework (like Request, Logger, Mailer) in controllers and other user-facing code ▸ Route model binding ▸ Form Requests ▸ Relying on constructor injection more confidently in custom classes

Slide 48

Slide 48 text

REQUESTING CLASSES FROM THE FRAMEWORK (LIKE REQUEST, LOGGER, MAILER) IN CONTROLLERS AND OTHER USER-FACING CODE // PostsController public function store(Request $request) { $this->validate($request, [ ... ]); Post::create($request->only(['title', 'body'])); return back(); }

Slide 49

Slide 49 text

ROUTE MODEL BINDING Route::get('/posts/{post}', function (Post $post) { return $post; });

Slide 50

Slide 50 text

FORM REQUESTS // PostsController public function update(UpdatePost $request) { // do stuff } // UpdatePost public function rules() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; }

Slide 51

Slide 51 text

RELYING ON CONSTRUCTOR INJECTION MORE CONFIDENTLY IN CUSTOM CLASSES

Slide 52

Slide 52 text

BONUS GOODY: CALLING METHODS app()->call([$object, $method]); app()->call('ClassName@method'); // e.g. app()->call([$this, 'doThingWithDependencies']); app()->call('Synchronizer@activate'); app()->call('SuperClient@get', ['/posts/2']);

Slide 53

Slide 53 text

SERVICE PROVIDERS

Slide 54

Slide 54 text

SERVICE PROVIDERS: WHAT ARE THEY? Central location to bind services for use by your application's code, usually grouped by functionality; the place where all of Laravel's bootstrapping takes place.

Slide 55

Slide 55 text

THE ANATOMY OF A SERVICE PROVIDER ▸ register: binding into the container ▸ boot: called after all registrations are complete; everything else here (registering event listeners, routes, or any other functionality) ▸ defer?: can we avoid running this service provider if certain bindings aren't requested? ▸ provides: if deferred, which bindings' requests should trigger running this service provider?

Slide 56

Slide 56 text

SERVICE PROVIDERS (AN EXAMPLE) class BroadcastServiceProvider extends ServiceProvider { protected $defer = true; public function register() { $this->app->singleton(BroadcastManager::class, ...); $this->app->singleton(BroadcasterContract::class, ...); $this->app->alias(BroadcastManager::class, BroadcastingFactory::class); } public function provides() { return [ BroadcastManager::class, BroadcastingFactory::class, BroadcasterContract::class, ]; } }

Slide 57

Slide 57 text

ASIDE: APP SERVICE PROVIDER

Slide 58

Slide 58 text

PACKAGES and SERVICE PROVIDERS

Slide 59

Slide 59 text

Many packages say to add two things in config/app.php, right? 1. service provider, 2. alias

Slide 60

Slide 60 text

transition time...

Slide 61

Slide 61 text

FACADES

Slide 62

Slide 62 text

!FACADES"

Slide 63

Slide 63 text

FACADES

Slide 64

Slide 64 text

INTERLUDE: FACADE DRAMA

Slide 65

Slide 65 text

FACADES Route::get(); Log::info(); DB::statement(); Auth::user();

Slide 66

Slide 66 text

WHAT IS A FACADE? A convenience class providing static access to non-static methods on classes resolved out of the container.

Slide 67

Slide 67 text

WHAT DEFINES A FACADE? class Cache extends Facade { protected static function getFacadeAccessor() { return 'cache'; } } Cache::get(); // same as: app('cache')->get();

Slide 68

Slide 68 text

CREATING YOUR OWN FACADE 1. Bind your class to the container 2. Define the facade class and point it to your bound class name 3. Profit, ostensibly

Slide 69

Slide 69 text

CREATING YOUR OWN FACADE (CONTINUED) Bind your class to the container app()->bind('twitter', function () { // ... });

Slide 70

Slide 70 text

CREATING YOUR OWN FACADE (CONTINUED) Define the facade class and point it to your bound class name class Twitter extends Facade { protected static function getFacadeAccessor() { return 'twitter'; } }

Slide 71

Slide 71 text

CREATING YOUR OWN FACADE (CONTINUED) Profit, ostensibly !

Slide 72

Slide 72 text

REAL TIME FACADES Prepend Facades\ to your namespace when you import your class and now use it as a Facade backed by that class. class Thing { public function do() { // ... } } Facades\Thing::do();

Slide 73

Slide 73 text

TESTING and the CONTAINER

Slide 74

Slide 74 text

TESTING AND THE CONTAINER Dependency injection aids tests; the container aids DI. Swap in a mock or a spy easily. // in code Route::get('whatever', function (MyService $service) { return $service->get('whatever'); }); // in test $mock = Mockery::mock('MyService'); app()->instance('MyService', $mock);

Slide 75

Slide 75 text

TESTING AND THE CONTAINER (CONTINUED) It works for Facades too: // in code Route::get('whatever', function () { return Twitter::post('here is my status!'); }); // in test $mock = Mockery::mock(MyTwitterClient::class); app()->instance('twitter', $mock);

Slide 76

Slide 76 text

FACADE-SPECIFIC MOCKING AND SPIES Cache::shouldReceive('get') ->with('key') ->andReturn('value');

Slide 77

Slide 77 text

MISCELLANY Binding arbitrary values app()->bind('countries', function () { return [ 'Afghanistan', // ... ]; }); dd(app('countries'));

Slide 78

Slide 78 text

MISCELLANY Array-access $app = app(); // or, more likely, injected $logger = $app['logger']; $app['mailer'] = new MailgunMailer(...); $app['countries'] = ['Afghanistan'];

Slide 79

Slide 79 text

MISCELLANY Contextual binding $app->when(Newsletter::class) ->needs(Mailer::class) ->give(MailgunMailer::class); $app->when(Notifier::class) ->needs(Mailer::class) ->give(PostmarkMailer::class);

Slide 80

Slide 80 text

CONCLUSION