
Laravel is one of the most popular PHP frameworks for building modern web applications. It’s elegant, expressive, and designed to make development enjoyable.
In this post, we’ll go step-by-step through the basics of Laravel 12, including setup, routes, views, Blade templates, and models — everything you need to get a simple app up and running.
I still remember when I first started learning Laravel many years ago — it felt exciting but also a bit overwhelming. Laravel is a full-featured framework, and at first glance, there’s a lot to take in.
With this guide, my goal is to introduce you to Laravel in the simplest way possible — focusing only on the essentials you need to understand how it works. From there, you’ll have a solid foundation to keep building at your own pace.
If I don’t go too deep into certain topics, it’s intentional — I want to keep things clear, avoid confusion, and prevent overheating your brain’s processor chip 🧠💥.
Much of this guide was inspired by the YouTube tutorial “Laravel From Scratch” by Traversy Media — one of my favorite teachers on the internet.
That tutorial was released a few years ago, so in this updated version, I’ve added notes and adjustments to reflect what’s changed in Laravel 12.
Full tutorial by Traversy Media:
Traversy Media Laravel Tutorial Link
Let’s get started. 🚀
Laravel Valet is a lightweight development environment for macOS that lets you run Laravel (and other PHP) sites locally — instantly, without messing around with virtual hosts or Docker setups.
It’s simple, fast, and perfect for developers who prefer a minimal setup.
I recommend following Brad Traversy’s setup guide here:
👉 Brad’s Laravel Valet Gist
For Windows users, you can use XAMPP (I recommend following Brad’s advice in his video).
On macOS, Apache often runs automatically on port 80, which is the same port Laravel Valet uses.
To avoid conflicts, stop Apache before using Valet:
sudo apachectl stop
or
brew services stop httpd
You can verify port 80 is now free with:
sudo lsof -i :80
If nothing shows up, the port is clear. Once Valet is installed, confirm it’s working by checking the version:
valet --version
You can also see all available Valet commands by running:
valet list
Navigate to the folder where you want to store your projects, then run: For the sake of having an easy guide and be used as complemetary or Brad's video I will use same naming:
laravel new laragigs
This will create a new Laravel project called laragigs. Next, “park” the directory with Valet:
valet park
Example:
/Users/you/Sites/laragigs → http://laragigs.test
/Users/you/Sites/blog → http://blog.test
__
Now open the project in VS Code:
code .
Visit your new site in the browser: 👉 http://laragigs.test
You should see the Laravel welcome page.
At this stage, let’s look at a few important folders in your new project (and ignore the rest for now):
routes/
└── web.php ← Main web routes
resources/views/
└── welcome.blade.php ← Your first Blade template
app/Models/ ← Where Eloquent models live
Routes are like a map for your web application. They define how your application should respond when a user visits a specific URL: ‘/about’, ‘/’, /listings/{id}’
The View is data that is going to be displayed to the user on their browser and the user can interact with it.
Blade is Laravel’s built-in templating engine. It lets you write clean, reusable HTML with simple, elegant syntax — like embedding PHP logic directly into your templates using null and control structures such as @if, @foreach, and more.
<h1>{{ $title }}</h1>
@foreach ($listings as $listing)
<p>{{ $listing->title }}</p>
@endforeach
Blade templates are stored inside the resources/views folder and always end with the .blade.php extension. If you wish you can also use plain php alternatively. You can learn more here: https://laravel-docs.readthedocs.io/en/latest/blade/
A Model in Laravel is a PHP class that symbolizes a database table. It is in charge of maintaining the data in the table and giving users a way to interact with it.
Go to routes/web.php
TIP: a shortcut in VSCODE is command + P and search for the name of the file
Let’s say you want to create a new page/url for the About page:
## web.php
Route::get('/about', function () {
return response('<h1>About</h1>', 200)
->header('Content-Type', 'text/plain');
});
Then you can go and refresh your browser: http://laragigs.test/about
And you will see the new route created. Notice in this example that we are directly passing the information/data to be rendered (the html h1). As opposed to passing a “view” from ‘resources/views’.
Check this 2 more examples, copy and paste and try to understand what is going on:
Route::get('/posts/{id}', function ($id) {
return response('Post '. $id);
})->where('id', '[0-9]+');
Above, we are creating a dynamic route and passing an id, also setting the id must be a number
Second example:
Route::get('/search', function(Request $request) {
// dd($request->name . ' ' . $request->city);
return $request->name . ' ' . $request->city;
});
In this example we are creating a search page and returning the query parameters http://laragigs.test/search?city=Brisbane&name=Nic In your browser you should see Name from city
Notice also dd(). is a helper function that stands for "Dump and Die." It is a crucial tool for debugging.
You can skip this part if you feel it as it will not affect the rest.
In the youtube tutorial Brad does a demostration creating a route for API. Laravel 12 does not include by default routes/api.php
If your application will also offer a stateless API, you may enable API routing using the install:api Artisan command (laravel documentation) :
php artisan install:api
The install:api command installs Laravel Sanctum, which provides a robust, yet simple API token authentication guard which can be used to authenticate third-party API consumers, SPAs, or mobile applications. In addition, the install:api command creates the routes/api.php
<h1>Listings Page</h1> on itRoute::get('/', function () {
return view('listings');
});
As you can see, now we are returning the view of Listings which matches with the name you have in resources/views/listings.blade.php. If you go to / you should see the content of that file.
Now let's pass some static data:
Route::get('/', function () {
return view('listings', [
'heading' => 'Latest Listings',
'listings' => [
[
'id' => 1,
'title' => 'Listing One',
'description' => 'This is the first listing'
],
[
'id' => 2,
'title' => 'Listing Two',
'description' => 'This is the second listing'
],
[
'id' => 3,
'title' => 'Listing Three',
'description' => 'This is the third listing'
],
]
]);
});
<h1>{{ $heading }}</h1>
@foreach ($listings as $listing)
<h2>
{{ $listing['title'] }}
</h2>~
<p>
{{ $listing['description'] }}
</p>
@endforeach
$heading & listings@foreach which are blade templating language, very similar to php but cleaner.<?php
namespace App\Models;
Class Listing {
public static function all() {
return [
[
'id' => 1,
'title' => 'Listing One',
'description' => 'This is the first listing'
],
[
'id' => 2,
'title' => 'Listing Two',
'description' => 'This is the second listing'
],
[
'id' => 3,
'title' => 'Listing Three',
'description' => 'This is the third listing'
],
];
}
public static function find($id) {
$listings = self::all();
foreach($listings as $listing) {
if($listing['id'] == $id) {
return $listing;
}
}
}
}
self. (special attribute to access functions/data inside of a php Class)💡 Example Let’s say you have two classes called User:
// File: app/Models/User.php
namespace App\Models;
class User {
// Eloquent model logic
}
// File: app/Http/Controllers/User.php
namespace App\Http\Controllers;
class User {
// Controller logic
}
Without namespaces, PHP would get confused — because it wouldn’t know which User you meant. But with namespaces, each one has its own address:
App\Models\User
App\Http\Controllers\User
So they can coexist perfectly fine. Laravel organizes almost everything using namespaces.
--
<h1>{{ $heading }}</h1>
@unless(count($listings) == 0)
<p>No listings found.</p>
@foreach ($listings as $listing)
<h2>
<a href="/listings/{{ $listing['id'] }}">
{{ $listing['title'] }}
</a>
</h2>~
<p>{{ $listing['description'] }}</p>
@endforeach
@else
<p>No listings found.</p>
@endunless
Listing model at the top of the file.listing view and pass it a single Listing object by calling the find method, which receives the id from the URL.## Single Listing
use App\Models\Listing;
Route::get('/listings/{id}', function($id) {
return view('listing', [
'listing' => Listing::find($id)
]);
});
<h1>{{ $listing['title'] }}</h1>
<h1>{{ $listing['description'] }}</h1>
You should then be able to see all listings in the homepage, and once clicked in each listing, navigate to the individual listing page.
So Far we covered some basics including:
So far, this has been a very basic introduction or refresher to Laravel. For a deeper dive, I highly recommend following Brad’s tutorial — he explains the key concepts very clearly with practical examples.
Below, I’ve included a cheat sheet of some of Laravel’s key concepts:
__
## General
php artisan list # Show all available Artisan commands
php artisan help <command> # Show help for a specific command
php artisan serve # Run local dev server at http://127.0.0.1:8000
php artisan tinker # Interactive shell to test code and Eloquent
php artisan clear # Clear cache, routes, config, and views
## Database
php artisan migrate # Run all pending migrations
php artisan migrate:rollback # Undo the last migration batch
php artisan migrate:fresh # Drop all tables and re-run all migrations
php artisan db:seed # Run all database seeders
php artisan migrate --seed # Run migrations and seed together
php artisan make:migration create_users_table # Create new migration file
## Make / Generate Files
php artisan make:model User # Create a new model
php artisan make:controller UserController # Create a new controller
php artisan make:migration create_posts_table # Create a new migration
php artisan make:seeder UsersTableSeeder # Create a new seeder
php artisan make:factory UserFactory # Create a model factory
php artisan make:middleware AuthMiddleware # Create new middleware
php artisan make:request StoreUserRequest # Create a form request validator
php artisan make:command MyCommand # Create a custom Artisan command
php artisan make:observer UserObserver # Create a model observer
## Cache / Config
php artisan cache:clear # Clear application cache
php artisan config:clear # Clear configuration cache
php artisan route:clear # Clear route cache
php artisan view:clear # Clear compiled Blade views
php artisan optimize # Optimize performance and cache routes/config/views
## Debugging / Info
php artisan route:list # Show all defined routes
php artisan migrate:status # Show which migrations have run
php artisan config:cache # Cache config for faster load times
php artisan schedule:list # Show scheduled tasks
That’s all for now — may the code be with you!