<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Laravel on Murat Useinov</title>
    <link>https://useinov.com/tags/laravel/</link>
    <description>Recent content in Laravel on Murat Useinov</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 11 Mar 2026 20:15:00 +0300</lastBuildDate>
    <atom:link href="https://useinov.com/tags/laravel/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>An LLM call is just IO</title>
      <link>https://useinov.com/posts/2026/llm-call-is-io/</link>
      <pubDate>Wed, 11 Mar 2026 20:15:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2026/llm-call-is-io/</guid>
      <description>&lt;p&gt;One method, response back. Laravel 13 makes calling a model feel like calling a mailer, and the demo level is now twenty minutes of work. Which is exactly when architecture starts to matter, because everybody will ship the demo.&lt;/p&gt;
&lt;p&gt;An LLM call is IO. Slow IO, expensive IO, IO that sometimes fails and sometimes lies. We know how to handle IO like that. We forget, because the response text looks smart and the API looks native to the framework.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 12 is a boring release</title>
      <link>https://useinov.com/posts/2025/laravel-12-boring/</link>
      <pubDate>Wed, 26 Feb 2025 23:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2025/laravel-12-boring/</guid>
      <description>&lt;p&gt;Laravel 12 came out on Monday. Tuesday evening I upgraded one mid-size project. The whole diff is composer constraints and the lock file. Tests green, deployed before midnight.&lt;/p&gt;
&lt;p&gt;People in chats are disappointed. Where are the big features. A major that mostly refreshes dependencies and starter kits, is it even a major.&lt;/p&gt;
&lt;p&gt;I remember 4.2 to 5.0. New folder structure, new config system, half of the packages dead on arrival. We planned it as a separate project, because it was one. Skip a major, and the next one cost double.&lt;/p&gt;</description>
    </item>
    <item>
      <title>The fastest request never reaches PHP</title>
      <link>https://useinov.com/posts/2024/http-caching/</link>
      <pubDate>Tue, 03 Dec 2024 12:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/http-caching/</guid>
      <description>&lt;p&gt;One header, and the FPM load graph fell off a cliff.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Cache-Control: public, s-maxage=60
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A public catalog page on one project renders the same HTML for every anonymous visitor. Same queries, same JSON from the search service, same template, thousands of times per hour. We profiled it, we tuned it, and only then asked the obvious question: why is PHP involved in the second request at all.&lt;/p&gt;
&lt;p&gt;HTTP had the answer before my career started. &lt;code&gt;public&lt;/code&gt; says a shared cache may store the response. &lt;code&gt;s-maxage&lt;/code&gt; gives the CDN or reverse proxy its own lifetime, separate from browser &lt;code&gt;max-age&lt;/code&gt;. With Varnish in front, the request path splits in two. MISS: full stack, FPM, database, sixty milliseconds. HIT: the proxy answers from memory, the PHP process never starts, the database never hears about it. Nothing in the application got faster. There was simply less application running.&lt;/p&gt;</description>
    </item>
    <item>
      <title>WebSockets change your operational model</title>
      <link>https://useinov.com/posts/2024/reverb-operations/</link>
      <pubDate>Wed, 03 Jul 2024 23:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/reverb-operations/</guid>
      <description>&lt;p&gt;Every client reconnects at once. That is what a deploy means now on one project of mine, since Reverb made WebSockets a first-party Laravel feature this spring and order updates go to the browser instead of polling. The code was the easy part.&lt;/p&gt;
&lt;p&gt;A classic PHP app is stateless between requests. An FPM worker takes a request, answers, forgets. Capacity planning is requests per second. A WebSocket server is the opposite animal: thousands of open connections that mostly do nothing, but each one holds memory and a file descriptor, and each one is state that dies with the process. Your quiet realtime feature has a thundering herd built in. Plan for reconnect storms, raise descriptor limits, and make the client reconnect with jitter, never on a fixed timer.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 11 removes files, not decisions</title>
      <link>https://useinov.com/posts/2024/laravel-eleven/</link>
      <pubDate>Mon, 25 Mar 2024 23:05:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/laravel-eleven/</guid>
      <description>&lt;p&gt;The diff of a Laravel 11 upgrade on one mid-size API this week is mostly deletions. Almost empty &lt;code&gt;app/&lt;/code&gt;, configuration in one fluent bootstrap file, the slim skeleton I wrote about in January now real. It feels great.&lt;/p&gt;
