Cheatsheet Laravel
Framework PHP para desenvolvimento web
Laravel
Collections
Create collection
$collection = collect([1, 2, 3, 4]); $collection = new Collection([1, 2, 3]); // Chainable methods: collect([1, 2, 3])->map(...)->filter(...)->values();
The collect() turns an array into a Collection with dozens of useful methods. All are chainable and don't mutate the original.
Where on collection
$ativos = $products->where('active', true);
$caros = $products->where('price', '>', 100);
$first = $products->whereIn('id', [1, 2, 3]);The where() filters by field value (loose comparison). It supports operators and variants: whereIn, whereBetween, whereNotNull.
Sum, Avg, Max, Min
$total = $products->sum('price');
$average = $products->avg('price');
$max = $products->max('price');
$min = $products->min('price');
$countdown = $products->count();Aggregation methods over a field: sum(), avg(), max(), min(), count(). They work on collections of models or arrays.
Reduce and Each
$total = collect([1, 2, 3])->reduce(
fn($carry, $n) => $carry + $n, 0
); // 6
$products->each(function ($p) {
$p->update(['visto' => true]);
});The reduce() accumulates a value while iterating. The each() runs an action on each element (without transforming). It returns the original collection.
Map (transform)
$double = collect([1, 2, 3])
->map(fn($n) => $n * 2);
// [2, 4, 6]
$names = $products->map(fn($p) => $p->name);The map() applies a function to each element and returns a new collection with the transformed results. It doesn't change the original.
GroupBy
$porCategoria = $products->groupBy('category');
// ['electronic' => [...], 'accessories' => [...]]
$porAno = $orders->groupBy(fn($p) => $p->created_at->year);The groupBy() groups elements by a field value or callback. It returns a collection of collections indexed by the key.
Contains and Search
$tem = $products->contains('name', 'Laptop');
$tem = collect([1, 2, 3])->contains(2);
$index = collect(['a', 'b'])->search('b'); // 1The contains() checks if an element with the value exists. The search() returns the index of the first occurrence.
Filter
$even = collect([1, 2, 3, 4])
->filter(fn($n) => $n % 2 === 0);
// [2, 4]
$ativos = $products->filter(fn($p) => $p->active);The filter() keeps only the elements that pass the test. Without a callback, it removes falsy values. Use values() to reindex.
SortBy
$sorted = $products->sortBy('price');
$desc = $products->sortByDesc('price');
$multi = $products->sortBy([['name', 'asc'], ['price', 'desc']]);The sortBy() sorts by a field (ascending). The sortByDesc() is descending. It accepts an array for multi-sort.
First, Last and Find
$first = $collection->first(); $last = $collection->last(); $largest = $collection->first(fn($n) => $n > 10); $pattern = $collection->first(fn($n) => $n > 100, 0);
The first() and last() get the extremes. With a callback, it returns the first that satisfies the condition. The second argument is the default value.
Pluck (extract column)
$names = $products->pluck('name');
// ['Laptop', 'Mouse', 'Keyboard']
$even = $products->pluck('name', 'id');
// [1 => 'Laptop', 2 => 'Mouse']The pluck() extracts a field from all elements. The second argument sets the key of the resulting array.
Unique and Values
$uniques = collect([1, 2, 2, 3])->unique();
// [1, 2, 3]
$cats = $products->unique('category');
$reindex = $cats->values();The unique() removes duplicates (by value or field). The values() reindexes the numeric keys after filtering.
Chunk and Split
$blocos = collect([1, 2, 3, 4, 5])->chunk(2); // [[1,2], [3,4], [5]] $groups = collect([1, 2, 3, 4])->split(2); // [[1,2], [3,4]]
The chunk() splits into blocks of N elements. The split() divides into N groups of roughly equal size.
Installation and Configuration
Create a new project
composer create-project laravel/laravel my-project cd my-project php artisan serve
Creates a new Laravel project with all dependencies. The composer downloads the framework and artisan serve starts the server at http://127.0.0.1:8000.
Environment variables
# .env
APP_NAME=MeuSite
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost
# Access in code:
$name = env('APP_NAME');
$debug = env('APP_DEBUG', false);Sensitive values live in the .env (never in the repository). Access them with env(), passing a default value the the second argument. In production use APP_DEBUG=false.
Install Breeze (auth)
composer require laravel/breeze --dev php artisan breeze:install blade npm install && npm run dev
The Breeze installs ready-made login, registration and password recovery. Choose the stack (blade, react or vue) and run npm install for the assets.
Laravel Installer
composer global require laravel/installer laravel new my-project
The laravel/installer is a faster alternative to create-project. After installing it globally, use laravel new to create projects.
Folder structure
app/ → Models, Controllers, Services bootstrap/ → Framework initialization config/ → Configuration files database/ → Migrations, seeders, factories public/ → index.php, CSS, JS, images resources/ → Blade views, assets routes/ → web.php, api.php, console.php storage/ → Logs, cache, uploads vendor/ → Composer dependencies
The default Laravel structure separates responsibilities. The app/ holds the logic, routes/ the routes and resources/views/ the Blade templates.
Configuration by files
// config/app.php
'name' => env('APP_NAME', 'Laravel'),
'timezone' => 'Europe/Lisbon',
'locale' => 'pt',
// Access:
$name = config('app.name');
config(['app.locale' => 'en']);Each file in config/ groups settings by area. Access with config('file.key') and set at runtime with config(['key' => value]).
Generate APP_KEY
php artisan key:generate
Generates the APP_KEY in the .env file. It is required for encryption of sessions, cookies and sensitive data. Without it the application won't work.
Maintenance mode
php artisan down php artisan down --secret=abc123 php artisan up
The down puts the site into maintenance (returns 503). With --secret you can access it via a secret URL. The up removes the maintenance.
Routes file web.php
// routes/web.php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});The routes/web.php file defines web routes with session and CSRF. For APIs use routes/api.php (no session, /api prefix).
Configure the database
# .env DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=my_bank DB_USERNAME=root DB_PASSWORD=
Edit the .env file at the project root with the database credentials. Laravel supports mysql, pgsql, sqlite and sqlsrv.
View version and info
php artisan --version php artisan about
The --version shows the framework version. The about (Laravel 9+) displays a complete summary: PHP version, drivers, environment and configuration.
Aliases and Facades
// config/app.php → aliases
'Cache' => Illuminate\Support\Facades\Cache::class,
'DB' => Illuminate\Support\Facades\DB::class,
// Use without import:
Cache::get('key');
DB::table('users')->get();The Facades give static access to services in the container. The aliases in config/app.php let you use them without use (e.g. Cache::, DB::).
Artisan CLI
List commands
php artisan list php artisan list --raw php artisan help make:model
The list shows all available Artisan commands. The help explains the syntax and options of a specific command.
Tinker (REPL)
php artisan tinker >>> App\Models\Product::count() >>> $p = App\Models\Product::find(1) >>> $p->name
The tinker is an interactive terminal (REPL) to test code and query models directly. Ideal for quick debugging and exploring the database.
Create Seeder and Factory
php artisan make:seeder ProdutoSeeder php artisan make:factory ProdutoFactory --model=Product
The seeder inserts fixed data into the DB. The factory generates realistic fake data for tests. Both live in database/.
Create Job and Command
php artisan make:job EnviarEmail php artisan make:command LimparLogs
The job is an asynchronous task for queues. The command is a custom Artisan command. Both live in app/.
Create Model
php artisan make:model Product php artisan make:model Product -mcrf # -m migration, -c controller, -r resource, -f factory
Creates the Model in app/Models/. The flags combine creation of a migration, resource controller and factory in a single command.
List routes
php artisan route:list php artisan route:list --name=products php artisan route:list --method=GET
Shows all registered routes with HTTP method, URI, name and middleware. The --name and --method filters help find specific routes.
Create FormRequest
php artisan make:request StoreProdutoRequest
Creates a dedicated validation class in app/Http/Requests/. It separates the rules from the controller, making it cleaner and reusable.
Schedule and run tasks
php artisan schedule:run # runs scheduled tasks php artisan queue:work # processes the queue php artisan queue:listen # processes with reload php artisan test # runs PHPUnit tests
The schedule:run runs cron tasks. The queue:work processes queued jobs. The test runs the PHPUnit test suite.
Create Controller
php artisan make:controller ProdutoController php artisan make:controller ProdutoController --resource php artisan make:controller Api/ProdutoController --api
Creates a controller in app/Http/Controllers/. The --resource generates the 7 CRUD methods and the --api generates 5 (without create/edit).
Clear caches
php artisan config:clear php artisan cache:clear php artisan view:clear php artisan route:clear php artisan optimize:clear # all at once
Clears the config, data, compiled views and route caches. The optimize:clear clears everything in a single command. Essential after changing configuration.
Create Middleware
php artisan make:middleware VerificaAdmin
Creates a middleware in app/Http/Middleware/. Register it in app/Http/Kernel.php or apply it directly on the route.
Create Migration
php artisan make:migration create_products_table php artisan make:migration add_sku_to_products_table --table=products
Creates a migration with a timestamp in database/migrations/. If the name contains create_x_table, Laravel generates the skeleton with Schema::create.
Optimize for production
php artisan config:cache php artisan route:cache php artisan view:cache php artisan optimize
Generates config, route and view caches for better performance in production. The optimize does config + route cache at once.
Create Policy and Event
php artisan make:policy ProductPolicy --model=Product php artisan make:event OrderCreated php artisan make:listener SendNotification --event=OrderCreated
The policy defines authorization rules per model. The event/listener implement the observer pattern to react to actions.
Routes
Basic GET route
Route::get('/products', [ProdutoController::class, 'index']);
Route::get('/hello', function () {
return 'Hello World';
});Defines a GET route that calls a controller method or a closure. The first argument is the URI and the second the handler.
Named route
Route::get('/products', [ProdutoController::class, 'index'])
->name('products.index');
// Generate URL:
$url = route('products.index');
$link = route('products.show', $product);The name() gives the route a name, allowing you to generate URLs with route(). If the URI changes, the links stay valid.
Restrict parameters
Route::get('/products/{id}', ...)
->whereNumber('id');
Route::get('/users/{name}', ...)
->where('name', '[A-Za-z]+');
Route::get('/files/{path}', ...)
->where('path', '.*');The where() applies regex to the parameters. Shortcuts: whereNumber, whereAlpha, whereAlphaNumeric, whereUuid.
Route model binding
Route::get('/products/{product}', function (Product $product) {
return $product->name;
});
// /products/1 → automatic Product::find(1)The model binding automatically injects the Model instance from the ID in the URL. If it doesn't exist, it returns 404 automatically.
POST, PUT, DELETE routes
Route::post('/products', [ProdutoController::class, 'store']);
Route::put('/products/{id}', [ProdutoController::class, 'update']);
Route::delete('/products/{id}', [ProdutoController::class, 'destroy']);
Route::patch('/products/{id}', [ProdutoController::class, 'patch']);Each HTTP verb has its method: post, put, patch, delete. The PUT replaces everything, the PATCH updates partially.
Route::resource
Route::resource('products', ProdutoController::class);
// Generates 7 routes:
// GET /products → index
// GET /products/create → create
// POST /products → store
// GET /products/{id} → show
// GET /products/{id}/edit → edit
// PUT /products/{id} → update
// DELETE /products/{id} → destroyThe resource registers the 7 RESTful routes at once. Use apiResource for APIs (excludes create and edit).
Redirect
Route::redirect('/old', '/new');
Route::redirect('/old', '/new', 301);
Route::permanentRedirect('/old', '/new');The redirect() redirects one URI to another (302 by default). The permanentRedirect() uses 301 (permanent, good for SEO).
Rate limiting
Route::middleware('throttle:60,1')->group(function () {
Route::get('/api/data', ...);
});
// 60 requests per minuteThe throttle limits requests per user. The format is throttle:max,minutes. Essential to protect APIs from abuse.
Route with parameters
Route::get('/products/{id}', function ($id) {
return "Product: " . $id;
});
Route::get('/users/{user}/posts/{post}', function ($user, $post) {
return [$user, $post];
});The parameters in braces are passed to the closure or method. You can have multiple parameters in the same URI.
Group with middleware
Route::middleware('auth')->group(function () {
Route::get('/panel', [PainelController::class, 'index']);
Route::get('/profile', [PerfilController::class, 'show']);
});The middleware() applies protection to all routes in the group. In this case, only authenticated users (auth) can access.
Route that returns a view
Route::view('/about', 'pages.about');
Route::view('/faq', 'pages.faq', ['title' => 'FAQ']);The Route::view() returns a view without needing a controller. The optional third argument passes data to the view.
Optional parameter
Route::get('/users/{id?}', function ($id = null) {
return $id ?? 'All the users';
});The ? makes the parameter optional. The variable must have a default value (= null) to work when the parameter is not provided.
Group with prefix
Route::prefix('admin')->name('admin.')->group(function () {
Route::get('/users', [AdminController::class, 'users'])
->name('users'); // admin.users
});
// URL: /admin/usersThe prefix() adds a segment to all URIs in the group. The name() prefixes the route names (e.g. admin.users).
Fallback route (404)
Route::fallback(function () {
return view('errors.404');
});The fallback runs when no route matches the request. It must be the last route registered in the file.
Controllers e Middleware
Basic controller
namespace App\Http\Controllers;
class ProdutoController extends Controller
{
public function index()
{
$products = Product::all();
return view('products.index', compact('products'));
}
}A controller groups the HTTP logic in a class. The method returns a view, JSON or redirect. It lives in app/Http/Controllers/.
Resource methods
index() → GET /products (list)
create() → GET /products/create (form)
store() → POST /products (save)
show() → GET /products/{id} (detail)
edit() → GET /products/{id}/edit (edit form)
update() → PUT /products/{id} (update)
destroy() → DELETE /products/{id} (delete)The 7 methods of a resource controller follow the REST convention. Each one maps to an HTTP verb and a specific URI.
Multiple middlewares
Route::get('/admin/relatorios', ...)
->middleware(['auth', 'admin', 'verificado']);Apply multiple middlewares in a chain with an array. They run in the given order: first auth, then admin, etc.
Request injection
public function store(Request $request)
{
$name = $request->input('name');
$email = $request->email;
$all = $request->all();
$so = $request->only(['name', 'email']);
}The Request is automatically injected by the container. Access the data with input(), the magic property or all()/only().
Redirect with flash
return redirect()->route('products.index')
->with('success', 'Product created!');
return back()->withInput()
->withErrors(['name' => 'Required']);The redirect() with with() stores a flash message in the session. The back() returns to the form with the data and errors.
Dependency Injection
public function show(Product $product, LoggerInterface $log)
{
$log->info("Visto: {$product->id}");
return view('products.show', compact('product'));
}The container automatically resolves the type-hinted dependencies. The model binding injects the model and the LoggerInterface is resolved via the container.
Return JSON
return response()->json([
'success' => true,
'data' => $products,
], 200);
return response()->json(['error' => 'Not found'], 404);The response()->json() converts arrays into a JSON response with the given status code. Ideal for REST APIs.
Basic middleware
public function handle(Request $request, Closure $next)
{
if (!auth()->user()->is_admin) {
abort(403, 'Access denied');
}
return $next($request);
}The middleware intercepts the request before the controller. If the condition fails, it calls abort(). If it passes, it calls $next($request) to continue.
HTTP responses
return response('OK', 200)
->header('X-Custom', 'value')
->cookie('token', $value, 60);
return response()->download($path);
return response()->file($path);The response() lets you set status, headers and cookies. The download() forces a file download and the file() shows it inline.
Invokable Controller
class MostrarPerfil
{
public function __invoke()
{
return view('profile');
}
}
Route::get('/profile', MostrarPerfil::class);An invokable controller has a single __invoke() method. You don't need to indicate the method in the route. Ideal for simple actions.
Register middleware
// app/Http/Kernel.php
protected $middlewareAliases = [
'admin' => \App\Http\Middleware\VerificaAdmin::class,
];
// Use in the route:
Route::get('/admin', ...)->middleware('admin');Register the alias in $middlewareAliases in the Kernel.php. Then apply it with middleware('alias') on routes or groups.
Abort and HTTP errors
abort(404); abort(403, 'Sem permission'); abort_if(!$product, 404); abort_unless(auth()->check(), 401);
The abort() throws an HTTP exception with the given code. The abort_if and abort_unless are conditional shortcuts.
Eloquent ORM
Basic Model
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = ['name', 'price', 'description'];
}The $fillable defines the mass-assignable fields (create/update). Without it, Laravel blocks mass assignment for security.
Create record
$product = Product::create([
'name' => 'Laptop',
'price' => 999.99,
]);
// Alternative:
$product = new Product();
$product->name = 'Laptop';
$product->save();The create() creates and saves at once (requires $fillable). The alternative instantiates, sets fields and calls save().
HasMany relationship
// In the Product model:
public function comentarios()
{
return $this->hasMany(Comentario::class);
}
// Usage:
$product->comentarios;
$product->comentarios()->create(['text' => 'Bom!']);The hasMany() defines that a product has many comments. The FK product_id lives in the comentarios table. Access it the a property.
Eager Loading (avoid N+1)
// N+1 (bad):
$orders = Order::all();
$orders[0]->client->name; // query per order
// Eager loading (good):
$orders = Order::with('client', 'items')->get();The with() loads the relationships in a single extra query, avoiding the N+1 problem. Essential for performance with lists of related records.
Select columns
Product::select('name', 'price')->get();
Product::distinct()->pluck('category');
Product::selectRaw('category, COUNT(*) the total')
->groupBy('category')->get();The select() brings only the given columns. The pluck() extracts a column the an array. The selectRaw() allows SQL expressions.
Fetch records
$products = Product::all(); $product = Product::find(1); $product = Product::findOrFail(1); $first = Product::first(); $last = Product::latest()->first();
The all() brings everything, find() searches by ID. The findOrFail() throws 404 if it doesn't exist. The latest() orders by created_at desc.
Update record
$product = Product::find(1);
$product->update(['price' => 899.99]);
// Or in bulk:
Product::where('category', 'electronic')
->update(['discount' => 10]);The update() updates the given fields. It can be used on an instance or directly on the query builder for bulk updates.
BelongsTo relationship
// In the Comentario model:
public function product()
{
return $this->belongsTo(Product::class);
}
// Usage:
$comentario->product->name;The belongsTo() is the inverse of hasMany. It indicates the comment belongs to a product. The FK lives in the child model.
Order and limit
Product::orderBy('price', 'desc')
->limit(10)
->get();
Product::inRandomOrder()->first();
Product::skip(20)->take(10)->get(); // offset 20The orderBy() orders by field and direction. The limit()/take() limits results and skip()/offset() skips records.
Soft Deletes
use Illuminate\Database\Eloquent\SoftDeletes;
class Product extends Model
{
use SoftDeletes;
}
// Migration:
$table->softDeletes(); // deleted_at columnThe SoftDeletes trait marks records the deleted without removing them. Use trashed() to check, restore() to recover and forceDelete() to delete permanently.
Where with conditions
Product::where('price', '>', 100)
->where('active', true)
->get();
Product::where('name', 'like', '%mouse%')->get();
Product::whereIn('id', [1, 2, 3])->get();Chain conditions with where(). It supports operators (>, <, like) and variants like whereIn, whereBetween, whereNull.
Delete record
$product = Product::find(1);
$product->delete();
// In bulk:
Product::where('active', false)->delete();
// With SoftDeletes:
$product->restore();
Product::withTrashed()->get();The delete() removes the record. With SoftDeletes, it only marks deleted_at. The restore() recovers it and withTrashed() includes the deleted ones.
ManyToMany relationship
// In the Product model:
public function categories()
{
return $this->belongsToMany(Category::class);
}
// Usage:
$product->categories()->attach($catId);
$product->categories()->detach($catId);
$product->categories()->sync([1, 2, 3]);The belongsToMany() requires a pivot table (category_product). The attach/detach add/remove and the sync replaces everything.
Pagination
$products = Product::paginate(15);
// In the Blade view:
{{ $products->links() }}
// API:
return response()->json($products);The paginate() splits results into pages and generates links automatically. The simplePaginate() shows only "previous/next".
Where with OR
Product::where('price', '>', 100)
->orWhere('highlight', true)
->get();
Product::where(function ($q) {
$q->where('price', '>', 100)
->orWhere('highlight', true);
})->where('active', true)->get();The orWhere() adds alternative conditions. To group logic, use a closure in the where() to avoid incorrect precedence.
FirstOrCreate / UpdateOrCreate
$product = Product::firstOrCreate(
['name' => 'Mouse'],
['price' => 25]
);
$product = Product::updateOrCreate(
['name' => 'Mouse'],
['price' => 30]
);The firstOrCreate() finds or creates if it doesn't exist. The updateOrCreate() finds and updates, or creates with all the values.
HasOne relationship
// In the User model:
public function profile()
{
return $this->hasOne(Profile::class);
}
// Usage:
$user->profile->bio;
$user->profile()->create(['bio' => 'Hello']);The hasOne() indicates a one-to-one relationship. Each user has a single profile. The FK lives in the related model's table.
Aggregations
$total = Product::count();
$caros = Product::where('price', '>', 100)->count();
$max = Product::max('price');
$average = Product::avg('price');
$sum = Product::sum('price');
$exists = Product::where('sku', 'X1')->exists();Aggregation methods: count(), max(), min(), avg(), sum(). The exists() checks existence without fetching data.
Migrations e Seeders
Structure of a migration
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->decimal('price', 8, 2);
$table->text('description')->nullable();
$table->timestamps();
});The Schema::create() creates the table. The id() is auto-increment and timestamps() adds created_at and updated_at.
Run migrations
php artisan migrate php artisan migrate:rollback php artisan migrate:rollback --step=2 php artisan migrate:fresh php artisan migrate:fresh --seed
The migrate runs the pending ones. The rollback undoes the last batch. The fresh drops everything and recreates. With --seed it runs the seeders afterwards.
Seeder with Factory
// Factory:
public function definition()
{
return [
'name' => fake()->word(),
'price' => fake()->randomFloat(2, 5, 500),
];
}
// Seeder:
Product::factory(50)->create();The factory generates realistic fake data with fake() (Faker). The create() saves to the DB; make() only instantiates without saving.
Factory with relationships
// Product with 3 comments:
Product::factory()
->has(Comentario::factory()->count(3))
->create();
// Comentario with product:
Comentario::factory()
->for(Product::factory())
->create();The has() creates related records (one-to-many). The for() sets the "parent" of the relationship. It simplifies creating interlinked data.
Column types
$table->string('name'); // VARCHAR(255)
$table->text('description'); // TEXT
$table->integer('quantity'); // INT
$table->unsignedBigInteger('user_id');
$table->boolean('active'); // TINYINT(1)
$table->decimal('price', 8, 2); // DECIMAL
$table->date('data_nascimento');
$table->timestamp('published_at');
$table->json('metadados');
$table->enum('state', ['active', 'inactive']);The Blueprint offers methods for each SQL type. The string() accepts an optional length: string('name', 100).
Alter existing table
Schema::table('products', function (Blueprint $table) {
$table->string('sku')->after('name');
$table->dropColumn('field_old');
});Use Schema::table() (not create) to modify existing tables. The dropColumn() removes columns.
Run seeders
php artisan db:seed php artisan db:seed --class=ProdutoSeeder php artisan migrate:fresh --seed
The db:seed runs the DatabaseSeeder. With --class it runs a specific one. The fresh --seed recreates everything with data.
Column modifiers
$table->string('state')->default('active');
$table->text('grade')->nullable();
$table->string('slug')->unique();
$table->integer('order')->after('name');
$table->string('code')->index();Modifiers: default() default value, nullable() accepts NULL, unique() unique value, index() creates an index, after() position.
Rename and check
Schema::rename('products', 'artigos');
Schema::hasTable('products');
Schema::hasColumn('products', 'sku');The rename() changes the table name. The hasTable() and hasColumn() check existence before conditional operations.
Call other seeders
// DatabaseSeeder.php
public function run()
{
$this->call([
UserSeeder::class,
ProdutoSeeder::class,
CategoriaSeeder::class,
]);
}The call() in the DatabaseSeeder runs seeders in order. Ideal to organize the seed by domain and control dependencies.
Foreign key
$table->foreignId('user_id')
->constrained()
->cascadeOnDelete();
$table->foreignId('category_id')
->constrained('categories')
->restrictOnDelete();The foreignId() creates the column and the constrained() defines the FK. Options: cascadeOnDelete() deletes in cascade, restrictOnDelete() prevents it.
Seeder with fixed data
public function run()
{
Product::create([
'name' => 'Keyboard',
'price' => 49.99,
]);
Product::insert([
['name' => 'Mouse', 'price' => 25],
['name' => 'Monitor', 'price' => 299],
]);
}The create() uses Eloquent (fires events). The insert() is faster for multiple records but ignores events and timestamps.
Factory states
// Define state:
public function inactive()
{
return $this->state(['active' => false]);
}
// Usage:
Product::factory()->inactive()->create();
Product::factory()->count(5)->inactive()->create();The states modify the factory for specific scenarios. Combine with count(), has() and relationships for complex data.
Blade
Show variable (escaped)
{{ $name }}
{{ $product->price }}
{{ strtoupper($title) }}The double braces {{ }} show the value while escaping HTML automatically (prevents XSS). It accepts any PHP expression.
Forelse (with fallback)
@forelse($products the $product)
<p>{{ $product->name }}</p>
@empty
<p>None product found.</p>
@endforelseThe @forelse combines loop with fallback. If the collection is empty, it shows the @empty block instead of rendering nothing.
Blade components
<x-alert type="success" />
<x-card title="Product">
<p>Content do slot</p>
</x-card>
{{-- resources/views/components/alert.blade.php --}}
<div class="alert {{ $type }}">{{ $slot }}</div>The components live in resources/views/components/. Props are attributes and the content goes in the $slot. Cleaner than partials.
Old value (old)
<input name="email" value="{{ old('email', '') }}">
<input name="name" value="{{ old('name', $product->name ?? '') }}">
@error('email')
<span class="error">{{ $message }}</span>
@enderrorThe old() restores the submitted value after a validation error. The @error shows the error message for the given field.
Unescaped HTML
{!! $contentHtml !!}
{!! $artigo->body !!}The braces with exclamation {!! !!} render raw HTML without escaping. Use only with trusted content (never user input).
$loop variable
@foreach($items the $item)
{{ $loop->iteration }} — {{ $item }}
@if($loop->first) First! @endif
@if($loop->last) Last! @endif
@if($loop->remaining) Faltam: {{ $loop->remaining }} @endif
@endforeachThe $loop variable gives iteration info: iteration (number), index (0-based), first, last, remaining, count.
CSRF token
<form method="POST" action="/products">
@csrf
<input type="text" name="name">
<button type="submit">Create</button>
</form>The @csrf generates a hidden token required in POST/PUT/DELETE forms. Without it, Laravel rejects with a 419 error.
Condition directives
@isset($product)
Product definido
@endisset
@empty($products)
List empty
@endempty
@unless($escondido)
Visible
@endunlessThe @isset checks if it exists, @empty if it is empty and @unless is the inverse of @if (runs if false).
If conditional
@if($user->isAdmin())
<p>Admin</p>
@elseif($user->isEditor())
<p>Editor</p>
@else
<p>User</p>
@endifThe @if, @elseif, @else and @endif directives work like PHP. They accept any boolean expression.
Inherit layout
{{-- resources/views/products/index.blade.php --}}
@extends('layouts.app')
@section('content')
<h1>Products</h1>
@endsection
@section('title', 'List de Products')The @extends inherits from the layout and @section fills the sections defined with @yield. The short form passes the value directly.
PUT/DELETE methods in forms
<form method="POST" action="/products/{{ $product->id }}">
@csrf
@method('PUT')
<input type="text" name="name" value="{{ $product->name }}">
</form>HTML only supports GET/POST. The @method('PUT') adds a hidden field that Laravel interprets the the given verb.
Stacks and Push
{{-- In the layout: --}}
@stack('scripts')
{{-- In the view: --}}
@push('scripts')
<script src="/js/products.js"></script>
@endpushThe @stack defines an accumulation point and @push adds content. Ideal for scripts and CSS specific to each page.
Foreach loop
@foreach($products the $product)
<p>{{ $product->name }} — {{ $product->price }}€</p>
@endforeachThe @foreach iterates over collections or arrays. It accesses each element the a variable. Ends with @endforeach.
Include partial
@include('partials.header')
@include('partials.card', ['title' => 'Product X'])
{{-- With condition: --}}
@includeWhen($user, 'partials.user-bar')The @include inserts another view. The second argument passes data. The @includeWhen only includes if the condition is true.
Auth and Guest
@auth
Hello, {{ auth()->user()->name }}
@endauth
@guest
<a href="/login">Faz login</a>
@endguestThe @auth shows content for authenticated users and @guest for visitors. An alternative to @if(auth()->check()).
Validation
Basic validation
$data = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'age' => 'required|integer|min:18',
]);The validate() checks the data and redirects with errors automatically if it fails. If it passes, it returns the validated data.
Custom messages
$request->validate($rules, [
'name.required' => 'The name field is required.',
'email.unique' => 'This email already exists.',
'age.min' => 'Must have at least :min years.',
]);The second argument of validate() defines messages by field.rule. The :min placeholder is replaced by the rule's value.
Manual validation
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}The Validator::make() gives full control over the flow. Check with fails() and redirect manually with withErrors() and withInput().
Most common rules
required → required email → formato de email min:8 → minimum 8 characters max:255 → maximum 255 numeric → only numbers integer → number integer boolean → true/false url → URL valid date → data valid unique:table → value unique na table exists:table → value must existir
Rules chain together with |. The unique and exists check against the database. They accept parameters: unique:users,email.
FormRequest (dedicated class)
class StoreProdutoRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required|string|max:255',
'price' => 'required|numeric|min:0',
];
}
public function authorize()
{
return true;
}
}The FormRequest isolates validation in a class. The authorize() controls permission. Inject it in the controller: store(StoreProdutoRequest $request).
Show errors in the view
@error('email')
<span class="text-danger">{{ $message }}</span>
@enderror
@if($errors->any())
<ul>
@foreach($errors->all() the $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endifThe @error shows a field's error. The $errors->any() checks if there are errors and $errors->all() lists them all.
Confirm password
$request->validate([
'password' => 'required|min:8|confirmed',
]);
// In the form: password_confirmation fieldThe confirmed rule requires a password_confirmation field with the same value. Used in registration and password change.
Validate arrays
$request->validate([
'items' => 'required|array|min:1',
'items.*.name' => 'required|string',
'items.*.quantity' => 'required|integer|min:1',
]);The * validates each element of an array. The array ensures it is an array and min:1 that it has at least one element.
Date rules
'data_start' => 'required|date', 'data_end' => 'required|date|after:data_start', 'nascimento' => 'date|before:today', 'agendamento' => 'date|after_or_equal:now',
Date rules: date validates the format, after: and before: compare with another date or field. Accepts today, now or a field name.
Optional field (nullable)
'phone' => 'nullable|string|max:20', 'website' => 'nullable|url', 'data_end' => 'nullable|date|after:data_start',
The nullable allows empty/null value. Without it, the other rules imply it is required. Ideal for optional fields with a specific format.
Conditional rules
use Illuminate\Validation\Rule;
'email' => [
'required',
Rule::unique('users')->ignore($user->id),
],
'type' => Rule::in(['fisica', 'juridica']),The Rule class allows complex rules. The ignore() excludes the current record on update. The Rule::in() validates against a list of values.
Validate files
$request->validate([
'photo' => 'required|image|mimes:jpg,png|max:2048',
'document' => 'file|mimes:pdf,doc|max:10240',
]);
// max in KB (2048 = 2MB)The image rule validates it is an image, mimes restricts types and max limits the size in KB. The file ensures it is an upload.
Authentication and Security
Current user
$user = auth()->user(); $name = auth()->user()->name; $id = auth()->id(); // Alternative: use Illuminate\Support\Facades\Auth; $user = Auth::user();
The auth() helper returns the authenticated user (or null). The auth()->id() is a shortcut for the ID. The Auth facade is equivalent.
Protect route with auth
Route::get('/panel', ...)->middleware('auth');
Route::middleware('auth')->group(function () {
Route::get('/profile', ...);
Route::get('/settings', ...);
});The auth middleware redirects to login if not authenticated. Apply it on individual routes or entire groups.
Use Policy
// In the controller:
$this->authorize('update', $product);
// Conditional:
if ($user->can('update', $product)) { ... }
// In the route:
Route::put('/products/{product}', ...)
->middleware('can:update,product');The authorize() checks the policy and throws 403 if denied. The can() returns a boolean. The can: middleware protects the route directly.
Encryption
use Illuminate\Support\Facades\Crypt;
$encriptado = Crypt::encryptString('dado secreto');
$original = Crypt::decryptString($encriptado);
$encriptado = Crypt::encrypt(['key' => 'value']);The Crypt encrypts/decrypts data with the APP_KEY. Unlike hashing, it is reversible. Use it for sensitive data you need to read later.
Check authentication
if (auth()->check()) {
// user autenticado
}
if (auth()->guest()) {
// not autenticado
}The check() returns true if there is an active session. The guest() is the inverse. Prefer check() over comparing user() !== null.
Password hash
use Illuminate\Support\Facades\Hash;
$hash = Hash::make($request->password);
if (Hash::check($input, $user->password)) {
// password correta
}The Hash::make() generates a secure hash with bcrypt. The Hash::check() compares input with the stored hash. Never store passwords in plain text.
CSRF protection
// In forms:
@csrf
// In AJAX (header):
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
}The CSRF token protects against cross-site request forgery. Required in POST/PUT/DELETE. In AJAX, send it in the X-CSRF-TOKEN header.
Sanitize input
// Blade escapes automatically:
{{ $input }} // safe
// Clean input:
$name = strip_tags($request->name);
$clean = htmlspecialchars($input, ENT_QUOTES);
// Never trust:
{!! $request->input('html') !!} // PERIGOSOBlade escapes with {{ }} by default. For user HTML, use strip_tags() or purification libraries. Never use {!! !!} with input.
Manual login
Auth::login($user);
Auth::login($user, remember: true);
// By credentials:
if (Auth::attempt(['email' => $email, 'password' => $pass])) {
return redirect()->intended('/panel');
}The login() authenticates programmatically. The attempt() checks credentials and creates the session. The intended() redirects to the original page.
Gate (simple authorization)
// AppServiceProvider:
Gate::define('admin', function ($user) {
return $user->role === 'admin';
});
// Usage:
if (Gate::allows('admin')) { ... }
if (Gate::denies('admin')) { ... }
Gate::authorize('admin');The Gate defines simple abilities based on the user. The allows() returns a boolean and authorize() throws 403 if denied.
Remember me
Auth::attempt($credentials, $request->remember);
// Check:
if (Auth::viaRemember()) {
// login via cookie "remember-me"
}The second argument of attempt() enables "remember me" with a persistent cookie. The viaRemember() checks if the login was via cookie.
Logout
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');The logout() ends the session. The invalidate() clears the data and regenerateToken() prevents session fixation (CSRF).
Policy (authorization by model)
class ProductPolicy
{
public function update(User $user, Product $product)
{
return $user->id === $product->user_id;
}
public function delete(User $user, Product $product)
{
return $user->id === $product->user_id;
}
}The policy defines authorization rules tied to a model. Each method receives the User and the model instance and returns a boolean.
Rate limiting on login
use Illuminate\Support\Facades\RateLimiter;
$key = 'login:' . $request->ip();
if (RateLimiter::tooManyAttempts($key, 5)) {
abort(429, 'Tentativas excedidas');
}
RateLimiter::hit($key, 60);The RateLimiter limits login attempts per IP. After 5 failures in 60 seconds, it blocks. Essential against brute force.
Helpers
dd() and dump()
dd($variable); // dump e morre dump($variable); // dump e continua dd($request->all()); dd(Product::find(1)->toArray());
The dd() shows the formatted content and stops execution (essential for debugging). The dump() shows but continues. They accept multiple arguments.
now() and Carbon
$now = now();
$tomorrow = now()->addDay();
$yesterday = now()->subDay();
$formato = now()->format('d/m/Y H:i');
$diff = now()->diffInDays($data);The now() returns a Carbon instance with date methods: addDay(), subMonth(), format(), diffInDays(), isWeekend().
old()
<input name="name" value="{{ old('name') }}">
<input name="email" value="{{ old('email', $user->email) }}">The old() retrieves the submitted value after a validation error. The second argument is the default value (e.g. the model's current value).
view() and response()
return view('products.index', ['products' => $data]);
return response('Text', 200)->header('X-Custom', 'v');
return response()->json(['ok' => true]);
return response()->download($path);The view() renders a Blade template with data. The response() creates HTTP responses with a custom status, headers and content.
env() and config()
$debug = env('APP_DEBUG', false);
$name = config('app.name');
// Set at runtime:
config(['app.locale' => 'en']);The env() reads from .env (only in config!). The config() reads from the configuration files. In production with cache, env() returns null outside of config.
redirect() and back()
return redirect('/products');
return redirect()->route('products.index');
return back();
return back()->with('msg', 'Done!');
return redirect()->away('https://external.com');The redirect() redirects to a URL or route. The back() returns to the previous page. The away() allows external URLs.
str() — strings
str('Hello World')->slug(); // hello-world
str('abc')->upper(); // ABC
str('text long')->limit(10); // text lon...
str('name_complete')->camel(); // nameComplete
str('FullName')->snake(); // name_completeThe str() helper offers a fluent API for strings: slug(), upper(), lower(), limit(), camel(), snake(), plural().
bcrypt() and tap()
$hash = bcrypt('password123');
// tap: runs a callback and returns the object
$user = tap(User::find(1), function ($u) {
$u->name = 'New Name';
$u->save();
});The bcrypt() is a shortcut for Hash::make(). The tap() runs a callback on an object and returns it, useful for inline operations.
route() and url()
$url = route('products.show', $product);
// http://site.test/products/1
$url = url('/products');
// http://site.test/products
$url = url()->current();The route() generates a URL for named routes (passing parameters). The url() generates from the root. The current() returns the current URL.
session()
// Store:
session(['key' => 'value']);
session()->put('key', 'value');
// Read:
$value = session('key', 'pattern');
// Flash (only next request):
session()->flash('msg', 'Success!');The session() reads and stores values. The flash() stores data only for the next request (temporary messages).
collect() and data_get()
$collection = collect([1, 2, 3]); // Safe nested access: $name = data_get($array, 'user.profile.name', 'Anonymous'); $value = data_get($obj, 'a.b.c', null);
The collect() creates collections. The data_get() accesses nested values with dot notation and a default value, with no risk of error on missing keys.
asset()
<img src="{{ asset('img/logo.png') }}">
<link href="{{ asset('css/app.css') }}">
<script src="{{ asset('js/app.js') }}"></script>The asset() generates a URL for files in the public/ folder. It respects the ASSET_URL from .env (useful for a CDN).
abort()
abort(404); abort(403, 'Sem permission'); abort_if(!$product, 404); abort_unless(auth()->check(), 401);
The abort() throws an HTTP exception. The abort_if() aborts if the condition is true and abort_unless() if it is false.
app() and resolve()
$service = app(MyClass::class);
$logger = app('log');
// Equivalent:
$service = resolve(MyClass::class);The app() resolves dependencies from the Service Container. Without arguments it returns the application instance. The resolve() is an alias.
Advanced Tips
Query caching
$products = Cache::remember('products', 3600, function () {
return Product::all();
});
// Permanent cache:
$data = Cache::rememberForever('config-site', fn() => Setting::all());The Cache::remember() stores the result for N seconds. If it exists in cache, it returns without running the closure. The rememberForever() never expires.
Events and Listeners
// Fire:
event(new OrderCreated($order));
// Listener:
class SendNotification
{
public function handle(OrderCreated $event)
{
$order = $event->order;
// send notification
}
}The event() fires an event. The registered listeners react to it. It decouples the logic: whoever creates the order doesn't need to know who notifies.
Model Observers
class ProdutoObserver
{
public function created(Product $product)
{
Log::info("Created: {$product->id}");
}
public function deleted(Product $product)
{
Cache::forget('products');
}
}The Observer reacts to model events: created, updated, deleted, saving. Register it in the provider with Product::observe().
DB transactions
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
$order->save();
$item->save();
$stock->decrement('quantity');
});
// If something fails, everything is revertedThe DB::transaction() runs operations atomically. If an exception occurs, it does an automatic rollback. Essential for interdependent operations.
Invalidate cache
Cache::forget('products');
Cache::flush();
// Tags (Redis/Memcached):
Cache::tags(['products'])->put('list', $data, 3600);
Cache::tags(['products'])->flush();The forget() removes a key and flush() clears everything. The tags allow grouping and invalidating related caches at once.
Service Container
// Bind in the provider: $this->app->bind(PaymentGateway::class, StripeGateway::class); // Singleton: $this->app->singleton(Logger::class, fn() => new FileLogger()); // Resolve: $gateway = app(PaymentGateway::class);
The container resolves dependencies automatically. The bind() maps interface→implementation. The singleton() creates a single shared instance.
Scheduler (cron tasks)
// routes/console.php or Kernel:
Schedule::command('emails:send')->daily();
Schedule::command('backup')->weekly();
Schedule::job(new GerarRelatorio())->hourly();
Schedule::call(fn() => Cache::flush())->dailyAt('03:00');The Scheduler schedules tasks without an external cron. Methods: daily(), hourly(), weekly(), everyMinute(), dailyAt().
Avoid N+1
// In development (AppServiceProvider):
Model::preventLazyLoading(!app()->isProduction());
// Correct:
$orders = Order::with('client', 'items.product')->get();
// Lazy by chunks:
Product::lazy(200)->each(fn($p) => $p->process());The preventLazyLoading() throws an error when it detects N+1 in development. Use with() for eager loading. The lazy() processes in chunks without loading everything.
Queues (Jobs)
// Dispatch: EnviarEmail::dispatch($user); EnviarEmail::dispatch($user)->delay(now()->addMinutes(5)); // Process: php artisan queue:work php artisan queue:work --tries=3
The dispatch() sends the job to the queue. The queue:work processes in the background. The delay() schedules it for later. Ideal for emails and heavy tasks.
Local Scopes
// In the model:
public function scopeAtivos($query)
{
return $query->where('active', true);
}
public function scopeCaros($query, $minimum)
{
return $query->where('price', '>', $minimum);
}
// Usage:
Product::ativos()->caros(100)->get();The scopes are reusable filters on the model. Prefix with scope and use without the prefix. They accept extra parameters.
Logs
use Illuminate\Support\Facades\Log;
Log::info('User entrou', ['id' => $user->id]);
Log::error('Failure no payment', ['order' => $id]);
Log::warning('Stock low', ['product' => $p->name]);
Log::debug('Query executed', ['sql' => $sql]);The Log writes to storage/logs/laravel.log. Levels: debug, info, warning, error, critical. The second argument is context.
File upload
$path = $request->file('photo')->store('photos', 'public');
$url = Storage::url($path);
// Validate:
$request->validate(['photo' => 'image|max:2048']);
// Download:
return Storage::download('files/doc.pdf');The store() saves the upload on the configured disk. The Storage::url() generates the public URL. Use the public disk with php artisan storage:link.
Create a Job
class EnviarEmail implements ShouldQueue
{
use Dispatchable, Queueable;
public function __construct(public User $user) {}
public function handle(Mailer $mailer)
{
$mailer->to($this->user)->send(new Welcome());
}
}The ShouldQueue marks the job for queues. The handle() contains the logic and receives dependencies via the container. The constructor receives the data.
Accessors and Mutators
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function nameComplete(): Attribute
{
return Attribute::make(
get: fn() => $this->name . ' ' . $this->surname,
set: fn($v) => ['name' => strtok($v, ' '), 'surname' => strtok(' ')],
);
}The Accessors transform values on read and the Mutators on write. Access it the a property: $user->name_complete.
HTTP Client
use Illuminate\Support\Facades\Http;
$response = Http::get('https://api.example.com/data');
$data = $response->json();
$response = Http::withToken($token)
->post('https://api.example.com/orders', ['item' => 1]);The Http client makes external calls with a fluent API. It supports get(), post(), headers, tokens, timeout and retry. It replaces cURL.
Notifications
$user->notify(new PedidoEnviado($order));
// In the notification:
public function via($notifiable)
{
return ['mail', 'database'];
}
public function toMail($notifiable)
{
return (new MailMessage)->subject('Shipped!')->line('...');
}The notify() sends notifications through multiple channels: mail, database, sms, slack. Each channel has its own toX() method.