<?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: Mzer Terdoo</title>
    <description>The latest articles on DEV Community by Mzer Terdoo (@terdoo-mzer).</description>
    <link>https://dev.to/terdoo-mzer</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F637982%2Fc46d6275-a50c-477f-9a85-5a0b67fc5e31.jpeg</url>
      <title>DEV Community: Mzer Terdoo</title>
      <link>https://dev.to/terdoo-mzer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/terdoo-mzer"/>
    <language>en</language>
    <item>
      <title>A UUID Lesson in Laravel</title>
      <dc:creator>Mzer Terdoo</dc:creator>
      <pubDate>Sat, 25 Jul 2026 22:55:06 +0000</pubDate>
      <link>https://dev.to/terdoo-mzer/a-uuid-lesson-in-laravel-5ia</link>
      <guid>https://dev.to/terdoo-mzer/a-uuid-lesson-in-laravel-5ia</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybc8onv3hjbc5ysnkr6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybc8onv3hjbc5ysnkr6x.png" alt="laravel uuid lesson" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;Lesons learned in Laravel while building&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Swyftride -  &lt;/em&gt;&lt;/strong&gt; &lt;em&gt;a WhatsApp bus-booking system&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;Early in the development of &lt;strong&gt;&lt;em&gt;Swyftride&lt;/em&gt;&lt;/strong&gt; , I decided every table would use UUIDs instead of auto-incrementing integers, generated at the database level. See the logic I used to generate uuids in my migration file below:&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="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&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;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gen_random_uuid()'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked for the most part until until trips creation actually revealed something I missed earlier. This broke the app in way, and I had to rethink this approach to one that truly served the purpose I had in mind.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;The bug&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Each time the I created a trip, right in the transaction, &lt;strong&gt;&lt;em&gt;n&lt;/em&gt;&lt;/strong&gt; number of seats needed to be generated based on the capacity of the bus assigned to that trip. See the code snippet below showing the DB transaction responsible for this :&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="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validatedTripData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validatedTripData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'trips'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$tripData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$trip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Trip&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;$tripData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'capacity'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Seat&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'trip_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$trip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'seat_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'available'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every seat insert into the &lt;strong&gt;seats&lt;/strong&gt; table failed since &lt;strong&gt;$trip-&amp;gt;id&lt;/strong&gt; was &lt;strong&gt;null. $trip&lt;/strong&gt; held every attribute I had set explicitly. Only &lt;strong&gt;$trip-&amp;gt;id&lt;/strong&gt; was missing. While a trip was indeed created, and a UUID sat in the &lt;strong&gt;trips&lt;/strong&gt; table, my php program knew nothing about this UUID that was created in the &lt;strong&gt;id column&lt;/strong&gt; at the database level.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;What happened?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Model::create()&lt;/strong&gt; only auto-populates a DB-generated key when the model is &lt;strong&gt;incrementing = true&lt;/strong&gt; that path calls &lt;strong&gt;insertGetId()&lt;/strong&gt;, which on Postgres compiles to &lt;strong&gt;INSERT ... RETURNING id.&lt;/strong&gt; For a non-incrementing key, &lt;strong&gt;create() j&lt;/strong&gt;ust inserts and stops.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Fix I discovered
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Concerns\HasUuids&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Trip&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasUuids&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$trip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Trip&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$trip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "018f2b5c-6a7f-7b12-9d6f-2f8a4e0c9c11"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Illuminate\Database\Eloquent\Concerns\HasUuids&lt;/strong&gt; trait on the model generates the UUID in PHP before the INSERT is built, via Eloquent's &lt;strong&gt;creating&lt;/strong&gt; event. According to Laravel’s official documentation, “&lt;a href="https://laravel.com/docs/13.x/eloquent#primary-keys:~:text=By%20default%2C%20the,for%20your%20models." rel="noopener noreferrer"&gt;&lt;em&gt;By default, the&lt;/em&gt;&lt;/a&gt;&lt;a href="https://laravel.com/docs/13.x/eloquent#primary-keys:~:text=By%20default%2C%20the,for%20your%20models." rel="noopener noreferrer"&gt;&lt;em&gt;HasUuids trait will generate UUIDv7 identifiers for your models&lt;/em&gt;&lt;/a&gt;”. What this then means isPostgres never has to invent anything since PHP already handed it a value, and because PHP generated it, PHP already knows it. &lt;strong&gt;$trip-&amp;gt;id&lt;/strong&gt; is populated the instant &lt;strong&gt;create()&lt;/strong&gt;returns. Seat creation worked immediately after.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;One side effect:&lt;/strong&gt; my &lt;strong&gt;DEFAULT gen_random_uuid()&lt;/strong&gt; at the schema level now never fires since PHP always supplies the value first. Not wrong, just dead weight and needed a cleanup.&lt;/p&gt;
&lt;h4&gt;
  
  
  What I could have done otherwise?
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;HasUuids&lt;/strong&gt; wasn’t the only fix I could have utilised. I could have kept the DB-level default and just asked Postgres to return the value it generated by utlising a different approach &lt;strong&gt;insertGetId([…]) — the query builder method&lt;/strong&gt;:&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="nv"&gt;$tripId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'trips'&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;insertGetId&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// line 1&lt;/span&gt;
&lt;span class="nv"&gt;$trip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Trip&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tripId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// line 2. check the inserted trip exists&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;insertGetId()&lt;/strong&gt; on the query builder effectively returns the id of the insert. Conceptually, it runs this under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;departure_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'lagos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'abuja'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'2026-07-25 22:00:00'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;RETURNING&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Returning id&lt;/strong&gt; is the id of the inserted record. However I had key other features I needed that eloquent provided -  &lt;strong&gt;$fillable&lt;/strong&gt; attribute to protect against mass assignment, and also attribute casting.&lt;/p&gt;

&lt;h4&gt;
  
  
  The mental model going forward
&lt;/h4&gt;

&lt;p&gt;This wasn’t a framework ambush. It was solving a problem at the wrong layer before checking whether Laravel already had an idiom for it. Database-level UUID generation isn’t a mistake in the abstract . The actual gap was narrower: not knowing that &lt;strong&gt;create()&lt;/strong&gt; has no &lt;strong&gt;Returning&lt;/strong&gt; equivalent for non-incrementing keys, and that Laravel's answer is a trait you opt into per model, not a schema default you set once.&lt;/p&gt;

&lt;p&gt;The question I now ask earlier, before writing custom logic at any layer: &lt;strong&gt;does the framework already have an idiomatic way to express this - and does that idiom actually fit what the rest of my system needs, or just what this one model needs?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>laravelframework</category>
      <category>laraveldevelopment</category>
      <category>webdev</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
