r/laravel

New: Object storage migrations with Laravel's read-through filesystem
▲ 37 r/laravel

New: Object storage migrations with Laravel's read-through filesystem

If you find yourself having to migrate from S3 to R2, R2 to B2, or even migrate within an R2 but maybe to different buckets or different prefixes or something like that, Laravel framework just released a new feature to the filesystem component that allows you to do that over time instead of in one big bang migration!

laravel.com
u/aarondf — 2 days ago

I tried Laravel Cloud (including preview environments). Drift Deploy is for teams who stay on Forge

A few days ago I posted asking if people still start new projects on Forge or have mostly moved to Cloud.

I also tried Cloud myself. It’s easy to get a Laravel app running, and its Preview Environments feature is simple to set up. If you want zero server work, that’s a strong option.

The replies were still full of Forge (and Ploi / VPS) for the usual reasons: cost, control, custom processes, and clients who don’t want AWS.

That’s where I still live for some work. I use Forge when I need a real URL for a pull request or a demo for a stakeholder/client.

For years I did that with Laravel Harbor. It still works: a CLI that talks to Forge from GitHub Actions, spins up a site for a branch, and tears it down when the PR is done.

The part that got old was doing that setup again for every app. Copy the provision + teardown workflows into the repo. Add the Forge token and server ID as secrets. Tweak domain, PHP version, env, and deploy script flags for that project. Then remember to keep those YAML files in sync when Harbor or the workflow changed. Fine for one app. Painful once you have a few Laravel repos on the same Forge server.

So I built Drift Deploy: connect GitHub + Forge once, then any Laravel app can get a preview URL on the server you already have.

  • Label a PR → preview goes up
  • Remove the label or merge → it comes down
  • No GitHub Actions per project

Not a Cloud-previews competitor. Cloud already has that. This is for people staying on Forge.

Early. Free for 2 projects. Paid later if the feedback says it’s worth it.

https://driftdeploy.dev

reddit.com
u/mehrun_codes — 2 days ago
▲ 320 r/laravel

Fully native Mac app with PHP, Laravel and Blade. No Electron, HTML/CSS/JS

This is what we've been building towards for almost 4 years: fully native UI, no PHP server, no Node. Just PHP embedded directly in a Swift shell, being executed immediately in response to button taps and other events, capable of re-rendering the UI at well over 240fps. From PHP & Laravel.

No HTML, no CSS, no JS, no Electron, no WebView.

And it can live directly alongside nativephp/mobile, which means we will have a single Laravel app that can render a fully functioning native app for iOS, Android, macOS, Windows and Linux from one codebase, just with a couple of Composer packages.

This is NativePHP Desktop v3 running SuperNative 🎉

Live demo on our livestream at 9am EST (roughly 12 hours from when I posted this)

u/simonhamp — 7 days ago
▲ 17 r/laravel

Laravel Forge vs Laravel Cloud for new projects?

Anyone still using Laravel Forge for new projects these days, or have you mostly switched to Laravel Cloud? Curious which one you prefer and why.

reddit.com
u/mehrun_codes — 7 days ago

Octane for better performance

Hi everyone,

I run a multi tenant platform. On a Forge server having 8GB memory. Performance is honestly not bad, my code is optimized, and I spend over 2 weeks fully optimizing the server to a point where it's has no point to further optimize.

Performance is great, but I'm a complete tool and I'm never happy. I looked into Octane as it promises faster performance. I've got to a point where I've implemented Octane on a development site. There were a few issues were leaks were happening between tenants. They're fixed as far as I can find them, and run tests.

But I'm still a bit worried something may slip through when I ship everything to production. I've already let AI audit everything a few times over and over and they cannot seem to find any flaws. We all know AI isn't perfect, so I'm wondering if people here on this subreddit have done something similar and have any experience they want to share.

Thanks in advance. Sorry for the long story.

reddit.com
u/Palnubis — 7 days ago
▲ 40 r/laravel+1 crossposts

I forked a dead PHP name parser because it couldn't tell a credential from a surname

I use theiconic/name-parser at work to split full-name strings into salutation, first name, last name, suffix, and so on. It does the boring parts well, but it has a bug that bit me on a list of clinicians: parse "Jane Doe DDS" and the last name comes back "Dds", with "Doe" shoved into the middle name. The dental credential became the surname. Almost every row with a trailing credential and no comma did some version of this. Upstream went quiet around 2020, so it never got fixed. I forked it: iliaal/nameparser.

