0

Mass assignment — $fillable vs $guarded, security implications

Intermediate5 min read·lv-12-003
securityinterview

Concept

Eloquent querying uses a fluent builder API that translates PHP method calls to SQL. The key principle: calling query methods on a Model class returns a Builder instance; calling get(), first(), find(), count(), etc. executes the query.

Finding records:

  • Model::find($id): Find by primary key, returns Model|null.
  • Model::findOrFail($id): Throws ModelNotFoundException if not found.
  • Model::findOrNew($id, $attributes): Find or create an unsaved model.
  • Model::findMany([1, 2, 3]): Find multiple by primary key.
  • Model::first(): First matching row or null.
  • Model::firstOrFail(): First or throws.
  • Model::firstOrCreate($attributes, $values): Find first matching or create.
  • Model::firstOrNew($attributes, $values): Find first matching or instantiate (not saved).
  • Model::updateOrCreate($attributes, $values): Update if found, create if not.

Retrieving collections:

  • Model::all(): All rows — avoid on large tables; use get() with constraints.
  • Model::get(): Execute query, return Collection.
  • Model::cursor(): Returns a LazyCollection with PHP generators — memory-efficient for large result sets (streams one row at a time).
  • Model::chunk(int $size, callable $callback): Retrieves in batches, passing each batch to the callback. Better than all() for large datasets.

Aggregates: count(), sum($column), avg($column), max($column), min($column) — execute immediately, return scalars.

Code Example

php
<?php
use App\Models\User;
use App\Models\Order;

// Finding by primary key
$user = User::find(42);              // null if not found
$user = User::findOrFail(42);        // throws ModelNotFoundException
$users = User::findMany([1, 2, 3]); // Collection of users

// First or create / update
$user = User::firstOrCreate(
    ['email' => 'alice@example.com'],              // search criteria
    ['name' => 'Alice', 'role' => 'customer']      // additional values if creating
);

User::updateOrCreate(
    ['email' => 'alice@example.com'],              // search criteria
    ['name' => 'Alice Updated', 'last_login' => now()] // set these values
);

// Querying with constraints
$activeAdmins = User::where('active', true)
    ->where('role', 'admin')
    ->orderBy('name')
    ->get();

// Pagination
$users = User::where('active', true)->paginate(15);       // LengthAwarePaginator
$users = User::where('active', true)->simplePaginate(15); // SimplePaginator (no total count)
$users = User::where('active', true)->cursorPaginate(15); // CursorPaginator (stable, no offset)

// Memory-efficient iteration for large tables
// chunk — batch processing
Order::where('status', 'pending')
    ->chunk(500, function($orders) {
        foreach ($orders as $order) {
            $order->process();
        }
    });

// cursor — generator-based, one row at a time
foreach (Order::cursor() as $order) {
    ProcessOrder::dispatch($order);
}

// Aggregates
$total = Order::where('status', 'completed')->count();   // int
$revenue = Order::where('status', 'completed')->sum('total'); // float
$avgOrder = Order::avg('total');

// select specific columns
$users = User::select('id', 'name', 'email')->get();

// exists / doesntExist
if (User::where('email', 'alice@example.com')->exists()) {
    // email is taken
}