<?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: Matteo Barbero</title>
    <description>The latest articles on DEV Community by Matteo Barbero (@maiobarbero).</description>
    <link>https://dev.to/maiobarbero</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%2F976354%2F830142ac-fbe9-4f2e-8df8-21c4817ce327.png</url>
      <title>DEV Community: Matteo Barbero</title>
      <link>https://dev.to/maiobarbero</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maiobarbero"/>
    <language>en</language>
    <item>
      <title>How to test Filament resources | Laravel Personal Finance Dashboard</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Thu, 19 Feb 2026 09:41:59 +0000</pubDate>
      <link>https://dev.to/maiobarbero/how-to-test-filament-resources-laravel-personal-finance-dashboard-eei</link>
      <guid>https://dev.to/maiobarbero/how-to-test-filament-resources-laravel-personal-finance-dashboard-eei</guid>
      <description>&lt;p&gt;Now that we have our resources set up, it's time to ensure they work as expected—and stay that way. In this lesson, we're going to write feature tests for our &lt;code&gt;BankAccount&lt;/code&gt;, &lt;code&gt;Budget&lt;/code&gt;, and &lt;code&gt;Category&lt;/code&gt; resources. We'll focus particularly on ensuring that users can only see &lt;em&gt;their own&lt;/em&gt; data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Pest
&lt;/h2&gt;

&lt;p&gt;First, we need to add &lt;strong&gt;Pest&lt;/strong&gt; to our project. Pest is a testing framework with a focus on simplicity and elegance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require pestphp/pest &lt;span class="nt"&gt;--dev&lt;/span&gt; &lt;span class="nt"&gt;--with-all-dependencies&lt;/span&gt;
php artisan pest:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to make sure our &lt;code&gt;User&lt;/code&gt; model is ready for Filament testing. To properly simulate a Filament user in our tests, our &lt;code&gt;User&lt;/code&gt; model should implement the &lt;code&gt;FilamentUser&lt;/code&gt; contract. This ensures that when we use &lt;code&gt;actingAs&lt;/code&gt;, the user is correctly recognized as having access to the panel.&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;app/Models/User.php&lt;/code&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="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Filament\Models\Contracts\FilamentUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Authenticatable&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;FilamentUser&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;canAccessPanel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Filament\Panel&lt;/span&gt; &lt;span class="nv"&gt;$panel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&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;You can view the setup and model changes in &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/b7df8af" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Bank Account Resource
&lt;/h2&gt;

&lt;p&gt;Let's start with the &lt;code&gt;BankAccountResource&lt;/code&gt;. We want to verify that we can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Render the page.&lt;/li&gt;
&lt;li&gt;List bank accounts belonging to the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NOT&lt;/strong&gt; list bank accounts belonging to other users.&lt;/li&gt;
&lt;li&gt;Create, edit, and delete accounts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create a new test file: &lt;code&gt;tests/Feature/Filament/Resources/BankAccountResourceTest.php&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rendering and Listing
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;actingAs($user)&lt;/code&gt; to sign in as a user, and then access the page URL.&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="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Filament\Resources\BankAccounts\Pages\ManageBankAccounts&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;App\Models\BankAccount&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;App\Models\User&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;Livewire\Livewire&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;Pest\Laravel\actingAs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'can render page'&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&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;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;actingAs&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ManageBankAccounts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getUrl&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;assertSuccessful&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'can list bank accounts'&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&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;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$bankAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&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;for&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;Livewire&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;actingAs&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ManageBankAccounts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;assertCanSeeTableRecords&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$bankAccount&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;h3&gt;
  
  
  Testing Scope (The "Other User" Test)
&lt;/h3&gt;

&lt;p&gt;This is a critical test. We create a user and &lt;em&gt;another&lt;/em&gt; user. We expect the current user effectively &lt;em&gt;not&lt;/em&gt; to see the other user's 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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cannot see other users bank accounts'&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&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;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$otherUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&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;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$otherBankAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&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;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$otherUser&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;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;Livewire&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;actingAs&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ManageBankAccounts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;assertCanNotSeeTableRecords&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$otherBankAccount&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;h3&gt;
  
  
  Actions: Create, Edit, Delete
&lt;/h3&gt;

&lt;p&gt;Filament provides excellent helpers to test actions directly.&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'can create bank account'&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&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;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;Livewire&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;actingAs&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ManageBankAccounts&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;mountAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'create'&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;setActionData&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My Main Bank'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'balance'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'1000.50'&lt;/span&gt;&lt;span class="p"&gt;,&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;callMountedAction&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;assertHasNoActionErrors&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;assertDatabaseHas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bank_accounts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'user_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&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;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'My Main Bank'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'balance'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100050&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Value as integer thanks to our Caster&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;Notice how we assert the database has the value &lt;code&gt;100050&lt;/code&gt; because of our logic handling money as integers!&lt;/p&gt;

&lt;p&gt;You can see the full test suite for the Bank Account resource in &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/26ca59e" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Budget and Category Resources
&lt;/h2&gt;

&lt;p&gt;The tests for &lt;code&gt;BudgetResource&lt;/code&gt; and &lt;code&gt;CategoryResource&lt;/code&gt; follow the exact same pattern. We need to ensure that detailed actions like creating and editing work, but most importantly, we must verify the scoping rules.&lt;/p&gt;

&lt;p&gt;For example, the Category tests ensure that a user can only edit their own categories.&lt;/p&gt;

&lt;p&gt;You can check the implementation for the &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/9f3d821" rel="noopener noreferrer"&gt;Budget resource tests here&lt;/a&gt; and the &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/fb1ac7a" rel="noopener noreferrer"&gt;Category resource tests here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Tests
&lt;/h2&gt;

&lt;p&gt;Finally, let's run our test suite to see everything green!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an output confirming that all resource tests are passing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS Tests\Feature\Filament\Resources\BankAccountResourceTest
✓ it can render page
✓ it can list bank accounts
✓ it cannot see other users bank accounts
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎓 Build this App &amp;amp; Get Certified (Free)
&lt;/h2&gt;

&lt;p&gt;If you found this guide helpful, you can build the entire application from scratch with my free course. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏗️ Advanced Laravel Architecture&lt;/li&gt;
&lt;li&gt;📊 Building Dashboards with FilamentPHP&lt;/li&gt;
&lt;li&gt;💰 Handling Money professionally in code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.maiobarbero.dev/courses/personal-finance-with-laravel/" rel="noopener noreferrer"&gt;&lt;strong&gt;→ Join other students on maiobarbero.dev&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to build a Voice-to-Text App with Laravel AI SDK</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Wed, 11 Feb 2026 08:39:27 +0000</pubDate>
      <link>https://dev.to/maiobarbero/how-to-build-a-voice-to-text-app-with-laravel-ai-sdk-1ida</link>
      <guid>https://dev.to/maiobarbero/how-to-build-a-voice-to-text-app-with-laravel-ai-sdk-1ida</guid>
      <description>&lt;h1&gt;
  
  
  Stop Writing Boilerplate: Build a Voice-to-Text App with Laravel AI SDK
&lt;/h1&gt;

&lt;p&gt;If you are a PHP developer, you have likely looked at AI integration with a mix of excitement and fatigue. Excitement for the possibilities, but fatigue from WRITING THE SAME BOILERPLATE code to handle multipart requests, API keys, and error handling for OpenAI or Anthropic.&lt;/p&gt;

&lt;p&gt;The new &lt;strong&gt;Laravel AI SDK&lt;/strong&gt; changes this. It turns complex AI interactions into fluid, Laravel-style syntax.&lt;/p&gt;

&lt;p&gt;In this guide, we won't just talk about it—we will build a functional &lt;strong&gt;Voice-to-Text&lt;/strong&gt; application in under 10 minutes. I call mine "Dettami", but you can call yours whatever you want.&lt;/p&gt;

&lt;p&gt;Let's write some code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Laravel 10+ (or the latest version)&lt;/li&gt;
&lt;li&gt;PHP 8.2+&lt;/li&gt;
&lt;li&gt;An OpenAI API Key&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Install the SDK
&lt;/h2&gt;