The root cause is that upstream runs every token through strtolower() before matching it against its credential dictionary. That throws away the one signal that separates a credential from a name. People write credentials in caps and names in title case. "Smith, Ma" is a person named Ma; "Smith, MA" is a master's degree with no recorded first name. Lowercasing deletes that distinction before anything looks at it. The fork reads an ambiguous token ("Do", "Vi", "MA", roman numerals) as a credential only when it is all-caps; title case keeps it as a name. So "Jane Doe DDS" keeps "Doe" and reads "DDS" as the suffix.

It also handles international surname particles now: "van den Heuvel", "de los Santos", "vom Bruch", "le Pen", "dos Santos", "dela Cruz", and "lo Russo" keep the full surname instead of orphaning the particle into the middle name. The comma form works too ("van der Berg, Johan" gives last name "van der Berg"), and there is an opt-in setSurnameFirst(true) for comma-less CJK order ("Mao Zedong" to last "Mao").

For batch imports there is an advisory getConfidence() that flags rows where casing couldn't decide, so you can route those to manual review instead of trusting every split. It is opt-in and does not change what parse() returns.

The honest limitation: casing is the signal, so uniform-case input (all-caps legacy data, or all-lowercase) carries none. The README says so plainly. It is a heuristic, not a universal global-name solver.

It is a maintained fork, not original work: The Iconic's parser (quiet since ~2020), Zachary Miller's PHP 8.3+ modernization, and my casing, credential, and international layer on top. PHP 8.3 through 8.5, PHPStan level 9, MIT.

composer require iliaal/nameparser

https://github.com/iliaal/nameparser

Happy to answer questions, especially from anyone parsing professional or registry name data.

u/Ilia0001 — 8 days ago
▲ 22 r/laravel+1 crossposts

I built an open-source Laravel client for ERPNext and Frappe

I've released kayedspace/laravel-erpnext, an MIT-licensed package for connecting Laravel applications to ERPNext and Frappe.

The main design decision was to treat DocTypes as generic resources. You can work with a standard or custom DocType by name without creating a PHP class, mapping, or registration first:

use Kayedspace\Erpnext\Facades\Erpnext;

$overdue = Erpnext::doctype('Sales Invoice')->query()
    ->where('status', 'Overdue')
    ->fields(['name', 'customer', 'outstanding_amount'])
    ->orderBy('creation', 'asc')
    ->limit(200)
    ->get();

The package also includes:

  • Token, Basic, Bearer, and cached Session authentication.
  • Frappe-aware filters and full-result pagination with each(), chunk(), and lazy().
  • Create, read, update, delete, and whitelisted document method calls.
  • Private-by-default file uploads, attachments, image optimization, and authenticated downloads.
  • Multi-tenant connection resolution and focused retries for rate limits or unavailable sites.
  • Optional typed wrappers for eight common DocTypes, including invoice and payment submission lifecycles.

I tried to keep the generic API as the normal path and make typed documents optional. ERPNext still decides required fields, permissions, custom fields, and which document methods are available.

Installation is:

composer require kayedspace/laravel-erpnext

Source: https://github.com/kayedspace/laravel-erpnext

Documentation: https://laravel-erpnext.kayed.dev

I would especially value feedback from people maintaining real Laravel-to-ERPNext integrations. Which part usually causes the most trouble in your projects: authentication, DocType queries, document lifecycles, files, or keeping local and ERPNext records synchronized?

u/3liusef — 6 days ago
▲ 35 r/laravel+2 crossposts

This Week In PHP Internals | August 12, 2026

While the Internals list is not technically directly Symfony related, it does affect every single one of us.

Hello world, it's Wednesday, August 12, 2026, and here's what happened This Week in PHP Internals.

11 stories this week, so let's get into it. But first, Your team adopted AI. Everyone says it made them faster. Ballast measures whether that's true — how much faster you're actually going, and whether what you ship is still holding up. 6.75 times the commits. Durability down 19 points. Now you know. It runs on your machine. It reads your git history, not your source — your code never goes anywhere, and nothing here is scored by a model. It's arithmetic you could check by hand. Setting it up isn't your job either. Paste one prompt into your coding agent and it does the whole thing. Find out for free today. ballast.now.

One correction before the top story. Last week we described the list() deprecation vote as deadlocked at 21 to 21. Derick Rethans pointed out that's the wrong word — a deadlock is when something is stuck and can't proceed. The vote wasn't stuck. It was simply tied, and voting carried on to the finish. He's right, we'll say it properly this week — and thanks, Derick, for keeping us precise.

