Dorokhov.codes

01. About Laravel

I will try to explain how Laravel works and its lifecycle.

laravel-lifecycle.jpg

The entry point for all requests to a Laravel application is the public/index.php file.

The index.php file loads the Composer autoloader:

require __DIR__.'/../vendor/autoload.php';

And then retrieves an instance of the Laravel application from bootstrap/app.php.

$app = require_once __DIR__.'/../bootstrap/app.php';

Http or Console

Think of the Console and Http directories as providing an API into the core of your application. The HTTP protocol and CLI are both mechanisms to interact with your application, but do not actually contain application logic. In other words, they are two ways of issuing commands to your application. The Console directory contains all of your Artisan commands, while the Http directory contains your controllers, middleware, and requests.

Here’s how the instance is created:

$app = new Illuminate\Foundation\Application( ... );
$app->singleton( ... );
$app->singleton( ... );
$app->singleton( ... );
return $app;

The $app instance is in fact a service container. It’s a powerful tool for managing class dependencies and performing dependency injection. Let’s see how we can use this service container.

Service container

The bind method is used to bind an abstract type (interface or abstract class) to a concrete implementation.

$app->bind(AbstractType::class, ConcreteImplementation::class);

You can bind a class name directly to itself, and Laravel will automatically resolve it when needed.

$app->bind(ConcreteClass::class);

The singleton method binds an abstract type to a concrete implementation and ensures that only one instance of the concrete class is created and reused.

$app->singleton(AbstractType::class, ConcreteImplementation::class);

Now it’s very easy to resolve dependencies (automatic dependency resolution):

class SomeClass {
    public function __construct(Dependency $dependency) {
        // Laravel resolves Dependency class here.
    }
}

You can manually resolve a class from the container using the make method.

$instance = $app->make(SomeClass::class);

Service providers

A service provider is a class that binds services (PHP classes) into the Laravel service container, making them available to the application. It doesn’t contain any service logic. Its task is to make bindings between interfaces and implementations.

One service provider can add as many services as needed, so we can have only one service provider for the whole application.

All service providers extend the Illuminate\Support\ServiceProvider class.

Let’s create a service provider. For example, we have some interface:

// app/Contracts/Logger.php

namespace App\Contracts;

interface Logger {
    public function log($message);
}

And we have an implementation:

// app/Services/FileLogger.php

namespace App\Services;

use App\Contracts\Logger;

class FileLogger implements Logger {
    public function log($message) {
        // Simulate logging to a file
        file_put_contents(storage_path('logs/app.log'), $message . PHP_EOL, FILE_APPEND);
    }
}

Now we can create a file for our service provider:

php artisan make:provider LoggerServiceProvider

And now we can add the service to the service container:

// app/Providers/LoggerServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\Logger;
use App\Services\FileLogger;