&lt;p&gt;First, forget about &lt;code&gt;guzzlehttp/guzzle&lt;/code&gt; manual calls. Pull in the first-party package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require laravel/ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, publish the configuration file. This is where you will define your "drivers" (OpenAI, Gemini, Mistral, etc.).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan &lt;span class="nb"&gt;install&lt;/span&gt;:ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your key to your &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPENAI_API_KEY=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Define the Route
&lt;/h2&gt;

&lt;p&gt;We need a single endpoint to accept the audio file. In your &lt;code&gt;routes/web.php&lt;/code&gt; or &lt;code&gt;routes/api.php&lt;/code&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="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Http\Controllers\TranscribeAudioController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/transcribe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TranscribeAudioController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'transcribe'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: The Logic (Where the Magic Happens)
&lt;/h2&gt;

&lt;p&gt;This is the part that usually hurts. Handling file uploads + external API calls is often messy.&lt;/p&gt;

&lt;p&gt;Create your controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:controller TranscribeAudioController &lt;span class="nt"&gt;--invokable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, look at how clean this solution is using the &lt;code&gt;Transcription&lt;/code&gt; facade. We don't need to manually construct a POST request to &lt;code&gt;https://api.openai.com/v1/audio/transcriptions&lt;/code&gt;. The SDK does it for us.&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Controllers&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;Illuminate\Http\Request&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;Illuminate\Support\Facades\Storage&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;Laravel\AI\Facades\Transcription&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;TranscribeAudioController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'audio'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|file|mimes:webm,mp3,wav'&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'audio'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 1. Save the file temporarily&lt;/span&gt;
        &lt;span class="c1"&gt;// We need a physical path to pass to the SDK&lt;/span&gt;
        &lt;span class="nv"&gt;$path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'temp_recordings'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$fullPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 2. Transcribe with one fluent method chain&lt;/span&gt;
            &lt;span class="c1"&gt;// The SDK automatically uses your configured default driver&lt;/span&gt;
            &lt;span class="nv"&gt;$text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Transcription&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fullPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMimeType&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;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// 3. Cleanup&lt;/span&gt;
            &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&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;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'text'&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;$text&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;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Throwable&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Log the error and return a user-friendly message&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&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;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Transcription failed'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;500&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;h3&gt;
  
  
  Why this matters
&lt;/h3&gt;

&lt;p&gt;Notice line 27: &lt;code&gt;Transcription::fromPath(...)&lt;/code&gt;. &lt;br&gt;
That is the abstraction we have been waiting for. It removes the cognitive load of remembering specific API parameters for every different provider. Today you use OpenAI, tomorrow you switch to Gemini—the code stays strictly the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: The Frontend (The "Recorder")
&lt;/h2&gt;

&lt;p&gt;For the frontend, you don't need a heavy React app. A simple Blade view with vanilla JS covers it perfectly.&lt;/p&gt;

&lt;p&gt;The core requirement is the browser's &lt;code&gt;MediaRecorder&lt;/code&gt; API. Here is the pseudo-code for what your JavaScript needs to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Request Microphone access (&lt;code&gt;navigator.mediaDevices.getUserMedia&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Record chunks of audio into a &lt;code&gt;Blob&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Send that Blob to our Laravel endpoint via &lt;code&gt;FormData&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a glimpse of the UI I built for this:&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fy3r22zuh95pfwlgdy152.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.amazonaws.com%2Fuploads%2Farticles%2Fy3r22zuh95pfwlgdy152.png" alt="Dettami Interface" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We just built a fully functional Voice-to-Text backend in &lt;strong&gt;about 20 lines of code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Laravel AI SDK respects your time. It handles the "plumbing" so you can focus on building the actual product.&lt;/p&gt;

&lt;p&gt;If you want to inspect the full source code for the demo project "Dettami", check the repository here:&lt;br&gt;
&lt;a href="https://github.com/maiobarbero/dettami" rel="noopener noreferrer"&gt;GitHub: maiobarbero/dettami&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep moving forward.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to create a resource in Filament v5 | Laravel Personal Finance Dashboard</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Tue, 10 Feb 2026 07:15:52 +0000</pubDate>
      <link>https://dev.to/maiobarbero/laravel-personal-finance-dashboard-add-filament-v5-resources-37lh</link>
      <guid>https://dev.to/maiobarbero/laravel-personal-finance-dashboard-add-filament-v5-resources-37lh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;👋 Note:&lt;/strong&gt; This article is an excerpt from my &lt;strong&gt;Free Laravel &amp;amp; Filament Finance Course&lt;/strong&gt;.&lt;br&gt;
The full course includes code-along lessons, quizzes, and a &lt;strong&gt;Completion Certificate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.maiobarbero.dev/courses/personal-finance-with-laravel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Start the Free Course &amp;amp; Get Certified&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  The First Filament Admin Panel
&lt;/h1&gt;

&lt;p&gt;We have our database structure. It's solid, typed, and waiting for data. But a database without an interface is like a library without doors, secure, but useless.&lt;/p&gt;

&lt;p&gt;Today, we build the doors.&lt;/p&gt;

&lt;p&gt;We are going to create our first &lt;strong&gt;Filament Resources&lt;/strong&gt;. In Filament, a "Resource" is the static representation of an entity in your admin panel. It describes how your model looks in a table, how it behaves in a form, and how it relates to the rest of your application.&lt;/p&gt;

&lt;p&gt;We will start with something fundamental: &lt;strong&gt;Categories&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Creating the Category Resource
&lt;/h2&gt;

&lt;p&gt;Filament provides a powerful artisan command to generate resources. We want our categories to be manageable quickly, without navigating away from the list. We want a "Simple Resource", one that handles Creating, Reading, Updating, and Deleting (CRUD) all on a single page using modals.&lt;/p&gt;

&lt;p&gt;Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:filament-resource Category &lt;span class="nt"&gt;--simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates two key files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;app/Filament/Resources/CategoryResource.php&lt;/code&gt;: The definition of the table and form.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app/Filament/Resources/CategoryResource/Pages/ManageCategories.php&lt;/code&gt;: The page controller that handles the actions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see the initial structure in &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/4935d64" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you visit &lt;code&gt;/admin/categories&lt;/code&gt; now, you might see a form with a &lt;code&gt;User&lt;/code&gt; dropdown. This is technically correct, a category belongs to a user, but practically wrong. When I create a category, it should be &lt;em&gt;mine&lt;/em&gt;. I shouldn't have to select myself from a list.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fnp3zz98yxvis0n9hpl8b.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fnp3zz98yxvis0n9hpl8b.webp" alt="Category Resource Initial" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Scoping and Context
&lt;/h2&gt;

&lt;p&gt;We have two problems to solve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Visibility&lt;/strong&gt;: I can see categories belonging to other users.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Creation&lt;/strong&gt;: I have to manually select the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Fixing Visibility
&lt;/h3&gt;

&lt;p&gt;To ensure I only see &lt;em&gt;my&lt;/em&gt; records, we strictly scope the Eloquent query used by the resource. We override the &lt;code&gt;getEloquentQuery&lt;/code&gt; method in &lt;code&gt;CategoryResource.php&lt;/code&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getEloquentQuery&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Builder&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getEloquentQuery&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;auth&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;id&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;Now, the table will only ever load records where &lt;code&gt;user_id&lt;/code&gt; matches the logged-in user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing Creation
&lt;/h3&gt;

&lt;p&gt;Next, we clean up the form. In &lt;code&gt;CategoryResource::form()&lt;/code&gt;, remove the &lt;code&gt;user_id&lt;/code&gt; input completely. We don't want the user to worry about it.&lt;/p&gt;

&lt;p&gt;But the database &lt;em&gt;needs&lt;/em&gt; that ID. So, we inject it programmatically.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;app/Filament/Resources/CategoryResource/Pages/ManageCategories.php&lt;/code&gt;, we modify the &lt;code&gt;CreateAction&lt;/code&gt;. We use &lt;code&gt;mutateFormDataUsing&lt;/code&gt; to intercept the form submission and add the current user's ID before it hits the database.&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="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getHeaderActions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;CreateAction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&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;mutateDataUsing&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="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auth&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;id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- This implies the user&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$data&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;We also remove the &lt;code&gt;user&lt;/code&gt; column from the table to keep it clean.&lt;/p&gt;