This week's top story: the verdict is in on the 35-ballot mass deprecation vote for PHP 8.6. Voting closed Monday at 13:00 UTC, and Gina P. Banyard posted the full results — 31 proposals accepted, 4 rejected. Start with the 4 that fell. Deprecating list() finished on a flat tie — 23 to 23, with 1 abstention — exactly 50 percent, nowhere near two-thirds. Reserving in, out, and inout failed at 8 to 21. The gettext _() alias survived at 10 to 22. And the dechunk filter — the item disputed all through the voting window — finished at 18 to 15 with 12 abstentions, 54.5 percent, and stays in the language.

Now last week's cliffhangers. Reserving let was balanced exactly on the two-thirds line 7 days ago — it found its margin and passed at 24 to 11, with 9 abstentions — 68.6 percent. Reserving is passed at 29 to 10, despite Rowan Tommins's warning about the Hamcrest testing library and its 500 million installs. And the define() case-insensitivity flag — the item Kamil Tekiela wanted simply deleted instead — passed without a single no vote, at 41 to 0. The vote also drew one final flag on its way out. Takuya Aramaki wrote in Friday, opening with: "Apologies for bringing this up so close to the end of the vote." His concern is the SplFileObject CSV methods item. He laid out the inconsistency plainly: "setCsvControl() is the only way to configure the delimiter, enclosure and escape character used by READ_CSV; the constructor does not accept them. If setCsvControl() is removed in PHP 9 while READ_CSV remains, READ_CSV is permanently locked to its defaults and tab-separated files can no longer be read through it." He asked that READ_CSV be deprecated alongside the methods, or that setCsvControl() stay until a replacement exists. No answer yet — and the item passed at 25 to 5, with 15 abstentions.

The final 3 ballots of the 8.6 season are settled, and they went 2 and 1. Caleb White's pipe assignment operator — |>= — was declined. The vote closed Tuesday morning at 14 yes, 12 no, and 7 abstentions — 53.8 percent, short of the two-thirds it needed. It had climbed all the way from dead even, but never got over the bar. Nick Sdot's readonly property defaults went the other way entirely. It closed Friday at 24 to 0, with 5 abstentions — it never drew a single no vote in 2 weeks. And Khaled Alam's const object property writes closed Saturday. He announced the result Sunday: accepted, 17 to 2 with 6 abstentions — 89.5 percent. With those 3 in the books alongside Duration and the deprecations, PHP 8.6's RFC season is over — the beta 1 tag brings the soft freeze this week, and beta 1 itself lands Thursday.

Ilija Tovilo posted a very late update to an RFC that passed 24 to 0 back in March. The closure optimizations RFC promised 2 things: a cache for stateless closures, and inference — the engine automatically detecting closures that never touch $this and treating them as static. That second part is out. Ilija found an edge case where a closure violates none of the RFC's inference rules and still makes an instance call — pass a callable string like "Foo::instanceCall" into an array_map inside the closure, and the rules never see it. He owned it completely, writing: "I failed to consider this case, and sadly this is not easy to detect via a new rule. For this reason, I have decided to omit static closure inference from the implementation and only merge the stateless closure cache." The practical takeaway: the cache — which carries most of the performance win — still ships in 8.6, but the engine won't infer anything for you. Mark your closures static yourself and you get the full benefit.

Ignace Nyamagana Butera's data encoding API — the base64, base16, base58, and base85 family — got a detailed security review from Sjoerd Langkemper on Monday. He's for it, noting: "the current base64_decode is very tolerant towards invalid input, causing both functional and security problems." Along the way he found errors in the RFC's own code examples, corrected them in a companion repository, and flagged a signature mismatch in the base85 functions. He's skeptical of one feature — the optional constant-time mode — arguing: "Constant-time algorithms are pretty difficult to develop and maintain", and suggesting PHP hand that job to libsodium or openssl instead. He also built a working implementation to test the API, introducing it with unusual billing: "LLMs and I have created an implementation here." And in the research footnotes: he spent real time evaluating the base85 variant from RFC 1924 before discovering: "that RFC was submitted in jest as an April fool's joke." Ignace thanked him for the remarks and is holding all implementation work until after 8.6 ships — Tim Düsterhus, who's building it, is busy with the release.

