<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Sporadic writings by Adam Campbell]]></title><description><![CDATA[I've been wandering the Internet for far too long, and leaving a bit of a trail as I go. Check on my pulse here.]]></description><link>https://adamcampbell.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1704203400674/_NbOIh8HV.png</url><title>Sporadic writings by Adam Campbell</title><link>https://adamcampbell.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 07:17:43 GMT</lastBuildDate><atom:link href="https://adamcampbell.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Bitbucket Pipelines for Laravel apps]]></title><description><![CDATA[I never thought I'd be in this situation, but here we are: I'm having to use Bitbucket for a project.
I have nothing against Bitbucket, per se. I've just been a Github user for ~14 years, and haven't had any reason to ever use Atlassian's product. Bu...]]></description><link>https://adamcampbell.dev/bitbucket-pipelines-for-laravel-apps</link><guid isPermaLink="true">https://adamcampbell.dev/bitbucket-pipelines-for-laravel-apps</guid><category><![CDATA[Bitbucket]]></category><category><![CDATA[bitbucket-pipelines]]></category><category><![CDATA[Laravel]]></category><dc:creator><![CDATA[Adam Campbell]]></dc:creator><pubDate>Fri, 17 May 2024 12:31:21 GMT</pubDate><content:encoded><![CDATA[<p>I never thought I'd be in this situation, but here we are: <strong>I'm having to use</strong> <a target="_blank" href="https://bitbucket.org"><strong>Bitbucket</strong></a> for a project.</p>
<p>I have nothing against Bitbucket, per se. I've just been a Github user for ~14 years, and haven't had any reason to ever use Atlassian's product. But a team my company, <a target="_blank" href="https://twitter.com/SolenoidStudios">Solenoid</a>, is working with right now had adopted it early on, so that's what we've got.</p>
<p>One thing they hadn't been able to set up yet is some nice automated processes for things like code formatting (with <a target="_blank" href="https://github.com/laravel/pint">Pint</a> or <a target="_blank" href="https://prettier.io/">Prettier</a>) or running the test suite when a PR is created. With <a target="_blank" href="https://github.com/features/actions">Github Actions</a> this is now a breeze, and commonplace in most Laravel apps I've worked on.</p>
<p>Bitbucket has a comparable solution called <a target="_blank" href="https://support.atlassian.com/bitbucket-cloud/docs/get-started-with-bitbucket-pipelines/">Bitbucket Pipelines</a>, designed to do the same thing. Unfortunately, I could find next to no examples online on how to create more elaborate Pipelines for Laravel apps. So, after crawling the docs and picking up pieces from here and there, I've got a nice Pipeline for our app that formats our code and runs our tests on every PR (and PR update).</p>
<p>Here it is:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># bitbucket-pipelines.yml</span>

<span class="hljs-attr">definitions:</span>
  <span class="hljs-attr">caches:</span>
    <span class="hljs-attr">composer:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">vendor</span>
    <span class="hljs-attr">node:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">node_modules</span>
  <span class="hljs-attr">services:</span>
    <span class="hljs-attr">mariadb:</span>
      <span class="hljs-attr">image:</span> <span class="hljs-string">mariadb:11.3</span>
      <span class="hljs-attr">environment:</span>
        <span class="hljs-attr">MYSQL_RANDOM_ROOT_PASSWORD:</span> <span class="hljs-string">'yes'</span>
        <span class="hljs-attr">MYSQL_DATABASE:</span> <span class="hljs-string">'laravel'</span>
        <span class="hljs-attr">MYSQL_USER:</span> <span class="hljs-string">'laravel'</span>
        <span class="hljs-attr">MYSQL_PASSWORD:</span> <span class="hljs-string">'secret'</span>
  <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span> <span class="hljs-meta">&amp;phpunit</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"Run PHPUnit tests"</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">fideloper/fly-laravel:8.3</span>
        <span class="hljs-attr">caches:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">composer</span>
        <span class="hljs-attr">services:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">mariadb</span>
        <span class="hljs-attr">script:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">ln</span> <span class="hljs-string">-f</span> <span class="hljs-string">-s</span> <span class="hljs-string">.env.ci</span> <span class="hljs-string">.env</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">composer</span> <span class="hljs-string">install</span> <span class="hljs-string">--no-interaction</span> <span class="hljs-string">--prefer-dist</span> <span class="hljs-string">--optimize-autoloader</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">php</span> <span class="hljs-string">artisan</span> <span class="hljs-string">migrate</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">php</span> <span class="hljs-string">artisan</span> <span class="hljs-string">test</span> <span class="hljs-string">--parallel</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span> <span class="hljs-meta">&amp;pint</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"Pint"</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">fideloper/fly-laravel:8.3</span>
        <span class="hljs-attr">caches:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">composer</span>
        <span class="hljs-attr">script:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">composer</span> <span class="hljs-string">install</span> <span class="hljs-string">--no-interaction</span> <span class="hljs-string">--prefer-dist</span> <span class="hljs-string">--optimize-autoloader</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">./vendor/bin/pint</span> <span class="hljs-string">-v</span> <span class="hljs-string">--preset</span> <span class="hljs-string">laravel</span>
        <span class="hljs-attr">artifacts:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">app/**</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">config/**</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">database/**</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">routes/**</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">tests/**</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span> <span class="hljs-meta">&amp;prettier</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"Prettier"</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">node:20</span>
        <span class="hljs-attr">caches:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">node</span>
        <span class="hljs-attr">script:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">prettier:fix</span>
        <span class="hljs-attr">artifacts:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">resources/**</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span> <span class="hljs-meta">&amp;commit</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"Commit back to branch"</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">atlassian/default-image:4</span>
        <span class="hljs-attr">script:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">git</span> <span class="hljs-string">diff</span> <span class="hljs-string">--quiet</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">git</span> <span class="hljs-string">diff</span> <span class="hljs-string">--staged</span> <span class="hljs-string">--quiet</span> <span class="hljs-string">||</span> <span class="hljs-string">git</span> <span class="hljs-string">commit</span> <span class="hljs-string">-a</span> <span class="hljs-string">-m</span> <span class="hljs-string">"[skip ci] Pint and Prettier"</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">git</span> <span class="hljs-string">push</span>

<span class="hljs-attr">pipelines:</span>
  <span class="hljs-attr">pull-requests:</span>
    <span class="hljs-string">'**'</span><span class="hljs-string">:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">parallel:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span> <span class="hljs-meta">*pint</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span> <span class="hljs-meta">*prettier</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span> <span class="hljs-meta">*commit</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">step:</span> <span class="hljs-meta">*phpunit</span>
</code></pre>
<p>As you can see, this Pipeline is capable of doing a few things:</p>
<ol>
<li><p>It will run our <strong>Pint</strong> and <strong>Prettier</strong> steps in parallel, tidying up any changes made to files in the PR. The fixes made are pushed into artifacts for the following step.</p>
</li>
<li><p>Artifacted code is then "quietly" diffed, and if there's diffs it will be committed back into the PR. The commit message is prefixed with <code>[skip ci]</code> as to not trigger an infinite loop of running Pipelines.</p>
</li>
<li><p>Finally, our Laravel test suite is run.</p>
</li>
</ol>
<p>If any of this fails it's visible on the PR itself. Bitbucket can also be configured to prevent merging unless all builds pass, so that's a nice way to ensure broken code doesn't accidentally get merged in.</p>
<p>Hopefully this is helpful for anyone else that is using Bitbucket and Laravel, willfully or not!</p>
]]></content:encoded></item><item><title><![CDATA[Run a Wordpress blog alongside your Laravel app]]></title><description><![CDATA[Many times over the years I've wanted to add a blog to an existing Laravel app.
Lately, this has especially been the case for "experiments" I've been building, whether they are micro-SaaS projects or pSEO sites. These sites are built to see if they c...]]></description><link>https://adamcampbell.dev/run-a-wordpress-blog-alongside-your-laravel-app</link><guid isPermaLink="true">https://adamcampbell.dev/run-a-wordpress-blog-alongside-your-laravel-app</guid><category><![CDATA[Laravel]]></category><category><![CDATA[WordPress]]></category><category><![CDATA[SEO]]></category><dc:creator><![CDATA[Adam Campbell]]></dc:creator><pubDate>Mon, 08 Jan 2024 14:53:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704724669055/48673c9e-fffa-4df2-800e-d09622a77b23.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-many-times-over-the-years-ive-wanted-to-add-a-blog-to-an-existing-laravel-app"><strong>Many times over the years I've wanted to add a blog to an existing Laravel app.</strong></h2>
<p>Lately, this has especially been the case for "experiments" I've been building, whether they are micro-SaaS projects or pSEO sites. These sites are built to see if they can drive traffic (and potentially revenue) and I don't want to invest a whole lot into them because they could get nuked just as fast.</p>
<p>95% of these experiments are Laravel apps because it's the tool I know best, and I know I can squeeze a ton of <strong>functionality and performance</strong> for my site using the framework.</p>
<p>Also top of mind is <strong>SEO</strong>. SEO is vital for any project with hopes of organic growth. Blogs are an excellent way to build an SEO presence. So with that in mind I know that that blog needs to sit <strong>within</strong> the root domain, not a subdomain, or I'm just shooting myself in the foot (This is <a target="_blank" href="https://www.oncrawl.com/technical-seo/subdomains-impact-seo-performance/">a big topic you can explore</a>).</p>
<p>This article outlines how I set up Wordpress blogs alongside existing Laravel apps so that I can have a powerful content platform without affecting functionality.</p>
<h3 id="heading-considerations">Considerations</h3>
<p>When considering my options a few come to mind:</p>
<ul>
<li><p><strong>Write my own</strong> - This is fine, but it's also limited. There's so much domain knowledge that has gone into blogging systems and CMSs that I'd want to leverage, but I don't want to have to build.</p>
</li>
<li><p><strong>Use Statamic</strong> - <a target="_blank" href="https://statamic.com">Statamic</a> is very awesome, I've used it on a handful of sites. Since it's a package that sits inside of a Laravel installation it's actually well suited for this case. Unfortunately, I'm not always in the position to fork up the money for a license for an experiment I'm running.</p>
</li>
<li><p><strong>Use Wordpress</strong> - <a target="_blank" href="https://wordpress.org">Wordpress</a> is free, it's easy to install, and it comes with all the bells and whistles I may or may not need. But it's got one major issue, which is that it is intended to be its own standalone app. Laravel's routing system makes it difficult to "ignore" a folder within the app and you'd need to fiddle with Apache or nginx settings.</p>
</li>
</ul>
<h3 id="heading-wordpress-laravel">Wordpress + Laravel = 🥰</h3>
<p>Faced with these options, it's clear none are perfect for my "cheap experiments" scenario.</p>
<p>But for what I'm going for Wordpress comes closest, so I decided that it was time to figure out how to make Wordpress work with an existing Laravel app without adding additional complex overhead.</p>
<p>I'm not 100% sure what I outline in this article is the <em>easiest or quickest</em> way that a Laravel and Wordpress marriage can be done. But I do know this set up absolutely works, is pretty elegant, and has allowed me to seamlessly incorporate Wordpress into my existing apps.</p>
<p>And you'll be happy to know that this is all just as possible using PostgreSQL as it is MySQL (I'll show you how)!</p>
<h2 id="heading-overview">Overview</h2>
<p>Before we start I want to lay out the objective so you can decide now if you want to keep reading or not.</p>
<p>What we're going to do is to <strong>use Wordpress as a headless CMS for our blog.</strong> By "headless" I mean Wordpress will only serve as the CMS; we will separately pull that data into our app by other means.</p>
<p>Wordpress will be set up as a <strong>distinct app</strong> from your Laravel app but it will <strong>share the database</strong>. The Wordpress data and content can be pulled into our app just like you would with any other resource.</p>
<p>It's intended that <strong>the Wordpress install will live on a subdomain</strong> (which is completely fine from an SEO perspective because it's just acting as a CMS) and your blog content will be presented within blog pages you create within your app.</p>
<p>For the sake of this example:</p>
<ul>
<li><p>The Laravel app is <code>mysite.test</code></p>
</li>
<li><p>The CMS app is <code>cms.mysite.test</code></p>
</li>
<li><p>The blog will live at the <code>/blog</code> path of your Laravel app, <code>mysite.test/blog</code></p>
</li>
</ul>
<h3 id="heading-implications">Implications</h3>
<p>The implications of this setup is that <strong>your Laravel app and your Wordpress install are two distinct projects</strong> and will require independent deployments.</p>
<p>Fortunately, once Wordpress is deployed it can be managed entirely from production, including framework and plugin updates, so you shouldn't need to be running two deployment processes after it's first installed.</p>
<p>Hopefully you're still with me. Let's press on.</p>
<h2 id="heading-requirements">Requirements</h2>
<p>There's a few things you'll need before getting started:</p>
<ul>
<li><p>An existing Laravel app that's up and running, with a database</p>
</li>
<li><p><a target="_blank" href="https://make.wordpress.org/cli/handbook/guides/installing/">wp-cli</a></p>
<ul>
<li><p>This is the official CLI for Wordpress. I refuse to download and unzip packages, so this is the nicer way to do it.</p>
</li>
<li><p>You can install wp-cli using curl or composer; I myself did it using brew (<code>brew install wp-cli</code>)</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-getting-started">Getting Started</h2>
<p>Install Wordpress as a new site <a target="_blank" href="https://make.wordpress.org/cli/handbook/how-to/how-to-install/">using the CLI</a>. Note the <code>path</code> argument is installing it into a <strong>new folder</strong>; just to remind you, this should be a distinct project.</p>
<pre><code class="lang-bash">wp core download --path=mysite-blog --skip-content
</code></pre>
<p>Ok, that was easy.</p>
<h3 id="heading-postgresql">PostgreSQL</h3>
<p><strong>If you're not using PostgreSQL</strong> feel free to skip ahead to the next section. Wordpress is built for MySQL out of the box.</p>
<p>Otherwise, follow me...</p>
<h4 id="heading-wordpress-on-postgresql">Wordpress on PostgreSQL</h4>
<p>I've been a PostgreSQL user for years. For most of that time I thought I had to spin up one-off MySQL DBs just to run Wordpress. But thanks to the <a target="_blank" href="https://github.com/PostgreSQL-For-Wordpress/postgresql-for-wordpress">PostgreSQL for Wordpress</a> project, that's not the case!</p>
<ol>
<li><p>Go to the <code>wp-content</code> folder</p>
</li>
<li><p>Install <a target="_blank" href="https://github.com/PostgreSQL-For-Wordpress/postgresql-for-wordpress">pg4wp</a></p>
</li>
</ol>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/kevinoid/postgresql-for-wordpress.git
</code></pre>
<ol>
<li>Move the <code>pg4wp</code> folder to the <code>wp-content</code> directory:</li>
</ol>
<pre><code class="lang-bash">mv postgresql-for-wordpress/pg4wp pg4wp
</code></pre>
<ol>
<li>Move the <code>db.php</code> file to the <code>wp-content</code> directory</li>
</ol>
<pre><code class="lang-bash">cp pg4wp/db.php db.php
</code></pre>
<p>That's it! Your Wordpress site will work in blissful harmony with your PostgreSQL database.</p>
<h2 id="heading-configuring-wordpress">Configuring Wordpress</h2>
<p>Return to root of your Wordpress project and finish preparing it for installation.</p>
<h3 id="heading-environment-variables">Environment Variables</h3>
<p>As someone who has reached developer enlightenment I prefer <strong>not</strong> to hard-code environment variables into configuration files. So to handle env variables better, we're going to do some quick setup.</p>
<p><a target="_blank" href="https://m.dotdev.co/secure-your-wordpress-config-with-dotenv-d939fcb06e24">Eric Barnes's wrote a great post about this back in 2017</a>, so we're going to follow his suggestions. There's been a few updates to the related packages since, but here's the steps:</p>
<ol>
<li>In your Wordpress project run <code>composer init</code> to create a <code>composer.json</code> file. If you don't want to do that you can just use this snippet (replace the values in <code>&lt;&gt;</code>s):</li>
</ol>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"&lt;repo name&gt;"</span>,
    <span class="hljs-attr">"authors"</span>: [
        {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"&lt;your name&gt;"</span>,
            <span class="hljs-attr">"email"</span>: <span class="hljs-string">"&lt;your email&gt;"</span>
        }
    ],
    <span class="hljs-attr">"require"</span>: {
        <span class="hljs-attr">"vlucas/phpdotenv"</span>: <span class="hljs-string">"^5.6"</span>
    }
}
</code></pre>
<ol>
<li><p>As you can see we're using the awesome <a target="_blank" href="https://github.com/vlucas/phpdotenv">vlucas/phpdotenv</a> package. Pull it in using <code>composer require vlucas/phpdotenv</code> or <code>composer install</code> if you manually created your <code>composer.json</code> file.</p>
</li>
<li><p>Create a <code>.env</code> file inside the project folder</p>
</li>
<li><p>Add the <code>.env</code> file to a <code>.gitignore</code> file. We don't want that landing in our commit history.</p>
</li>
<li><p>Set <strong>the same database variables from your Laravel app</strong> into the <code>.env</code>:</p>
</li>
</ol>
<pre><code class="lang-bash">DB_HOST=127.0.0.1
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
</code></pre>
<p>Now your Wordpress site is leveraging environment variables, which will work both locally and in production.</p>
<h3 id="heading-finishing-configuration">Finishing Configuration</h3>
<ol>
<li>Create the config file:</li>
</ol>
<pre><code class="lang-bash">cp -rp wp-config-sample.php wp-config.php
</code></pre>
<ol>
<li>Run <code>wp config shuffle-salts</code> to set all your SALT values.</li>
</ol>
<div data-node-type="callout">
<div data-node-type="callout-emoji">📣</div>
<div data-node-type="callout-text">Heads up: Running <code>wp config shuffle-salts</code> is a quick way of setting all of the SALT values in your <code>wp-config.php</code> file, but it completely jacks up the line-spacing. So if you're OCD about your files you may want to do it manually.</div>
</div>

<ol>
<li><p>Update the new <code>wp-config.php</code> file with your DB settings.</p>
<ol>
<li><p>If you're using environment variables ensure you're including the autoload script at the top, and properly referring to the values using <code>$_ENV</code>.</p>
</li>
<li><p>If you're not using environment variables just drop the necessary values right in there like a complete savage</p>
</li>
</ol>
</li>
</ol>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">require_once</span>(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">'/vendor/autoload.php'</span>);
$dotenv = Dotenv\Dotenv::createImmutable(<span class="hljs-keyword">__DIR__</span>);
$dotenv-&gt;load();

<span class="hljs-comment">/**
 * The base configuration for WordPress
 *
 * The wp-config.php creation script uses this file during the installation.
 * You don't have to use the web site, you can copy this file to "wp-config.php"
 * and fill in the values.
 *
 * This file contains the following configurations:
 *
 * * Database settings
 * * Secret keys
 * * Database table prefix
 * * ABSPATH
 *
 * <span class="hljs-doctag">@link</span> https://wordpress.org/documentation/article/editing-wp-config-php/
 *
 * <span class="hljs-doctag">@package</span> WordPress
 */</span>

<span class="hljs-comment">/** The name of the database for WordPress */</span>
define( <span class="hljs-string">'DB_NAME'</span>, $_SERVER[<span class="hljs-string">'DB_DATABASE'</span>] );

<span class="hljs-comment">/** Database username */</span>
define( <span class="hljs-string">'DB_USER'</span>, $_SERVER[<span class="hljs-string">'DB_USERNAME'</span>] );

<span class="hljs-comment">/** Database password */</span>
define( <span class="hljs-string">'DB_PASSWORD'</span>, $_SERVER[<span class="hljs-string">'DB_PASSWORD'</span>] );

<span class="hljs-comment">/** Database hostname */</span>
define( <span class="hljs-string">'DB_HOST'</span>, $_SERVER[<span class="hljs-string">'DB_HOST'</span>] );

<span class="hljs-comment">/** Database charset to use in creating database tables. */</span>
define( <span class="hljs-string">'DB_CHARSET'</span>, <span class="hljs-string">'utf8'</span> );

<span class="hljs-comment">/** The database collate type. Don't change this if in doubt. */</span>
define( <span class="hljs-string">'DB_COLLATE'</span>, <span class="hljs-string">''</span> );
</code></pre>
<h2 id="heading-installation">Installation</h2>
<p>We've made great progress and now it's time to install! First, let's review what we've done:</p>
<ul>
<li><p>✅ We've created a standalone Wordpress install</p>
</li>
<li><p>✅ We've configured it to connect to our main Laravel app database</p>
</li>
<li><p>✅ We're leveraging environment variables to keep secrets where they belong</p>
</li>
</ul>
<p>It may seem like it's a lot of work, but after you do it once it only takes a minute.</p>
<h4 id="heading-now-its-time-to-install-wordpress"><strong>Now it's time to install Wordpress.</strong></h4>
<p>If you're working in Valet now is a good time to link this domain. I like to set up the Wordpress install on a subdomain of my Laravel site to emulate how it'll work in production, but you don't <em>have</em> to do that.</p>
<pre><code class="lang-bash">valet link cms.mysite
</code></pre>
<p>It should now be available at <a target="_blank" href="http://cms.mysite.test">http://cms.mysite.test</a>.</p>
<p>Visit the new URL to complete setup – it should redirect you to the installation path.</p>
<p>Once the installation flow is complete you should have your site setup and running now, whether you use MySQL or PostgreSQL. Congrats!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704719022441/fa2231fe-28ee-46b0-bbbb-3dbb790fbcb6.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-viewing-your-blog">Viewing your Blog</h2>
<p>Next is the part we've been waiting for: <strong>pulling in blog content into our Laravel site.</strong></p>
<h3 id="heading-staying-eloquent">Staying Eloquent</h3>
<p>One of the great parts of Laravel is having Eloquent models to easily interact with database records. Fortunately for us, the <a target="_blank" href="https://github.com/corcel/corcel">Corcel</a> library has done the heavy lifting to bring Eloquent structure to Wordpress resources.</p>
<blockquote>
<p>Corcel is a collection of PHP classes built on top of Eloquent ORM (from Laravel framework), that provides a fluent interface to connect and get data directly from a WordPress database.</p>
</blockquote>
<ol>
<li>Return to your <strong>Laravel project folder</strong> (not the WP app) and install Corcel:</li>
</ol>
<pre><code class="lang-bash">composer require jgrossi/corcel
</code></pre>
<ol>
<li>Publish the <code>corcel.php</code> config file:</li>
</ol>
<pre><code class="lang-bash">php artisan vendor:publish --provider=<span class="hljs-string">"Corcel\Laravel\CorcelServiceProvider</span>
</code></pre>
<ol>
<li><p>Open your <code>database.php</code> file and make a copy of your primary database connection configuration. We want Corcel to use our primary database <strong>but</strong> we need to tell it that the Wordpress resources are found in tables prefixed with <code>wp_</code>.</p>
<ol>
<li><p>Set the key to <code>wordpress</code></p>
</li>
<li><p>Change the <code>prefix</code> value to <code>wp_</code></p>
</li>
</ol>
</li>
<li><p>Edit the <code>corcel.php</code> config file and change the <code>connection</code> value to use your new configuration:</p>
</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-string">'connection'</span> =&gt; <span class="hljs-string">'wordpress'</span>
</code></pre>
<h3 id="heading-creating-blog-pages">Creating blog pages</h3>
<p>Now you can create a controller and view that will serve up your blog index and posts, or however your app is set up to present views.</p>
<p>Out of the box Corcel comes with <code>Post</code> models, with all necessary relationships, but you can extend it to add your own functionality. Check out <a target="_blank" href="https://github.com/corcel/corcel#-posts">https://github.com/corcel/corcel</a> for a deep dive on all the things you can do.</p>
<p>Here's a quick example of a <code>BlogController</code> to serve up your blog and blog posts:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Corcel</span>\<span class="hljs-title">Model</span>\<span class="hljs-title">Post</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BlogController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span>(<span class="hljs-params"></span>)
    </span>{
        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'blog.index'</span>, [
            <span class="hljs-string">'posts'</span> =&gt; Post::query()
                -&gt;type(<span class="hljs-string">'post'</span>)
                -&gt;published()
                -&gt;newest()
                -&gt;paginate(<span class="hljs-number">10</span>),
        ]);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">show</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $slug</span>)
    </span>{
        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'blog.show'</span>, [
            <span class="hljs-string">'post'</span> =&gt; Post::slug($slug)-&gt;first(),
        ]);
    }
}
</code></pre>
<p>And of course you'll need some routes:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-comment">// routes/web.php</span>

