12. Conclusion
This document has presented the development of a MVC web application using the [Laravel] framework: an application whose HTML pages are generated by the server, using [Blade] views, without a browser-side [JavaScript] framework. He followed the [Flask] course step by step, which itself builds on the [Spring MVC], [ASP.NET Core MVC], and [NestJS] courses: actions and routing to views, forms, internationalization, security, and data access, followed by a comprehensive case study.
The same 28 examples and the same application were thus written using five frameworks: in [TypeScript], in [C#], in [Java], in [Python], and in [PHP]. The concepts are the same; only the names and mechanisms differ. The correspondences between [Flask] and [Laravel]:
[Flask] | [Laravel] |
manufacturer [create_app], items stored in [app.extensions] | [bootstrap/app.php], service providers, container ([$singletons], injected by the manufacturer) |
[requirements.txt], virtual environment, [pip] | [composer.json], [composer.lock], folder [vendor], [Composer] |
[config.py], variables [FLASK_...] ([app.config]) | file [.env], folder [config], functions [env] and [config] |
blueprints, [@bp.get], [@bp.post] | [routes/web.php], controllers, named routes, route groups |
[render_template], [redirect], [url_for] | [view], [redirect], [route] |
[request.args], [request.form], converters ([<int:id>]) | object [Request], action parameters, constraints ([whereNumber]), model link |
[Pydantic]; [WTForms]: validators, [form.validate()], [form.errors] | [Validator], form requests ([FormRequest]), rules, [$errors], [old()] |
views [Jinja2], [{{ }}], [{% for %}], [{% if %}] | views [Blade], [{{ }}], [@foreach], [@if] |
[{% extends %}] and blocks, [{% include %}], macros | [@extends] and sections, [@include], components ([<x-...>]) |
context processor ([@app.context_processor]) | view composer ([View::composer]) |
Flash messages ([flash]), in the signed session cookie | session flash data ([session()->flash], [with]), session stored in files or in an encrypted cookie |
[before_request], [after_request], [teardown_request] | middleware (global, group, route), method [terminate] |
[@app.errorhandler] | [withExceptions], [render], [abort] |
[before_request] and hand-written decorators ([PyJWT], [bcrypt]) | guards ([Auth::viaRequest]), middleware [auth], [Gate], facade [Hash] ([firebase/php-jwt]) |
[CSRFProtect] from [Flask-WTF] | [PreventRequestForgery] middleware, [@csrf] directive (419 response) |
dictionaries JSON, handwritten function [t(...)] | folder [lang], function [__()], [App::setLocale] |
[Babel] | [Carbon] ([isoFormat]), [Number] (extension [intl]) |
in-memory dictionaries, [threading.Lock] | cache ([Cache], [Cache::lock]), [RateLimiter] |
[SQLAlchemy]: reporting entities, [select(...)], sessions | [Eloquent]: templates, query builder, relationships ([belongsTo], [with]) |
[with sessions.begin() as session:] | [DB::transaction(...)] |
[version_id_col], managed by [SQLAlchemy] | [WHERE version = ?] and [version + 1], written to the DAO layer |
[python app.py] in debug mode (automatic reloading) | [php artisan serve] (nothing to reload: each request re-reads the code) |
WSGI server: [Waitress], [Gunicorn] | Web server ([nginx], [Apache]) and [PHP-FPM] |
Regarding [Flask], the key points to note are:
- [Laravel] is a full-stack framework: routing, views, validation, sessions, caching, authentication, translation, data access, email sending, task queues... everything is provided, and everything is designed to work together. This is the opposite of the micro-framework [Flask]: much less code to write, but a broader framework to learn, where many things happen “automatically” (sessions, encrypted cookies, anti-CSRF tokens, returning to the form after an input error). The chapter on the request lifecycle and the analysis of requests with [curl] served to make these processes visible;
- the [PHP] application is rebuilt with each request (Example 01): nothing survives in memory from one request to the next. Anything that needs to persist goes into the session (a single user), the cache (all users), the database, or cookies. Conversely, code changes take effect without a restart, and a failed request never affects subsequent ones;
- the service container creates controllers, middleware, and services, and provides them with their dependencies based on their constructor types: dependency injection is at the heart of the framework, as seen in [Spring] or [ASP.NET Core];
- facades ([Cache::], [DB::], [Auth::], [Hash::]...) and global functions ([view], [redirect], [session], [config], [__]...) provide access to services anywhere, in a single line. This is very convenient; it’s also a way to hide a class’s dependencies, which constructor injection makes visible;
- almost everything is middleware: cookies, sessions, anti-CSRF protection, authentication, authorizations, and our own controls (language, roles, pass). The same mechanism is used for all requests, for a group of routes, or for a single one;
- [PHP] is a language with runtime type checking: the declared types of parameters, properties, and results ([int], [?Client], [Collection]) are checked on every call. Since version 8, classes such as [readonly], enumerations, constructor promotion, named arguments, and the null-safe operator ([?->]) have made it an expressive and safe language;
- [Eloquent] follows the Active Record pattern: a model can read and write itself ([Doctor::find(1)], [$doctor->save()]), with very little code. Its queries closely resemble the SQL they generate, and monitoring the queries ([DB::listen]) allows you to verify what is being sent to the database. It is important to consider preloading the relationships ([with]); otherwise, you will quickly run into the “N+1 queries” problem again;
- The [Blade] views are compiled into [PHP] and cached: they are fast, automatically escape the content they display, and their components ([<x-field>]) keep the forms concise.
The case study shows that a classic HTML application, without the essential [JavaScript], remains a simple and robust solution: each page is a URL, the browser’s “Back” button works, and the server controls everything. The limited use of [JavaScript] (confirmation windows) enhances the user experience without being necessary for functionality. [Laravel] can also support more interactive applications, without leaving [PHP] ([Livewire]) or with a browser-side [JavaScript] framework ([Inertia], with [Vue.js] or [React]): this will be covered in other documents.
For further information, consult the [Laravel] documentation (laravel.com/docs), particularly the pages dedicated to the request lifecycle, the service container, validation, [Blade], and [Eloquent], as well as the documentation for [PHP] (php.net/manual/en), the section on [Composer] (getcomposer.org/doc), and compare this application with its counterparts [Flask], [Spring MVC], [ASP.NET Core MVC], and [NestJS].