The first RFC aimed past the freeze is already here. Weilin Du proposed IntlRelativeDateTimeFormatter on Friday, targeting PHP 8.7 — a wrapper for ICU's locale-aware relative time, the "in 3 days" and "last Sunday" strings, in every language ICU speaks. Ignace asked the obvious question: 8.6 just gained a Duration class — shouldn't this accept one? Weilin argued the types don't fit, since Duration is stopwatch time and this formatter wants a unit: "We don't know how to deal with 90 minutes here. It can be 90 minutes or 1.5 hour." And weekdays, months, and quarters aren't durations at all. David Carlier pushed for enums and a namespace; Weilin is keeping class constants and the global Intl prefix for consistency with the existing intl extension, and filed modernization under future scope. One suggestion did land immediately: by Saturday the constructor had grown an optional NumberFormatter parameter, with Weilin reporting: "The implementation is way more smoother than I expected."

The generics conversation is parked until September — the implementations aren't waiting. Carlos Granados posted a pre-RFC Thursday: he took Rob Landers's experimental reified branch — built on Seifeddine Gmati's bound-erased proposal — and worked it into something complete, with a full write-up of the changes and findings. He argued the original deserved better: "I think that this was a very valid proposal that should have been explored in more detail." Rob's reply was brief, noting: "You really should have reached out instead of a working in isolation. Join us in discord, the proposal is delayed until September-ish." Which raised a practical question — what Discord? Rob posted channel links; Carlos, a Discord newcomer, still couldn't get in. Larry Garfield finally supplied the address, phpc.chat, with a review: "The PHP Community chat is unofficial, but lately it's where the big names are hanging out, including a lot of Internals regulars. Beware, the Internals channel is annoyingly noisy and has a hard time staying on topic." And I can personally vouch for that statement. Then Monday brought a third generics experiment: Alexander Lisachenko shared a userland proof-of-concept — a Composer package — where specialized classes share the compiled method bodies, so each specialization costs one small structure per method instead of a full copy of the opcodes.

Liam Hammett's native markup expressions RFC — JSX-style HTML in PHP — got the one review nobody else could write. T.J. L, who maintains the XHP extension — the long-running ancestor of this exact idea — posted his first message ever to internals. He corrected one detail in the RFC's history section, then confirmed its central argument from experience: he wrote: "While it is technically possible for extensions to add new syntax, it is unreasonable to expect tools to be aware of that syntax. I can absolutely confirm that the biggest point of friction in using XHP today is the fact that static analysis tools like psalm or phpstan can't analyze files, code using XHP cannot be formatted or linted with php-cs-fixer..." In other words, the case for putting markup in core, signed by the person who spent years doing it the other way. He also brought 3 asks: context passing through a component tree without threading attributes; a ruling on inline SVG, which leans on XML features the HTML-only RFC excludes; and a note that dropping per-tag objects means no runtime validation of tags and attributes — XHP's original selling point — which he says JSX gets away with "in large part because of the Typescript ecosystem". No response from Liam yet.

Quick hits. Juris Evertovskis ran a temperature check on isset: expressions inside the square brackets still throw warnings and deprecations even though isset silences everything else, and he put his conclusion bluntly: "To me it looks like isset is not doing its job." He'd like the brackets silenced too — no replies yet. The did-you-mean error suggestions are officially not being rushed: Jorg Sowa announced: "I will finish it after feature freeze", and Larry Garfield agreed, adding: "If it doesn't happen until 2027, that's OK." Jorg also picked up his VCS account this week — approved by Ilija Tovilo — with the session extension in his sights. And the list has a new face: Sepehr Mahmoudi introduced himself Tuesday with a pull request already open and an array_search_range idea in hand; mickmackusa pointed him at array_find_key() and suggested making the case on the list before writing more code, and Yuya Hamada thanked him for the contribution.

So that's the week: the 35-ballot deprecation vote landed 31 to 4 — list() survives on a flat tie, dechunk survives, and let squeaked through; the pipe assignment operator was declined while readonly defaults and const object writes made it in, closing out 8.6's RFC season; closure inference got walked back to just the cache; and the first 8.7 RFC is already on the table. Links to every thread are below. Thanks again to Ballast.now for supporting this week's episode. We're Artisan Build. See you next week.

youtube.com
u/ProjektGopher — 7 days ago
▲ 58 r/laravel+1 crossposts

Double - a modern PHP mocking library focused on developer experience

After a few weeks of livestreaming the development process and dogfooding it in real projects, I'm excited to officially announce Double.

Double is a modern PHP mocking library focused on developer experience.

It stands on the shoulders of Mockery and RSpec. So there isn't much to learn. You get to enjoy a smoother DX.

A few things I wanted to improve:

  • Less technical terminology
  • Single, streamlined APIs
  • Human failure messages

With Double, you create a double for your class and write expectations. Double handles the details.

use JMac\Testing\Double;

$repository = Double::for(BookRepository::class);
$repository->expects('find')->with(123)->returns($book);

