Day 7: Completing the CRUD Cycle

Summary Today’s Definition of Done: Shoot owner can edit description and color Shoot owner can destroy a shoot after confirmation Edit and Delete controls visible from dashboard Work Session The first thing I did for Day 7 is start working on the SiteController to add the edit and update methods. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public function edit(Site $site) { if ($site->user_id !== auth()->id()) { abort(403); } return view('sites.edit', compact('site')); } public function update(Request $request, Site $site) { if ($site->user_id !== auth()->id()) { abort(403); } $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'nullable|string|max:1000', 'theme_color' => 'required|string|in:blue,green,purple', ]); $site->update($validated); return redirect()->route('dashboard')->with('success', 'Shoot updated successfully!'); } Then I added the destroy method just to get everything in place in the controller. ...

January 17, 2026

Day 6: Templating & Dynamic Styling

Summary Today’s Definition of Done: Template Creation Primary colors changed based on theme_color. name and description rendered in HTML Tenant/SiteController returns html instead of json. Work Session In order to handle tenant homepages I created a new view at resources/views/tenant/home.blade.php. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ $site->name }}</title> @vite(['resources/css/app.css', 'resources/js/app.js']) </head> <body class="bg-gray-100 font-sans antialiased"> @php $colors = [ 'blue' => 'bg-blue-600 border-blue-800', 'green' => 'bg-green-600 border-green-800', 'purple' => 'bg-purple-600 border-purple-800', ]; $themeClasses = $colors[$site->theme_color] ?? $colors['blue']; @endphp <div class="min-h-screen flex flex-col items-center justify-center"> <div class="max-w-2xl w-full bg-white shadow-xl rounded-lg overflow-hidden border-t-8 {{ $themeClasses }}"> <div class="p-8"> <h1 class="text-4xl font-extrabold text-gray-900 mb-4"> {{ $site->name }} </h1> <p class="text-lg text-gray-600 leading-relaxed mb-6"> {{ $site->description ?? 'This shoot is just beginning to grow.' }} </p> <div class="pt-6 border-t border-gray-100"> <p class="text-sm text-gray-500 italic"> Sprouted by {{ $site->user->name }} </p> </div> </div> </div> </div> </body> </html> Then I updated Tenant\SiteController to use the new view I just created. ...

January 16, 2026

Day 5: Dynamic Rendering

Summary Before I went to bed yesterday I decided to lean into the Rhizome theme a bit harder and renamed Sites to Shoots. Along that same these instead of creating sites we’ll be sprouting shoots. I did not rename anything in the database, middleware, or controller. I just changed the views to reflect the botanical theme. For Day 5 my goal was to transform shoots from identical placeholders into dynamic pages by allowing users to define a description and a theme color during the “sprouting” process. ...

January 15, 2026

Day 4: Creating a Form

Summary Wednesdays are usually pretty busy for me since I go to church after work and don’t get home until very near the kids bedtime so I decided to break today into 2 sessions. Going into session 1 I had the following goals: Set up the “Landlord” controller Create the route Ensure the Site model works with User relationship Work Session 1 Using php artisan again I created the SiteController controller. Not to be confused with Tenant\SiteController that I created yesterday. ...

January 14, 2026

Day 3: Tenant Aware Routing

Summary Going into today’s session I had the following goals: Middleware correctly pulls sudomain from host string Middleware throws 404 if no slug in database Navigating to mountainlotus.rhizomecms.test displays site name As part of the setup I added some test domains to /etc/hosts so I could work with them and not have to mess with a dns server. 1 2 3 127.0.0.1 rhizomecms.test 127.0.0.1 mountainlotus.rhizomecms.test 127.0.0.1 another-site.rhizomecms.test The Work Session Using php artisan again I created the IdentifyTenant middleware. ...

January 13, 2026

Day 2: Models & Migrations

Summary Going into today’s session I had the following goals: Create a Site model Create a sites table that can be “owned” by a user Create a site belonging to a user I referenced the Laravel migrations doc at laravel.com/docs/12.x/migrations and asked Gemini a few clarifying questions when I ran into issues that I couldn’t quickly find an answer to. I was also aquainted with php artisan today, and it feels very much like working with Ruby on Rails scaffolding. ...

January 12, 2026

Day 1: Scoping a Multi-Tenant CMS

Phase 1: Prep Day (Day 1) 1. Pick your poison Learning Laravel. I haven’t actively used php on purpose for anything since Late php 4/Early php 5. I want to jump back in and refresh my php skills so I plan to do so by learning the Laravel framework. ...

January 10, 2026

The 21-Day Sprint: A Framework for Functional Fluency

The Inspiration In the book The First 20 Hours, Josh Kaufman argues that the 10000 hour rule only applies to becoming a world class expert and about 20 hours is all that is needed to become “good enough” at most hobbies or professional skills. Why 21 Days? The goal of any sprint shouldn’t be mastery. It should be functional fluency. Becoming good enough to use the new skill to create something and overcome barriers that may come up along the way. ...

January 10, 2026