<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paul Rijke</title>
    <description>The latest articles on DEV Community by Paul Rijke (@parijke).</description>
    <link>https://dev.to/parijke</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F565074%2F10aee393-d1c0-4779-b142-53008bb7229b.jpg</url>
      <title>DEV Community: Paul Rijke</title>
      <link>https://dev.to/parijke</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/parijke"/>
    <language>en</language>
    <item>
      <title>Distinguishing Stubs from Mocks in Mockery</title>
      <dc:creator>Paul Rijke</dc:creator>
      <pubDate>Sun, 01 Feb 2026 17:39:15 +0000</pubDate>
      <link>https://dev.to/parijke/distinguishing-stubs-from-mocks-in-mockery-1416</link>
      <guid>https://dev.to/parijke/distinguishing-stubs-from-mocks-in-mockery-1416</guid>
      <description>&lt;p&gt;In modern unit testing, the clarity of your test code is just as important as the logic itself. For years, PHP developers using Mockery have relied on &lt;code&gt;shouldReceive()&lt;/code&gt; as a "catch-all" for test doubles. However, as the library has evolved, a clearer distinction between Stubs and Mocks has emerged that makes tests more readable and less prone to false positives.&lt;/p&gt;

&lt;p&gt;This need for this shift was highlighted recently by Joel Clermont in his article, “&lt;a href="https://masteringlaravel.io/daily/2024-11-05-dont-forget-this-when-writing-mock-assertions" rel="noopener noreferrer"&gt;Don’t forget this when writing mock assertions&lt;/a&gt;”. He pointed out a common pitfall: using &lt;code&gt;shouldReceive()&lt;/code&gt; without an explicit expectation like &lt;code&gt;once()&lt;/code&gt;. In these cases, Mockery defaults to "zero or more" calls, meaning your test might pass even if the method is never actually executed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Fundamental Difference&lt;/strong&gt;&lt;br&gt;
  To write better tests, we must distinguish between how data enters your system and how signals leave it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stubs (Indirect Input): These provide the "canned responses" (data) your application needs to function. You don't necessarily care if the method is called once, twice, or at all—you just need it to return a specific value when it is called.&lt;/li&gt;
&lt;li&gt;Mocks (Indirect Output): These verify that your system performed a specific action, such as sending an email or saving a record. The verification is the goal of the test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Implementation: &lt;code&gt;allows()&lt;/code&gt; vs. &lt;code&gt;expects()&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Mockery v1 introduced a more expressive syntax to replace the ambiguous &lt;code&gt;shouldReceive()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario A: Stubbing with &lt;code&gt;allows()&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Use this when your test object needs data from a dependency. For example, a repository finding a record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// We are providing data so the application can continue (Input)&lt;/span&gt;
&lt;span class="nv"&gt;$userRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;allows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;andReturns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why use this?&lt;/strong&gt; Using &lt;code&gt;allows()&lt;/code&gt; signals to other developers that this is a dependency setup, not a strict requirement. If your implementation changes and calls find() twice instead of once, the test won't break unnecessarily. It is resilient to refactoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario B: Mocking with &lt;code&gt;expects()&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Use this when you must verify that a side-effect occurred. For example, ensuring a notification was dispatched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// We are verifying that an action took place (Output)&lt;/span&gt;
&lt;span class="nv"&gt;$paymentGateway&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;expects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why use this?&lt;/strong&gt; The &lt;code&gt;expects()&lt;/code&gt; method automatically sets a default requirement that the method must be called exactly once. It eliminates the risk mentioned by Joel Clermont where an assertion is forgotten. If the method isn't called, the test fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Comparison Summary&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Mockery method&lt;/th&gt;
&lt;th&gt;Primary purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stub&lt;/td&gt;
&lt;td&gt;$m-&amp;gt;allows()&lt;/td&gt;
&lt;td&gt;Provide data to the test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mock&lt;/td&gt;
&lt;td&gt;$m-&amp;gt;expects()&lt;/td&gt;
&lt;td&gt;Verify an action happened&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy&lt;/td&gt;
&lt;td&gt;$m-&amp;gt;shouldReceive()&lt;/td&gt;
&lt;td&gt;Ambiguous / Combined&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why stop using &lt;code&gt;shouldReceive()&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
While &lt;code&gt;shouldReceive()&lt;/code&gt; is still working, it lacks intent. When a developer reads a test using &lt;code&gt;shouldReceive()&lt;/code&gt;, they have to scan the rest of the chain for &lt;code&gt;once()&lt;/code&gt; or &lt;code&gt;andReturn()&lt;/code&gt; to understand what is being tested.&lt;/p&gt;