$service = new CatalogService($repository);
$service->lookup(123);

$repository->received('recordView')->with($book);

When an expectation fails, you get a proper test failure (not an exception). Along with a human-friendly message showing what actually happened and, where appropriate, a suggestion.

I also generated modern documentation with AI and ui.sh, where you may learn more about Double.

I've wanted to build this for years. So I'm all-in on Double. I've already converted all of my own test suites from Mockery to Double. I created a free Double Converter to automate the process.

This is still v0. While I believe it's already beyond feature parity with Mockery, I want to continue to improve the developer experience.

u/mccreaja — 8 days ago

Query Builder for Agents

Just wanted to share a little Laravel package I've been working on: https://github.com/J-T-McC/ai-query-builder

The idea is to let AI query your Laravel data securely without giving it direct access to SQL. You define the schema, relationships, allowed operations, etc, then the AI generates a structured query that gets validated and turned into an Eloquent query.

It can also be easily added to the Laravel AI SDK as a tool.

The schema can be adjusted programmatically for each user based on what they're permitted to access, and you can also define hard scoping conditions that always apply to the query, like limiting results to the current user's data.

Some use cases I've been playing with are letting users search their calendar in plain English, building custom reports, or just asking questions about their data in your app.

Still pretty early, but I've been having fun with it and figured I'd share it here. I'm curious what other tools or packages people have been using for this kind of thing.

u/Bigdrums — 8 days ago

Making cache with DynamoDB 6.7x faster

This is a story about how I identified a slow DynamoDB cache in my Laravel app and reduced its latency from 20ms to 2ms.

---

A few disclaimers:

  • Topic researched and prepared by a human (me);
  • Formatted by AI;
  • Originally, I was preparing this article for a third-party engineering blog, but it didn’t work out, so I thought, well, since I already have the material - let's make it useful anyway.

---

Caching is one of the most effective techniques for improving application performance: instead of recomputing an expensive value on every request, you return an already-computed result from a fast data store. Laravel ships with several cache drivers for this: array (in-memory), apc, redis, database, and one of them being DynamoDB.

DynamoDB is a powerful, scalable, globally available data store. In particular, AWS advertises single-digit-millisecond latency for read operations, given an appropriately designed schema. It’s tempting to read that and assume that simply using DynamoDB gets you this “global storage” cache with single-digit (<10ms) latency across the application, immediately, out of the box.

There’s a catch.

The gap between the promise and reality

Once DynamoDB has been created and configured as a cache “out of the box,” the minimum latency you’re likely to see for a single, cold request to read or write data is around 25ms. In some cases it might be up to 50ms. In badly configured setups, it can take up to 100ms, which is a lot to pay for a single cache read.

Compare that to the alternatives: Redis can provide latency around 500µs (less than 1ms), and even the local filesystem can serve a cache read in sub-millisecond. So what makes DynamoDB such a slow cache by comparison? And what’s the bare minimum we could actually get out of it? Spoiler: 2ms - significant improvement over the “out of the box” floor.

Understanding the request cycle

First, we need to understand how PHP (Laravel) interacts with DynamoDB. It starts with establishing the HTTP connection; for every request, DynamoDB uses HTTP as its communication protocol, which is much “heavier” than what MySQL or Redis use on top of the TCP (layer 4) layer. DynamoDB operates at the highest possible layer of the OSI model (layer 7), and most of the cost of using DynamoDB comes from this protocol.

Knowing this, the timeline to get data from DynamoDB has two major parts:

  1. Establishing the HTTP connection to DynamoDB, including DNS resolution, TCP handshake, TLS handshake, and sending the request itself.
  2. Processing the request by DynamoDB - the time DynamoDB itself takes to authenticate, execute the read/write, and send back a response, once the request has actually arrived.

In some cases there’s one more step at the very beginning: obtaining an access token. It’s quite fast, but can still take a few milliseconds. If we run the tests, both major parts are roughly 10ms each under “normal” conditions, which makes 20ms the floor - the very minimum we could get. In practice, it will often be higher.

DynamoDB's server-side latency floor

DynamoDB's own response time factors into that floor too, even though tuning it isn't the focus here. What keeps it fast: the right schema, indexes, properly configured read/write capacity, autoscaling, and so on; each a deep topic on its own, including the specifics of the cache table's own schema. Assume the best case on DynamoDB's side for this article: 1ms, realistic under good conditions. AWS promises <10ms, but under ideal conditions it can genuinely be just a few ms.

The network part

