DevTools

Cheatsheet Laravel

Framework PHP para desenvolvimento web

Back to languages
Laravel
167 cards found
Categories:
Versions:

Collections


13 cards
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'); // 1

The 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


12 cards
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


14 cards
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


14 cards
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}   → destroy

The 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 minute

The 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/users

The 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


12 cards
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


18 cards
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 20

The 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 column

The 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


13 cards
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


15 cards
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>
@endforelse

The @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>
@enderror

The 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
@endforeach

The $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
@endunless

The @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>
@endif

The @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>
@endpush

The @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>
@endforeach

The @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>
@endguest

The @auth shows content for authenticated users and @guest for visitors. An alternative to @if(auth()->check()).

Validation


12 cards
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>
@endif

The @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 field

The 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


14 cards
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') !!}  // PERIGOSO

Blade 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


14 cards
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_complete

The 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


16 cards
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 reverted

The 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.