Laravel Form Requests
In Laravel, validating forms is key to maintaining the safety and accuracy of user data. The FormRequest component simplifies this by keeping the …
ReadThe Pending Object pattern plays a key role in Laravel, as it is utilized almost in all its aspects. It offers an exceptional developer experience (DX) for artisans.
Have you ever wondered what transpires when you utilize the Mail::to
method?
1Mail::to($request->user())
2 ->send(new OrderShipped($order));
The to()
method here does not yield a Mail object. Rather, it results in a PendingMail
object.
1namespace Illuminate\Mail;
2
3class Mailer
4{
5 public function to($users, $name = null)
6 {
7 if (! is_null($name) && is_string($users)) {
8 $users = new Address($users, $name);
9 }
10
11 return (new PendingMail($this))->to($users);
12 }
13}
The advantage of this approach is that each Mail::to
will have its exclusive pending object where you can invoke several methods to modify any aspect related to this specific mail object you’ve initiated.
1namespace Illuminate\Mail;
2
3class PendingMail
4{
5 public function __construct(MailerContract $mailer)
6 public function locale($locale)
7 public function to($users)
8 public function cc($users)
9 public function bcc($users)
10 public function send(MailableContract $mailable)
11 public function queue(MailableContract $mailable)
12 public function later($delay, MailableContract $mailable)
13}
So, if we investigate what the cc()
method is doing:
1/**
2 * Set the recipients of the message.
3 *
4 * @param mixed $users
5 * @return $this
6 */
7public function cc($users)
8{
9 $this->cc = $users;
10
11 return $this;
12}
It seems to resemble a Data Transfer Object (DTO), in which you use setters and getters for data exchange between your application’s layers. However, a significant distinction in Laravel’s core approach is that Pending Objects are actionable. This principle is apparent in methods such as send
and queue
.
1public function send(MailableContract $mailable)
2{
3 return $this->mailer->send($this->fill($mailable));
4}
5
6public function queue(MailableContract $mailable)
7{
8 return $this->mailer->queue($this->fill($mailable));
9}
there exists an array of Pending Objects for you to explore and comprehend their functionality:
Now, let’s attempt constructing a Pending Action for a CSV exporter.
1$users = User::all()->toArray();
2
3CsvExporter::from($users)
4 ->columns(['email', 'username'])
5 ->noHeaders()
6 ->download()
This example demonstrates a CSV exporter and how a Pending Object can assist us in constructing this CSV file. First, we’ll create the CsvExporter
class.
1namespace App\Services\Exporter;
2
3class CsvExporter
4{
5 public function from(array $data): PendingCsvExport
6 {
7 return new PendingCsvExport($data, $this);
8 }
9
10 public function generate(array $data, array $columns, string $delimiter = ',', bool $includeHeaders = true): string
11 {
12 $output = fopen('php://temp', 'r+');
13
14 if ($includeHeaders && !empty($data) && !empty($columns)) {
15 fputcsv($output, $columns, $delimiter);
16 }
17
18 foreach ($data as $row) {
19 $selectedData = [];
20 foreach ($columns as $column) {
21 $selectedData[] = $row[$column] ?? null;
22 }
23 fputcsv($output, $selectedData, $delimiter);
24 }
25
26 rewind($output);
27 $csvContent = stream_get_contents($output);
28 fclose($output);
29
30 return $csvContent;
31 }
32}
Beyond the generate method, I’ve chosen a straightforward approach to demonstrate and create a real CSV export function. However, you can opt for a package specifically designed for this task if you wish.
Next, let’s establish the PendingCSVExport object.
1namespace App\Services\Exporter;
2
3use Illuminate\Support\Facades\Response;
4
5class PendingCsvExport
6{
7 protected array $data;
8 protected array $columns = [];
9 protected bool $includeHeaders = true;
10 protected string $delimiter = ',';
11 protected CsvExporter $exporter;
12
13 public function __construct(array $data, CsvExporter $exporter)
14 {
15 $this->data = $data;
16 $this->exporter = $exporter;
17 }
18
19 public function columns(array $columns)
20 {
21 $this->columns = $columns;
22 return $this;
23 }
24
25 public function noHeaders()
26 {
27 $this->includeHeaders = false;
28 return $this;
29 }
30
31 public function delimiter(string $delimiter)
32 {
33 $this->delimiter = $delimiter;
34 return $this;
35 }
36
37 public function download($filename = 'export.csv')
38 {
39 $content = $this->exporter->generate($this->data, $this->columns, $this->delimiter, $this->includeHeaders);
40
41 return Response::make($content, 200, [
42 'Content-Type' => 'text/csv',
43 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
44 ]);
45 }
46}
See here how our PendingObject holds some properties about the CSV layout and data. Then a single action method download
. Later on you can add more actions like stream
, queue
, and mail
. To either queue the export and send it as a mail. Or to just generate the CSV and mail it directly to the user.
Have you ever considered the mechanism behind dispatch jobs operating in numerous ways?
1ProcessPodcast::dispatch();
2
3ProcessPodcast::dispatch()->onQueue('emails');
Observing how dispatch
merely dispatches the job, but also, if you chain a method like onQueue
, it takes that into account and still dispatches the job, you might wonder about the driver behind this operation. The answer lies in the magic __destruct
method.
1public function __destruct()
2{
3 if (! $this->shouldDispatch()) {
4 return;
5 } elseif ($this->afterResponse) {
6 app(Dispatcher::class)->dispatchAfterResponse($this->job);
7 } else {
8 app(Dispatcher::class)->dispatch($this->job);
9 }
10}
So what actually occurs here is that when you write SomeJob::dispatch()
, it only returns a PendingObject. Subsequently, PHP invokes the __destruct
method when it commences the garbage collection process (you can read more about it from the PHP.NET Documentation
) Laravel leverages this technique to conveniently self-execute the pending object, eliminating the need for you to trigger a concluding method such as ->run()
or ->send()
.
And that wraps up our discussion on the Pending Object pattern.
Happy Coding!
In Laravel, validating forms is key to maintaining the safety and accuracy of user data. The FormRequest component simplifies this by keeping the …
ReadI’ve always been interested in real-time technologies, especially when dealing with high traffic and complex issues. I once encountered a …
Read