Now, about the network. Establishing a connection is a fairly expensive operation, especially for highly available, performant systems (APIs), and in some cases we simply can’t afford it. Under normal conditions, we have to establish a new HTTP connection to the DynamoDB server for every single incoming (user) request. This costs time and resources.

Luckily, PHP allows us to open a connection once and reuse it multiple times. This already happens within a single process (prior to the 8.5 release): when we make consecutive requests, we’re reusing the already-established connection. But when there are multiple instances of our application (or, more precisely, a fresh client gets constructed on every incoming request, which is what actually happens in a typical Laravel app, if Octane is not being used), each one establishes its own new connection to DynamoDB for every new user request.

This can be improved with persistent connections (added in 8.5) that get shared across requests, eliminating the need to redo the “expensive” DNS lookup and TLS handshake every time a new user request comes in. This reduces the network time down to roughly 1ms, which is close to the bare minimum for transferring data within the same AWS region.

One more thing: the AWS SDK and the access token

There’s one more piece to this puzzle: the AWS SDK and the access token. Depending on how credentials are configured, there are several ways an application can authenticate against the AWS API: static IAM access keys (AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY), an EC2 Instance Profile, an ECS Task Role, STS, and others. Some of these are simple; others require making one more API call to AWS to obtain a valid access token before the application can even talk to DynamoDB.

So, depending on the configuration, we potentially get at least one more HTTP request to an authentication server, which can cost anywhere from 2 to 10ms, sometimes more. And since we already know these requests are expensive (DNS, TLS, and the rest), we should avoid paying that cost repeatedly. There’s a way to do that: leverage the credential caching the AWS SDK already offers. What it does is simple: obtain the credentials once, keep reusing them until they’re about to expire, then refresh them. One fetch, many reuses, instead of one fetch per request.

The code

Laravel lets you override how any cache store gets built via Cache::extend() which we will use to override the dynamodb driver so every DynamoDbClient it builds reuses (1) a persistent curl connection and (2) a cached credential provider, instead of paying for a fresh connection and a fresh credential fetch on every single request:

&lt;?php 

// app/Providers/AnyServiceProvider.php

public function boot(): void
{
    $persistentHandle = curl_share_init_persistent([
        CURL_LOCK_DATA_DNS,
        CURL_LOCK_DATA_SSL_SESSION,
        CURL_LOCK_DATA_CONNECT,
    ]);

    Cache::extend('dynamodb', function ($app, $config) use ($shareHandle) {
    
        $client = new DynamoDbClient([
            'region' =&gt; $config['region'],
            'version' =&gt; 'latest',
            'endpoint' =&gt; $config['endpoint'] ?? null,
            
            // (1) Persistent connection: reuses DNS/TLS/the socket
            'http' =&gt; ['curl' =&gt; [CURLOPT_SHARE =&gt; $persistentHandle]],
            
            // (2) Cached credentials: reused across requests via the APC store
            'credentials' =&gt; CredentialProvider::defaultProvider([
                'credentials' =&gt; new AwsCredentialsCache(Cache::store('apc')),
            ]),
        ]);

        return Cache::repository(new DynamoDbStore(
            $client,
            $config['table'],
            $config['attributes']['key'] ?? 'key',
            $config['attributes']['value'] ?? 'value',
            $config['attributes']['expiration'] ?? 'expires_at',
            $app['config']['cache.prefix'] ?? '',
        ));
    });
}

The result

So, after applying both techniques (cached credentials and persistent curl handle), we get a significant latency reduction from roughly 20ms down to 2ms: 1ms for the network and 1ms for processing.

Real measurements were taken on an AWS EC2 instance connecting to a DynamoDB table in the same region. This particular test didn’t need a perfectly controlled, “ideal” testing environment, because the magnitude of the improvement is large enough to speak for itself - 100 iterations on each side, reading real data from a populated cache table:

Default configuration Optimized configuration
Median 19.10ms 2.84ms
Average 17.76ms 3.36ms

One important note on that 2ms number: it applies only to a warm connection. The very first request on a given process is still "cold" and pays the default ~20ms; that's exactly why the average above is skewed by that single cold cycle sitting among 99 warm ones.

It's also worth being precise about which "warm" this is about, because there are really three distinct states:

  • Cold - no connection exists yet. Full DNS lookup, TCP handshake, and TLS handshake, plus a fresh credential fetch. This is the ~20ms case.
  • Warm, within the same request - a second (or third, or tenth) DynamoDB call made while handling one single user request. This is already fast; with or without anything from this article, it's just Guzzle's normal in-process connection pooling, and it's been true all along.
  • Warm, across requests - a DynamoDB call made while handling a new user request, reusing a connection and credentials left over from a previous, already-finished user request on the same worker. Without persistent connections, this doesn't exist as a state at all: the connection from the previous request gets thrown away the moment that request finishes, so the next one starts cold regardless of how recently the worker was warm.