&lt;p&gt;You can view the specific changes to scope the data in &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/481115e" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fgrt408gfsxba4fcfjls0.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fgrt408gfsxba4fcfjls0.webp" alt="Category Resource Scoped" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Bank Accounts
&lt;/h2&gt;

&lt;p&gt;Money needs a home. Let's apply the exact same logic to &lt;strong&gt;Bank Accounts&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Generate the resource: &lt;code&gt;php artisan make:filament-resource BankAccount --simple&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Scope the query in &lt;code&gt;BankAccountResource&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Inject the &lt;code&gt;user_id&lt;/code&gt; in &lt;code&gt;ManageBankAccounts&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you check your database or use artisan tinker, you'll notice our &lt;code&gt;MoneyCast&lt;/code&gt; is working silently in the background. When you type "1000" in the balance field, it's stored as &lt;code&gt;100000&lt;/code&gt; (cents) in the database, assuming you treated the input as a distinct number.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wait, there's a nuance here.&lt;/em&gt; Our form is currently generic. Later, we'll want to ensure we are handling decimals correctly in the UI so the user types "10.00" and we store "1000". For now, we are trusting the raw input.&lt;/p&gt;

&lt;p&gt;Check the implementation of the Bank Account resource &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/388d79e" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Budgets and Presentation
&lt;/h2&gt;

&lt;p&gt;Finally, let's tackle &lt;strong&gt;Budgets&lt;/strong&gt;. This is where Filament's UI components really shine.&lt;/p&gt;

&lt;p&gt;Generate the resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:filament-resource Budget &lt;span class="nt"&gt;--simple&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Budgets have a &lt;code&gt;type&lt;/code&gt; (Reset or Rollover). In our database, this is an Enum string. In Filament, we want to visualize this instantly using colors.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Badge Column
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;BudgetResource.php&lt;/code&gt;, we can use a &lt;code&gt;TextColumn&lt;/code&gt; but format it as a &lt;code&gt;badge&lt;/code&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="nc"&gt;TextColumn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'type'&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;badge&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;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BudgetType&lt;/span&gt; &lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;BudgetType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Reset&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;BudgetType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Rollover&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'warning'&lt;/span&gt;&lt;span class="p"&gt;,&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;searchable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple chaining transforms a boring text string into a vibrant, communicative UI element.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Ff1zvib5f9i6kqha3s2rm.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Ff1zvib5f9i6kqha3s2rm.webp" alt="Budget Resource Badge" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Formatting Money
&lt;/h3&gt;

&lt;p&gt;We also want to display the budget amount as actual currency, not just a raw number. Filament has a dedicated &lt;code&gt;money&lt;/code&gt; formatter.&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="nc"&gt;TextColumn&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'amount'&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;money&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'EUR'&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;sortable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a polished, professional look immediately.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fbs7xetjfmf173uy3jv2e.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fbs7xetjfmf173uy3jv2e.webp" alt="Budget Resource Money" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can explore the Budget resource implementation in &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/8c43685" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;, and see how we refined the badge colors &lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/b9a292c" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We now have three functional pillars of our personal finance application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Categories&lt;/strong&gt; to label our spending.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Bank Accounts&lt;/strong&gt; to store our wealth.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Budgets&lt;/strong&gt; to control our impulses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And importantly, they are completely private to the logged-in user.&lt;/p&gt;

&lt;p&gt;In the next lesson, we will test our first Resources. Keep building.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎓 Build this App &amp;amp; Get Certified (Free)
&lt;/h2&gt;

&lt;p&gt;If you found this guide helpful, you can build the entire application from scratch with my free course. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏗️ Advanced Laravel Architecture&lt;/li&gt;
&lt;li&gt;📊 Building Dashboards with FilamentPHP&lt;/li&gt;
&lt;li&gt;💰 Handling Money professionally in code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.maiobarbero.dev/courses/personal-finance-with-laravel/" rel="noopener noreferrer"&gt;&lt;strong&gt;→ Join other students on maiobarbero.dev&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>laravel</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Testing the New Laravel AI SDK: Building 'Dettami' a voice-to-text app</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Sat, 07 Feb 2026 13:46:06 +0000</pubDate>
      <link>https://dev.to/maiobarbero/testing-the-new-laravel-ai-sdk-building-dettami-a-voice-to-text-app-8mf</link>
      <guid>https://dev.to/maiobarbero/testing-the-new-laravel-ai-sdk-building-dettami-a-voice-to-text-app-8mf</guid>
      <description>&lt;p&gt;The landscape of AI integration in PHP has just shifted. With the release of the &lt;strong&gt;Laravel AI SDK&lt;/strong&gt;, what used to require complex API wrangling or third-party packages is now a first-party citizen of the framework we love.&lt;/p&gt;

&lt;p&gt;I couldn't resist. I had to take it for a spin.&lt;/p&gt;

&lt;p&gt;The result is &lt;strong&gt;Dettami&lt;/strong&gt; (Italian for "Dictate to me"), a simple web app that records your voice and transcribes it instantly. And when I say "simple," I mean it. The speed at which I went from &lt;code&gt;laravel new&lt;/code&gt; to a working prototype was exhilarating.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Goal: Frictionless Audio Transcription
&lt;/h2&gt;

&lt;p&gt;I wanted to build something tangible. Not just a "Hello World" for AI, but a feature I would actually use: a quick way to record thoughts and get them as text.&lt;/p&gt;

&lt;p&gt;Here is what the interface looks like. Clean, focused, and ready to listen.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fub618yiz7u605sdwox0s.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fub618yiz7u605sdwox0s.webp" alt="Dettami Homepage" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Implementation
&lt;/h2&gt;

&lt;p&gt;Installing the SDK was the first step, and as you'd expect from the Laravel ecosystem, it was seamless.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require laravel/ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the real magic happened in the controller. In the past, handling audio uploads, converting formats, and sending them to an API like OpenAI's Whisper or Google's Speech-to-Text involved a fair bit of boilerplate. You had to manage the file, ensure the format was correct, build the multipart request, handle the response...&lt;/p&gt;

&lt;p&gt;With the Laravel AI SDK, the complexity vanishes. It abstracts the "how" and lets you focus on the "what."&lt;/p&gt;

&lt;p&gt;Here is the core logic from my &lt;code&gt;Dettami&lt;/code&gt; controller. Look at how expressive it is:&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'audio'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'audio'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Store the file temporarily&lt;/span&gt;
        &lt;span class="nv"&gt;$path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;storeAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dettami'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'recording.webm'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$fullPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// The magic happens here. One fluents line.&lt;/span&gt;
            &lt;span class="nv"&gt;$transcription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Transcription&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fullPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'audio/webm'&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;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Clean up&lt;/span&gt;
            &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&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;json&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'success'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'transcription'&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;$transcription&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;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Throwable&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ... error handling&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;That's it. &lt;code&gt;Transcription::fromPath(...) -&amp;gt; generate()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The SDK handles the heavy lifting of communicating with the configured AI provider. It feels native. It feels right.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Frontend Experience
&lt;/h3&gt;

&lt;p&gt;For the frontend, I kept it simple using Blade and vanilla JavaScript with the &lt;code&gt;MediaRecorder&lt;/code&gt; API. The goal was to provide immediate feedback. You click record, you speak, you get text.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fdo3zp99o2dnenmpjt276.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fdo3zp99o2dnenmpjt276.webp" alt="Recording in progress" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Transcription: Translation
&lt;/h2&gt;

&lt;p&gt;The power of this SDK doesn't stop at transcription. Once you have the text, the possibilities are endless. You can summarize it, rewrite it, or—as I experimented with—translate it.&lt;/p&gt;

&lt;p&gt;Because the SDK provides a unified interface for these operations, adding a translation step is just as trivial as the transcription itself. Whether you are using OpenAI, Anthropic, or Gemini, the code remains consistent and clean.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fztk5ixkwd0lzb58dev60.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fztk5ixkwd0lzb58dev60.webp" alt="Translated Output" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections
&lt;/h2&gt;

