Collision v5.0 released
– nunomaduro.com - submitted by Nuno Maduro
With Laravel 8 coming soon, Nuno just released Collision v5.0. Let's see the changes merged into this release.
Read more [nunomaduro.com]
Posts tagged with laravel
– nunomaduro.com - submitted by Nuno Maduro
With Laravel 8 coming soon, Nuno just released Collision v5.0. Let's see the changes merged into this release.
Read more [nunomaduro.com]
Laravel 8 offers a shiny new way to group multiple jobs into one batch. This will allow you to easily check how many job there are in a batch, what to total progress is and even cancel all jobs in a batch.
In this blog post, I'd like to share how we will use this feature in the upcoming v3 of Mailcoach. We'll also take a look at how batches are implemented under the hood in Laravel.
Join 9,500+ smart developers
Get my monthly newsletter with what I learn from running Spatie, building Oh Dear, and maintaining 300+ open source packages. Practical takes on Laravel, PHP, and AI that you can actually use.
No spam. Unsubscribe anytime. You can also follow me on X.
"Freek’s newsletter is one of the best ways to stay updated with the Laravel and PHP ecosystem. It consistently highlights useful packages, tools, and ideas from the community, especially the amazing work coming from Spatie. As a Laravel developer building SaaS and web platforms, I find it extremely helpful to discover practical tools and insights that improve my development workflow."
Earlier this year, our team released laravel-dashboard, a package that allows you to set up powerful dashboards in no time! In this talk, given at this years Laracon EU Online, I demonstrate how you can use it and how it works under the hood. Want to see more package source dives? Considering…
– tighten.co - submitted by Jamison Valenta
Here's how Tighten modernizes a legacy PHP application.
Read more [tighten.co]
Blade components are a wonderful feature of Laravel. In most examples you can see them being used for small UI elements. Did you know you can use them for layouts as well? Want to see more videos like this one? Head over to the Readable Laravel video series on our website.
Form requests are often used for validation purposes only, but they can do a whole lot more. You can manipulate data before and after validation, and you can easily add methods of your own.
In this video I'll demonstrate all these possiblities.
Want to see more videos like this one? Head over to the Readable Laravel video series on our website.
Using our laravel-model-cleanup package, you can easily remove old unneeded records from your database. We recently released a new major version that adds support for safely cleaning out huge tables.
In this blog post, I'd like to introduce this new release of the package to you.
Yesterday, the very first Laravel Worldwide Meetup was held. At the event, which was live streamed on YouTube, Joseph Silber gave an introduction to lazy collections. Mohamed Said demonstrated some very powerful additions coming to queues in Laravel 8.
You can watch a recording of the meetup below.
Since version 5, Laravel has a built-in scheduler to perform tasks at regular intervals. In normal circumstances, these scheduled tasks will run just fine.
Out of the box, Laravel doesn't offer a way to see the status of the scheduled tasks. When did they run, how long did a task run, which tasks did throw an exception?
Laravel Schedule Monitor is a new Spatie package that monitors all schedule tasks in a Laravel app. In this blog post, I'd like to introduce the package to you.

A few days ago, we improved the email notifications sent by Oh Dear. The email notifications now contain links that allow you to snooze further emails.

In this blog post, I'd like to explain why and how we added them.
Earlier this year, we added the ability to snooze notifications to Oh Dear. Each different check in Oh Dear got a snooze setting screen. On that screen, users can choose how long we shouldn't send notifications for a check.

We also introduced advanced Slack notifications. Whenever you get a notification, you can snooze further notification using the little menu underneath a notification. This way, you can snooze a check without even having to visit the Oh Dear website. Handy!

These advanced Slack got a lot of attention from us because we're using Slack notifications ourselves. But let's take a look at which notification channels are used the most at Oh Dear.
In the Oh Dear database, all notification preferences are stored in a table called notification_destinations. In the channel column, the name of the channel (mail, slack, nexmo), and so on is stored.
This query gives us the percentage for each different channel.
SELECT
channel,
ROUND(COUNT(channel) / (
SELECT
count(*)
FROM notification_destinations) * 100) AS percentage
FROM
notification_destinations
GROUP BY
channel
ORDER BY
percentage DESC
Here are the results.
mail: 82%slack: 13%nexmo: 2%pushover: 1%webhooks: 1%discord: 1%Even though our team relies on Slack notifications, the vast majority of Oh Dear subscribers use mail. It's easy to understand why: everybody already has an email address, and most people check their email regularly.
Because emails are being used so much for sending notifications, we decided to give them a little love by adding snooze links. Here's how such links look like.

Some email client visit each link an email to preload content. That's why we don't snooze immediately after clicking the link in the mail, but show a confirmation dialog first.
This is how it looks like when you click a link.
.
After clicking the button, notifications will be snoozed.

