Skip to content

Commit

Permalink
Merge pull request #127 from StrykeSlammerII/5.0
Browse files Browse the repository at this point in the history
Chapter 7 proofreading
  • Loading branch information
lcharette authored Jan 25, 2024
2 parents ca953ff + a3a01f2 commit d2bed6c
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 45 deletions.
2 changes: 1 addition & 1 deletion pages/06.sprinkles/03.recipe/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The Sprinkle Recipe dictates how your sprinkle is built, like a blueprint. UserF

Each sprinkle **must have** a recipe. It's not possible for a sprinkle to exist without a recipe, as it won't be possible to expose its class and service to the framework. It's possible however to customize other sprinkles, as we'll see later on this page.

## The Sprinkle Recipe
## The `SprinkleRecipe` Interface

The Sprinkle Recipe is a simple PHP class that provides standard methods which will be called by services to retrieve information about your sprinkle structure and the class it's registering. Every sprinkle recipe **MUST** implement the `UserFrosting\Sprinkle\SprinkleRecipe` interface. If you started from the [Skeleton](/structure/introduction#the-app-skeleton-your-project-s-template), you already have a basic recipe.

Expand Down
8 changes: 4 additions & 4 deletions pages/07.dependency-injection/01.concept/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $owl = new Owl();

This might seem like a nice, convenient way of packaging things - after all, `Nest` seems like an implementation detail that we shouldn't have to worry about. However, what happens if we come along later with an `ImprovedNest`, and we want our `Owl` to use that instead?

Unfortunately we can't. Our classes `Owl` and `Nest` are what we would call **tightly coupled** - Owls can use Nests and _only_ Nests. Dependency injection solves this problem:
Unfortunately, we can't. Our classes `Owl` and `Nest` are what is called **tightly coupled** - Owls can use Nests and _only_ Nests. Dependency injection solves this problem:

```php
class Owl
Expand Down Expand Up @@ -104,10 +104,10 @@ class Owl
}
```

In the above example, it doesn't matter if `Owl` received a `Nest` or an `ImprovedNest`, or even a `SuperDuperNest`, as long as they all obey the same definition defined by the `NestInterface`. Moreover, the Owl class can confidently call the `getSize` method of the injected `$nest` property, because interface make sure that method is available, no matter which implementation of the `NestInterface` it receive.
In the above example, it doesn't matter if `Owl` received a `Nest` or an `ImprovedNest`, or even a `SuperDuperNest`, as long as they all obey the same definition defined by the `NestInterface`. Moreover, the Owl class can confidently call the `getSize` method of the injected `$nest` property, because the interface makes sure that method is available, no matter which implementation of the `NestInterface` it receives.

Using interfaces to declare what kind of object a class is expected to receive, even if you don't plan to have multiple "nests" types, is a key element in *Autowiring* that we'll see shortly.
Using interfaces to declare what kind of object a class is expected to receive, even if you don't plan to have multiple "nest" types, is a key element in *Autowiring* that we'll see shortly.

This is of course a contrived example, but the general strategy of keeping your classes loosely coupled is a good way to make your code more reusable and easily tested.