&lt;p&gt;Building &lt;strong&gt;Dettami&lt;/strong&gt; was a reminder of why I love this framework. It respects my time. The &lt;strong&gt;Laravel AI SDK&lt;/strong&gt; isn't just a wrapper; it's an opinionated, elegant way to bring intelligence into your applications without the headache.&lt;/p&gt;

&lt;p&gt;For architects and developers, this means we can stop worrying about &lt;em&gt;connecting&lt;/em&gt; to AI and start worrying about &lt;em&gt;what we can build&lt;/em&gt; with it. Ideally, the barrier to entry has been lowered to the floor.&lt;/p&gt;

&lt;p&gt;If you haven't tried it yet, fire up a terminal.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;composer require laravel/ai&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And build something amazing. You can find the full source code for Dettami on &lt;a href="https://github.com/maiobarbero/dettami" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. I recommend starting with &lt;a href="https://github.com/maiobarbero/dettami/commit/33f3380" rel="noopener noreferrer"&gt;this commit&lt;/a&gt; to see the transcription logic in isolation.&lt;/p&gt;

&lt;p&gt;Keep moving forward.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>php</category>
      <category>ai</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Laravel Personal Finance Dashboard: creating the database</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Thu, 05 Feb 2026 07:59:55 +0000</pubDate>
      <link>https://dev.to/maiobarbero/laravel-personal-finance-dashboard-creating-the-database-644</link>
      <guid>https://dev.to/maiobarbero/laravel-personal-finance-dashboard-creating-the-database-644</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;👋 Note:&lt;/strong&gt; This article is an excerpt from my &lt;strong&gt;Free Laravel &amp;amp; Filament Finance Course&lt;/strong&gt;.&lt;br&gt;
The full course includes code-along lessons, quizzes, and a &lt;strong&gt;Completion Certificate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.maiobarbero.dev/courses/personal-finance-with-laravel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Start the Free Course &amp;amp; Get Certified&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Creating the Database
&lt;/h1&gt;

&lt;p&gt;Every great application stands on the shoulders of a solid data model. If the database schema isn't right, everything else, from your eloquently written controllers to your beautiful Filament resources, will feel like fighting gravity.&lt;/p&gt;

&lt;p&gt;In this lesson, we are going to define the core entities of our Personal Finance application. We aren't just creating tables; we are defining the &lt;em&gt;vocabulary&lt;/em&gt; of our domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Entities
&lt;/h2&gt;

&lt;p&gt;Our application needs to track money moving in and out. To do that effectively, we need four primary concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Bank Accounts&lt;/strong&gt;: Where the money lives.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Categories&lt;/strong&gt;: How we classify the money (e.g., "Utilities", "Dining Out").&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Budgets&lt;/strong&gt;: Our financial goals or limits.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Transactions&lt;/strong&gt;: The heart of the system, the actual record of spending or earning.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Generating the Artifacts
&lt;/h2&gt;

&lt;p&gt;Laravel makes scaffolding these incredible easy. We need a Model, a Migration, a Factory, and a Seeder for each of our entities. Instead of running four commands per entity, we can use the &lt;code&gt;-mfs&lt;/code&gt; flags.&lt;/p&gt;

&lt;p&gt;Run the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:model Category &lt;span class="nt"&gt;-mfs&lt;/span&gt;
php artisan make:model Budget &lt;span class="nt"&gt;-mfs&lt;/span&gt;
php artisan make:model BankAccount &lt;span class="nt"&gt;-mfs&lt;/span&gt;
php artisan make:model Transaction &lt;span class="nt"&gt;-mfs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a flurry of green success messages. We now have our files ready to be sculpted.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on "Down" Methods
&lt;/h2&gt;

&lt;p&gt;Open up one of your new migration files. You'll see an &lt;code&gt;up&lt;/code&gt; method and a &lt;code&gt;down&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;I'm going to ask you to do something that might feel rebellious: &lt;strong&gt;delete the &lt;code&gt;down&lt;/code&gt; method.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why? In this project, we are adopting a &lt;strong&gt;Fix Forward&lt;/strong&gt; strategy. In a production environment with real data, rolling back a migration (especially one that drops columns or tables) is destructive and risky. If we make a mistake in a migration that has already run, we don't roll it back, we create a &lt;em&gt;new&lt;/em&gt; migration to fix the issue. This keeps our database history linear and truthful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, go ahead and remove &lt;code&gt;public function down(): void&lt;/code&gt; from all your new migration files. It clarifies our intent: we only move forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the Schema
&lt;/h2&gt;

&lt;p&gt;Let's define the structure of our tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Enums
&lt;/h3&gt;

&lt;p&gt;Before we get to the tables, we need a way to define the type of a Budget. Is it a fixed budget that we reset every month? Or is it a rolling budget that we keep track of over time? Let's use a PHP Enum for this to ensure type safety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:enum Enums/BudgetType
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;app/Enums/BudgetType.php&lt;/code&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Enums&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;BudgetType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Reset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'reset'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;Rollover&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'rollover'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getLabel&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Reset&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Reset'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Rollover&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Rollover'&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;h3&gt;
  
  
  2. Migrations
&lt;/h3&gt;

&lt;p&gt;Now, let's fill in our &lt;code&gt;up&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;create_categories_table.php&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Categories are simple. They have a name and belong to a user.&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="nc"&gt;Schema&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;'categories'&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="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&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;id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;foreignId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_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;constrained&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;cascadeOnDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;timestamps&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;unique&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'name'&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;&lt;strong&gt;&lt;code&gt;create_budgets_table.php&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
A budget tracks a limit for a specific period.&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="nc"&gt;Schema&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;'budgets'&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="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&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;id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;foreignId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_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;constrained&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;cascadeOnDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'amount'&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'type'&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="s1"&gt;'fixed'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;timestamps&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;unique&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'name'&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;&lt;strong&gt;&lt;code&gt;create_bank_accounts_table.php&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Represents a physical or digital account.&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="nc"&gt;Schema&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;'bank_accounts'&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="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&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;id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;foreignId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_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;constrained&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;cascadeOnDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;unsignedBigInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'balance'&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;timestamps&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;unique&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'name'&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;&lt;strong&gt;&lt;code&gt;create_transactions_table.php&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The center of our universe. Links everything together.&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="nc"&gt;Schema&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;'transactions'&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="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&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;id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;foreignId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_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;constrained&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;cascadeOnDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;foreignId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bank_account_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;constrained&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;cascadeOnDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'description'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;foreignId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'category_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;constrained&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;cascadeOnDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;foreignId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'budget_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;constrained&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;cascadeOnDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'note'&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;nullable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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;unsignedInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'amount'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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;timestamps&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;blockquote&gt;
&lt;p&gt;[!TIP]&lt;br&gt;
Notice we store money as &lt;code&gt;integers&lt;/code&gt; (cents) rather than &lt;code&gt;floats&lt;/code&gt; or &lt;code&gt;decimals&lt;/code&gt;. Floating point math can be imprecise. storing $10.00 as &lt;code&gt;1000&lt;/code&gt; is deeply robust.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. The Money Cast
&lt;/h2&gt;

&lt;p&gt;Since we are storing money as integers (cents) but want to work with it as standard units (dollars/euros) in our code, let's create a custom Cast. This encourages consistency across our application.&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:cast MoneyCast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;app/Casts/MoneyCast.php&lt;/code&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Casts&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;Illuminate\Contracts\Database\Eloquent\CastsAttributes&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;Illuminate\Database\Eloquent\Model&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;MoneyCast&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CastsAttributes&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Cast the given value.
     *
     * @param  array&amp;lt;string, mixed&amp;gt;  $attributes
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$attributes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Prepare the given value for storage.
     *
     * @param  array&amp;lt;string, mixed&amp;gt;  $attributes
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$attributes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&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;h2&gt;
  
  
  4. The Models
&lt;/h2&gt;