An important thing to note is that you don't have to be logged in for these links to work. These links have a signature has appended. Oh Dear uses this signature to verify the link hasn't been tampered with.
As you might suspect, Oh Dear is built using the Laravel framework. Laravel has baked in support for signed URLs.
Oh Dear offers multiple types of checks: uptime, certificate health, broken links, mixed content, ... Each check that we need to perform on a site, is stored a row in the checks table of the DB. In the Check model, we added this function to generate a URL to snooze a check.
// on the Check model
public function signedSnoozeUrl(int $minutes, string $email): string
{
return URL::temporarySignedRoute('signed.snooze', now()->addMinutes(60), [
'check' => $this,
'minutes' => $minutes,
'email' => urlencode($email),
]);
}
We weren't comfortable with sending out links that would stay valid forever. All of the signed URLs we generate are only valid for 60 minutes. The $minutes being passed to the function above represent the minutes how long notifications for this check should be snoozed. The email being passed is the email address to which the generated link will be sent. We'll use that value to log which email address snoozed a particular check.
Next, let's look at the notification itself. Laravel has excellent support for sending notifications, and we leverage that functionality in a big way.
Here's the toMail function in the UptimeCheckFailedNotification.
public function toMail(NotificationDestination $notifiable): MailMessage
{
return (new MailMessage)
->from('alert@ohdear.app', 'Oh Dear')
->subject($this->getMainMessage())
->markdown('mail.uptime.uptimeCheckFailed', [
'run' => $this->run,
'check' => $this->run->check,
'email' => $notifiable->routeNotificationForMail(),
]);
}
In Oh Dear, the notifiable of a notification isn't a user, but a NotificationDestination model. Using that model allows us to have a flexible notification system where single users and teams can define multiple notification destinations for several channels (mail, Slack, SMS, ...). I could write a length blogpost about our notification system itself, but I'm not going to deep diver into it for now.
Here's the code of that mail.uptime.uptimeCheckFailed view that notification uses.
@component('mail::message')
# Oh Dear!
[{{ $run->site()->label }}]({{ $run->site()->url }}) seems down.
@component('mail::table')
| Component | Value |
|:------------- |:------- |
| URL: | {{ $run->site()->url }} |
| Error description: | {{ $run->checkerResult()->getErrorDescription() }} |
| Detected at: | {{ $run->ended_at->toTeamTimezone($run->site()->team) }} |
@endcomponent
For more details, have a look at the online report.
@component('mail::button', ['url' => $run->result_url])
View full report
@endcomponent
We'll send you another mail as soon as it is back up (or when it stays down for another hour).
@include('mail.partials.snooze')
Don't want to receive mails when sites go down? Turn off the "Site down" switch on the [team notification settings]({{ route('team.notifications.mail') }}) and/or on the [{{ $run->site()->label }} notification settings]({{ route('site.notifications.mail', $run->site()->id) }}).
Thank you for using Oh Dear!
@endcomponent
You can see here that the snooze links itself are stored in a partial. We can easily use that partial in the email views of all the other notifications.
Here's the mail.partials.snooze view.
Snooze notifications for this check:
[for 15 minutes]({{ $check->signedSnoozeUrl(15, $email) }})
[for an hour]({{ $check->signedSnoozeUrl(60, $email) }})
[for a day]({{ $check->signedSnoozeUrl(60 * 24, $email) }})
[for a week]({{ $check->signedSnoozeUrl(60 * 24 * 7, $email) }})
Now that you know how those snooze URLs are built and sent, let's turn our attention to what happens when somebody clicks such a link in an email. In the routes file, we've set up these routes.
Route::middleware('signed')->prefix('/check/{check}/snooze/{minutes}/{email}')->group(function () {
Route::get('/', [SnoozeCheckController::class, 'askConfirmation'])->name('signed.snooze');
Route::post('/', [SnoozeCheckController::class, 'snooze']);
});
The get route is responsible for displaying the confirmation screen. In the post action the check will be snoozed.
That signed middleware is the one provided by Laravel. It will throw an exception when trying to visit the route with an invalid URL. More on the later.
Here's the SnoozeCheckController that handles the actual requests.
namespace App\Http\Front\Controllers\Check;
use App\Domain\Check\Models\Check;
use App\Domain\Notification\Actions\SnoozeCheckAction;
class SnoozeCheckController
{
public function askConfirmation(Check $check, int $minutes)
{
return view('front.snoozeCheck.askConfirmation', [
'check' => $check,
'until' => $this->humanReadableTimeUntil($minutes),
]);
}
public function snooze(Check $check, int $minutes, string $email)
{
$until = now()->addMinutes($minutes);
(new SnoozeCheckAction())->execute($check, $until, $email);
return view('front.snoozeCheck.snoozed', [
'check' => $check,
'until' => $this->humanReadableTimeUntil($minutes),
]);
}
protected function humanReadableTimeUntil(int $minutes): string
{
return now()
->addMinutes($minutes)
->longAbsoluteDiffForHumans();
}
}
At the heart of this controller is SnoozeCheckAction in the snooze method, which does the actual work to snooze a check. This snoozing logic has been put in an action class so we can reuse it in other parts of our application (via our API, the controller that handles the UI of the snooze screen when logged in Oh Dear) where checks can be snoozed.
Action classes are a thing of beauty when you have logic that needs to be reused through an application. You can read more on action classes in this blog post. In the upcoming Laravel Beyond CRUD course, there will be an entire chapter dedicated to action classes.
That longAbsoluteDiffForHumans() function in the code snippet will be convenient to prepare a string to be displayed in the view. It will return one hour when you called it on a carbon instance with a value of one our in the future, 30 minutes when the value is 30 minutes in the future, and so on.
Here's the content of front.snoozeCheck.askConfirmation view.
<x-minimal-layout>
<section class="bg-white text-gray-700 p-8 mt-8 sm:mt-16 text-center text-xl leading-relaxed shadow-lg">
<div class="mb-4">
Do you want snooze {{ strtolower($check->human_readable_check_type) }} notifications for <span
class="font-bold">{{ $check->site->label }}</span>
for <span class="font-bold">{{ $until }}</span>?
</div>
<form class="form" method="POST">
@csrf
<button class="button" type="submit">Snooze for {{ $until }}</button>
</form>
</section>
</x-minimal-layout>
When it's rendered, it will look something like this.