&lt;p&gt;Now look at what survived untouched. The use case class that wraps an order state change in a transaction. The listener that must fire only after commit, because it queues an email about a row that must exist. The cache invalidation that follows every write to a heavily read table. The decision that validation lives in a request class and business rules one layer deeper. None of this is skeleton. None of it got smaller.&lt;/p&gt;</description>
    </item>
    <item>
      <title>The shrinking Laravel skeleton</title>
      <link>https://useinov.com/posts/2024/laravel-slim-skeleton/</link>
      <pubDate>Sat, 06 Jan 2024 22:25:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2024/laravel-slim-skeleton/</guid>
      <description>&lt;p&gt;No &lt;code&gt;app/Http/Kernel.php&lt;/code&gt;. That is the first thing you notice in the Laravel 11 preview, and the first thing every comment thread asks: where did my middleware go.&lt;/p&gt;
&lt;p&gt;The skeleton is the loudest change this quarter, louder than any feature. An &lt;code&gt;app/&lt;/code&gt; directory that is almost empty, no folder of middleware stubs, a pile of service providers collapsed into one, config files trimmed.&lt;/p&gt;
&lt;p&gt;The middleware did not go anywhere. It moved into the framework. The mechanism exists exactly as before, only the file does not, and &lt;code&gt;bootstrap/app.php&lt;/code&gt; gets a small fluent API to reconfigure the stack when you actually need to.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 10 is a quiet major</title>
      <link>https://useinov.com/posts/2023/laravel-ten-quiet/</link>
      <pubDate>Thu, 16 Feb 2023 20:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/laravel-ten-quiet/</guid>
      <description>&lt;p&gt;&lt;code&gt;Process::run(&#39;gzip -k dump.sql&#39;)&lt;/code&gt;. Out of the whole Laravel 10 release this week, that is the line I will actually use.&lt;/p&gt;
&lt;p&gt;The rest is quiet. Skeleton and framework code got native type declarations instead of docblocks, PHP 8.1 is the floor, and there is the Process facade. That is roughly the whole story. Some people are disappointed. I am not. A major release that is mostly maintenance means the framework is an adult.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Before Laravel 10: raise the baseline first</title>
      <link>https://useinov.com/posts/2023/laravel-upgrade-baseline/</link>
      <pubDate>Sat, 07 Jan 2023 22:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2023/laravel-upgrade-baseline/</guid>
      <description>&lt;p&gt;&lt;code&gt;composer why-not php 8.1&lt;/code&gt;. One command, and it tells you who holds you back. Run it before making any plans.&lt;/p&gt;
&lt;p&gt;Laravel 10 comes next month and requires PHP 8.1. One project I help with is on Laravel 9 and PHP 8.0. The temptation is one heroic branch: new PHP, new framework, new package versions. When that branch breaks in production, you will not know which of the three changes broke it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 9 and the boring major</title>
      <link>https://useinov.com/posts/2022/laravel-nine-cadence/</link>
      <pubDate>Fri, 25 Feb 2022 12:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2022/laravel-nine-cadence/</guid>
      <description>&lt;p&gt;One evening. That was the Laravel 9 upgrade on a mid-size project last week. Most of the diff was &lt;code&gt;composer.json&lt;/code&gt; and a few config files.&lt;/p&gt;
&lt;p&gt;Laravel 9 is out this month. Symfony 6 components under the hood, PHP 8.0 minimum, Flysystem 3. And from now on, one major per year. The upgrade guide is short, and the &amp;ldquo;high impact&amp;rdquo; section is shorter. This is a policy: small majors, often.&lt;/p&gt;
&lt;p&gt;I want to defend this policy, because I remember the other kind. A project that sat on an old major for three years, because &amp;ldquo;we will upgrade when there is time&amp;rdquo;. There is never time. Then the version goes EOL, a security fix forces the jump, and you pay three years of drift in one horrible month. Packages you depend on drop support one by one, and suddenly it is not a framework upgrade anymore. It is archaeology.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Octane and state that outlives the request</title>
      <link>https://useinov.com/posts/2021/octane-shared-state/</link>
      <pubDate>Wed, 05 May 2021 22:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2021/octane-shared-state/</guid>
      <description>&lt;p&gt;One customer sees another customer&amp;rsquo;s cart. No crash, no error in the log. That is the bug Laravel Octane brings to an old codebase, and the benchmarks are the least interesting part of the story.&lt;/p&gt;
&lt;p&gt;Octane is out in beta, Swoole and RoadRunner became first-class overnight. In FPM every request gets a fresh application. Boot, handle, die. The model forgives everything. Under Octane the framework boots once and workers reuse it. Bootstrap cost drops to near zero, hence the pretty numbers. But every singleton is now shared between requests, and between users.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 8: the interesting parts are not on the marketing page</title>
      <link>https://useinov.com/posts/2020/laravel-8-maturity/</link>
      <pubDate>Fri, 11 Sep 2020 23:15:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/laravel-8-maturity/</guid>
      <description>&lt;p&gt;&lt;code&gt;-&amp;gt;refundedTwice()&lt;/code&gt;. That is the line from Laravel 8 I care about, and it is nowhere on the release page. Jetstream gets the screenshots. Class-based factories and the queue changes get my attention, because both are about production.&lt;/p&gt;
&lt;p&gt;Factories used to be closures registered through a global function. Now they are classes with states:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Order&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;factory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;paid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;has&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;OrderItem&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;factory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Test data quality decides test quality. Most integration suites on our project test the happy path fifty times, because the default factory returns a fresh, valid, boring record. The bugs live somewhere else. An order refunded twice. A user registered before the migration added the column. A subscription that expired in the middle of renewal. A factory state gives such a monster a name, and once the name exists, people write tests with it. Named ugly data is the cheapest test improvement I know.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Sanctum and choosing how much auth you need</title>
      <link>https://useinov.com/posts/2020/sanctum-auth-level/</link>
      <pubDate>Thu, 26 Mar 2020 23:05:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2020/sanctum-auth-level/</guid>
      <description>&lt;p&gt;Airlock lived for about a week. Laravel 7 shipped it this month, a trademark scare followed, and it became Sanctum. Fast rename, same idea. And the idea is good, because it names a problem people solve badly.&lt;/p&gt;
&lt;p&gt;The problem: your own SPA needs to talk to your own API. For years the reflex was OAuth2. Install Passport, stand up an authorization server, issue JWTs to a frontend on the same domain as the backend. All that machinery to authenticate first-party code against itself. OAuth is a delegation protocol, it lets a third party act for a user. When there is no third party, you are running a passport office for your own family.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Eloquent observers and hidden control flow</title>
      <link>https://useinov.com/posts/2019/eloquent-events-cost/</link>
      <pubDate>Wed, 27 Feb 2019 12:40:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2019/eloquent-events-cost/</guid>
      <description>&lt;p&gt;Three thousand welcome emails. A colleague ran a user import on one project last month: loop over a CSV, &lt;code&gt;$user-&amp;gt;save()&lt;/code&gt;, go home. Next morning we found the script had also warmed the search index three thousand times and invalidated cache after every row. Nobody wrote that in the import script. The observers did.&lt;/p&gt;
&lt;p&gt;Laravel 5.8 came out yesterday, and reading the changelog brought that evening back, so here is the note.&lt;/p&gt;</description>
    </item>
    <item>
      <title>One payment is enough</title>
      <link>https://useinov.com/posts/2019/laravel-job-idempotency/</link>
      <pubDate>Mon, 21 Jan 2019 20:35:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2019/laravel-job-idempotency/</guid>
      <description>&lt;p&gt;Charge the card, send the receipt, ack the job. The worker lost its Redis connection between step one and step three. The queue delivered the job again. The customer paid twice. Support learned some new words from him.&lt;/p&gt;
&lt;p&gt;Retries are not an edge case. Laravel retries failed jobs by design, and you want that, because networks blink. So every job with a side effect must answer one question: what happens if this runs twice. &amp;ldquo;It will not run twice&amp;rdquo; is not an answer. It will.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Anatomy of email verification</title>
      <link>https://useinov.com/posts/2018/email-verification-anatomy/</link>
      <pubDate>Tue, 18 Sep 2018 18:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2018/email-verification-anatomy/</guid>
      <description>&lt;p&gt;&lt;code&gt;email_verified_at&lt;/code&gt;, a timestamp. Laravel 5.7 came out this month with email verification built in, and this column is the first thing I noticed. A boolean would cost the same and answer less. A timestamp answers not only whether, but when, and when a support ticket arrives half a year later, &amp;ldquo;when&amp;rdquo; is the question.&lt;/p&gt;
&lt;p&gt;The tutorials say: implement &lt;code&gt;MustVerifyEmail&lt;/code&gt;, put the &lt;code&gt;verified&lt;/code&gt; middleware on routes, done. True, and boring. The interesting part is how the feature is put together. It is a small example of a cross-cutting feature done right.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Where the checkout logic goes</title>
      <link>https://useinov.com/posts/2018/thin-controllers-laravel/</link>
      <pubDate>Mon, 13 Aug 2018 22:15:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2018/thin-controllers-laravel/</guid>
      <description>&lt;p&gt;Validate input, reserve stock, create the order, charge the card, fire events. Five steps in one checkout action, and the fat model versus fat controller argument offers only two rooms for them. Both rooms are wrong.&lt;/p&gt;
&lt;p&gt;Put it all in the controller and you cannot run checkout from anywhere except HTTP. No console command, no queue job, no test without the kernel. Put it in the Order model and the model now knows about payments, stock and notifications, a strange set of friends for an Eloquent class.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Horizon, or the queue becomes visible</title>
      <link>https://useinov.com/posts/2018/laravel-horizon-queues/</link>
      <pubDate>Sat, 20 Jan 2018 10:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2018/laravel-horizon-queues/</guid>
      <description>&lt;p&gt;&lt;code&gt;redis-cli llen queues:default&lt;/code&gt; and hope. That was my queue monitoring for years. Horizon replaces it with a real dashboard, and I did not know how much I needed one until I saw it.&lt;/p&gt;
&lt;p&gt;The mental shift matters more than the UI. &lt;code&gt;dispatch()&lt;/code&gt; is the beginning of the work, not the end. The job still has to wait in Redis, run, maybe fail, maybe retry. All of that was invisible. Now it is on one screen: throughput, wait time per queue, failed jobs with the full payload and the exception.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 5.5 and package auto-discovery</title>
      <link>https://useinov.com/posts/2017/laravel-auto-discovery/</link>
      <pubDate>Thu, 31 Aug 2017 15:45:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2017/laravel-auto-discovery/</guid>
      <description>&lt;p&gt;Laravel 5.5 came out yesterday, an LTS: two years of bug fixes, three of security fixes. Good news for projects that outlive the hype cycle, which is most of them.&lt;/p&gt;
&lt;p&gt;The feature in every screenshot is package auto-discovery. A package declares its service provider in its own &lt;code&gt;composer.json&lt;/code&gt;, and after &lt;code&gt;composer require&lt;/code&gt; it is registered. No editing of &lt;code&gt;config/app.php&lt;/code&gt;, no provider line copied from the readme.&lt;/p&gt;
&lt;p&gt;Convenient. Now look at what got deleted. Installing a package used to be two steps, and the second step was consent: a line in my own config saying this code runs inside my application at boot. Now &lt;code&gt;composer require&lt;/code&gt; is the whole ceremony. The list of code that runs at boot lives in &lt;code&gt;vendor/&lt;/code&gt;, spread over other people&amp;rsquo;s files. &lt;code&gt;php artisan package:discover&lt;/code&gt; shows it, and &lt;code&gt;dont-discover&lt;/code&gt; in composer.json switches it off per package. But the default flipped from explicit to implicit.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel Dusk and what browser tests are for</title>
      <link>https://useinov.com/posts/2017/laravel-dusk/</link>
      <pubDate>Thu, 26 Jan 2017 18:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2017/laravel-dusk/</guid>
      <description>&lt;p&gt;Laravel 5.4 came out on Tuesday, and the first thing I installed was Dusk. Browser tests without a Selenium server: it talks to ChromeDriver directly, the API is fluent, and a failed test leaves a screenshot behind.&lt;/p&gt;
&lt;p&gt;My first test was the login path:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;browse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$browser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$browser&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;visit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/login&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;user@example.com&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;secret&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;press&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Log in&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;assertPathIs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/home&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It passed on the third run. The first run failed because the button on the test box had different text. The second failed because a JS animation was slower than the wait. This is the normal life of a browser test. It sees the real application, and the real application moves.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 5.3: the app is not a website anymore</title>
      <link>https://useinov.com/posts/2016/laravel-53-notifications/</link>
      <pubDate>Fri, 26 Aug 2016 12:30:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2016/laravel-53-notifications/</guid>
      <description>&lt;p&gt;&lt;code&gt;Mail::send()&lt;/code&gt; inside the checkout controller. A second copy inside the API controller. Then someone adds the chat webhook to one copy and forgets the other. Every project has this code. I have written it more than once.&lt;/p&gt;
&lt;p&gt;Laravel 5.3 came out this week, and the two big pieces, Notifications and Passport, both point the same way. The application is no longer a thing that renders HTML. It is a core that talks to browsers, mobile clients and third parties, and HTML is one of the outputs.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Middleware groups in Laravel 5.2</title>
      <link>https://useinov.com/posts/2016/laravel-middleware-groups/</link>
      <pubDate>Mon, 08 Feb 2016 21:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2016/laravel-middleware-groups/</guid>
      <description>&lt;p&gt;An IP whitelist check placed after auth. That was the whole bug. Every scanner bot on the internet was going through session start and a user lookup just to be told to go away. The fix was to move one line up in the Kernel.&lt;/p&gt;
&lt;p&gt;Laravel 5.2 makes that line easier to see. Middleware groups, and the request pipeline is finally written down in one place:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$middlewareGroups&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;web&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;\App\Http\Middleware\EncryptCookies&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;\Illuminate\Session\Middleware\StartSession&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nx&#34;&gt;\App\Http\Middleware\VerifyCsrfToken&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s1&#34;&gt;&amp;#39;api&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s1&#34;&gt;&amp;#39;throttle:60,1&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s1&#34;&gt;&amp;#39;bindings&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before 5.2, sessions and CSRF were global middleware. They ran for everything, including API routes that have no use for cookies. Now &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;api&lt;/code&gt; are two separate pipelines, and you can read each one top to bottom.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 5.1 and the dependency ladder</title>
      <link>https://useinov.com/posts/2015/dependency-ladder/</link>
      <pubDate>Sun, 14 Jun 2015 19:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2015/dependency-ladder/</guid>
      <description>&lt;p&gt;PHP 5.5.9. That is the minimum for Laravel 5.1, which came out this week, the first Laravel with LTS: two years of bug fixes, three of security fixes. Two weeks ago I wrote the same about Symfony 2.7. Two frameworks arriving at the same idea in one summer is no coincidence. PHP applications got old enough to have a lifecycle.&lt;/p&gt;
&lt;p&gt;Draw the ladder for your project once. OS at the bottom. Then the PHP version the OS ships, or the one you build. Then the framework. Then the packages on top. Every rung has its own end-of-life, and the rungs are coupled. If production sits on some old distro with 5.4, the 5.1 upgrade is suddenly an ops project. And above you, half the packages will require 5.1 in their next major, so staying behind slowly cuts you off from fixes.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Three ways to get a dependency in Laravel</title>
      <link>https://useinov.com/posts/2015/container-or-facade/</link>
      <pubDate>Mon, 09 Mar 2015 21:10:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2015/container-or-facade/</guid>
      <description>&lt;p&gt;Same repository, three ways to get it in Laravel 5.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 1. Constructor injection
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;OrderRepository&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$orders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nv&#34;&gt;$this&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;orders&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$orders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 2. Facade
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$order&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Orders&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 3. Service locator
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;$orders&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;App&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;App\Repositories\OrderRepository&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All three work. The container resolves everything either way. The difference is in what the class tells you about itself.&lt;/p&gt;
&lt;p&gt;With constructor injection the dependencies are in the signature. You open the class, you read the constructor, you know what it needs. A test passes a mock and never touches the container.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Laravel 5 and the death of the models folder</title>
      <link>https://useinov.com/posts/2015/laravel-five-structure/</link>
      <pubDate>Wed, 25 Feb 2015 22:20:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2015/laravel-five-structure/</guid>
      <description>&lt;p&gt;&lt;code&gt;app/models&lt;/code&gt; is gone. Laravel 5 came out at the start of the month, and of everything in the release the folder I notice most is the one that no longer exists.&lt;/p&gt;
&lt;p&gt;The old skeleton had &lt;code&gt;app/models&lt;/code&gt; and &lt;code&gt;app/controllers&lt;/code&gt;. You knew where things go because the framework told you. Laravel 5 removes that. There is &lt;code&gt;app/&lt;/code&gt;, it maps to the &lt;code&gt;App&lt;/code&gt; namespace through PSR-4, and inside it you organize classes however you want.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Queues in Laravel 4.2: the user should not wait for your SMTP</title>
      <link>https://useinov.com/posts/2014/laravel-queues/</link>
      <pubDate>Thu, 27 Nov 2014 20:50:00 +0300</pubDate>
      <guid>https://useinov.com/posts/2014/laravel-queues/</guid>
      <description>&lt;p&gt;1.8 seconds for one registration request. I put a timer around it on a project last month. 1.5 of those seconds was the welcome email going out over SMTP.&lt;/p&gt;
&lt;p&gt;The user waits almost two seconds and looks at a spinner, for a handshake with a mail server he will never hear about.&lt;/p&gt;
&lt;p&gt;Laravel 4.2 makes the fix one line:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;Queue&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;SendWelcomeEmail&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The controller returns in 300 ms. A worker picks the job up and sends the email. &lt;code&gt;php artisan queue:listen&lt;/code&gt; to start, beanstalkd or Redis behind it, the &lt;code&gt;failed_jobs&lt;/code&gt; table for jobs that died.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