&lt;p&gt;Now we breathe life into our schemas by defining relationships and behaviors in our Models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;User.php&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The user owns everything.&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;// ... imports&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Relations\HasMany&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;User&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Authenticatable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... traits&lt;/span&gt;

    &lt;span class="c1"&gt;// ... fillable &amp;amp; hidden&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;bankAccounts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;HasMany&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;HasMany&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Category&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;budgets&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;HasMany&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Budget&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;HasMany&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;&lt;strong&gt;&lt;code&gt;Category.php&lt;/code&gt;&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&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="cd"&gt;/** @use HasFactory&amp;lt;\Database\Factories\CategoryFactory&amp;gt; */&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\HasMany&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;&lt;strong&gt;&lt;code&gt;Budget.php&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Here we treat our &lt;code&gt;type&lt;/code&gt; as an Enum and &lt;code&gt;amount&lt;/code&gt; with our new MoneyCast.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Budget&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="cd"&gt;/** @use HasFactory&amp;lt;\Database\Factories\BudgetFactory&amp;gt; */&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'amount'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;casts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Enums\BudgetType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Casts\MoneyCast&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\HasMany&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;&lt;strong&gt;&lt;code&gt;BankAccount.php&lt;/code&gt;&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankAccount&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="cd"&gt;/** @use HasFactory&amp;lt;\Database\Factories\BankAccountFactory&amp;gt; */&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'balance'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;casts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'balance'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Casts\MoneyCast&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\HasMany&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;&lt;strong&gt;&lt;code&gt;Transaction.php&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The nexus. Note the mass assignment protection and casting.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Transaction&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="cd"&gt;/** @use HasFactory&amp;lt;\Database\Factories\TransactionFactory&amp;gt; */&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'bank_account_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'category_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'budget_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'description'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'amount'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'note'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;casts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Casts\MoneyCast&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;bankAccount&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BankAccount&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Category&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;\Illuminate\Database\Eloquent\Relations\BelongsTo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Budget&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;h2&gt;
  
  
  Seeding and Verification
&lt;/h2&gt;

&lt;p&gt;With our structure defined, let's spin up the database.&lt;/p&gt;

&lt;p&gt;First, update your &lt;code&gt;DatabaseSeeder.php&lt;/code&gt; to call the new seeders:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseSeeder&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Seeder&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;WithoutModelEvents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Seed the application's database.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserSeeder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CategorySeeder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BudgetSeeder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BankAccountSeeder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TransactionSeeder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;(We'll cover Factories in depth in a future lesson, but for now, ensure your factories create dummy data).&lt;/p&gt;

&lt;p&gt;Then, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan migrate:fresh &lt;span class="nt"&gt;--seeder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's verify our work using Tinker, the best tool for checking your data reality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan tinker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try fetching a user and their transactions:&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;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;\App\Models\User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;transactions&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;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see a Collection of Transaction models, congratulations. You have successfully mapped the physical world of finance into your digital domain.&lt;/p&gt;

&lt;p&gt;Finally, before we commit, let's make sure our code style is impeccable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./vendor/bin/pint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep moving forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎓 Build this App &amp;amp; Get Certified (Free)
&lt;/h2&gt;

&lt;p&gt;If you found this guide helpful, you can build the entire application from scratch with my free course. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏗️ Advanced Laravel Architecture&lt;/li&gt;
&lt;li&gt;📊 Building Dashboards with FilamentPHP&lt;/li&gt;
&lt;li&gt;💰 Handling Money professionally in code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.maiobarbero.dev/courses/personal-finance-with-laravel/" rel="noopener noreferrer"&gt;&lt;strong&gt;→ Join other students on maiobarbero.dev&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>laravel</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Laravel Personal Finance Dashboard: setting up the project with Filament v5</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Tue, 03 Feb 2026 08:33:26 +0000</pubDate>
      <link>https://dev.to/maiobarbero/setting-up-laravel-and-filament-for-personal-finance-5om</link>
      <guid>https://dev.to/maiobarbero/setting-up-laravel-and-filament-for-personal-finance-5om</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;👋 Note:&lt;/strong&gt; This article is an excerpt from my &lt;strong&gt;Free Laravel &amp;amp; Filament Finance Course&lt;/strong&gt;.&lt;br&gt;
The full course includes code-along lessons, quizzes, and a &lt;strong&gt;Completion Certificate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.maiobarbero.dev/courses/personal-finance-with-laravel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Start the Free Course &amp;amp; Get Certified&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Starting a new project is always a mix of excitement and anxiety. The blank slate. Infinite possibilities. But today, we're not just writing code; we're building a tool for financial freedom. A system that serves &lt;em&gt;us&lt;/em&gt;, tailored to our specific needs, without the bloat of generic SaaS products.&lt;/p&gt;

&lt;p&gt;In this first lesson, we will lay the foundation. We are going to build a robust, modern application using strictly typed &lt;strong&gt;Laravel 12&lt;/strong&gt; and the elegant &lt;strong&gt;Filament&lt;/strong&gt; admin panel.&lt;/p&gt;

&lt;p&gt;Let's get our hands dirty.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Foundation
&lt;/h2&gt;

&lt;p&gt;First, let's create our canvas. Open your terminal and breathe in that new project smell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;laravel new laravel_filament_personal_finance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want a clean slate, so when promoted, select:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;No starter kit&lt;/strong&gt;: We are building this artisan-style.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Pest&lt;/strong&gt;: For testing. It’s elegant and expressive.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;SQLite&lt;/strong&gt;: Simplify the infrastructure. For a personal tool, it's fast, zero-conf, and easy to backup.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Init Git repo&lt;/strong&gt;: Yes. Version control is non-negotiable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the command finishes, navigate into your new fortress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;laravel_filament_personal_finance
composer &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that the heart is beating:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;code&gt;http://localhost:8000&lt;/code&gt;. You should see the default Laravel splash screen. We are live.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/1e9aefa" rel="noopener noreferrer"&gt;View the initial setup commit&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Supercharging with AI
&lt;/h2&gt;

&lt;p&gt;I believe in leveraging every tool available to maintain flow. We'll add &lt;strong&gt;Laravel Boost&lt;/strong&gt;, an MCP server that acts as a bridge between our app and AI agents. Think of it as giving your AI pair programmer direct sensory access to your codebase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require laravel/boost &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan boost:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted, select your IDE or specific configuration. This will generate a configuration block like this (the below one is the Google Antigravity configuration):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"laravel-boost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/usr/bin/php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"/home/&amp;lt;your-username&amp;gt;/&amp;lt;path-to-project&amp;gt;/artisan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"boost:mcp"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, your AI assistant can understand your project structure, run artisan commands, and debugging becomes a conversation rather than a struggle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/8d05ee9" rel="noopener noreferrer"&gt;View the Boost configuration commit&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Admin Panel
&lt;/h2&gt;

&lt;p&gt;We don't want to spend weeks building authentication views and CRUD tables from scratch. We want to focus on the &lt;em&gt;logic&lt;/em&gt; of our finances. Enter &lt;strong&gt;Filament v5&lt;/strong&gt;. It’s the gold standard for TALL stack admin panels.&lt;/p&gt;

&lt;p&gt;Install it via composer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require filament/filament:&lt;span class="s2"&gt;"^5.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, install the panel. We'll call ours &lt;code&gt;admin&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan filament:install &lt;span class="nt"&gt;--panels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Enter &lt;code&gt;admin&lt;/code&gt; when asked for the ID.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This scaffolds the entire admin infrastructure. It's almost magical how much time this saves us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/maiobarbero/laravel_filament_personal_finance/commit/18c5b5e" rel="noopener noreferrer"&gt;View the Filament setup commit&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. First Access
&lt;/h2&gt;

&lt;p&gt;Reference without access is useless. Let's create an admin user to unlock the gates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:filament-user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts to set your name, email, and password. Then, restart your server if it's not running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;/admin&lt;/code&gt;. Login with your new credentials.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fvmokf82ultop07a82iji.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fvmokf82ultop07a82iji.webp" alt="Admin Dashboard" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You are now looking at the control center of your future financial system. It's empty now, but soon it will be the source of truth for your wealth.&lt;/p&gt;

