LARAVEL Case Studies & Practical Application 4 — Questions and Answers
Question 1: A Laravel app has a users table and a roles table with a many-to-many relationship. A query for all admins is returning duplicate users. What is the Eloquent fix?
- Add ->distinct() or use whereHas('roles', fn($q) => $q->where('name', 'admin')) instead of a JOIN (Correct answer)
- Add a UNIQUE constraint to the pivot table's user_id column
- Use groupBy('users.id') in raw SQL
- Delete duplicate rows from the role_user pivot table
Correct answer: Add ->distinct() or use whereHas('roles', fn($q) => $q->where('name', 'admin')) instead of a JOIN
whereHas() issues an EXISTS subquery that avoids duplicate rows without needing DISTINCT or GROUP BY.
Question 2: You are building a RESTful API and want to transform Eloquent models into consistent JSON responses (hiding certain fields, renaming keys, adding computed fields). Which Laravel feature is purpose-built for this?
- API Resources (JsonResource and ResourceCollection) (Correct answer)
- Appending attributes using $appends on the model
- Overriding toJson() on the model
- Using response()->json() with a manually built array in each controller
Correct answer: API Resources (JsonResource and ResourceCollection)
API Resources provide a transformation layer between models and JSON responses, supporting field hiding, renaming, and computed properties.
Question 3: A subscription app must prevent a user from subscribing twice to the same plan. The check and insert happen in two separate database calls. Under concurrent requests, a race condition causes duplicate subscriptions. What is the correct fix?
- Wrap the check and insert in a DB::transaction() with a SELECT ... FOR UPDATE lock or use firstOrCreate() with a unique index (Correct answer)
- Add a sleep(1) before the insert to reduce collision chance
- Check for duplicates in the Blade template before showing the subscribe button
- Use a Redis cache flag to track pending subscriptions
Correct answer: Wrap the check and insert in a DB::transaction() with a SELECT ... FOR UPDATE lock or use firstOrCreate() with a unique index
A unique database index is the ultimate guard, and SELECT FOR UPDATE inside a transaction prevents concurrent reads from both passing the check.
Question 4: Your application must support multiple payment gateways (Stripe, PayPal, Square) and allow switching between them via a config value. What design pattern should the Laravel service container enforce?
- Bind a PaymentGatewayInterface in the container and resolve the concrete class based on config('payment.driver') (Correct answer)
- Create a PaymentController for each gateway and route to the correct one
- Use environment-specific .env files to swap gateway credentials only
- Hardcode each gateway behind a switch statement in the checkout controller
Correct answer: Bind a PaymentGatewayInterface in the container and resolve the concrete class based on config('payment.driver')
Binding an interface in the service container and resolving based on config enables runtime gateway switching without changing calling code.
Question 5: A content management Laravel app uses soft deletes. An editor accidentally deleted an article. Which Eloquent method restores it?
- Article::withTrashed()->find($id)->restore() (Correct answer)
- Article::find($id)->undelete()
- DB::table('articles')->where('id', $id)->update(['deleted_at' => null])
- Article::onlyTrashed()->find($id)->save()
Correct answer: Article::withTrashed()->find($id)->restore()
withTrashed() includes soft-deleted records in the query scope, and restore() sets deleted_at back to null.
Question 6: You need to write a feature test that verifies a POST /orders endpoint stores a new order in the database and dispatches an OrderPlaced event. Which assertions cover both requirements?
- assertDatabaseHas('orders', [...]) and Event::assertDispatched(OrderPlaced::class) (Correct answer)
- response->assertStatus(200) alone verifies both side effects
- assertSeeInDatabase() and Log::assertLogged()
- Queue::assertPushed(OrderPlaced::class) and assertRedirect()
Correct answer: assertDatabaseHas('orders', [...]) and Event::assertDispatched(OrderPlaced::class)
assertDatabaseHas() confirms persistence while Event::assertDispatched() verifies the event was fired during the fake-event test run.
Question 7: A high-traffic Laravel app caches rendered HTML fragments in Redis. After a content update, stale HTML persists. What cache invalidation strategy integrates cleanest with Eloquent?
- Clear relevant cache keys inside an Eloquent observer's saved() and deleted() methods (Correct answer)
- Set a very short TTL (10 seconds) so stale data expires quickly
- Flush the entire Redis cache on every model save
- Require users to hard-refresh their browser to bypass the cache
Correct answer: Clear relevant cache keys inside an Eloquent observer's saved() and deleted() methods
Observers fire on model lifecycle events, making them the ideal place to invalidate specific cache keys without broad flushes.
A Laravel app has a users table and a roles table with a many-to-many relationship.
A query for all admins is returning duplicate users.
What is the Eloquent fix?