That third state is the entire point. Persistent connections don't make a single request faster than Guzzle's pooling already would, they make the next request start warm instead of cold, turning "warm for the duration of one request" into "warm for the lifetime of the process." It might seem like this doesn't matter much, since a request is already warm from its second DynamoDB call onward regardless, but that's exactly the gap: without a persistent connection, every single new user request starts cold again, while with one, the connection stays warm indefinitely, across all of them.

What this saves at scale

A few milliseconds per request doesn't sound like much on its own, but it looks different once you multiply it out.

Without persistent connections, every request pays the cold cost, because that third state (warm across requests) simply doesn't exist. At the measured median of 19.10ms, one thousand requests cost:

1,000 × 19.10ms ≈ 19,100ms ≈ 19.1 seconds

of cumulative request latency. With persistent connections, only the very first request on a worker is cold (pays the same ~19.10ms), the other 999 land at the warm, steady-state median of 2.84ms:

(1 × 19.10ms) + (999 × 2.84ms) ≈ 19.10ms + 2,837.16ms ≈ 2,856ms ≈ 2.86 seconds

That's roughly 16.2 seconds of cumulative latency eliminated per thousand requests, an ~85% reduction. Not wall-clock time for any single user, since real traffic is concurrent, not sequential, but 16 seconds of latency that no longer has to be paid by something, somewhere, for every thousand reads. Scale that to the millions of requests a real application serves, and it adds up fast.

Hope this was useful; happy to answer questions.

reddit.com
u/Prestigious-Type-973 — 7 days ago

AI writes your API in five minutes. What do you bring?

In 2026 you open Claude or Copilot, type "build me a Laravel REST API with authentication" and five minutes later you have something running. Great. Now one question: did you actually read that code, or did you just check that it ran?

You didn't write it and you didn't decide any of it. If it falls over in production tomorrow, your only move is reopening the same chat and begging the model to sort it out, because you know nothing about that code that you didn't already know before generating it.

The question that matters is what you put in it. If the answer is "the prompt", your contribution is a request anyone in the world can write for a fifth of your salary. At that point the story about AI replacing us sooner or later no longer concerns you: you're already replaced. Your company just hasn't put it in writing yet.

And if you're a junior or mid dev, the problem is doubled. A senior spots a query that won't hold at a glance, smells a leaky validation from a mile away. You don't have that eye yet, and if you delegate the thinking to the model too, you never will. You're gambling away exactly the years you should be building it in.

The alternative is not giving up the model, it's refusing to take its output on faith. It generates, you decide whether that query survives real traffic, whether that validation covers the input nobody thought of, whether that authorization rule lets one user read another user's data. Those are questions the model won't ask on its own. You have to ask them, and you can only ask them if you know they exist.

Writing code is worth nothing anymore, because anyone can do it in five minutes. What's worth something is the thinking you put into it. If you put in none, the most expensive and slowest link in the production chain is you.

That's why I wrote a book: to put you back on the deciding side. The model writes a single endpoint very well, but an API is a chain of decisions that condition each other, and asked for one piece at a time that coherence is exactly what you lose. So the book follows one API from start to finish, from a clean Laravel install to a deploy on a VPS. The domain is deliberately boring (a catalog of books!) so you spend your time understanding how an API is put together, not decoding the business. Inside are the things you need to be able to judge when the model generates: what your status codes are actually telling the client, where validation ends and authorization begins, what breaks under load once the data gets real, what tells you the work is actually done. The last chapter is about Claude Code, and it sits at the end for a precise reason: after nineteen chapters the model works for you. Read only that one and you stay someone who works for the model.

If you've been working with Laravel for years and you move through Policies, queues and Resources with your eyes closed, this book isn't for you. If instead you're looking for a way to churn out APIs without having to understand them, that book exists, but I didn't write it.

The chapter on eager loading and N+1 is online for free, in full:
antonio.popolizio.it/laravel-rest-apis/sample.pdf
That's the point where most Laravel APIs fall over once the data gets real. No form, no email: you download it and that's it. The rest is on Amazon: antonio.popolizio.it/laravel-rest-apis.

I'm curious how many people here actually read generated code line by line, and how many just check that it runs.

reddit.com
u/tonyjoe-dev — 10 days ago