&lt;p&gt;We have a running application, a powerful admin interface, and the AI tooling to help us build faster. The structure is set. In the next lesson, we will start modeling the domain.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  🎓 Build this App &amp;amp; Get Certified (Free)
&lt;/h2&gt;

&lt;p&gt;If you found this guide helpful, you can build the entire application from scratch with my free course. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏗️ Advanced Laravel Architecture&lt;/li&gt;
&lt;li&gt;📊 Building Dashboards with FilamentPHP&lt;/li&gt;
&lt;li&gt;💰 Handling Money professionally in code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.maiobarbero.dev/courses/personal-finance-with-laravel/" rel="noopener noreferrer"&gt;&lt;strong&gt;→ Join other students on maiobarbero.dev&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>laravel</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to set up a global .gitignore</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Wed, 07 May 2025 06:33:13 +0000</pubDate>
      <link>https://dev.to/maiobarbero/how-to-set-up-a-global-gitignore-4e09</link>
      <guid>https://dev.to/maiobarbero/how-to-set-up-a-global-gitignore-4e09</guid>
      <description>&lt;p&gt;If you work with multiple Git repositories, you’ve likely run into the frustration of ignoring the same files (like &lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;Thumbs.db&lt;/code&gt;, or your IDE settings) over and over again. Fortunately, Git allows you to define a global &lt;code&gt;.gitignore&lt;/code&gt; file that applies to all repositories on your system. This is done through the &lt;code&gt;core.excludesfile&lt;/code&gt; configuration option.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk you through how to set up a global &lt;code&gt;.gitignore&lt;/code&gt; file using &lt;code&gt;git config&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Why Use a Global .gitignore?
&lt;/h2&gt;

