TailwindMerge for Laravel

I've been writing more Blade + Tailwind the past few weeks. Coming from React + Tailwind I really missed tailwind-merge and clsx, but luckily came across this great package from Sandro Gehri.

{{-- components/button.blade.php --}}
<button
{{ $attributes
->merge(['type' => 'submit'])
->twMerge('bg-green-500') }}
>
{{ $slot }}
</button>
 
{{-- home/index.blade.php --}}
<x-button class="bg-red-500">
A red button
</x-button>

Source code and installation instructions on GitHub.

Solving MySQL 5.7 authentication method problems in Laravel

Last week I was setting up al old Laravel project that required MySQL 5.7. I installed DBngin so I could have the legacy MySQL version up and running alongside the modern MySQL 8. Smooth sailing so far, until my Laravel app wanted to connect to the database.

SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client

It took some googling to find a solution that worked. Most solutions recommended changing the authentication method in MySQL, but this was already configured correctly for me. What I had to do was explicitly configure a socket for MySQL to connect with. I added the following to my .env file:

DB_SOCKET=/tmp/mysql_3307.sock

Replace 3307 with the port number you configured your MySQL server to run on.

Laravel export v1

Earlier this week, we tagged spatie/laravel-export v1. I wrote the bulk of this package 5 years ago. (Wow, I was surprised by this, time really does fly sometimes!) But I never tagged a stable version because I wanted to add more features. Instead, I chose the way of Arrakis and decided it was ready for a v1.

Laravel Export was inspired by Next.js. Next allows you to write your React app and access data on the server, to export it to a static site after. Next does this by crawling your routes. I built exactly this for Laravel using our crawler package. After configuring, you can run an artisan command to export your static site to a folder.

php artisan export

This is a great fit for websites you don't want full blown hosting for but just want to drop on something like Vercel or Netlify. Docs & details in the repository!

Adding stale while revalidate functionality to Laravel's cache

A stale while revalidate cache macro by Rias Van der Veken. With stale while revalidate, expired cache items will still be used when requested, but the data will be revalidated right after. That means the current request will be handled faster than if the cache would have to be revalidated, and the next request will receive fresh data.

Stale while revalidate is often used in web applications, popularized by Vercel's SWR React library.

Stefan Zweifel on storing user preferences in a Laravel application

Stefan Zweifel explains how he stores user preferences in a Laravel application using spatie/laravel-data. With the data package, you can store user settings as a blog of JSON in your database—so you don't need to update your table schema for every change—and have a typed object to work with in code.

He mentions poor query performance as a possible tradeoff if you need to query the database for a specific value.

One thing to keep in mind is that querying for specific settings can lead to performance issues and should probably be avoided.

If your app regularly needs to query for users who have selected a particular date_format , it's better to promote this setting to its own column. This makes the work of your database and possible indexing much easier.

If this is something you need, you could solve it with a virtual column mapped to a JSON value of the settings object. There's a nice tutorial on that on the Kirschbaum blog

Laravel closure validation rules

Today I was looking for a way to create a custom Laravel validation rule without the overhead of a new class. The rule I needed would only be used in one place, so wanted to keep it close to (or in) the request class.

Upon re-reading the validation docs, I learned that Laravel supports closures as rules.

class JournalEntryRequest extends Request
{
public function rules(): array
{
return [
//
'lines' => [
function (string $attribute, mixed $value, Closure $fail) {
$debit = collect($value)->where('type', 'debit')->sum('amount');
$credit = collect($value)->where('type', 'credit')->sum('amount');
 
if ($debit !== $credit) {
$fail("Debit and credit don't match.");
}
 
if ($debit !== 0) {
$fail("Amount must be greater than 0.");
}
},
]
];
}
}

Just what I needed!

Comparing two database columns in Laravel

When you want to compare two database columns in Laravel, you can't use where because it treats the argument you're comparing to as a value.

Instead, Laravel has a whereColumn method you can use to compare a value to another column's value.

// Retrieve posts that were updated after
// they were published.
 
Post::query()
->whereColumn('updated_at', '>', 'published_at')
->get();

Eager load relations in Laravel using withWhereHas

When using whereHas in Laravel, it's not uncommon to also eager load the relation using with.

$posts = Post::query()
->with('author')
->whereHas('author', function (Builder $query) {
$query->where('name', 'Seb');
})
->get();

Laravel also has a more succinct method that combines the two: withWhereHas.

$posts = Post::query()
->withWhereHas('author', function (Builder $query) {
$query->where('name', 'Seb');
})
->get();

Resolving a new instance of a singleton in Laravel

In Laravel, you can register a class as a singleton to always resolve the same object.

However, you might want to build another instance of the class. You could manually construct the class without Laravel's container, but if it has a bunch of dependencies it can be tedious.

With the build method, Laravel won't resolve a registered instance of the class, but build a new one with the container.

// AppServiceProvider::register()
$this->app->singleton(MastodonClient::class);
// Resolve the singleton instance from the container
$mastodon = resolve(MastodonClient::class);
 
// Build a new instance
$anotherMastodon = app()->build(MastodonClient::class);

This can be useful when a Laravel package registers a class as a singleton but you need another instance.