Herd ignores ZDOTDIR and writes its shell configuration to ~/.zshrc

hey all!

I’ve noticed that Laravel Herd doesn’t appear to respect Zsh’s ZDOTDIR setting on macOS.

My ~/.zshenv contains:

export ZDOTDIR="$HOME/.config/zsh"

Therefore, my active Zsh configuration is located at:

~/.config/zsh/.zshrc

However, Herd adds its configuration to:

~/.zshrc

This includes entries such as:

# Herd injected NVM configuration
export NVM_DIR="$HOME/Library/Application Support/Herd/config/nvm"

# Herd injected PHP binary
export PATH="$HOME/Library/Application Support/Herd/bin/":$PATH

Because ZDOTDIR is set, Zsh doesn’t read ~/.zshrc, so Herd’s injected configuration has no effect. I have to copy the relevant PHP and PATH settings into my active $ZDOTDIR/.zshrc manually.

Herd’s bundled uninstall script also appears to target $HOME/.zshrc directly rather than resolving `$ZDOTDIR`.

Environment:

Herd 1.29.0
macOS 26.5.2
Zsh 5.9

Has anyone else encountered this? Is there a supported workaround besides manually maintaining the Herd configuration inside $ZDOTDIR/.zshrc?

It would be helpful if Herd detected ZDOTDIR before modifying .zshrc, while continuing to use ~/.zshrc when the variable is unset.

reddit.com
u/aegis87 — 11 days ago
▲ 30 r/laravel

Recent PHP/Laravel interview questions?

Has anyone here had a PHP/Laravel interview recently?

If so, what kind of questions did you get? Mostly interested in mid/senior roles.

Would be great if you could share your experience. Thanks!

reddit.com
u/ChadTurkifiedAzeri — 14 days ago

PagibleAI CMS 0.12 — a modular, MIT-licensed CMS for Laravel 11–13

PagibleAI CMS is a set of open-source packages that adds content management directly to an existing Laravel application.

It is installed through Composer and runs within your application, so you keep control of authentication, business logic, content, data, and infrastructure.

The packages combine a Vue 3 administration interface with structured content, hierarchical page trees, reusable elements, version history, previews, scheduled publishing, full-text search, GraphQL, JSON:API, and optional AI-assisted editing.

PagibleAI supports SQLite, MariaDB, MySQL, PostgreSQL, and SQL Server, including database-native full-text search. It can be used for anything from a small blog to a multi-domain or multi-tenant application.

The project is modular rather than an all-or-nothing CMS. You can build a custom distribution containing only the packages your Laravel project needs.

Laravel-native rather than a hosted platform

Editors get visual content management, drag-and-drop page trees, immutable revisions, media handling, and reusable content.

Developers can expose content through JSON:API, manage content via GraphQL API, use Blade for traditional server-rendered sites, extend the content schemas, and integrate the CMS with the rest of the Laravel application.

AI features are optional and provider-independent. They cover writing, translation, transcription, image generation, and image manipulation. The MCP integration also enables compatible agents to manage content through explicit, version-aware operations.

What’s new in PagibleAI CMS 0.12

  • Frontend access control and private media: Pages can be public, restricted to authenticated users, or protected by named access rules. Stripe, Paddle, and Mollie integrations can grant access after payment. Media attached to protected content can be stored privately and delivered only after authorization.

  • Five additional themes: Bold, Estate, Journal, Luxury, and Style provide starting points for product sites, real-estate projects, publications, fashion sites, and premium brands. Each includes reusable demo content and additional content blocks.

  • More capable administration tools: Pages, elements, and files now support more individual and bulk-editing operations. The release also improves sorting, list and filter updates, drag-and-drop uploads, previews, SVG handling, and audio and video controls.

Feedback from Laravel developers would be very welcome, especially from those working with structured content, multi-tenancy, database portability, or AI-assisted editorial workflows.

u/aimeos — 11 days ago

Laravel Cloud Office Hours (8/11): Why Private Cloud + Q&amp;A

We're doing another Laravel Cloud Office Hours stream next week on August 11th at 12pm EDT (4pm UTC) with Devon. This time, we have some special guests from Clair joining us to talk about why they moved to Cloud, and why they ended up on Private Cloud specifically!

Feel free to drop any Cloud questions in the comments ahead of time, into Slido, or ask them live in chat during the stream.

Submit a question: → https://app.sli.do/event/k8N1AYn9h5sqDmiAsAkMXH

YouTube stream: → https://www.youtube.com/watch?v=rI4AMcMq2BM

reddit.com
u/leahtcodes — 13 days ago