If you've spent some time building applications with Django and have decided to explore Laravel, you may initially feel like you're learning backend development all over again.
You're not.
The syntax is different. The project structure is different. The terminology is different. But many of the architectural ideas behind Laravel will feel surprisingly familiar if you've worked with Django.
This article is a practical guide for Django developers transitioning to Laravel. Instead of learning Laravel concepts in isolation, we'll map them to their Django equivalents and build a mental model of how a Laravel application works.
By the end, you should be able to look at a Laravel project and understand where routes, controllers, models, validation, authentication, business logic, and database operations belong.
1. Django vs Laravel: The Mental Translation
The easiest way to start learning Laravel as a Django developer is to create a mental translation table.
| Django | Laravel |
|---|---|
| Django project | Laravel application |
| Django app | No exact equivalent |
urls.py |
routes/web.php, routes/api.php
|
| View function / Class-Based View | Controller method |
| Django ORM | Eloquent ORM |
| Model | Model |
models.py |
app/Models/ |
| DRF Serializer | API Resource / Form Request |
| Django Forms | Form Requests |
| Middleware | Middleware |
settings.py |
.env + config/
|
manage.py |
artisan |
| Django migrations | Laravel migrations |
| Django templates | Blade |
JsonResponse / DRF Response |
response()->json() |
| Django signals | Events / Observers |
| Management commands | Artisan commands |
pip |
Composer |
requirements.txt |
composer.json |
| Celery | Laravel Queues / Jobs |
The important thing is not to assume that these are exact one-to-one replacements.
They aren't.
They are simply useful mental bridges.
For example, a Laravel API Resource and a Django REST Framework Serializer both help shape API responses, but they work differently.
2. The Laravel Request Lifecycle
Before learning individual Laravel components, understand what happens when a request reaches your application.
Imagine a React frontend sends:
POST /api/properties
A simplified Laravel request lifecycle looks like this:
React / Browser / Mobile App
|
v
HTTP Request
|
v
public/index.php
|
v
Bootstrap
|
v
Middleware
|
v
Router
|
v
Controller
|
+----+----+
| |
v v
Validation Business Logic
|
v
Model
|
v
Database
|
v
Resource
|
v
JSON Response
|
v
Client
This is the architecture you should keep in your head while learning Laravel.
3. Laravel Project Structure
A fresh Laravel project looks roughly like this:
my-project/
│
├── app/
│ ├── Console/
│ ├── Exceptions/
│ ├── Http/
│ │ ├── Controllers/
│ │ ├── Middleware/
│ │ └── Requests/
│ │
│ ├── Models/
│ └── Providers/
│
├── bootstrap/
│
├── config/
│
├── database/
│ ├── factories/
│ ├── migrations/
│ └── seeders/
│
├── public/
│ └── index.php
│
├── resources/
│ ├── views/
│ ├── css/
│ └── js/
│
├── routes/
│ ├── web.php
│ ├── api.php
│ └── console.php
│
├── storage/
│
├── tests/
│
├── vendor/
│
├── .env
├── artisan
└── composer.json
If you're coming from Django, this structure might initially feel strange.
Django encourages you to break functionality into applications:
project/
├── users/
├── properties/
├── bookings/
└── payments/
Laravel doesn't enforce that approach.
Instead, a typical Laravel application organizes code around responsibilities:
app/
├── Models/
├── Http/
│ ├── Controllers/
│ ├── Requests/
│ └── Middleware/
└── Services/
You can still organize a large Laravel project by domain or feature, but Laravel itself doesn't force you to create an "app" for every feature.
4. Routes: Django's urls.py vs Laravel Routes
If you've used Django, routing is easy to understand.
In Django you might have:
path(
"properties/",
views.properties
)
Laravel uses:
Route::get(
'/properties',
[PropertyController::class, 'index']
);
Laravel routes are commonly placed in:
routes/
├── web.php
├── api.php
└── console.php
For example:
Route::get('/properties', [
PropertyController::class,
'index'
]);
POST:
Route::post('/properties', [
PropertyController::class,
'store'
]);
PUT:
Route::put('/properties/{id}', [
PropertyController::class,
'update'
]);
DELETE:
Route::delete('/properties/{id}', [
PropertyController::class,
'destroy'
]);
Laravel makes the HTTP method explicit in the route definition.
5. Route Parameters
In Django you might write:
path(
"properties/<int:id>/",
views.property_detail
)
Laravel:
Route::get(
'/properties/{id}',
[PropertyController::class, 'show']
);
Then your controller can receive the ID:
public function show($id)
{
//
}
A request such as:
GET /properties/10
gives:
$id = 10;
Laravel also supports a more powerful feature called route model binding, which we'll get to later.
6. Controllers: Django Views vs Laravel Controllers
This is one of the easiest concepts to transfer.
A Django function-based view might look like:
def properties(request):
properties = Property.objects.all()
return JsonResponse({
"properties": list(
properties.values()
)
})
A Laravel controller could look like:
class PropertyController extends Controller
{
public function index()
{
$properties = Property::all();
return response()->json([
'properties' => $properties
]);
}
}
Conceptually:
Django View
≈
Laravel Controller Method
Create a controller with Artisan:
php artisan make:controller PropertyController
Laravel creates:
app/Http/Controllers/PropertyController.php
7. Models and Eloquent
If there is one Laravel feature Django developers will understand almost immediately, it's Eloquent.
Eloquent is Laravel's ORM.
Django:
Property.objects.all()
Laravel:
Property::all();
Django:
Property.objects.get(id=1)
Laravel:
Property::find(1);
Django:
Property.objects.filter(
status="available"
)
Laravel:
Property::where(
'status',
'available'
)->get();
Django:
Property.objects.filter(
price__gt=10000
)
Laravel:
Property::where(
'price',
'>',
10000
)->get();
The syntax is different, but the underlying idea is the same:
Application
|
v
ORM
|
v
Database
8. Creating Models
Create a Laravel model:
php artisan make:model Property
You will get something like:
app/Models/Property.php
A model might look like:
class Property extends Model
{
protected $fillable = [
'name',
'location',
'price',
];
}
You can then create records:
$property = Property::create([
'name' => 'Apartment A',
'location' => 'Nairobi',
'price' => 25000,
]);
The Django equivalent is:
Property.objects.create(
name="Apartment A",
location="Nairobi",
price=25000
)
9. Mass Assignment
One Laravel concept that might initially confuse Django developers is mass assignment.
You'll frequently see:
protected $fillable = [
'name',
'location',
'price',
];
This controls which attributes can be assigned through methods such as:
Property::create([
'name' => 'Apartment A',
'location' => 'Nairobi',
'price' => 25000,
]);
It's an important security mechanism.
You should understand $fillable and $guarded early when learning Eloquent.
10. Migrations
If you've worked with Django migrations, Laravel migrations will feel familiar.
Django:
python manage.py makemigrations
python manage.py migrate
Laravel:
php artisan make:migration create_properties_table
Then:
php artisan migrate
A migration might look like:
Schema::create('properties', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('location');
$table->decimal('price', 10, 2);
$table->timestamps();
});
Compare that with Django:
class Property(models.Model):
name = models.CharField(max_length=255)
location = models.CharField(max_length=255)
price = models.DecimalField(
max_digits=10,
decimal_places=2
)
The syntax is different, but you're expressing the same database structure.
11. Artisan: Laravel's manage.py
If Django has:
python manage.py
Laravel has:
php artisan
Artisan is one of the most important tools in the Laravel ecosystem.
Some commands you'll use frequently:
php artisan serve
Start the development server.
php artisan route:list
Display your application's routes.
php artisan make:model Property
Create a model.
php artisan make:controller PropertyController
Create a controller.
php artisan make:request StorePropertyRequest
Create a validation request.
php artisan make:resource PropertyResource
Create an API Resource.
php artisan migrate
Run migrations.
php artisan migrate:rollback
Roll back migrations.
php artisan migrate:fresh
Drop all tables and rebuild the database.
php artisan db:seed
Run seeders.
php artisan optimize:clear
Clear Laravel's cached configuration, routes, views, and other optimized files.
A good rule:
If you're wondering whether Laravel has a CLI command for something, check Artisan first.
12. Relationships
Laravel's Eloquent relationships are another area where Django developers should feel at home.
Suppose a property belongs to a landlord.
Django:
class Property(models.Model):
landlord = models.ForeignKey(
User,
on_delete=models.CASCADE
)
Laravel:
class Property extends Model
{
public function landlord()
{
return $this->belongsTo(
User::class
);
}
}
Now you can access:
$property->landlord;
13. One-to-Many Relationships
Suppose a landlord owns multiple properties.
Laravel:
class Landlord extends Model
{
public function properties()
{
return $this->hasMany(
Property::class
);
}
}
Then:
$landlord->properties;
Django might be:
landlord.properties.all()
assuming you configured the appropriate related_name.
14. Many-to-Many Relationships
Django:
class Student(models.Model):
courses = models.ManyToManyField(
Course
)
Laravel:
public function courses()
{
return $this->belongsToMany(
Course::class
);
}
Then:
$student->courses;
Again, the terminology changes but the database relationship remains the same.
15. Eager Loading
One of the most important ORM concepts is avoiding unnecessary database queries.
Laravel:
Property::with('landlord')->get();
Django's rough equivalent:
Property.objects.select_related(
"landlord"
)
For collection relationships:
Property::with('bookings')->get();
is conceptually similar to:
Property.objects.prefetch_related(
"bookings"
)
This is important because both Django and Laravel applications can suffer from the infamous N+1 query problem.
16. Validation: DRF Serializers vs Laravel Form Requests
This is where the architecture starts becoming noticeably different.
In Django REST Framework, you might put validation inside a serializer:
class PropertySerializer(
serializers.ModelSerializer
):
class Meta:
model = Property
fields = "__all__"
def validate_price(self, value):
if value < 0:
raise serializers.ValidationError(
"Price cannot be negative"
)
return value
Laravel commonly separates request validation into a Form Request.
Create one:
php artisan make:request StorePropertyRequest
Then:
class StorePropertyRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => [
'required',
'string',
'max:255'
],
'location' => [
'required',
'string'
],
'price' => [
'required',
'numeric',
'min:0'
],
];
}
}
Then inject it into your controller:
public function store(
StorePropertyRequest $request
) {
//
}
Laravel automatically validates the incoming request before your controller method proceeds.
17. API Resources: Think DRF Serializer Output
Laravel has API Resources for transforming models into API responses.
Create one:
php artisan make:resource PropertyResource
Then:
class PropertyResource extends JsonResource
{
public function toArray(
Request $request
): array {
return [
'id' => $this->id,
'name' => $this->name,
'location' => $this->location,
'price' => $this->price,
];
}
}
Then:
return new PropertyResource($property);
For collections:
return PropertyResource::collection(
Property::all()
);
A useful mental model is:
DRF Serializer
|
+---- validation/input
|
+---- representation/output
Laravel
|
+---- Form Request
| -> validation
|
+---- API Resource
-> representation
Laravel separates these responsibilities more explicitly.
18. Middleware
Django developers already understand middleware.
Django:
MIDDLEWARE = [
...
]
Laravel:
Route::middleware('auth')->group(
function () {
// protected routes
}
);
You can create middleware using Artisan:
php artisan make:middleware CheckUserRole
For example:
if ($request->user()->role !== 'admin') {
abort(403);
}
Middleware is useful for concerns that should happen before or after controller execution, such as:
- Authentication
- Logging
- Rate limiting
- CORS
- Role checks
- Request modification
19. Authentication and Authorization
Laravel supports several authentication approaches.
One commonly encountered solution for API and SPA applications is Laravel Sanctum.
A typical architecture might look like:
React
|
v
Laravel API
|
v
Authentication
|
v
Controller
If you're coming from Django REST Framework and JWT, don't assume Sanctum is simply "Laravel's JWT."
Sanctum provides API token authentication and SPA authentication capabilities, but its model and workflow differ from JWT-based authentication.
Laravel also provides:
- Gates
- Policies
- Middleware
- Authentication guards
These become especially important as your application grows.
20. Policies and Gates
Suppose a landlord should only be allowed to update their own property.
You could check this directly inside a controller, but Laravel provides authorization mechanisms such as Policies.
Conceptually:
User
|
v
Can this user update this property?
|
+---- YES ---> Continue
|
+---- NO ----> 403 Forbidden
This keeps authorization logic separate from your business logic.
If you've used Django permissions, this is an area you'll want to explore carefully.
21. Environment Variables and Configuration
Laravel uses a .env file for environment-specific configuration.
For example:
APP_NAME=Laravel
APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_database
DB_USERNAME=root
DB_PASSWORD=
Django developers may recognize this approach from using packages such as python-decouple or django-environ.
Laravel configuration is stored under:
config/
For example:
config/
├── app.php
├── database.php
├── auth.php
├── cache.php
└── filesystems.php
Django developers can think of this as a more distributed equivalent of settings.py.
22. Blade Templates
Laravel's server-side templating engine is called Blade.
Django:
<h1>{{ property.name }}</h1>
Blade:
<h1>{{ $property->name }}</h1>
Django:
{% for property in properties %}
{{ property.name }}
{% endfor %}
Blade:
@foreach($properties as $property)
{{ $property->name }}
@endforeach
If you're building a Laravel API consumed by React, you may use Blade very little.
If you're building a traditional Laravel web application, Blade becomes much more important.
23. Dependency Injection and the Service Container
This is one of the Laravel concepts I recommend Django developers spend extra time understanding.
Suppose your controller depends on a service:
class PropertyController extends Controller
{
public function __construct(
PropertyService $propertyService
) {
$this->propertyService =
$propertyService;
}
}
Laravel's Service Container can resolve that dependency for you.
Conceptually:
Controller
|
| requires
v
PropertyService
|
v
Laravel Service Container
This is Laravel's dependency injection system.
Once you understand the Service Container, concepts such as service providers, bindings, interfaces, and dependency injection become much easier.
24. Services and Business Logic
Laravel applications often introduce service classes for complex business logic.
For example:
app/
└── Services/
├── PropertyService.php
├── BookingService.php
└── PaymentService.php
A service might contain:
class PropertyService
{
public function create(
array $data
) {
return Property::create($data);
}
}
Then your controller becomes:
public function store(
StorePropertyRequest $request
) {
$property =
$this->propertyService->create(
$request->validated()
);
return new PropertyResource(
$property
);
}
The idea is to prevent controllers from becoming massive.
Instead of:
Controller
├── validation
├── authorization
├── payment
├── database logic
├── email
├── notifications
└── business rules
you can have:
Controller
|
v
Service
|
+---- Model
+---- Payment
+---- Notification
+---- Events
Don't create a service class for every two-line database query, though.
Use this architecture when it actually improves separation of responsibilities.
25. Events and Listeners
Laravel has an event-driven architecture available through Events and Listeners.
Imagine a user registers:
User registers
|
v
UserRegistered event
|
+----> SendWelcomeEmail
|
+----> CreateProfile
|
+----> NotifyAdmin
This keeps secondary operations out of the main request logic.
Django developers may find this somewhat similar to using signals, although Laravel Events and Listeners provide a different and often more explicit architecture.
26. Jobs and Queues
If you've used Celery with Django, Laravel's Jobs and Queues should make sense.
Suppose sending 10,000 emails would make a request slow.
Instead:
HTTP Request
|
v
Create Job
|
v
Queue
|
v
Worker
|
v
Send Emails
Create a job:
php artisan make:job SendNewsletter
Then dispatch it:
SendNewsletter::dispatch();
The user doesn't necessarily have to wait for the expensive operation to finish.
This is extremely useful for:
- Emails
- Notifications
- Image processing
- Reports
- Payments
- Imports
- Exports
- Heavy calculations
27. Scheduling
Laravel also includes a task scheduling system.
You might have a task that needs to run regularly:
Every day
|
v
Find overdue rent
|
v
Send reminders
Or:
Every hour
|
v
Clean expired sessions
This functionality can be combined with Artisan commands and queued jobs.
If you're coming from Django, think of this as part of the territory often handled by cron, Celery Beat, management commands, or similar tools.
28. Seeders and Factories
Laravel provides database seeders:
php artisan db:seed
For example:
Property::create([
'name' => 'Apartment A',
'location' => 'Nairobi',
'price' => 25000,
]);
Factories allow you to generate test/development data.
For example:
Property::factory()
->count(50)
->create();
This is conceptually similar to using tools such as factory_boy in Django projects.
29. A Complete Laravel API Example
Let's put the pieces together.
Suppose we're creating:
POST /api/properties
The route:
Route::post(
'/properties',
[PropertyController::class, 'store']
);
The request validation:
class StorePropertyRequest
extends FormRequest
{
public function rules(): array
{
return [
'name' => [
'required',
'string',
'max:255'
],
'location' => [
'required',
'string'
],
'price' => [
'required',
'numeric',
'min:0'
],
];
}
}
The model:
class Property extends Model
{
protected $fillable = [
'name',
'location',
'price',
];
}
The controller:
class PropertyController extends Controller
{
public function store(
StorePropertyRequest $request
) {
$property = Property::create(
$request->validated()
);
return new PropertyResource(
$property
);
}
}
The resource:
class PropertyResource extends JsonResource
{
public function toArray(
Request $request
): array {
return [
'id' => $this->id,
'name' => $this->name,
'location' => $this->location,
'price' => $this->price,
];
}
}
The client sends:
POST /api/properties
Content-Type: application/json
{
"name": "Apartment A",
"location": "Nairobi",
"price": 25000
}
Laravel processes it:
POST /api/properties
|
v
Route
|
v
PropertyController
|
v
StorePropertyRequest
|
v
validated()
|
v
Property::create()
|
v
Eloquent
|
v
MySQL
|
v
PropertyResource
|
v
JSON Response
That's a real Laravel API architecture.
30. Translating a Django Project to Laravel
Let's say you've already built a landlord/tenant system in Django.
Your Django architecture might look like:
Django
│
├── users
├── properties
├── bookings
├── payments
└── maintenance
The Laravel equivalent could look like:
Laravel
│
├── Models
│ ├── User.php
│ ├── Property.php
│ ├── Booking.php
│ ├── RentPayment.php
│ └── MaintenanceTicket.php
│
├── Http
│ ├── Controllers
│ │ ├── AuthController.php
│ │ ├── PropertyController.php
│ │ ├── BookingController.php
│ │ └── PaymentController.php
│ │
│ ├── Requests
│ │ ├── LoginRequest.php
│ │ ├── StorePropertyRequest.php
│ │ └── StoreBookingRequest.php
│ │
│ └── Resources
│ ├── UserResource.php
│ ├── PropertyResource.php
│ └── BookingResource.php
│
└── Services
├── AuthService.php
├── PropertyService.php
└── PaymentService.php
Then your API flow becomes:
React
|
| POST /api/properties
v
Laravel Router
|
v
PropertyController
|
v
StorePropertyRequest
|
v
Authorization
|
v
PropertyService
|
v
Property Model
|
v
MySQL
|
v
PropertyResource
|
v
JSON
|
v
React
This is a very useful architecture for someone already comfortable with Django REST Framework.
31. What You Should NOT Do as a Django Developer
One of the biggest mistakes when moving frameworks is trying to force your old framework's architecture into the new framework.
Don't assume:
Laravel = Django written in PHP
It isn't.
For example, don't automatically create:
PropertySerializer
PropertyView
PropertyForm
PropertyService
just because that's how you might structure something in Django.
Instead, understand what Laravel provides and use each component where it makes sense.
Likewise, don't put everything inside controllers simply because Laravel makes it easy.
A controller containing 500 lines of business logic isn't good Laravel architecture.
32. The Laravel Architecture to Learn
I'd recommend learning Laravel in roughly this order.
Level 1 — Foundation
Learn:
PHP
↓
Composer
↓
Laravel installation
↓
Project structure
↓
Artisan
↓
Routes
↓
Controllers
Level 2 — Database
Then:
Migrations
↓
Models
↓
Eloquent
↓
Relationships
↓
Query Builder
↓
Factories
↓
Seeders
Level 3 — API Development
Then:
HTTP Requests
↓
Validation
↓
Form Requests
↓
API Resources
↓
Pagination
↓
JSON responses
Level 4 — Security
Then:
Authentication
↓
Middleware
↓
Policies
↓
Gates
↓
Authorization
↓
Sanctum
Level 5 — Laravel Architecture
Then:
Service Container
↓
Dependency Injection
↓
Service Providers
↓
Services
↓
Events
↓
Listeners
↓
Jobs
↓
Queues
Level 6 — Production
Finally:
Caching
↓
Queues
↓
Scheduling
↓
Logging
↓
Testing
↓
Workers
↓
Deployment
33. The Most Important Concepts to Focus On
If you've already read the Django documentation, don't try to learn every Laravel feature immediately.
Focus heavily on these:
1. Eloquent
Understand:
Model::all();
Model::find();
Model::where();
Model::create();
Model::update();
Model::delete();
Then move into relationships and eager loading.
2. Form Requests
Understand how Laravel validates incoming data.
3. API Resources
Understand how Laravel transforms models into API responses.
4. Middleware
Understand where authentication, rate limiting, and request processing happen.
5. Policies and Gates
Understand authorization.
6. Service Container
This is one of Laravel's most important architectural concepts.
7. Dependency Injection
Understand how Laravel resolves dependencies.
8. Jobs and Queues
Especially if you're building production applications.
9. Events and Listeners
Learn how to decouple secondary operations from your main application flow.
10. Artisan
You'll use it constantly.
34. The Mental Model I Recommend
If you're transitioning from Django, remember this:
LARAVEL
Request
|
v
Middleware
|
v
Route
|
v
Controller
|
+----------+----------+
| |
v v
Form Request Policy
Validation Authorization
|
v
Service
|
v
Eloquent
|
v
Database
|
v
Resource
|
v
JSON Response
You don't have to use every box in every request.
A simple endpoint might be:
Request
↓
Route
↓
Controller
↓
Model
↓
Response
A more complex endpoint might be:
Request
↓
Middleware
↓
Route
↓
Form Request
↓
Policy
↓
Controller
↓
Service
↓
Eloquent
↓
Event
↓
Job
↓
Resource
↓
Response
The architecture grows with the complexity of the application.
35. Final Thoughts
Moving from Django to Laravel isn't really starting over.
If you've already built applications with Django, you already understand most of the important backend concepts.
You know:
- HTTP
- Routing
- REST APIs
- Models
- ORMs
- Databases
- Relationships
- Migrations
- Authentication
- Authorization
- Middleware
- Validation
- Serialization
- Background tasks
Laravel simply implements many of these ideas differently.
The biggest shift is learning Laravel's way of organizing those responsibilities.
Instead of thinking:
"Where is the Laravel version of my Django file?"
start thinking:
"What responsibility am I trying to solve, and which Laravel component is designed for it?"
Once you start thinking that way, Laravel becomes much easier to understand.
And if you've already built a Django REST API, you're not beginning your Laravel journey as a beginner.
You're learning a new ecosystem using knowledge you already have.
The fastest way to make the transition stick is to build something you already understand in Django.
For example:
Landlord/Tenant API
|
+-- Authentication
+-- Users
+-- Properties
+-- Bookings
+-- Rent Payments
+-- Maintenance Tickets
+-- Roles
+-- Authorization
+-- Notifications
Build the same system in Laravel while consciously mapping:
Django Laravel
urls.py → routes
views.py → controllers
models.py → Eloquent models
DRF serializers → requests/resources
ORM → Eloquent
middleware → middleware
manage.py → Artisan
Celery → Jobs/Queues
settings.py → config + .env
That's when Laravel stops looking like a completely different framework and starts becoming another way of solving problems you already know how to solve.
Top comments (0)