&lt;p&gt;By switching to &lt;code&gt;allows()&lt;/code&gt; and &lt;code&gt;expects()&lt;/code&gt;, the intent is communicated immediately. You clearly separate the setup of your environment from the assertions of your behavior.&lt;/p&gt;

</description>
      <category>mockery</category>
      <category>php</category>
      <category>testing</category>
    </item>
    <item>
      <title>Why 100% PHP Test Coverage is (Mostly) BS</title>
      <dc:creator>Paul Rijke</dc:creator>
      <pubDate>Thu, 07 Aug 2025 06:28:01 +0000</pubDate>
      <link>https://dev.to/parijke/why-100-php-test-coverage-is-mostly-bs-11l1</link>
      <guid>https://dev.to/parijke/why-100-php-test-coverage-is-mostly-bs-11l1</guid>
      <description>&lt;p&gt;Alright, let's talk testing. Specifically, let's talk about this almost religious pursuit of &lt;strong&gt;100% test coverage&lt;/strong&gt;. You see it plastered on project dashboards, whispered in hushed tones during code reviews, and sometimes even demanded by management who might not &lt;em&gt;fully&lt;/em&gt; grasp what it actually means. And for years, I, like many others, bought into it. "100% coverage means my code is perfect, right?" Oh, the blissful naivety. Now? I'm here to tell you, from the trenches of countless hours spent wrestling with PHPUnit and Co., that chasing 100% test coverage is, in large part, utter BS.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Metric Trap: Chasing Numbers, Not Confidence
&lt;/h3&gt;

&lt;p&gt;This is the big one for me. When you aim for 100% test coverage in PHP, you're inevitably shifting your focus from &lt;strong&gt;writing good, robust code&lt;/strong&gt; to &lt;strong&gt;writing code that &lt;em&gt;looks&lt;/em&gt; like it's being tested&lt;/strong&gt;. And it’s eerily similar to that old programmer cliché: measuring performance by counting lines of code. More lines of code doesn't mean better code. More lines of &lt;em&gt;test&lt;/em&gt; code doesn't automatically mean more confidence in your application.&lt;/p&gt;

&lt;p&gt;I've spent an embarrassing amount of time writing tests that, frankly, are only there to tick a box. Tests that assert the obvious, tests that just call a method with some arbitrary data and check if it returns &lt;em&gt;something&lt;/em&gt;, tests that barely scratch the surface of what could actually go wrong. It’s the equivalent of having a chef boast about the sheer number of obscure spices they crammed into a dish, rather than the fact that the steak is perfectly cooked and the sauce is a revelation. I’ve been there, staring at a coverage report, desperately trying to get that last 0.5% out of some obscure getter method, while the core logic of my application still feels… a bit wobbly. The pursuit of coverage becomes the &lt;em&gt;goal&lt;/em&gt;, rather than a &lt;em&gt;means&lt;/em&gt; to building reliable software. (&lt;a href="https://en.wikipedia.org/wiki/Goodhart%27s_law" rel="noopener noreferrer"&gt;Goodhart's Law&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The "Good Enough" Siren Song: Complacency by Report
&lt;/h3&gt;