[notice=tip]You can learn more, and see other examples on the [PHP-DI Website : Understanding Dependency Injection](https://php-di.org/doc/understanding-di.html).[/notice]
[notice=tip]You can learn more, and see other examples, on the [PHP-DI Website : Understanding Dependency Injection](https://php-di.org/doc/understanding-di.html).[/notice]
30 changes: 15 additions & 15 deletions pages/07.dependency-injection/02.the-di-container/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Three main steps are required to create the object:
2. The `$handler` object requires a `$formatter` object, which we inject using `setFormatter()`;
3. The `$handler` object also requires the path to the log file.

This is a lot of code to write just to create one measly object! It would be great if we could somehow encapsulate the creation of the object, but without creating tight couplings by doing that within the object itself.
This is a lot of code to write just to create one measly object! It would be great if we could somehow encapsulate the creation of the object without creating tight couplings within the object itself.

This is where the **dependency injection container (DIC)** comes into play. The DIC handles basic management of dependencies, encapsulating their creation into simple callbacks. We will call these callbacks **services**.

Expand All @@ -39,19 +39,19 @@ This is where the **dependency injection container (DIC)** comes into play. The
1. dependency injection is a method for writing better code
2. a container is a tool to help injecting dependencies

You don't need a container to do dependency injection. However a container can help you.
You don't need a container to do dependency injection. However, a container can make injections easier.
[/notice]

UserFrosting uses [_PHP-DI 7_](https://php-di.org) has it's DIC implementation since it provides many powerful features that we rely on:
UserFrosting uses [_PHP-DI 7_](https://php-di.org) as it's DIC implementation since it provides many powerful features that we rely on:

1. It creates dependencies lazily ("on demand"). Any service (and its dependencies) won't be created until the first time I actually try to access them.
1. It creates dependencies lazily ("on demand"). Any service (and its dependencies) won't be created until the first time we access them.
2. Once an object has been created in the container, the same object is returned in each subsequent call to the container.
3. It has the ability to automatically create and inject dependencies.
4. It has powerful Slim 4 integration.
4. It has powerful Slim 4 integration.

Taken together, this means we can define our services without needing to worry about when and where their dependencies are created in our application's lifecycle.
Taken together, we can define our services without needing to worry about when and where their dependencies are created in our application's lifecycle.

[notice=note]When we talk about services, this might bring to mind an anti-pattern called the **Service Locator Pattern**. It is true that the DIC _can_ be used as a service locator, especially if you inject the entire container into your objects. With the exception of Models and a few other types of classes that have a very large number of dependencies, we try to avoid implementing the Service Locator Pattern whenever possible.[/notice]
[notice=note]When we talk about services, this might bring to mind an anti-pattern called the **Service Locator Pattern**. It is true that the DIC _can_ be used as a service locator, especially if you inject the entire container into your objects. With the exception of Models and a few other classes with very large numbers of dependencies, we try to avoid implementing the Service Locator Pattern whenever possible.[/notice]

### Autowiring

Expand All @@ -72,7 +72,7 @@ class Owl
}
```

When using _PHP-DI_ to create an Owl, the container detects that the constructor takes a `Nest` object (using the [type declarations](http://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration)). Without any configuration, **and as long as the constructor argument is properly typed**, PHP-DI will create an `Nest` instance (if it wasn't already created) and pass it as a constructor parameter. The equivalent code would now be :
When using _PHP-DI_ to create an Owl, the container detects that the constructor takes a `Nest` object (using [type declarations](http://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration)). Without any configuration, **and as long as the constructor argument is properly typed**, PHP-DI will create an `Nest` instance (if it wasn't already created) and pass it as a constructor parameter. The equivalent code would now be :

```php
$owl = $container->get(Owl::class);
Expand All @@ -86,9 +86,9 @@ It's very simple, doesn't require any configuration, and it just works !

### Service Providers & Definitions

Sometimes classes might be a bit more complex to instantiate, especially third party ones (eg. the logger object from before). Or you might want to use a different class based on some configuration value. You might also want a class to be replaced by another one (eg. our `ImprovedNest`). In theses cases, autowiring cannot be used. This is where PHP-DI **definition** comes handy. PHP-DI loads the definitions you have written and uses them like instructions on how to create objects.
Sometimes classes might be a bit more complex to instantiate, especially third party ones (eg. the logger object from before). Or you might want to use a different class based on some configuration value. You might also want a class to be replaced by another one (eg. our `ImprovedNest`). In these cases, autowiring cannot be used. This is where PHP-DI **definition** comes handy. PHP-DI loads the definitions you have written and uses them like instructions on how to create objects.

UserFrosting sets up its services through **service provider** classes. Each Sprinkle can define as many service provider it need and register them in their [Recipe](/dependency-injection/adding-services). For example, the Services Provider class for the previous `Logger` example would look like this:
UserFrosting sets up its services through **service provider** classes. Each sprinkle can define as many service providers as it needs and register them in the [Recipe](/dependency-injection/adding-services). For example, the Services Provider class for the previous `Logger` example would look like this:

```php
use Monolog\Formatter\LineFormatter;
Expand Down Expand Up @@ -120,17 +120,17 @@ class LoggerServicesProvider implements ServicesProviderInterface
}
```

This definitions uses [PHP-DI factories](https://php-di.org/doc/php-definitions.html#factories) syntax. From the PHP-DI documentation:
This definition uses the [PHP-DI factories](https://php-di.org/doc/php-definitions.html#factories) syntax. From the PHP-DI documentation:

> Factories are PHP callables that return the instance. They allow to easily define objects lazily, i.e. each object will be created only when actually needed (because the callable will be called when actually needed).
>
> Just like any other definition, factories are called once and the same result is returned every time the factory needs to be resolved.
>
> Other services can be injected via type-hinting (as long as they are registered in the container or autowiring is enabled).
You'll notice that the callable used to create a `Logger` object takes two parameters, `StreamHandler` and `LineFormatter`. This allows us to inject theses services inside this definition. When `Logger` is created (or injected), both `StreamHandler` and `LineFormatter` will be injected using their own definition.
You'll notice that the callable used to create a `Logger` object takes two parameters: `StreamHandler` and `LineFormatter`. This allows us to inject these services inside the definition. When `Logger` is created (or injected), both `StreamHandler` and `LineFormatter` will be injected using their own definition.

[notice=note]The `LineFormatter` definition is different. It uses the [object syntax](https://php-di.org/doc/php-definitions.html#objects) instead of the _Factories_ syntax.[/notice]
[notice=note]The `LineFormatter` definition is different. It uses the [object syntax](https://php-di.org/doc/php-definitions.html#objects) instead of the _factories_ syntax.[/notice]

[notice]You can learn more about PHP Definitions in the [PHP-DI Documentation](https://php-di.org/doc/php-definitions.html#definition-types)[/notice]

Expand Down Expand Up @@ -165,7 +165,7 @@ return [
];
```

But why are interface really needed? If `ImprovedNest` extends `Nest`, wouldn't the constructor accept an `ImprovedNest` anyway if you typed-hinted against `Nest`? Well, yes... But it won't work the other way around. For example :
But why are interfaces really needed? If `ImprovedNest` extends `Nest`, wouldn't the constructor accept an `ImprovedNest` anyway if you type-hinted against `Nest`? Well, yes... But it won't work the other way around. For example :


```php
Expand Down Expand Up @@ -196,4 +196,4 @@ $test = new AcceptImprovedNest($nest); // Throws TypeError Exception, Nest is no

[notice=info]In most cases it's considered "best practice" to type-hint against interfaces, unless you explicitly required a specific class to fit a very specific need, said class is very basic and it's not worth it, or you don't plan on ever extending or distributing your code.[/notice]

The next page shows a small list the **default services** that ship with UserFrosting, as well as tips and trick to replace. After that, we talk about how you can **add** your own services, **extend** existing services, or completely **replace** certain services in your own Sprinkle.
The next page shows a small list of the **default services** that ship with UserFrosting, as well as tips for using them. After that, we talk about how you can **add** your own services, **extend** existing services, or completely **replace** certain services in your own sprinkle.
Loading

0 comments on commit d2bed6c

Please sign in to comment.