class LoggerServiceProvider extends ServiceProvider {
    /**
     * Register services.
     *
     * @return void
     */
    public function register() {
        // Binding the Logger interface to the FileLogger implementation
        $this->app->bind(Logger::class, FileLogger::class);
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot() {
        // Additional bootstrapping tasks, if needed
    }
}

Then we need to register the service provider:

// config/app.php

'providers' => [
    // Other providers...

    App\Providers\LoggerServiceProvider::class,
],

Now, you have a simple service provider that binds the Logger interface to the FileLogger implementation. You can use dependency injection to access the logger throughout your application.

// Example usage in a controller or another class
use App\Contracts\Logger;

public function logMessage(Logger $logger) {
    $logger->log('This message will be logged.');
    return 'Message logged successfully!';
}

Core files

The Laravel framework is built using the Illuminate library components.

Class Description
Illuminate\Foundation\Application Represents the Laravel application instance.
Illuminate\Foundation\Bootstrap\BootstrapServiceProvider Bootstraps the application and its components.
Illuminate\Foundation\Console\Kernel Handles console commands.
Illuminate\Foundation\Http\Kernel Handles HTTP requests and middleware.
Illuminate\Foundation\ProviderRepository Manages service providers.
Illuminate\Foundation\ComposerScripts Scripts executed during Composer events.
Illuminate\Foundation\ApplicationEvents Defines events related to the application’s lifecycle.
Illuminate\Database\Eloquent\Model Base class for Eloquent models.
Illuminate\Database\Query\Builder Query Builder for constructing database queries.
Illuminate\Routing\Router Handles route definition and request handling.
Illuminate\Routing\Controller Base class for controllers.
Illuminate\Http\Request Represents an HTTP request.
Illuminate\Http\Response Represents an HTTP response.
Illuminate\Http\JsonResponse Specialized class for JSON responses.
Illuminate\Events\Dispatcher Manages events and event listeners.
Illuminate\Events\Listener Base class for event listeners.
Illuminate\Validation\Validator Performs data validation.
Illuminate\Validation\Rule Pre-defined validation rules.
Illuminate\Cache\CacheManager Manages various cache stores.
Illuminate\Cache\Repository Represents a cache store.
Illuminate\Filesystem\Filesystem Unified interface for interacting with the filesystem.
Illuminate\Config\Repository Manages configuration settings.
Illuminate\Session\SessionManager Manages session storage and retrieval.
Illuminate\Session\Store Represents a session store.
Illuminate\Translation\Translator Translates messages in the application.

Kernels

Next, the incoming request is sent to either the HTTP kernel or the console kernel, depending on the type of request that is entering the application.

app/Http/Kernel.php
app/Console/Kernel.php

The HTTP kernel extends the Illuminate\Foundation\Http\Kernel class, which defines an array of bootstrappers that will be run before the request is executed. These bootstrappers configure error handling, configure logging, detect the application environment, and perform other tasks that need to be done before the request is actually handled.

It receives a Request and returns a Response. Think of the kernel as being a big black box that represents your entire application. Feed it HTTP requests and it will return HTTP responses.

Let’s take a look at the public/index.php file:

$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Kernel::class);

$response = $kernel->handle(
    $request = Request::capture()
)->send();

$kernel->terminate($request, $response);

Middlewares

Middlewares allow us to perform actions before and after the HTTP request enters the controller.

Middlewares are typically defined in the app/Http/Middleware directory.

Middleware structure:

// Example Middleware
namespace App\Http\Middleware;

use Closure;

class MyMiddleware {
    public function handle($request, Closure $next) {
        // Perform actions before the request reaches the controller
        return $next($request);
    }

    public function terminate($request, $response) {
        // Perform actions after the response is sent
    }
}

A middleware is defined in the $middleware property of the Kernel class (app/Http/Kernel.php).

// Example in Kernel.php
protected $middleware = [
    // ...
    \App\Http\Middleware\MyMiddleware::class,
];

Views

Views separate your controller logic from your presentation logic and are stored in the resources/views directory.

Since this view is stored at resources/views/greeting.blade.php, we may return it using the global view helper like so:

Route::get('/', function () {
    return view('greeting', ['name' => 'James']);
});

Views may also be returned using the View facade:

use Illuminate\Support\Facades\View;
 
return View::make('greeting', ['name' => 'James']);

There’s another interesting thing called a view composer. If you have common logic that needs to be shared across multiple views, a view composer allows you to encapsulate that logic in a single location. The data provided by the view composer is automatically bound to the view each time it is rendered, reducing the need to pass data explicitly from controllers.

So if we see that we write the same code for different controllers and views, we can reduce it using a view composer.

A view composer typically resides in a service provider’s boot method.

View::composer('welcome', function ($view) {
    $view->with('key', 'value');
});

Multiple views:

View::composer(
    ['profile', 'dashboard'],
    function ($view) { ... }
);

The composer method also accepts the * character as a wildcard, allowing you to attach a composer to all views:

use Illuminate\Support\Facades;
use Illuminate\View\View;
 
Facades\View::composer('*', function (View $view) {
    // ...
});
<!-- Example in a Blade view -->
<p>{{ $key }}</p>