&lt;p&gt;Here’s where the laziness factor kicks in, and I’m not proud to admit how often I’ve fallen victim to it. The moment that little coverage report proudly displays "100%", a little voice in my head (and sometimes, let's be honest, my manager’s voice) says, "Great! It’s covered. We’re done." And that’s incredibly dangerous. It’s the ultimate permission slip to stop thinking critically about the &lt;em&gt;quality&lt;/em&gt; of the tests, or more importantly, the &lt;em&gt;quality&lt;/em&gt; of the code itself.&lt;/p&gt;

&lt;p&gt;I’ve seen projects where, once 100% coverage was achieved, the motivation to refactor, to improve the existing code, or to even write &lt;em&gt;better&lt;/em&gt; tests for new features just… evaporated. Why bother, when the number is already perfect? It’s like a student finishing an essay by hitting the word count and then deciding they’ve aced the assignment, without actually re-reading it to ensure it’s coherent or persuasive. This "good enough" mentality is the antithesis of craftsmanship. It encourages a superficial approach where the &lt;em&gt;appearance&lt;/em&gt; of thoroughness trumps the actual &lt;em&gt;substance&lt;/em&gt; of robust, maintainable code. I’ve definitely been guilty of hitting that 100% and thinking, "Phew, dodged that bullet," instead of thinking, "Okay, now how can I make this &lt;em&gt;truly&lt;/em&gt; solid?"&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Mission Critical Gets Drowned Out by the Noise
&lt;/h3&gt;

&lt;p&gt;This is the often-overlooked consequence. When you're chasing 100% coverage, your precious development time and mental energy get spread incredibly thin. Less significant, boilerplate code gets the same attention – the same agonizing over test cases – as the mission-critical, complex pieces of logic that actually drive your application.&lt;/p&gt;

&lt;p&gt;Think about it: some parts of your PHP application are absolutely vital. These are the bits that handle transactions, user authentication, core business rules. These are the areas where a bug can have catastrophic consequences. But when you’re aiming for absolute coverage, you end up spending an equal amount of time writing tests for a simple helper function that converts a string to lowercase. The focus gets diluted. The truly important, high-risk areas don't get the concentrated, expert attention they deserve because you’re too busy trying to cover every single line of code, no matter how trivial. I’ve witnessed firsthand how this leads to perfectly tested but ultimately flawed applications because the &lt;em&gt;critical&lt;/em&gt; pieces were treated with the same level of scrutiny as the decorative bits. It’s like a surgeon spending more time polishing their scalpel than focusing on the intricate organ they’re operating on.&lt;/p&gt;

&lt;h3&gt;
  
  
  So, What's the Alternative?
&lt;/h3&gt;

&lt;p&gt;Look, I'm not saying testing is bad. Far from it. Good, well-written tests are the bedrock of maintainable software. But we need to be smarter about &lt;em&gt;how&lt;/em&gt; we test and &lt;em&gt;what&lt;/em&gt; we aim for. Instead of a blind pursuit of 100%, let's focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Meaningful tests:&lt;/strong&gt; Tests that actually verify behavior, edge cases, and potential failure points in your critical logic.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Confidence over coverage:&lt;/strong&gt; Aim to have high confidence in the most important parts of your application, rather than a high number for everything.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Testing what matters:&lt;/strong&gt; Invest your time in testing the complex, risky, and core functionalities. Let the simple setters and getters fend for themselves, or test them with a minimal, sensible approach.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Continuous improvement:&lt;/strong&gt; See testing as an ongoing process, not a one-time checkbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chasing 100% PHP test coverage can feel like a tangible, reassuring goal, but in my experience, it's a distraction from the real work of building high-quality, resilient software. It’s time we stopped worshipping the coverage report and started focusing on the actual confidence and quality it’s supposed to represent. Because honestly, a well-tested 90% is far more valuable than a poorly tested, complacency-driven 100%.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>TIL: Why PHPMD and SonarCloud don’t always agree</title>
      <dc:creator>Paul Rijke</dc:creator>
      <pubDate>Sat, 12 Apr 2025 07:50:07 +0000</pubDate>
      <link>https://dev.to/parijke/til-why-phpmd-and-sonarcloud-dont-always-agree-20eh</link>
      <guid>https://dev.to/parijke/til-why-phpmd-and-sonarcloud-dont-always-agree-20eh</guid>
      <description>&lt;p&gt;Here’s a good one for the “quirks of tooling” file. We set up both SonarCloud and PHPMD to enforce a limit on the number of public methods in our classes. Same limit, same rules... or so we thought. But then SonarCloud flagged way more issues than PHPMD, and we were left scratching our heads. Naturally, we dove in to figure out what was going on. Spoiler alert: it wasn’t a bug—it was a &lt;em&gt;feature&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;PHPMD Has a Secret List of “Favorite” Methods&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;After some investigation, we discovered that PHPMD doesn’t count every method by default. Instead, it skips certain methods based on their names. The documentation claims that only &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt; methods are ignored by the &lt;code&gt;TooManyMethods&lt;/code&gt; rule. &lt;/p&gt;

&lt;p&gt;But wait! The &lt;strong&gt;actual default behavior&lt;/strong&gt; is even sneakier. Here’s the real regex pattern PHPMD uses by default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"ignorepattern"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"(^(set|get|is|has|with))i"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in reality, PHPMD also ignores methods starting with &lt;code&gt;is&lt;/code&gt;, &lt;code&gt;has&lt;/code&gt;, and &lt;code&gt;with&lt;/code&gt;. This explains why SonarCloud was flagging more issues—SonarCloud doesn’t play favorites.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Fixing the Mismatch&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;We decided we didn’t want any special treatment—every method should count, no matter what it starts with. The obvious first step was to leave the &lt;code&gt;ignorepattern&lt;/code&gt; empty. But PHPMD didn’t seem to understand “empty” the way we expected, so that didn’t work. &lt;/p&gt;

&lt;p&gt;To get PHPMD to count &lt;em&gt;all&lt;/em&gt; methods, we had to explicitly override the default pattern. Here’s the configuration we landed on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;rule&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"rulesets/codesize.xml/TooManyMethods"&lt;/span&gt; &lt;span class="na"&gt;message=&lt;/span&gt;&lt;span class="s"&gt;"The {0} {1} has {2}. Consider refactoring {1} to keep the number of methods under {3}."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"maxmethods"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"ignorepattern"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"(!)i"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/rule&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;rule&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"rulesets/codesize.xml/TooManyPublicMethods"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"maxmethods"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"ignorepattern"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"(!)i"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/rule&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The regex &lt;code&gt;(!)i&lt;/code&gt; essentially tells PHPMD: “Don’t ignore &lt;em&gt;anything&lt;/em&gt;. Count every single method.” Now, PHPMD and SonarCloud are on the same page, and our rule enforcement is consistent.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;What We Learned&lt;/strong&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;PHPMD is full of surprises:&lt;/strong&gt; Its default rules skip over methods starting with certain prefixes, and the documentation doesn’t tell the whole story.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don’t assume tools behave the same way:&lt;/strong&gt; Just because two tools seem to check for the same thing doesn’t mean their definitions match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regex saves the day:&lt;/strong&gt; To make PHPMD truly count everything, use a pattern like &lt;code&gt;(!)i&lt;/code&gt; to ensure no methods are skipped.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;A Fun Thought&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Maybe PHPMD’s on to something. If we ignored all tasks starting with “fold” or “clean,” wouldn’t life seem a whole lot easier? Unfortunately, our code doesn’t work that way, so neither can we.&lt;/p&gt;

</description>
      <category>php</category>
      <category>cleancode</category>
      <category>phpmd</category>
      <category>sonarcloud</category>
    </item>
    <item>
      <title>Getting Doctrine's ChangeSet in a postUpdate event</title>
      <dc:creator>Paul Rijke</dc:creator>
      <pubDate>Fri, 30 Sep 2022 09:11:27 +0000</pubDate>
      <link>https://dev.to/parijke/getting-doctrines-changeset-in-a-postupdate-event-59ba</link>
      <guid>https://dev.to/parijke/getting-doctrines-changeset-in-a-postupdate-event-59ba</guid>
      <description>&lt;p&gt;Recently, I had the need of having the changes of an entity in a lifecycle event after the database was updated. Normally, the postUpdate event doesn't have access to this information. So, what to do?&lt;/p&gt;

&lt;p&gt;I figured, that storing the changes during onFlush or preUpdate would do the trick, but how? I did not want to enforce extra database calls. Therefor, the in memory ArrayAdapter of symfony cache to the rescue.&lt;/p&gt;

&lt;p&gt;According to the documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generally, this adapter is useful for testing purposes, as its contents are stored in memory and not persisted outside the running PHP process in any way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So it is gone after the running process? Exactly what I needed. Here's how I set it during a preUpdate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class DoctrineSubscriber implements EventSubscriberInterface
{
    private ArrayAdapter $arrayAdapter;

    public function __construct(
    ) {
        $this-&amp;gt;arrayAdapter = new ArrayAdapter();
    }

    public function getSubscribedEvents(): array
    {
        return [
            Events::preUpdate, //OR
            Events::onFlush,
            // THEN
            Events::postUpdate,

        ];
    }

    public function preUpdate(PreUpdateEventArgs $args): void
    {
        $entity = $args-&amp;gt;getObject();

        if ($entity instanceof Meeting) {
            $this-&amp;gt;arrayAdapter-&amp;gt;get($entity-&amp;gt;getId(), fn () =&amp;gt; $args-&amp;gt;getEntityChangeSet());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you can easily retrieve it using the stored id as the key, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public function postUpdate(LifeCycleEventArgs $args)
    {
        $entity = $args-&amp;gt;getObject();

        if ($entity instanceof Meeting) {
            $id = $entity-&amp;gt;getId();
            $changeSet = $this-&amp;gt;arrayAdapter-&amp;gt;getItem($id)-&amp;gt;get();
        }

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If for some reason the database wasn't updated due to errors, the postUpdate isn't called and the changeset disappears. Just as I needed.&lt;/p&gt;

&lt;p&gt;If you want to be sure on listeners or subscribers changes your entity before persistence, you should/could use the onFlush event instead, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public function onFlush(OnFlushEventArgs $eventArgs): void
    {
        $uow = $eventArgs-&amp;gt;getObjectManager()-&amp;gt;getUnitOfWork();

        foreach ($uow-&amp;gt;getScheduledEntityUpdates() as $entity) {
            if ($entity instanceof Meeting) {
                $this-&amp;gt;arrayAdapter-&amp;gt;get($entity-&amp;gt;getId(), fn () =&amp;gt; $uow-&amp;gt;getEntityChangeSet($entity));
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because onFlush is the last event called before database persistence, we're pretty sure nothing altered the changeset afterwards.&lt;/p&gt;

&lt;p&gt;As a disclaimer... I use uuid as the id, so I am pretty sure the cache key will be unique for that change.&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>doctrine</category>
    </item>
    <item>
      <title>Using SQLite in test, and why you should be carefull.</title>
      <dc:creator>Paul Rijke</dc:creator>
      <pubDate>Mon, 15 Feb 2021 10:07:22 +0000</pubDate>
      <link>https://dev.to/parijke/using-sqlite-in-test-what-i-learned-15p3</link>
      <guid>https://dev.to/parijke/using-sqlite-in-test-what-i-learned-15p3</guid>
      <description>&lt;p&gt;I recently started using phpunit. Yeah, don't shout at me...! I am just a self taught programmer, and I have to do everything all by myself. Anyway, I become to like testing.&lt;/p&gt;

&lt;p&gt;As I am using Symfony a lot, I always watch for tips and tricks. Then I came along this video series of Gary Clarke, which by the way, I respect highly as I am learning much of his videos.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://www.youtube.com/watch?v=J46I-cctz7M&amp;amp;t=370s" rel="noopener noreferrer"&gt;this&lt;/a&gt; video he shows a trick of using an in-memory SQLite database for very fast test-runs. After a little investigation, I started to use it as well.&lt;/p&gt;

&lt;p&gt;All went fine, until I started to test my (date-driven) validations. In a specific case, I had to test if a new requested date was bigger than a value in the database. This is for preventing overlapping date ranges.&lt;/p&gt;

&lt;p&gt;The used validation runs a DQL in the repository, and if records are returned, the dates overlap.&lt;/p&gt;

&lt;p&gt;It worked perfectly in my dev environment (MariaDB), but when I started to write the tests, one assertings continually failed, which actually was valid in my manual dev test.&lt;/p&gt;

&lt;p&gt;Here's the QueryBuilder logic involved in the validations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; return $this-&amp;gt;createQueryBuilder('e')
     -&amp;gt;andWhere('e.asset = :asset')
     -&amp;gt;andWhere('(:requestDateTo &amp;gt; e.fromDate AND :requestDateFrom &amp;lt; e.toDate)')
     -&amp;gt;setParameter('requestDateFrom', $booking-&amp;gt;getFromDate())
     -&amp;gt;setParameter('requestDateTo', $booking-&amp;gt;getToDate())
     -&amp;gt;setParameter('asset', $booking-&amp;gt;getAsset())
     -&amp;gt;getQuery()
     -&amp;gt;execute();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this worked perfectly fine in my dev environment, but a certain assertion (which I knew had to be right) did fail in test every time.&lt;/p&gt;

&lt;p&gt;Long story short, after a lot of debugging I discovered that SQLite (via DQL) is treating a  DateTime variable differently. This translates in SQL like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select * where '2021-01-01 00:00:000000' &amp;gt; date_from;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a database record exist in the database with type = datetype and value is '2021-01-01', this doesn't return a record in MariaDB, but it will in SQLite. It seems SQLite treats '2021-01-01 00:00:000000' &amp;gt; '2021-01-01' as true, while MariaDB treats this as false.&lt;/p&gt;

&lt;p&gt;I eventually solved it by forcing the date parameters in the (SQLite) matching formats, which also works in MariaDB. Ended up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; return $this-&amp;gt;createQueryBuilder('e')
     -&amp;gt;andWhere('e.asset = :asset')
     -&amp;gt;andWhere('(:requestDateTo &amp;gt; e.fromDate AND :requestDateFrom &amp;lt; e.toDate)')
     -&amp;gt;setParameter('requestDateFrom', $booking-&amp;gt;getFromDate()-&amp;gt;format('Y-m-d'))
     -&amp;gt;setParameter('requestDateTo', $booking-&amp;gt;getToDate()-&amp;gt;format('Y-m-d'))
     -&amp;gt;setParameter('asset', $booking-&amp;gt;getAsset())
     -&amp;gt;getQuery()
     -&amp;gt;execute();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, as using SQLite in-memory is a badass trick to quickly run databased involved tests, beware of the quirks that can come up when using a different database typology.&lt;/p&gt;

</description>
      <category>mariadb</category>
      <category>symfony</category>
      <category>phpunit</category>
      <category>sqlite</category>
    </item>
  </channel>
</rss>