Our goal was to keep this confirmation screen very simple. We assume that when clicking a snooze link, you simply want to confirm this action and nothing else. This simple approach makes it very easy for people to snooze a check from their mobile device.
You can see in Blade view above that the confirmation form has no action. Because it has no action, the same signed URL will be used to send the POST request.
After clicking the button on the form, the check will be snoozed and this screen will be displayed.

Should a user want to see more details, the "Snooze settings" button can be clicked. This route is behind an auth check, so users will need to log in first.
You might have noticed that the askConfirmation Blade view uses a x-minimal-layout Blade component. You might have only used Blade components for small pieces of HTML, but they can be used for layouts as well.
Here's the content of the front.layouts.minimalLayout view which is the view that will be rendered when using the x-minimal-layout tag.
<html lang="en">
<head>
<link rel="stylesheet" href="https://use.typekit.net/otv6pzl.css">
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
<link rel="stylesheet" href="{{ url('assets/css/fontawesome.min.css') }}">
</head>
<body class="font-front">
<div class="min-h-screen flex flex-col p-10 bg-gray-200">
{{ $slot }}
</div>
</body>
</html>
These signed URLs are valid for an hour only. When an expired link is clicked, Laravel will show a generic error screen. That's not very user friendly. It would be much better to display a screen that says the link is expired.
Luckily, this is easy to achieve. The signed middleware throws an Illuminate\Routing\Exceptions\InvalidSignatureException exception. In the exception handler, we can handle that particular exception and show a custom view.
// in app/Exceptions/Handler.php
public function render($request, Throwable $exception)
{
if ($exception instanceof InvalidSignatureException) {
return response()->view('errors.link-expired')->setStatusCode(Response::HTTP_FORBIDDEN);
}
return parent::render($request, $exception);
}

