PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
131: Laravel Queues and Jobs
A few years ago, I was reviewing a PR for a junior dev who had built a sleek onboarding flow. Everything looked great until I actually tested the "Sign Up" button. I clicked it, and the browser just... hung. Ten seconds later, the page finally loaded. I dug into the code and found that the controller was sequentially sending a welcome email, pinging a CRM API, and generating a PDF welcome kit before returning the response to the user. The user was essentially waiting for three different external servers to respond before they could even see their dashboard.
That's a classic mistake. In a web environment, the user's request should be as short as possible. If a task doesn't need to happen *right this second* for the page to render, it shouldn't be happening in the request cycle. This is where Laravel Queues come in. They allow you to offload time-consuming tasks to a background process, letting you tell the user "Success!" immediately while the server handles the heavy lifting in the background.
Defining Work with Job Classes
In Laravel, a "Job" is simply a class that represents a specific task. Instead of putting your logic directly in a controller, you wrap it in a Job class. You can generate one quickly using php artisan make:job ProcessNewsletterSubscription. When you open that file, you'll see a handle() method. This is where the actual work lives.
I always recommend passing only the necessary IDs into the Job's constructor rather than passing full Eloquent models. Why? Because the Job is serialized and stored in your database or cache. If you pass a whole User model and that user changes their email address between the time the job is dispatched and the time it's executed, the job will be working with stale data. Pass the ID, and fetch the fresh model inside the handle() method.
public function handle(NewsletterService $service)
{
// We fetch the fresh user record inside the handler
$user = User::findOrFail($this->userId);
$service->subscribe($user);
}
To trigger this, you just call ProcessNewsletterSubscription::dispatch($user->id);. At that point, Laravel pushes a record onto your queue and immediately continues to the next line of code in your controller.
Managing the Queue Pipeline
Now, the job is "dispatched," but it isn't actually running yet. It's just sitting in a list. To make it move, you need a queue driver and a worker. By default, Laravel uses the sync driver, which—to be honest—is useless for actual background processing because it executes the job immediately in the current request. For anything real, you'll want to switch your QUEUE_CONNECTION in the .env file to database or redis.
If you go with the database driver, you'll need to run the migrations to create the jobs table. Once that's set, you need a process running on your server to watch that table and execute the jobs. That's what the worker does:
php artisan queue:work
In a production environment, you wouldn't just run this in your terminal and hope for the best. You'd use a process monitor like Supervisor to ensure that if the queue:work process crashes, it gets restarted automatically. I've spent way too many hours debugging "why aren't my emails sending?" only to realize the worker had crashed three days prior.
Handling Failure and Retries
External APIs are flaky. They time out, they hit rate limits, and they go down for maintenance. If your job fails, you don't want it to just vanish into the void. Laravel gives you an elegant way to handle this by defining a $tries property on your job class.
By setting public $tries = 5;, Laravel will attempt to run the job again if an exception is thrown. You can even use backoff to tell Laravel to wait a few seconds or minutes between attempts, giving the external API time to recover. If the job still fails after all attempts, it gets moved to a failed_jobs table. You can then inspect the error and use php artisan queue:retry all to push them back into the queue once you've fixed the underlying bug.
📋 Practical Task
Implementing a Background PDF Invoice Generator
You are working on an accounting app. Currently, the app generates a PDF invoice using a heavy library and emails it to the client directly inside the InvoiceController@store method, causing the UI to freeze for several seconds.
Your task:
- Create a new Job class called
GenerateInvoicePdf. - Modify the Job to accept an
$invoiceIdin its constructor. - In the
handle()method, simulate the PDF generation and emailing process (you can useLog::info("Generating PDF for invoice {$this->invoiceId}");andsleep(3);to simulate the delay). - Update the
InvoiceController@storemethod to dispatch this job instead of running the logic inline. - Ensure the job is configured to retry up to 3 times in case of a simulated failure.
There are no comments for now.