These ten questions run the length of the chapter, and every one of them turns on behavior that catches working Laravel 2,157 developers out. The code runs on Laravel 13 against the bookshop tables in SQLite 4,756 . Treat each numbered snippet as separate, run nothing, and write down exactly what it returns, prints or throws, and why. The answers, with the reasoning and the section each comes from, are in Appendix H.
Questions
// routes/api.php. The alias 'api.key' is RequireApiKey, which answers 401 unless the
// X-Api-Key header matches. Product 1 exists; product 999 does not.
Route::get('/products/{product}', fn (Product $product) => $product->only('id', 'sku'))
->middleware('api.key');
Route::get('/raw/{product}', fn (Product $product) => ['exists' => $product->exists,
'id' => $product->id])->withoutMiddleware(SubstituteBindings::class);
// routes/web.php. OrderPolicy::view returns Response::denyAsNotFound() unless the order
// is yours. Ben owns order 2 only, order 99 does not exist, and APP_DEBUG is false.
Route::get('/orders/{order}', fn (Order $order) => $order->only('id'))->can('view', 'order');
// Each line runs inside a feature test, and no API key is sent.
// 1. Which two statuses? Which two after bootstrap/app.php adds
// $middleware->prependToPriorityList(SubstituteBindings::class, RequireApiKey::class)?
echo $this->getJson('/api/products/1')->status(), ' ',
$this->getJson('/api/products/999')->status(), "\n";
// 2. Neither request fails. What does each return?
echo $this->getJson('/api/raw/1')->content(), ' ',
$this->getJson('/api/raw/999')->content(), "\n";
// 3. Both are 404s. How can an API client still tell a private order from a missing one?
echo $this->actingAs($ben)->getJson('/orders/1')->content(), "\n";
echo $this->actingAs($ben)->getJson('/orders/99')->content(), "\n";// 4. Product has #[Fillable(['sku', 'title', 'price'])] and a stock column that defaults
// to 0; Review has #[Guarded(['id'])] and an is_featured column. One product exists.
$p = Product::create(['sku' => 'BK-9', 'title' => 'Go', 'price' => 9,
'id' => 50, 'stock' => 7]);
echo json_encode([$p->id, $p->stock, $p->update(['stock' => 3]), $p->fresh()->stock]), "\n";
$r = new Review(['rating' => 5, 'ID' => 9, 'is_featured' => 1, 'spam' => 1]);
echo json_encode($r->getAttributes()), "\n";
// 5. Five orders belong to two customers. How many queries does each line send?
DB::enableQueryLog();
$q = fn (callable $work) => [DB::flushQueryLog(), $work(), count(DB::getQueryLog())][2];
echo $q(fn () => Order::all()->each(fn ($o) => $o->customer->name)), ' ';
echo $q(fn () => Order::with('customer')->get()
->each(fn ($o) => $o->customer->orders->count())), ' ';
echo $q(fn () => Order::with('customer')->get()
->each(fn ($o) => $o->customer->orders()->count())), "\n";
// 6. Product::casts() returns ['price' => 'decimal:2', 'active' => 'boolean',
// 'released_on' => 'datetime:Y-m-d'].
$p = (new Product)->forceFill(['price' => 12.5, 'active' => 'false',
'released_on' => '2026-01-31']);
var_dump($p->price, $p->active);
echo $p->released_on, ' | ', $p->toArray()['released_on'], "\n";// 7. A migration makes a review's author optional. What does `migrate --pretend` print
// for customer_id, and what does the create() call do?
$table->foreignId('customer_id')->constrained()->nullable();
Review::create(['product_id' => 1, 'customer_id' => null, 'rating' => 4]);
// 8. Validator::make() on plain arrays, outside any HTTP request.
$v = fn (array $d, string $r) => Validator::make($d, ['f' => $r])->errors()->all();
echo json_encode([$v(['f' => '10'], 'max:5'), $v(['f' => '10'], 'integer|max:5')]), "\n";
echo json_encode([$v(['f' => ''], 'integer'), $v(['f' => null], 'integer'),
$v(['f' => 'abc'], 'integer'), $v([], 'integer')]), "\n";
$data = ['f' => '', 'g' => 1];
echo json_encode(Validator::make($data, ['f' => 'integer'])->validated()), "\n";// 9. CACHE_STORE=database, and neither key exists yet.
$runs = ['null' => 0, 'false' => 0];
foreach (range(1, 3) as $i) {
Cache::remember('a', 60, function () use (&$runs) { $runs['null']++; return null; });
Cache::remember('b', 60, function () use (&$runs) { $runs['false']++; return false; });
}
echo json_encode($runs), ' ', json_encode([Cache::has('a'), Cache::has('b')]), "\n";
var_dump(Cache::get('a', 'default'), Cache::get('b', 'default'), Cache::add('b', true, 60));
// 10. QUEUE_CONNECTION=database. How many "attempt" lines reach the log under the worker
// below, where does the job end up, and what changes on the sync connection?
#[Tries(3)]
class Flaky implements ShouldQueue
{
use Queueable;
public function handle(): void
{
Log::info('attempt '.$this->attempts());
throw new RuntimeException('warehouse down');
}
}
Flaky::dispatch(); // then: php artisan queue:work --tries=5 --stop-when-empty
// With QUEUE_CONNECTION=sync instead:
try {
Flaky::dispatch();
echo "queued\n";
} catch (RuntimeException $e) {
echo 'caught: ', $e->getMessage(), "\n";
}