&lt;p&gt;Some files are specific to your system or development environment and should never be committed to &lt;em&gt;any&lt;/em&gt; project. Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS system files (&lt;code&gt;.DS_Store&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Windows thumbnails (&lt;code&gt;Thumbs.db&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;IDE configs (&lt;code&gt;.idea/&lt;/code&gt;, &lt;code&gt;.vscode/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Log files (&lt;code&gt;*.log&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Local environment files (&lt;code&gt;.env&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of duplicating the same ignore rules in every repo, a global &lt;code&gt;.gitignore&lt;/code&gt; keeps things DRY (Don't Repeat Yourself).&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Step-by-Step Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create the Global .gitignore File
&lt;/h3&gt;

&lt;p&gt;Start by creating a &lt;code&gt;.gitignore_global&lt;/code&gt; file in your home directory (or anywhere you prefer):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ~/.gitignore_global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, edit it and add the rules you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
.DS_Store

&lt;span class="c"&gt;# Windows&lt;/span&gt;
Thumbs.db

&lt;span class="c"&gt;# IDEs&lt;/span&gt;
.vscode/
.idea/

&lt;span class="c"&gt;# Logs&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;.log

&lt;span class="c"&gt;# Env files&lt;/span&gt;
.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Configure Git to Use the Global File
&lt;/h3&gt;

&lt;p&gt;Tell Git to use this file globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; core.excludesfile ~/.gitignore_global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify it worked with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global core.excludesfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should output the path you just set.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Introducing Astro Academia</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Mon, 03 Mar 2025 10:49:38 +0000</pubDate>
      <link>https://dev.to/maiobarbero/introducing-astro-academia-i6d</link>
      <guid>https://dev.to/maiobarbero/introducing-astro-academia-i6d</guid>
      <description>&lt;h2&gt;
  
  
  A Free Website Template for Academics
&lt;/h2&gt;

&lt;p&gt;Astro Academia is a website template built using Astro, a modern static site generator. The template is designed to be easy to use and customize, even for those who may not have extensive web development experience. It includes several features that are particularly useful for academics, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publication List: Easily showcase your research papers and publications.&lt;/li&gt;
&lt;li&gt;CV Page: Display your curriculum vitae in a clean and organized manner.&lt;/li&gt;
&lt;li&gt;Blog: Share your thoughts and updates with a built-in blog.&lt;/li&gt;
&lt;li&gt;Responsive Design: Ensure your website looks great on all devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Easy to Customize
&lt;/h3&gt;

&lt;p&gt;The template is built with simplicity in mind. You can easily customize the content and appearance of your website by editing a few configuration files and markdown files. The use of Tailwind CSS allows for easy styling adjustments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built with Astro
&lt;/h3&gt;

&lt;p&gt;Astro is a modern static site generator that offers excellent performance and flexibility. It allows you to use your favorite front-end frameworks like React, Vue, or Svelte, while still generating static HTML for fast load times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free and Open Source
&lt;/h3&gt;

&lt;p&gt;Astro Academia is completely free to use and open source. You can find the source code on GitHub. Contributions and feedback are welcome!&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Following the official documentation you will be able to edit the layout and to add data like your cv and your pubblications. Astro Academia also support blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I created Astro Academia to help academics easily create and maintain their personal websites. It is a free and open-source template that is easy to use and customize. I hope this project can help many academics showcase their work and achievements without the hassle of building a website from scratch.&lt;/p&gt;

&lt;p&gt;Feel free to check out the project on GitHub, or the official preview, and contribute if you have any improvements or suggestions!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/maiobarbero/astro_academia" rel="noopener noreferrer"&gt;Github repository&lt;/a&gt;&lt;br&gt;
&lt;a href="https://maiobarbero.github.io/astro_academia/" rel="noopener noreferrer"&gt;Preview&lt;/a&gt;&lt;br&gt;
&lt;a href="https://astro.build/themes/details/astro-academia/" rel="noopener noreferrer"&gt;Astro Theme Page&lt;/a&gt;&lt;/p&gt;

</description>
      <category>astro</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Deploy a WordPress Theme with GitHub Actions</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Tue, 11 Jun 2024 08:56:07 +0000</pubDate>
      <link>https://dev.to/maiobarbero/deploy-a-wordpress-theme-with-github-actions-4kal</link>
      <guid>https://dev.to/maiobarbero/deploy-a-wordpress-theme-with-github-actions-4kal</guid>
      <description>&lt;h2&gt;
  
  
  Is it possible to deploy a WordPress theme directly from GitHub?
&lt;/h2&gt;

&lt;p&gt;Minified code, “bundler” of various types, package manager for JavaScript and PHP and so forth… These are just some of the reasons for choosing to &lt;strong&gt;automate the deployment of a WordPress Theme.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sure it’s always possible to upload files with ftp every time, but why do it manually when GitHub can take care of everything?&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcome to the guide to automate the deployment
&lt;/h2&gt;

&lt;p&gt;First, the theme code must be in a GitHub repository.&lt;/p&gt;

&lt;p&gt;Create a .github folder inside the repo. Inside this folder create another folder called workflows and inside it a main.yml file. You can give this file any name you like, the important thing is to keep the .yml extension.&lt;/p&gt;

&lt;p&gt;The folder structure will look like this: &lt;strong&gt;.github / workflows / main.yml&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The .yml file
&lt;/h3&gt;

&lt;p&gt;Let’s start creating our workflow to automate the deployment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Deploy via FTP
on:
  push:
    branches: [ main ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;name will simply be the name that we will display in the Actions tab of the repository&lt;/em&gt;&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fx2ggstdouvroj86my1t0.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.amazonaws.com%2Fuploads%2Farticles%2Fx2ggstdouvroj86my1t0.png" alt=" " width="800" height="41"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;on: things get interesting here. We define our trigger here. In this case we want to deploy to the push, but we have added a filter: we are only interested in the push done to the main branch. In this way we can have a development branch, perhaps automating the deployment of this on a staging site as well.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  build:
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we come to the actions, jobs, which will follow, by default in parallel, our workflow. build will be the name of our only job&lt;/p&gt;

&lt;p&gt;runs-on: where do we want to run our workflow? GitHub gives us several possibilities listed here.&lt;/p&gt;

&lt;p&gt;The last three lines are used to set a default shell for all runs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; steps:
    - uses: actions/checkout@v2
      with:
        fetch-depth: 2
    - name: FTP Deploy WP Theme
      uses: SamKirkland/FTP-Deploy-Action@4.3.2
      with:
        server: FTP SERVER
        username: FTP USERNAME
        password: ${{ secrets.FTP_PASSWORD }}
        server-dir: FTP DIR
        exclude: |
          **/.git*
          **/.git*/**
          **/node_modules/**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are at the final steps of our workflow. We just have to worry about writing our ftp server address and username, as well as the destination folder (our theme folder).&lt;/p&gt;

&lt;p&gt;Since this file will be accessible from the repository, we can keep some settings like our password secret. To do this we can set a new secret key from the &lt;strong&gt;Setting / Secrets / Actions tab&lt;/strong&gt;&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Ft9davfjlty9835munl22.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.amazonaws.com%2Fuploads%2Farticles%2Ft9davfjlty9835munl22.png" alt=" " width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Secret key GitHub repository&lt;br&gt;
Are you tired of reading and just want to make a nice copy and paste? Find the .yml file and ready-made folder structure &lt;a href="https://github.com/maiobarbero/WordPress-Theme-Deploy-Action" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>wordpress</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Deploy a Grafana dashboard with Docker on AWS EC2</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Tue, 07 May 2024 11:23:28 +0000</pubDate>
      <link>https://dev.to/maiobarbero/deploy-a-grafana-dashboard-with-docker-on-aws-ec2-5f6o</link>
      <guid>https://dev.to/maiobarbero/deploy-a-grafana-dashboard-with-docker-on-aws-ec2-5f6o</guid>
      <description>&lt;p&gt;Monitoring system performance and logs with Grafana is indeed straightforward, especially when working with Docker environments. Grafana seamlessly integrates with Docker, providing prebuilt dashboards that instantly visualize key metrics and logs from your containers and hosts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch EC2 instance
&lt;/h2&gt;

&lt;p&gt;To embark on your AWS journey, you'll need a server instance, and AWS offers the perfect playground with its free tier. Launch a Linux instance effortlessly, and let the experimentation begin!&lt;/p&gt;

&lt;p&gt;Click on launch EC2 instance and select &lt;strong&gt;t2.micro&lt;/strong&gt; to use the free tier.&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2F1ggzmdk16wcf0cb6n71s.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.amazonaws.com%2Fuploads%2Farticles%2F1ggzmdk16wcf0cb6n71s.png" alt="Launch EC2 Dashboard" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we need some more configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a key pair (for the login) and download it in a safe place;&lt;/li&gt;
&lt;li&gt;Add a security group or create a new one;
-Allow traffic from both ssh and internet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you can launch your EC2 instance &lt;/p&gt;

&lt;h3&gt;
  
  
  Open ports
&lt;/h3&gt;

&lt;p&gt;For these project we need to open these ports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3000&lt;/strong&gt; for Grafana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9090&lt;/strong&gt; for Prometheus&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3100&lt;/strong&gt; for Loki&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9100&lt;/strong&gt; for node-exporter&lt;/li&gt;
&lt;/ul&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fh7dprsbb1m9xhzn09wcd.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.amazonaws.com%2Fuploads%2Farticles%2Fh7dprsbb1m9xhzn09wcd.png" alt="EC2 port settings" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;navigate to the EC2 Dashboard and select your EC2 instance&lt;/li&gt;
&lt;li&gt;click on the security group associated and the click edit&lt;/li&gt;
&lt;li&gt;configure the inbound rule selecting &lt;strong&gt;Custom TCP Rule&lt;/strong&gt; as the type. Enter the port number and select &lt;strong&gt;Anywhere&lt;/strong&gt; as the source.&lt;/li&gt;
&lt;li&gt;Repeat the last step for all the four ports&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install Docker
&lt;/h2&gt;

&lt;p&gt;With your Linux instance up and running, it's time to unlock the power of containerization by installing Docker. This game-changing platform will enable you to effortlessly deploy and manage your applications, including the highly coveted Grafana dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect via ssh
&lt;/h3&gt;

&lt;p&gt;In your terminal run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -i "&amp;lt;path_to_your_key.pem&amp;gt;" ec2-user@&amp;lt;Public IPv4 DNS&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type yes and you are inside your instance!&lt;/p&gt;

&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.amazonaws.com%2Fuploads%2Farticles%2Fw2pinneuqoixf3zi9tau.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.amazonaws.com%2Fuploads%2Farticles%2Fw2pinneuqoixf3zi9tau.png" alt="SSH Connection" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Docker and Docker Compose
&lt;/h3&gt;

&lt;p&gt;To install Docker run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo yum update
sudo yum install -y docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can install Docker Compose. Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run Docker service
&lt;/h3&gt;

&lt;p&gt;If everything since here went fine you only need to start the Docker daemon. To do this run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl start docker
sudo systemctl enable docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Copy the project
&lt;/h2&gt;

&lt;p&gt;You can download the source code from the repo and copy it into the EC2 with &lt;strong&gt;scp&lt;/strong&gt;, or clone directly into the EC2 instance, and check if &lt;code&gt;git&lt;/code&gt; is installed. &lt;/p&gt;

&lt;p&gt;To clone the repo run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/maiobarbero/grafana-prometheus-loki.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Edit the configuration's files
&lt;/h3&gt;

&lt;p&gt;Now with &lt;strong&gt;vim&lt;/strong&gt; edit the configuration file, in particular if you are using &lt;a href="https://github.com/maiobarbero/grafana-prometheus-loki" rel="noopener noreferrer"&gt;this repository&lt;/a&gt; edit &lt;code&gt;config/prometheus/prometheus-config.yml&lt;/code&gt; and replace localhost with your private IP4.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the application
&lt;/h2&gt;

&lt;p&gt;Here we are, the last step. Inside your project folder run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it.&lt;/p&gt;

&lt;p&gt;Go to your-public-ip:3000 and you have your Grafana instance!!!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/@chrisliverani?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Chris Liverani&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/turned-on-flat-screen-monitor-dBI_My696Rk?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Docker-compose cheat sheet</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Tue, 16 Jan 2024 15:19:09 +0000</pubDate>
      <link>https://dev.to/maiobarbero/docker-compose-cheat-sheet-2kgi</link>
      <guid>https://dev.to/maiobarbero/docker-compose-cheat-sheet-2kgi</guid>
      <description>&lt;p&gt;A cheat sheet to create you docker-compose.yml &lt;code&gt;version: '3.8'&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the image
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# build from Dockerfile&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
  &lt;span class="c1"&gt;# alternative to use a folder with more than one Dockerfile:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./dir&lt;/span&gt; &lt;span class="c1"&gt;# path to Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.dev&lt;/span&gt; &lt;span class="c1"&gt;#name of the file&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt; &lt;span class="c1"&gt;# specify the target in a multi-stage build&lt;/span&gt;
  &lt;span class="c1"&gt;# build from image&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu:14.04&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Init
&lt;/h2&gt;

&lt;p&gt;Run the service as PID 1. Set this option to true to enable this feature for the service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpine:latest&lt;/span&gt;
    &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Expose port
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:80"&lt;/span&gt;  &lt;span class="c1"&gt;# guest:host&lt;/span&gt;
&lt;span class="c1"&gt;# expose ports to linked services (not to host)&lt;/span&gt;
&lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Commands and Entrypoint
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# command to execute&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle exec thin -p &lt;/span&gt;&lt;span class="m"&gt;3000&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;bundle&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;thin&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;-p&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3000&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# override the entrypoint&lt;/span&gt;
&lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app/start.sh&lt;/span&gt;
&lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;php&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;-d&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;vendor/bin/phpunit&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# environment vars&lt;/span&gt;
&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;RACK_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;
&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RACK_ENV=development&lt;/span&gt;

&lt;span class="c1"&gt;# environment vars from file&lt;/span&gt;
&lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
&lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;.env&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;.development.env&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Health check
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# declare service healthy when `test` command succeed&lt;/span&gt;
&lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1m30s&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
  &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;40s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# make sure `db` is alive before starting&lt;/span&gt;
&lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;


&lt;span class="c1"&gt;# make sure `db` is healthy before starting&lt;/span&gt;
&lt;span class="c1"&gt;# and db-init completed without failure&lt;/span&gt;
&lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
&lt;span class="na"&gt;db-init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_completed_successfully&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Volumes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/lib/mysql&lt;/span&gt; &lt;span class="c1"&gt;# anonimous volume&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./host_path:/docker_container_path&lt;/span&gt; &lt;span class="c1"&gt;# bind mount&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./host_path&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/docker_container_path&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;volume&lt;/span&gt;
    &lt;span class="c1"&gt;# no source. Override the previous volumes for a specific path &lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/node_modules&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Restart
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# automatically restart container&lt;/span&gt;
&lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;span class="c1"&gt;# always, on-failure, no (default)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  User
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# specifying user&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# specifying both user and group with ids&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0:0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Starts existing containers.&lt;/span&gt;
docker-compose start

&lt;span class="c"&gt;# Stops running containers.&lt;/span&gt;
docker-compose stop

&lt;span class="c"&gt;# Pauses running containers.&lt;/span&gt;
docker-compose pause

&lt;span class="c"&gt;# Unpauses paused.&lt;/span&gt;
docker-compose unpause

&lt;span class="c"&gt;# Lists containers.&lt;/span&gt;
docker-compose ps

&lt;span class="c"&gt;# Builds, (re)creates, starts, and attaches to containers for a service.&lt;/span&gt;
docker-compose up

&lt;span class="c"&gt;# Stops and removes containers.&lt;/span&gt;
docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to contribute leave a comment or fork the project repo:&lt;br&gt;
&lt;a href="https://github.com/maiobarbero/docker_compose_cheatsheet" rel="noopener noreferrer"&gt;https://github.com/maiobarbero/docker_compose_cheatsheet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Started from &lt;a href="https://devhints.io/docker-compose" rel="noopener noreferrer"&gt;https://devhints.io/docker-compose&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>cheatsheet</category>
      <category>devops</category>
    </item>
    <item>
      <title>Set up Swagger in a Laravel project</title>
      <dc:creator>Matteo Barbero</dc:creator>
      <pubDate>Mon, 04 Dec 2023 12:42:19 +0000</pubDate>
      <link>https://dev.to/maiobarbero/use-swagger-in-laravel-project-4ad9</link>
      <guid>https://dev.to/maiobarbero/use-swagger-in-laravel-project-4ad9</guid>
      <description>&lt;h3&gt;
  
  
  What is Swagger?
&lt;/h3&gt;

&lt;p&gt;Swagger is an open-source framework to facilitate API documentation and development. It provides a standardised way to describe, document, and consume RESTful web services. Swagger makes it easier for developers and clients to understand and interact with services.&lt;/p&gt;

&lt;p&gt;At its core, Swagger utilises a specification format known as OpenAPI (formerly Swagger Specification). OpenAPI is a language-agnostic description format used to define RESTful APIs. It allows developers to articulate the functionality of their APIs, including details such as available endpoints, request/response formats, authentication methods, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why should you use it?
&lt;/h3&gt;

&lt;p&gt;One of the key advantages of Swagger is its interactive documentation, based on OpenAPI specification. This documentation not only serves as a comprehensive guide for developers but also enables clients to explore and test the API directly from a user-friendly interface. It's like having a fully documented and configured Postman inside the browser!&lt;/p&gt;

&lt;p&gt;In summary, Swagger and OpenAPI work hand in hand to streamline the API development process, providing a standardized way to define, document, and consume RESTful APIs. This ensures that APIs are well-documented, easily understood, and straightforward to integrate, ultimately fostering collaboration between API providers and consumers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use it with Laravel
&lt;/h3&gt;

&lt;p&gt;There are some packages to integrate Swagger inside a Laravel, the one that I suggest to you is &lt;a href="https://github.com/DarkaOnLine/L5-Swagger" rel="noopener noreferrer"&gt;darkaonline/l5-swagger&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First of all you need to install it in your project with Composer&lt;br&gt;
&lt;code&gt;composer require "darkaonline/l5-swagger"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then inside your .env file you need to put a variable called L5_SWAGGER_CONST_HOST, like the one below used with Sail&lt;br&gt;
&lt;code&gt;L5_SWAGGER_CONST_HOST=http://0.0.0.0/api/v1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can now publish the configuration file for &lt;strong&gt;&lt;code&gt;darkaonline/l5-swagger&lt;/code&gt;&lt;/strong&gt;. Just run this command:&lt;br&gt;
&lt;code&gt;php artisan vendor:publish --provider "L5Swagger\L5SwaggerServiceProvider"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now we have to set up some rules for the Swagger inside our controllers and methods and generate it.&lt;/p&gt;

&lt;p&gt;We put our first comment inside the app/Http/Controllers/Controller.php file. This comment simply describes our API. It will be the "header" text in our page &lt;em&gt;(L5_SWAGGER_CONST_HOST/api/documentation)&lt;/em&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="cd"&gt;/**

* @OA\Info(

* version="1.0.0",

* title="MyAPI Documentation",

* description="Documentation for the public API",

* @OA\Contact(

* email="maiowebdesign@gmail.com"

* ),

* @OA\License(

* name="Apache 2.0",

* url="http://www.apache.org/licenses/LICENSE-2.0.html"

* )

* )

*

* @OA\Server(

* url=L5_SWAGGER_CONST_HOST,

* description="ArticCMS API Server"

* )



*

* @OA\Tag(

* name="myAPI",

* description="API Endpoints of MyAPI"

* )

*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can define our method in the seme way, adding a comment block before the methods inside the controllers. This is an example for a controller that get all the post inside a blog project. We can easy add our query parameters, like with_catgeories or with_author, and test them directly in the browser. We can also bind the param to an Enum, and define the response codes.&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="cd"&gt;/**

* @OA\Get(

* path="/posts",

* operationId="getPostsList",

* tags={"Posts"},

* summary="Get list of posts",

* description="Returns a list of posts with optional filters.",

* @OA\Parameter(

* name="with_categories",

* in="query",

* description="Include categories associated with each post",

* @OA\Schema(type="boolean")

* ),

* @OA\Parameter(

* name="with_author",

* in="query",

* description="Include author associated with each post",

* @OA\Schema(type="boolean")

* ),

* @OA\Parameter(

* name="sort_by_date",

* in="query",

* description="Sort the posts by date",

* @OA\Schema(type="boolean")

* ),

* @OA\Parameter(

* name="lang",

* in="query",

* description="Language of the posts",

* @OA\Schema(

* type="string",

* enum=App\Enums\LocaleEnum::class,

* example="en_GB"

* )

* ),

* @OA\Parameter(

* name="status",

* in="query",

* description="Status of the posts",

* @OA\Schema(

* type="string",

* enum=App\Enums\Post\PostStatusEnum::class,

* example="draft"

* )

* ),

* @OA\Parameter(

* name="paginate",

* in="query",

* description="Number of posts per page for pagination (default: 15)",

* @OA\Schema(type="integer", default=15)

* ),

* @OA\Response(

* response=200,

* description="Successful operation",

* @OA\JsonContent(

* type="array",

* @OA\Items(ref="#/components/schemas/Post")

* )

* ),

* )

*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's also possible to describe our models to have an in-browser documentation of our entities. The method is always the same, just add a comment before your model class. The one below is the definition of a Post model&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="cd"&gt;/**

* @OA\Schema(

* title="Post",

* description="Post model",

* @OA\Property(property="id", type="integer", example=1),

* @OA\Property(property="title", type="string", example="Sample Title"),

* @OA\Property(property="body", type="string", example="Lorem ipsum..."),

* @OA\Property(

* property="status",

* type="string",

* enum=App\Enums\Post\PostStatusEnum::class,

* example="draft",

* description="Status of the post"

* ),

* @OA\Property(

* property="language",

* type="string",

* enum=App\Enums\LocaleEnum::class,

* example="en_GB",

* description="Language of the post"

* ),

* @OA\Property(property="user_id", type="integer", example=1),

* @OA\Property(property="created_at", type="string", format="date-time"),

* @OA\Property(property="updated_at", type="string", format="date-time"),

* )

*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>laravel</category>
      <category>api</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