<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">Support</span>\<span class="hljs-title">Facades</span>\<span class="hljs-title">Route</span>;

Route::get(<span class="hljs-string">'/blog'</span>, [\App\Http\Controllers\BlogController::class, <span class="hljs-string">'index'</span>])-&gt;name(<span class="hljs-string">'blog.index'</span>);
Route::get(<span class="hljs-string">'/blog/{slug}'</span>, [\App\Http\Controllers\BlogController::class, <span class="hljs-string">'show'</span>])-&gt;name(<span class="hljs-string">'blog.show'</span>);
</code></pre>
<h2 id="heading-wrap-up">Wrap Up</h2>
<p>There you have it: <strong>a Wordpress blog deployed against your current infrastructure and ready for you to tap into within your primary app.</strong></p>
<p>While I think there's definitely another path to this with having the Wordpress installation live <strong>inside</strong> your app in a <code>/blog</code> folder it means you'll need to configure server configs to get that path to resolve Wordpress's <code>index.php</code> file and related routing.</p>
<p>I am interested in this solution as an alternative though, so if you know how to do it please let me know!</p>
<p>And of course, questions and comments are most welcome, as well as suggestions for improving this post to include your particular scenarios.</p>
<h3 id="heading-resources">Resources:</h3>
<p>Here's some links to sites I referenced while pulling this article together:</p>
<ul>
<li><p><a target="_blank" href="https://m.dotdev.co/secure-your-wordpress-config-with-dotenv-d939fcb06e24">https://m.dotdev.co/secure-your-wordpress-config-with-dotenv-d939fcb06e24</a></p>
</li>
<li><p><a target="_blank" href="https://inovector.com/blog/wordpress-as-a-headless-cms-for-your-laravel-website">https://inovector.com/blog/wordpress-as-a-headless-cms-for-your-laravel-website</a></p>
</li>
<li><p><a target="_blank" href="https://www.enterprisedb.com/postgres-tutorials/how-deploy-wordpress-highly-available-postgresql">https://www.enterprisedb.com/postgres-tutorials/how-deploy-wordpress-highly-available-postgresql</a></p>
</li>
<li><p><a target="_blank" href="https://www.codeable.io/blog/laravel-wordpress/">https://www.codeable.io/blog/laravel-wordpress/</a> <a target="_blank" href="https://www.cloudways.com/blog/laravel-wordpress/">https://www.cloudways.com/blog/laravel-wordpress/</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Re-launching StatusTicker]]></title><description><![CDATA[StatusTicker is an app that I first built in 2017 under the most noble of developer compulsions... it was something I needed, a solution didn't exist, so I built it.
Next week I'm going to be re-launching it on Product Hunt. But before I get into tha...]]></description><link>https://adamcampbell.dev/re-launching-statusticker</link><guid isPermaLink="true">https://adamcampbell.dev/re-launching-statusticker</guid><category><![CDATA[StatusTicker]]></category><category><![CDATA[SaaS]]></category><category><![CDATA[Self Improvement ]]></category><dc:creator><![CDATA[Adam Campbell]]></dc:creator><pubDate>Tue, 02 Jan 2024 17:54:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704218009721/5490202e-2081-4649-8a81-d01b7251696a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://statusticker.com">StatusTicker</a> is an app that I first built in 2017 under the most noble of developer compulsions... it was something I needed, a solution didn't exist, so I built it.</p>
<p>Next week I'm going to be re-launching it on <a target="_blank" href="https://www.producthunt.com/products/statusticker">Product Hunt</a>. But before I get into that, let me tell a story...</p>
<h2 id="heading-a-bit-of-statusticker-history">A bit of StatusTicker history</h2>
<p><strong>The idea was simple:</strong> instead of managing subscriptions for the myriad different cloud services and APIs my team used I would aggregate status feeds into a single "ticker" and present them on a screen in our office.</p>
<p>Green boxes were good, red boxes said something was broken upstream, yellow boxes were forewarning of problems to come, and blue boxes indicated the provider was undergoing maintenance.</p>
<p>I also added the ability to notify about status changes via email, SMS, Slack, etc. The Slack integration in particular was nice because it just became a steady stream of awareness within our team Slack instance.</p>
<p>It was simple and it was effective. So I turned it into a SaaS product. I also had a lot of fun tackling all of the different feeds and formats. <a target="_blank" href="https://twitter.com/jbrooksuk">James Brooks</a> recently told me that I was one of the first people to integrate with <a target="_blank" href="https://cachethq.io/">Cachet</a>, which is pretty cool.</p>
<h2 id="heading-statusticker-today">StatusTicker today</h2>
<p><strong>6+ years later and StatusTicker lives on.</strong> Over that time I've had 1000s of people sign up for the service, and many many more hitting any of the public status pages I make available. I've added many new features, improved SEO, and changed my pricing structure. I've chatted with other makers and have tried to connect with customers to learn about what would make the product better.</p>
<p>But candidly it's never been as successful (or profitable) as I'd like. At all.</p>
<p>There's probably a few good reasons for that (we'll save the app roast for another article).</p>
<p>But one in particular I've come to terms with lately is entirely about me, and it's that I have another strong developer tendency:</p>
<p><strong>I focus on <em>building</em> and avoid <em>marketing</em> like the plague.</strong></p>
<h2 id="heading-leaving-the-fort">Leaving the Fort</h2>
<p>From what I've seen this is a pretty common trait among our ilk, and I've been meditating on why that is...</p>
<ul>
<li><p>Maybe our own experiences being sold and marketed to has left us jaded</p>
</li>
<li><p>Maybe we're lacking confidence and/or conviction that our solution has value, or at least more value than another solution</p>
</li>
<li><p>Maybe we don't know where to go to tell people about it without being driven out of town (ie. Reddit or Hackernews)</p>
</li>
<li><p><em>&lt;insert your own reasons here&gt;</em></p>
</li>
</ul>
<p><strong>I think for me it has been a bit of all of those.</strong></p>
<p>But I also know that lately I've become very interested in pushing myself into places where <em>I'm not comfortable</em>.</p>
<p>The time I've spent watching others who are finding the types of success that I want for myself are doing it because they are exploring new places, pushing their own boundaries, discovering good things, and then telling (and selling) people on their experiences.</p>
<p>And I think I used to be a lot more like this, then I went all corporate and forgot how. Well, time for me to leave the fort again and blaze new trails.</p>
<h2 id="heading-moving-to-6040">Moving to 60/40</h2>
<p><strong>Look, I'm not aiming to become someone who is just trying to sell all the time.</strong></p>
<p>What I do want is to become better at valuing the things I envision and create and expressing that value to others. I absolutely want to help people succeed in their own goals.</p>
<p>I now believe doubling-down on sharing my experiences and connecting with people is the best way to do that. It doesn't always come naturally... it's like hitting the gym after a long hiatus (yet another topic for another day).</p>
<p>So, In 2024 I'm going to be focusing on going <strong>60/40</strong>:</p>
<ul>
<li><p>60% sharing/selling/marketing (uncomfortable)</p>
</li>
<li><p>40% building (comfortable)</p>
</li>
</ul>
<p>Ideally, that becomes 80/20 over time but I need to start realistically 😅</p>
<p>This blog is a big part of that 60% (so far)</p>
<h2 id="heading-so-about-that-relaunch">So about that relaunch...</h2>
<p>As I said, I'm doing another <a target="_blank" href="https://www.producthunt.com/products/statusticker">StatusTicker launch on Product Hunt</a> next week (January 9th, 2024). If you support it, awesome, thanks in advance.</p>
<p><strong>For me this is about learning and honing.</strong> I'll be <em>thrilled</em> if I gain new subscribers through this launch. But just as importantly it's an opportunity for me to improve content, messaging, and voice. To force myself to speak boldly and confidently about what I've built and be convicted that it will help other people too. And that if someone finds value in it that it's totally OK for me to ask for appropriate compensation in return (ya, I'm of the old $5/sub squad...)</p>
<p>Maybe none of this resonates with you, and to that I give you a hat tip.</p>
<p>But if you've ever found yourself wrestling with these thoughts as a creator then I encourage you to flip the script, push yourself to do some uncomfortable stuff, and see what comes out of it.</p>
<p><strong>You might be surprised.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Moving from Inertia.js to Laravel Livewire]]></title><description><![CDATA[I've been a huge Inertia.js fan and user for years, and was one of the first adopters after my buddy Jonathan Reinink released it back in 2019. Paired with Vue.js, it just makes spinning up app UIs so easy.
Livewire has been around for a long time to...]]></description><link>https://adamcampbell.dev/moving-from-inertiajs-to-laravel-livewire</link><guid isPermaLink="true">https://adamcampbell.dev/moving-from-inertiajs-to-laravel-livewire</guid><category><![CDATA[Laravel]]></category><category><![CDATA[Livewire]]></category><category><![CDATA[Inertia.js]]></category><category><![CDATA[Vue.js]]></category><dc:creator><![CDATA[Adam Campbell]]></dc:creator><pubDate>Mon, 01 Jan 2024 14:35:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704118891518/a8839c09-c6c1-4ede-b0b7-7ad8604a615c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've been a huge <a target="_blank" href="https://inertiajs.com">Inertia.js</a> fan and user for years, and was one of the first adopters after my buddy <a target="_blank" href="https://twitter.com/reinink">Jonathan Reinink</a> released it back in 2019. Paired with <a target="_blank" href="https://vuejs.org">Vue.js</a>, it just makes spinning up app UIs so easy.</p>
<p><a target="_blank" href="https://livewire.laravel.com/">Livewire</a> has been around for a long time too. <a target="_blank" href="https://twitter.com/calebporzio">Caleb Porzio</a> released v1 around the same time and has been incrementally improving it through 3 major releases ever since.</p>
<p>I was on-site at <strong>Laracon Nashville</strong> when v3 was officially launched. The tech and features looked super impressive, and the crowd was pumped.</p>
<p>But I had never used Livewire myself, so the enthusiasm was a little lost on me.</p>
<h3 id="heading-thats-about-to-change-with-this-blog-post"><strong>That's about to change with this blog post.</strong></h3>
<p>I'm taking a new app I'm starting to build in my tried-and-true <strong>VILT</strong> (Vue/Inertia/Laravel/Tailwind) stack and I'm going to try to convert it and build it out in a <strong>TALL</strong> (Tailwind/Alpine/Laravel/Livewire) stack instead.</p>
<p>Like I said, I've never even touched Livewire before so this is a <strong>blind run</strong>. I'm also dividing this post into a few parts as I go through the process.</p>
<p>Time to get pumped 😎</p>
<h2 id="heading-removing-inertiajs">Removing Inertia.js</h2>
<p>I've already built out some of the functionality of this app, so this post is going to skip the parts about setting up a new Laravel project. In fact, I've also already got a bunch of Vue pages and Inertia controllers in place, which I'm going to need to convert over.</p>
<p>But, first things first... <strong>removing Inertia</strong>.</p>
<p>Admittedly, this feels strange and a disloyal, but I believe in my heart I'll be forgiven.</p>
<p>Let's go.</p>
<pre><code class="lang-bash">composer remove inertiajs/inertia-laravel
</code></pre>
<p>and</p>
<pre><code class="lang-bash">npm uninstall @inertiajs/vue3
</code></pre>
<p>And while I'm there, I might as well incur some collateral damage and remove <a target="_blank" href="https://github.com/tighten/ziggy">Ziggy</a>.</p>
<pre><code class="lang-bash">composer remove tightenco/ziggy
</code></pre>
<p>There, the Bandaid has been ripped off.</p>
<h2 id="heading-updating-assets">Updating Assets</h2>
<p>Since my app is currently built as a Vue + Inertia.js SPA I need to perform some surgery on a few files to get it rebuilding.</p>
<p>Firstly, I need to remove all of the Inertia-related boilerplate from my <code>app.js</code> file:</p>
<p>Before:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">'../css/app.css'</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'./bootstrap'</span>