If you don't want any entries in your log, in Flare, or any error tracker of your liking, you should add InvalidSignatureException to the $dontReport array in the exception handler.
protected $dontReport = [
AuthenticationException::class,
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
// ...
InvalidSignatureException::class,
];
In Oh Dear, nearly every piece of functionality is covered by tests. The snooze links are no exception. My favorite tests are ones where we check that behavior is correct. Those tests don't care about how a feature is implemented. Most of the time, they don't reach into the database or check the app's internal state. Instead, we check if the app behaves correctly.
In the first test, we will generate a signed URL to snooze a check for an hour. We're going to visit the URL using a POST request and assert that the check is snoozed. We're going to assert that one second before the hour is over, the check is still snoozed. One second later, the check isn't snoozed anymore.
public function setUp(): void
{
parent::setUp();
$this->check = factory(Check::class)->create([
'type' => CheckType::UPTIME,
]);
}
/** @test */
public function it_can_snooze_a_check_for_an_hour_using_a_signed_url()
{
TestTime::freeze();
$signedSnoozeCheckUrl = $this->check->signedSnoozeUrl(60, 'test@example.com');
$this->post($signedSnoozeCheckUrl)->assertSuccessful();
$this->assertTrue($this->check->isSnoozed());
TestTime::addMinutes(60)->subSecond();
$this->assertTrue($this->check->isSnoozed());
TestTime::addSecond();
$this->assertFalse($this->check->isSnoozed());
}
The TestTime class is provided by the spatie/test-time.
In the test above, we use the isSnoozed function to determine if the check is snoozed. We don't have to write a test for that function, as it is already covered by a couple of dedicated tests for the snooze functionality.
In a second test, we're going to make sure that we display the right snooze time to the user.
/** @test */
public function it_displays_the_right_time_span()
{
$signedSnoozeCheckUrl = $this->check->signedSnoozeUrl(60, 'test@example.com');
$this->get($signedSnoozeCheckUrl)
->assertSuccessful()
->assertSee('snoozed for 1 hour');
$signedSnoozeCheckUrl = $this->check->signedSnoozeUrl(30, 'test@example.com');
$this->get($signedSnoozeCheckUrl)
->assertSuccessful()
->assertSee('snoozed for 30 minutes');
}
In a final test, we're going to make sure that the URL can't be tampered with.
/** @test */
public function it_will_not_snooze_a_check_if_the_url_is_tampered_with()
{
/** @test */
public function it_will_not_snooze_a_check_if_the_url_is_tampered_with()
{
$signedSnoozeCheckUrl = $this->check->signedSnoozeUrl(60, 'test@example.com') . 'make-url-invalid';
$this->get($signedSnoozeCheckUrl)->assertStatus(Response::HTTP_FORBIDDEN);
$this->post($signedSnoozeCheckUrl)->assertStatus(Response::HTTP_FORBIDDEN);
$this->assertFalse($this->check->isSnoozed());
}
}
You could argue that the test above isn't needed because we're not responsible for testing the framework code. But for security related things, we're rather safe than sorry. This test proves that we're using the functionality that Laravel offers correctly. For example, should we forget to apply the signed middleware to our routes, this test will fail.
I hope you enjoyed this little tour on why and how we implementation action links for email notifications. If you want to see it in action, consider registering at Oh Dear. There's a free trial period of 10 days.
I'm proud to announce our team is creating a new video course called Laravel Beyond CRUD. In this course, you'll learn various patterns to build a large scale Laravel app with your team. Even if you're not working on large Laravel apps, there are lots of things to learn to improve your projects.
This course will be offered as a premium video course, and as a book. The course will be presented by Brent, who is in the lead at various large scale client projects at Spatie. The book, also written by Brent, is beautifully designed by my colleague Seb.
At the Laravel Beyond CRUD website, you can subscribe to our email list. We'll notify you as soon as the course is available in September. We'll also mail you an extra preview.
I'm happy to announce the official Laravel Worldwide Meetup. This monthly meetup will be streamed via YouTube. The first edition will be held on 14th July at 18:00 UTC.
There is a select group of people, sometimes jokingly referred to as "The Elite" that you often see speaking at Laracons and other events. Even though you will probably see some of these fine artisans on the stream, the main goal of this meetup is to introduce new speakers to the community.
– johnbraun.blog - submitted by John Braun
John Braun shares why and how he refactored to Livewire polling and replaced the event-driven WebSockets approach.
Read more [johnbraun.blog]
Bruno Falcao explains a nice strategy when creating resources in Nova
Read more [stitcher.io]
Jonathan Reinink published another excellent blogpost on db performance.
Read more [reinink.ca]
Last week I held an online meetup. My two guests were JMac and Nuno Maduro. 00:00 Welcome 02:00 Exploring Laravel Workbench (JMac) 30:00 Introducing Pest 0.2 (Nuno Maduro) Show links Laravel Shift: https://laravelshift.com Pest: https://pestphp.com
Barry Vd. Heuvel, the creator of Laravel Debugbar, compares Debugbar against Telescope.
Read more [barryvdh.nl]
A few weeks ago, Jmac tweeted out an excellent idea. What if we could use try and catch in a collection chain?
Collections are the jam. Yet they leave me dreaming of more.
— Jason McCreary (@gonedark) June 1, 2020
Take this block that performs some custom validation logic by leveraging a value object constructor.
Collections definitely streamline it, but what if I could also chain the exception handling… 🔥 pic.twitter.com/4jj0uFgwWb
Meanwhile, Jmac and I did a few code pairing sessions to work on a possible implementation. We've added try and catch methods to the laravel-collection-macros package.
In this blog post, I'd like to share what you can do with these methods and how they work under the hood.
Dries Vints was a guest on my stream. He shared his newest project, called Blade UI Kit.
We released a new package called spatie/laravel-cronless-schedule. It can run the Laravel scheduler (or any other command really), without relying on cron. Instead of cron, a never-ending ReactPHP loop is used.
In this blog post, I'd like to introduce the package to you.