LENKRAD - The modern PHP core
Basics
Advanced
Deployment

Dependency injection container

Lenkrad uses auto-wiring to enable dependency injection in a convenient way.

Concept

The auto-wiring loading mechanism uses existing instances of injected classes or creates a new instance if the current controller is the first member using the class. Some classes are already instantiated by the app itself (e.g. Injections, Setup, NeoanApp).

Using the DI-Container

You can automatically inject any class adhering to the following:

  • Has an invoke-method
  • Invoke-method returns instance of the class itself (singleton)

Example


            ?php

            namespace App\MyRoute;

            use Auth;
            use Neoan\NeoanApp;
            use App\Middleware\RequiresAuth;

            class MyRoute implements Routable
            {

                // simply define your required injections
                public function __invoke(NeoanApp $app, RequiresAuth $authenticated): array
                {
                    // and use them as you please
                    return [
                        'web-path' => $app->webPath,
                        'current-user' => $authenticated->user
                    ];
                }
            }
        

Manual registration

If you want/need to ensure the existence of an instance and it's properties, you can use the injection provider directly:


            ?php

            use Neoan\NeoanApp;

            ...

            $app = new NeoanApp($setup);
            $app->injectionProvider->set(App\MyClass, new App\MyClass())

            ...

        

Creating an injectable class

To inspire your creativity, let's look at some examples.

Example 1: Auth


            ?php

            namespace App\Middleware;

            use Neoan\Routing\Interfaces\Routable;
            // using https://packagist.org/packages/neoan3-apps/stateless
            use Neoan3\Apps\Stateless;


            class Auth implements Routable
            {

                private ?array $auth;

                public function __invoke(): self
                {
                    try{
                        $this->auth = Stateless::validate();
                    } catch (\Exception $e) {
                        $this->auth = null;
                    }
                    return $this;
                }

                public function isLoggedIn(): bool
                {
                    return (bool) $this->auth;
                }

                public function getUserId()
                {
                    return $this->auth['id'] ?? null;
                }

            }

        

Example 2: Making a model injectable (this is esoteric!)


            ?php

            namespace App\User;

            use Neoan\Model\Attributes\IsPrimaryKey;
            use Neoan\Model\Model;

            class User extends Model
            {
                #[IsPrimaryKey]
                public int $id;

                ...

                public function __invoke(Auth $auth): static
                {
                    $this->rehydrate($auth->getUserId())
                    return $this;
                }

            }

        

Injecting vs. Middleware

In these examples, one can see that auto-wiring injections often makes the use of chaining middleware in routes redundant. Choosing which path to take is not only a question of preference, however. One should take into consideration the project necessities and structure as a whole. To help you make that decision, we recommend the following (non-binding) rule:

Are values passed on?

Injection is probably the easier approach

Is the class conditional towards execution?

Middleware is probably the easier approach

Is both the case?

Design your class to be used as both and use it simultaneously as middleware and DI

Before you move on

Many references on this page assume default settings. Your project might differ in behavior, paths etc.