<span class="hljs-keyword">import</span> { createInertiaApp } <span class="hljs-keyword">from</span> <span class="hljs-string">'@inertiajs/vue3'</span>
<span class="hljs-keyword">import</span> { resolvePageComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'laravel-vite-plugin/inertia-helpers'</span>
<span class="hljs-keyword">import</span> { createApp, h } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> { ZiggyVue } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../vendor/tightenco/ziggy/dist/vue.m'</span>

<span class="hljs-keyword">const</span> appName = <span class="hljs-keyword">import</span>.meta.env.VITE_APP_NAME
<span class="hljs-keyword">const</span> appUrl = <span class="hljs-keyword">import</span>.meta.env.VITE_APP_URL

createInertiaApp({
  <span class="hljs-attr">title</span>: <span class="hljs-function">(<span class="hljs-params">title</span>) =&gt;</span> <span class="hljs-string">`<span class="hljs-subst">${title}</span> - <span class="hljs-subst">${appName}</span>`</span>,
  <span class="hljs-attr">resolve</span>: <span class="hljs-function">(<span class="hljs-params">name</span>) =&gt;</span>
    resolvePageComponent(<span class="hljs-string">`./Pages/<span class="hljs-subst">${name}</span>.vue`</span>, <span class="hljs-keyword">import</span>.meta.glob(<span class="hljs-string">'./Pages/**/*.vue'</span>)),
  setup({ el, App, props, plugin }) {
    <span class="hljs-keyword">return</span> createApp({ <span class="hljs-attr">render</span>: <span class="hljs-function">() =&gt;</span> h(App, props) })
      .use(plugin)
      .use(ZiggyVue)
      .provide(<span class="hljs-string">'appName'</span>, appName)
      .provide(<span class="hljs-string">'appUrl'</span>, appUrl)
      .mount(el)
  },
  <span class="hljs-attr">progress</span>: <span class="hljs-literal">false</span>,
})
</code></pre>
<p>After:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">'../css/app.css'</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'./bootstrap'</span>
</code></pre>
<p>I deleted my <code>ssr.js</code> file too, and good riddance. SSR has always been the biggest pain with SPAs and maybe one of the biggest reasons I'm excited about trying Livewire.</p>
<p>Next I'm cleaning up my <code>vite.config.js</code> file to remove any references to Vue, Ziggy, and SSR.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text">All of this cleaning up has also resulted in me removing a ton of additional Vue-related packages that are no longer needed. If you're going through this process be sure to do the same.</div>
</div>

<h1 id="heading-adding-livewire">Adding Livewire</h1>
<p><strong>Now, to add in Livewire.</strong></p>
<p>Heading over to the <a target="_blank" href="https://livewire.laravel.com/docs/nesting">Livewire docs</a>, I see that installing it is pretty straightforward:</p>
<pre><code class="lang-bash">composer require livewire/livewire
</code></pre>
<p>OK. That's done.</p>
<p>I'm going to skip adding the <code>livewire.php</code> config file unless I discover I need it later on. I'm also going to assume the rest of the OOB settings will work for me.</p>
<p>It's cool that this single package provides both the Laravel services as well as the necessary "wiring" scripts for Livewire.</p>
<h2 id="heading-views">Views</h2>
<p>OK, so far I've removed Inertia, added Livewire, and hacked up some of the build files for my app. Now that all of the assets have been changed I'm left with:</p>
<ul>
<li><p>An app that won't build</p>
</li>
<li><p>A single Blade <code>app.blade.php</code> file that's kind of useless</p>
</li>
<li><p>A bunch of vestigial Vue files</p>
</li>
<li><p>Controller methods that are broken</p>
</li>
</ul>
<p>This means the lion's share of the work that remains will be updating views, so let's get started with the layout files.</p>
<h3 id="heading-layouts">Layouts</h3>
<p>I saw in the Quickstart section that a default layout could be created easily, so I'll do that:</p>
<pre><code class="lang-bash">php artisan livewire:layout
</code></pre>
<p>Et voila! 🤙</p>
<p>Before I continue I'm going to move some of the stuff from the head of my old <code>app.blade.php</code> file into this new layout file in <code>/resources/views/components/</code><a target="_blank" href="http://layouts.app"><code>layouts.app</code></a><code>.blade.php.</code></p>
<p>And I deleted the old <code>app.blade.php</code> so I didn't confuse myself later.</p>
<h3 id="heading-testing-it-all-out">Testing it all out</h3>
<p>Before I go any further with the rest of my views I need to test this out to see if I'm close. I'm going to just add a landing page with some content, and we'll see if it loads.</p>
<p>Since I'm OCD about folder structure I'm going a little fancier with the component generation:</p>
<pre><code class="lang-sh">php artisan make:livewire Home\\Index
</code></pre>
<p>🎉🎉🎉</p>
<p>Ok, so, I guess I need to start the server so Tailwind can run:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>And then I can visit my local site...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704117621765/16b61bdb-7946-4a1c-bd50-e4d1090b0dca.png" alt class="image--center mx-auto" /></p>
<p>Oh ya, forgot about that. I've deleted that middleware and removed the reference from <code>App\Http\Kernel.php</code>. Let's try again...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704117633227/15912838-ff7e-4326-8352-1bbe8edc20da.png" alt class="image--center mx-auto" /></p>
<p><strong>Closer.</strong></p>
<p>But I just realized I never did anything with my controller, so I need to do that first.</p>
<p>And this is the first time I'm hitting a real paradigm shift with Livewire, because it's not that I need to change my controller, <strong>it's that I need to delete it</strong>.</p>
<p>Instead I'm <strong>mapping this route right to the Livewire component.</strong></p>
<p>This is going to take some getting used to. But I'm OK with it.</p>
<p>I discover these are called <a target="_blank" href="https://livewire.laravel.com/docs/components#full-page-components">full-page components</a> and they leverage my layout file I just created. I'm also pleased to see I can still use different layouts for different components. And Caleb has gone absolutely bananas offering <em>3(? 4?)</em> different ways of doing this, as I assume will be the pattern all over Livewire.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704117671157/07e003b7-9fc0-4bd8-bcd8-ab8d6e537eb4.png" alt class="image--center mx-auto" /></p>
<p><strong>It's alive!</strong></p>
<p>I update the page with some memorable content for this landmark event, try out live reload with a Tailwind change, and even use a snazzy attribute to change the page title.</p>
<p>I'm feeling the pump start to swell...</p>
<h1 id="heading-realizing-my-limitations">Realizing my Limitations</h1>
<p>I don't want to squash any excitement but I'm realizing there's a ton of Inertia scaffolding that I get from Jetstream that I've not loaded into this app for Livewire.</p>
<p>So, <strong>I'm going to do the opposite of what's typically recommended and I'm going to run a Jetstream install into an existing app.</strong></p>
<p>First, make sure I have the latest Jetstream installed:</p>
<pre><code class="lang-sh">composer require laravel/jetstream
</code></pre>
<p>Second, Install Jetstream with Livewire:</p>
<pre><code class="lang-sh">php artisan jetstream:install livewire
</code></pre>
<p>There's a bunch of other steps you can take when installing this, so browse the <a target="_blank" href="https://jetstream.laravel.com/installation.html">Jetstream docs</a> to see what you need for your project.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">❗</div>
<div data-node-type="callout-text"><strong>Disclaimer:</strong> I ended up having to spin up a temporary new Laravel project with Livewire-flavored Jetstream so I could copy a bunch of views in my project. Stuff was broken, and this was easier.</div>
</div>

<h2 id="heading-conclusion">Conclusion</h2>
<p><strong>OK, that's it for Part 1.</strong> I successfully have my app re-<em>wired</em> for Livewire and it's ready to have the rest of the components reworked.</p>
<p>Here's what I know is ahead of me and what I'll be tackling in the next post(s):</p>
<ul>
<li><p><strong>Rebuilding my two layouts</strong>, a "Public" layout for non-authed users and an "App" layout for the actual app</p>
</li>
<li><p><strong>Building a dynamic dashboard</strong> that displays multiple data points at the same time</p>
</li>
<li><p><strong>CRUD-style pages</strong> for some of the resources that users can manage</p>
</li>
<li><p><strong>Sortable, filterable tables</strong> that contain 1000s of rows of data</p>
</li>
</ul>
<p>It feels a little daunting right now (part of the reason why I stopped at this point) but I think if I take it a bit at a time I'm going to learn a ton.</p>
<p>Thanks for reading.</p>
]]></content:encoded></item></channel></rss>