<?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: Lee Mason</title>
    <description>The latest articles on DEV Community by Lee Mason (@leemason).</description>
    <link>https://dev.to/leemason</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%2F524553%2F077038ac-e5e7-43f0-8f58-dc196f4a629a.jpeg</url>
      <title>DEV Community: Lee Mason</title>
      <link>https://dev.to/leemason</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leemason"/>
    <language>en</language>
    <item>
      <title>WPDock - A Simple WordPress Development Environment Using Docker</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Tue, 26 Jan 2021 13:23:04 +0000</pubDate>
      <link>https://dev.to/wpgitupdater/wpdock-a-simple-wordpress-development-environment-using-docker-84d</link>
      <guid>https://dev.to/wpgitupdater/wpdock-a-simple-wordpress-development-environment-using-docker-84d</guid>
      <description>&lt;p&gt;Here at &lt;a href="https://wpgitupdater.dev"&gt;WP Git Updater&lt;/a&gt; we are announcing a new WordPress development environment using Docker!&lt;/p&gt;

&lt;p&gt;WordPress already provides a docker image which can be used for development, but it has a &lt;a href="https://github.com/docker-library/wordpress/issues/480"&gt;few&lt;/a&gt; &lt;a href="https://github.com/docker-library/wordpress/issues/30"&gt;problems&lt;/a&gt; that are not dealt with in the usual docker way. We never really found a configurable solution to these problems, which always meant using the docker image requiring some repeated “must know” steps for everything the containers need to be built. Or reverting to using developer machines local php/apache/mysql or another dev environment tool.&lt;/p&gt;

&lt;p&gt;We have used various other tools and they do not work well for us. Some just take over the host machine ports without telling you, others just do not provide all the features we need. WPDock is purposely designed to be a very thin layer over docker.&lt;/p&gt;

&lt;p&gt;By design, the WordPress image is just WordPress. There is no https support, mail catching or database access. This means again polluting your dev machines MySql tool of choice with numerous connection profiles.&lt;/p&gt;

&lt;p&gt;We wanted to find a way to run the WordPress Docker stack as it’s a perfect use case for docker in a team/collaboration environment. With Docker Compose we wanted a setup you can just "start" that includes some common tools used for WordPress local development.&lt;/p&gt;

&lt;p&gt;We set out to achieve the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single command to start/stop the whole environment&lt;/li&gt;
&lt;li&gt;Easy access to the MySql database&lt;/li&gt;
&lt;li&gt;Mail catching out of the box&lt;/li&gt;
&lt;li&gt;No WordPress loopback or Rest Api health check errors&lt;/li&gt;
&lt;li&gt;Simple access to &lt;code&gt;WP-CLI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;HTTPS/domain name support&lt;/li&gt;
&lt;li&gt;As little development machine pollution as possible&lt;/li&gt;
&lt;li&gt;Simple database sharing between team members&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enter WPDock
&lt;/h2&gt;

&lt;p&gt;WPDock is a very simple bash script that gives you all of the above. All you need is docker for basic operation, and if you want https/tls support there is only 1 other dependency &lt;a href="https://github.com/FiloSottile/mkcert"&gt;mkcert&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Checkout the repository and install instructions over on &lt;a href="https://github.com/wpgitupdater/wpdock"&gt;Github&lt;/a&gt;. WPDock is a simple executable you just need to place in your path.&lt;/p&gt;

&lt;p&gt;You can create a WPDock environment with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wpdock init 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;.env&lt;/code&gt; file in your current folder with the required environment variables for operation. It will also add the &lt;code&gt;.env&lt;/code&gt; file and the &lt;code&gt;.wpdock&lt;/code&gt; folder to a gitignore file and finally it will create a &lt;code&gt;.htaccess&lt;/code&gt; file which prevents these dot files ever being accessible should you mistakenly upload them to a server.&lt;/p&gt;

&lt;p&gt;Then to run the whole stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wpdock up or wpdock start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it!&lt;/p&gt;

&lt;p&gt;With this (and depending on what you have changed in the &lt;code&gt;.env&lt;/code&gt; file. You will have access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress on &lt;code&gt;localhost:8080&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;PhpMyAdmin on &lt;code&gt;localhost:8181&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mailhog catching WordPress emails for review on &lt;code&gt;localhost:8282&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;MySql on &lt;code&gt;localhost:33060&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your current directory is bound as the &lt;code&gt;wp-content&lt;/code&gt; folder on the WordPress container and WordPress is already installed for you to start developing with.&lt;/p&gt;

&lt;p&gt;What's more, the only development machine pollution is the ports being bound. WpDock will not use anything other than docker and those ports. This is perfect and gives you the flexibility to adjust the .env variables in another folder and run multiple instances at the same time!&lt;/p&gt;

&lt;p&gt;This also follows WP Git Updaters recommended structure for WordPress source controlled setups. With this setup you can &lt;code&gt;git init&lt;/code&gt; in the bound &lt;code&gt;wp-content&lt;/code&gt; folder, ignore the uploads folder and your all set. &lt;/p&gt;

&lt;h3&gt;
  
  
  Need to access &lt;code&gt;WP-CLI&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Of course we have made this simple too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wpdock user list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any commands not recognised by &lt;code&gt;wpdock&lt;/code&gt; will pass through to the cli container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sharing Databases Between Team Members
&lt;/h3&gt;

&lt;p&gt;You can of course use various mature tools to synchronise databases. But for simple usage &lt;code&gt;wpdock&lt;/code&gt; provides &lt;code&gt;dump&lt;/code&gt; and &lt;code&gt;import&lt;/code&gt; commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wpdock dump &lt;span class="c"&gt;# dump.sql will be in your current directory&lt;/span&gt;
wpdock import &lt;span class="c"&gt;# will import the dump.sql file in your current directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stopping Environment
&lt;/h3&gt;

&lt;p&gt;Stopping the environment can be done in a few ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wpdock stop &lt;span class="c"&gt;# stops the containers&lt;/span&gt;
wpdock down &lt;span class="c"&gt;# stops and removes the containers&lt;/span&gt;
wpdock destroy &lt;span class="c"&gt;# stops and removes the containers + shared volumes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other Commands
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;[service]&lt;/code&gt; can be one of: &lt;code&gt;wordpress&lt;/code&gt;, &lt;code&gt;cli&lt;/code&gt;, &lt;code&gt;db&lt;/code&gt;, &lt;code&gt;mailhag&lt;/code&gt;, &lt;code&gt;phpmyadmin&lt;/code&gt;, and &lt;code&gt;caddy&lt;/code&gt; (more on this later).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wpdock &lt;span class="nb"&gt;help&lt;/span&gt; &lt;span class="c"&gt;# displays command help&lt;/span&gt;

wpdock file .env &lt;span class="c"&gt;# displays the default env variables should you want to create or amend an existing .env file&lt;/span&gt;

wpdock &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;service] &lt;span class="c"&gt;# eg: wpdock exec wordpress bash&lt;/span&gt;

wpdock run &lt;span class="o"&gt;[&lt;/span&gt;service] … &lt;span class="c"&gt;# eg: wpdock run cli bash&lt;/span&gt;

wpdock logs &lt;span class="o"&gt;[&lt;/span&gt;service] &lt;span class="c"&gt;# tails all container logs, or pass a service to view specific logs&lt;/span&gt;

wpdock compose … &lt;span class="c"&gt;# proxy commands directly to docker-compose&lt;/span&gt;

wpdock … &lt;span class="c"&gt;# is the same as wpdock run cli wp …&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see WPDock is a very thin layer over Docker Compose commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Domains with Valid Local TLS/HTTPS Certificates
&lt;/h2&gt;

&lt;p&gt;Inline with the &lt;a href="https://12factor.net/dev-prod-parity"&gt;The Twelve Factor App&lt;/a&gt; recommendations on Dev/Prod parity.&lt;br&gt;
In 2021 there really isn’t an excuse not to use TLS even for local development.&lt;/p&gt;

&lt;p&gt;What’s more if you plan on using any form of shopping cart plugins without TLS you're likely to run into some issues over plain http.&lt;/p&gt;

&lt;p&gt;WPDock provides a simple option to enable custom domains with https for your development environments.&lt;/p&gt;

&lt;p&gt;Let's take a look and how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wpdock up &lt;span class="nt"&gt;--https&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above command and the &lt;code&gt;WORDPRESS_SITE_HOST&lt;/code&gt; .env variable setup you can develop locally with trusted TLS encryption for WordPress!&lt;/p&gt;

&lt;h3&gt;
  
  
  How does this work?
&lt;/h3&gt;

&lt;p&gt;WpDock needs 1 additional tool to provide this feature: &lt;a href="https://github.com/FiloSottile/mkcert"&gt;mkcert&lt;/a&gt;.&lt;br&gt;
Mkcert adds a locally trusted CA to your development machine, and allows you to generate TLS certificates against that CA.&lt;/p&gt;

&lt;p&gt;This means no more untrusted certificate warnings.&lt;/p&gt;

&lt;p&gt;First install Mkcert as detailed in their documentation. Run &lt;code&gt;mkcert -install&lt;/code&gt; once to install the CA and let WpDock take over from there.&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;wpdock up —https&lt;/code&gt; the following additional steps are performed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wpdock&lt;/code&gt; will generate a &lt;code&gt;Caddyfile&lt;/code&gt; in the &lt;code&gt;.wpdock&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wpdock&lt;/code&gt; will generate certificate and key files from your &lt;code&gt;WORDPRESS_SITE_HOST&lt;/code&gt; env variable in the &lt;code&gt;.wpdock/certs&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;A new caddy web server container will be started, with the generated files mapped into the correct locations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caddy is a super simple to use web server which is perfect for what &lt;code&gt;wpdock&lt;/code&gt; needs to provide TLS support.&lt;br&gt;
The generated &lt;code&gt;Caddfyfile&lt;/code&gt; simply creates a reverse proxy from &lt;code&gt;WORDPRESS_SITE_HOST&lt;/code&gt; to the wordpress container&lt;/p&gt;

&lt;p&gt;The Caddy container does need to bind to ports &lt;code&gt;80&lt;/code&gt; and &lt;code&gt;443&lt;/code&gt; on your development machine to provide this mapping.&lt;/p&gt;

&lt;p&gt;Now all you need to do is make sure &lt;code&gt;WORDPRESS_SITE_HOST&lt;/code&gt; points to &lt;code&gt;127.0.0.1&lt;/code&gt; via your &lt;code&gt;/etc/hosts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Your website will be available over https/tls, automatically forwarding http requests, with a trusted certificate.&lt;/p&gt;

&lt;p&gt;Additionally &lt;code&gt;wpdock&lt;/code&gt; will update your database and replace the &lt;code&gt;localhost:${PORT}&lt;/code&gt; urls with the new domain name.&lt;/p&gt;

&lt;p&gt;When you stop your development environment, everything is cleaned up. Port &lt;code&gt;80&lt;/code&gt; and &lt;code&gt;443&lt;/code&gt; are free again to be used.&lt;/p&gt;

&lt;p&gt;Head over to the Project to find out how to install and more on the commands: &lt;a href="https://github.com/wpgitupdater/wpdock"&gt;https://github.com/wpgitupdater/wpdock&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback Welcome
&lt;/h2&gt;

&lt;p&gt;We hope you enjoy using WPDock. For us it's a small but useful addition to our development workflow.&lt;/p&gt;

&lt;p&gt;WPDock is open source and can be found at &lt;a href="https://github.com/wpgitupdater/wpdock"&gt;https://github.com/wpgitupdater/wpdock&lt;/a&gt;. Any improvements/suggestions are more than welcome.&lt;/p&gt;

&lt;p&gt;At Wp Git Updater we develop locally using Apple machines, but we believe all of the commands will also work on Linux environments (please let us know if you have any issues).&lt;/p&gt;

&lt;p&gt;For windows users we are more than happy to accept PR’s that provide support if it can be achieved.&lt;/p&gt;

&lt;p&gt;Stay tuned, in the next few posts we are going to cover some of the features in more detail, and show you a setup automatically resolving your chosen tld to localhost using DNSMasq.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>docker</category>
      <category>github</category>
    </item>
    <item>
      <title>And We're Live!</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Tue, 19 Jan 2021 15:59:23 +0000</pubDate>
      <link>https://dev.to/wpgitupdater/and-we-re-live-3m5i</link>
      <guid>https://dev.to/wpgitupdater/and-we-re-live-3m5i</guid>
      <description>&lt;p&gt;Over the last few months we have been testing, adding new features and getting ready for the big launch.&lt;/p&gt;

&lt;p&gt;Its finally here with automated theme and plugin updates, run via gitlab action workflows.&lt;/p&gt;

&lt;p&gt;We still have much more we are working on that will be coming in the future:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;mu-plugin updates&lt;/li&gt;
&lt;li&gt;Gitlab, Bitbucket, Travis and Circle CI integrations&lt;/li&gt;
&lt;li&gt;Auto update disabler plugin for plugins managed by WP Git Updater&lt;/li&gt;
&lt;li&gt;Auto merging updates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Additionally in the coming days we will be launching our Docker based WordPress development environment we use internally for WordPress development.&lt;/p&gt;

&lt;p&gt;As part of the launch we are offering you a massive &lt;strong&gt;40% discount&lt;/strong&gt; on any of the subscription plans &lt;strong&gt;FOREVER&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Simply use the coupon code &lt;strong&gt;LAUNCH&lt;/strong&gt; when subscribing.&lt;/p&gt;

&lt;p&gt;But don’t wait too long, the &lt;strong&gt;LAUNCH&lt;/strong&gt; coupon expires the 28th of February.&lt;/p&gt;

&lt;p&gt;Before then you can take advantage of the 10 day free trial offered for new users.&lt;/p&gt;

&lt;p&gt;Once again thank you for your interest in WP Git Updater, if you need support, want to request a feature, or just catch up please don’t hesitate to let us know&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wpgitupdater.dev/register"&gt;Signup Today!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>github</category>
    </item>
    <item>
      <title>WP Git Updater – A Month In Review</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Thu, 07 Jan 2021 16:35:33 +0000</pubDate>
      <link>https://dev.to/leemason/wp-git-updater-a-month-in-review-4log</link>
      <guid>https://dev.to/leemason/wp-git-updater-a-month-in-review-4log</guid>
      <description>&lt;p&gt;At &lt;a href="https://gambitnash.co.uk"&gt;Gambit Nash&lt;/a&gt; we have been working on a service to automate WordPress plugin and theme updates for Git source controlled sites called &lt;a href="https://wpgitupdater.dev"&gt;WP Git Updater&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;WP Git Updater will be available for usage before the end of January 2021 and we are super excited about it.&lt;/p&gt;

&lt;p&gt;We have been using the service internally for some time alongside some beta testers to make sure it’s ready for a wider audience on launch day.&lt;/p&gt;

&lt;p&gt;Now is probably a good time to take a look at the service it provides and how it aids our development team.&lt;/p&gt;

&lt;p&gt;I’m going to focus on the usage in 3 of our WordPress repositories that have a variety of plugins and themes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Projects
&lt;/h2&gt;

&lt;p&gt;Each of the projects is a WordPress site, that has between 10-25 plugins, and they all have child theme setups using different parent themes.&lt;/p&gt;

&lt;p&gt;Additionally each project has staging and production environments, where staging is auto deployed from the develop branch in GIT when changes occur.&lt;/p&gt;

&lt;p&gt;Each project has a number of “boilerplate” plugins we use for all our WordPress websites, namely: Jetpack, Yoast SEO, Post SMTP, and WP Git Updaters handy &lt;a href="https://wordpress.org/plugins/display-git-status/"&gt;Display Git Status&lt;/a&gt; plugin.&lt;/p&gt;

&lt;p&gt;These types of projects are the target use cases for WP Git Updater. But it’s in no way limited to them.&lt;/p&gt;

&lt;p&gt;At its heart WP Git Updater creates branches and Pull Requests for plugin or theme updates, how you choose to organise your Git strategy or deploy changes is up to you. WP Git Updater is agnostic in that regard.&lt;/p&gt;

&lt;p&gt;The projects were chosen specifically due to the high numbers of plugins in use on them.&lt;/p&gt;

&lt;p&gt;It’s no secret that with WordPress websites you should aim to use as few plugins as possible, so we would categories the above stats as “high” usage projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stats
&lt;/h2&gt;

&lt;p&gt;Although highly variable depending on the quantity and especially the update frequency of the plugins or themes in question. We chose the 3 projects above to try and give you a feel for the anticipated usage you may have if you used WP Git Updater.&lt;/p&gt;

&lt;p&gt;Over a 30 day period WP Git Updater performed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;67&lt;/strong&gt; Total Updates&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;59&lt;/strong&gt; Plugin Updates&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8&lt;/strong&gt; Parent Theme Updates&lt;/p&gt;

&lt;p&gt;These updates were relatively even across the 3 projects.&lt;/p&gt;

&lt;p&gt;This would put us in the “Professional” plan with plenty of room to spare, we could estimate from this that the plan would support 5 or more high usage projects for Gambit Nash.&lt;/p&gt;

&lt;p&gt;In reality we have many more projects which is why the rest of this article is so important for us.&lt;/p&gt;

&lt;p&gt;The Professional plan is priced at $50 per month, so what do the above stats mean as a cost/benefit ratio on 5 projects?&lt;/p&gt;

&lt;p&gt;The answer as always is “it depends”. For us the breakdown isn’t just as simple as the number of updates performed:&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we even know updates are required?
&lt;/h3&gt;

&lt;p&gt;Well before WP Git Updater our update strategy was a mixture of scheduled maintenance reviews for lower activity projects, and general awareness of the team interacting with the project on a day to day basis.&lt;/p&gt;

&lt;p&gt;This isn’t necessarily a developer, but someone in the team needs to review the project’s status.&lt;/p&gt;

&lt;p&gt;This is especially painful as we need to allocate time to that team member to physically visit the staging sites, review any update notifications and then create tasks to be scheduled.&lt;/p&gt;

&lt;p&gt;We have a pretty streamlined project management process using Monday.com but that’s out of the scope of this article.&lt;/p&gt;

&lt;p&gt;Even with our streamlined approach an optimistic estimate per project just for the updates would be around 5 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does WP Git Updater compare?
&lt;/h3&gt;

&lt;p&gt;Well as we use Monday.com we can automate task creation so the cost to us is zero.&lt;/p&gt;

&lt;p&gt;But taking Monday.com out of the equation our team members get emails when the pull request is created, from this they can action right away.&lt;/p&gt;

&lt;p&gt;So per project the time shrinks to below 1 minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time Saved
&lt;/h3&gt;

&lt;p&gt;(5 min x 5) – (1 min x 5) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;20 minutes per month&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How much time do we need to plan updates?
&lt;/h3&gt;

&lt;p&gt;Our old approach would be a conversation between the project managers and developers.&lt;/p&gt;

&lt;p&gt;The first topic is always “where’s the changelog”, so off to google the plugin repository, search for the plugin, click the development tab and review.&lt;/p&gt;

&lt;p&gt;Precious minutes lost with the same problem every time. I would estimate 20 seconds per plugin optimistically.&lt;/p&gt;

&lt;p&gt;With WP Git Updater the changelog is pulled into the pull request for you, so it’s right in your email! It’s probably 5 seconds per plugin.&lt;/p&gt;

&lt;p&gt;Knowing from experience there are a lot of common plugins/themes, so a quick review of the changelog and past experience most estimates per plugin could be agreed in a few minutes. This is no different with WP Git Updater so is left out of any comparison.&lt;/p&gt;

&lt;p&gt;The unit of measure here is “per plugin” so let’s average that out at 12 plugins per project. Giving us 20 mins and 5 mins respectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time Saved
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;15 minutes per month&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How much time does a developer need to perform the update?
&lt;/h3&gt;

&lt;p&gt;This is a difficult question to answer as every update is different.&lt;/p&gt;

&lt;p&gt;What we can say is many updates can be signed off quickly by an experienced developer just by reviewing a diff of the changes.&lt;/p&gt;

&lt;p&gt;To review a diff of an update without WP Git Updater you need to ensure you’re on the latest commit, run your local environment, perform the update either via the admin or cli, open the diffing tool of your choice and review.&lt;/p&gt;

&lt;p&gt;With WP Git Updater you click the email link to see the diff in GitHub. It really is that simple.&lt;/p&gt;

&lt;p&gt;For these types of updates the difference per plugin can be minutes (or more depending on how you run your local development environment).&lt;/p&gt;

&lt;p&gt;For larger updates you would still likely want to perform initial testing in your local environment, but you can skip a step and save time even here. As WP Git Updater has already updated the plugin in a new branch, you can just checkout the update branch, run your environment and perform usual testing.&lt;/p&gt;

&lt;p&gt;Between those two optimisations at Gambit Nash we would estimate on 5 projects over a month they could save us around 1-2 hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time Saved
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1.5 hours per month&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How quickly can we get those updates to the testing team?
&lt;/h3&gt;

&lt;p&gt;Without WP Git Updater the answer is monthly as per our update schedule. This is obviously sequential too.&lt;/p&gt;

&lt;p&gt;Once we have made the updates the testing team needs to validate them before ever going to production.&lt;/p&gt;

&lt;p&gt;Now we can react to updates more effectively, we can also provide the testing team with a steady stream of testing to perform.&lt;/p&gt;

&lt;p&gt;Our updates are no longer just a scheduled block of work.&lt;/p&gt;

&lt;p&gt;The time impact isn’t in the testing itself, but in building a backlog of work the testing team can complete.&lt;/p&gt;

&lt;p&gt;While no direct time savings are made, having a steady backlog of testing requirements reduces the context shifting, waiting and communication requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time Saved
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;30 minutes per month&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Totals
&lt;/h3&gt;

&lt;p&gt;Above we have been pretty pessimistic about the time savings, there are areas hard to quantify but its clear WP Git Updater has had a positive impact on our workflows. If it wasn’t our own product I’d be much more inclined to increase those estimates.&lt;/p&gt;

&lt;p&gt;Across just 5 projects for $50 at &lt;strong&gt;least 2 hours&lt;/strong&gt; can be saved.&lt;/p&gt;

&lt;p&gt;Most of this is developer time.&lt;/p&gt;

&lt;p&gt;With an average developer salary of $60-$80 per hour that’s a monthly saving worth considering.&lt;/p&gt;

&lt;p&gt;Especially when you consider this is just 5 projects. What about 10, 20, 100?&lt;/p&gt;

&lt;p&gt;But time and savings are not why this service is important.&lt;/p&gt;

&lt;h3&gt;
  
  
  How often should we perform updates?
&lt;/h3&gt;

&lt;p&gt;Before WP Git Updater we always aimed to do this every month for every project.&lt;/p&gt;

&lt;p&gt;We still do monthly updates for every project now. But there is a massive difference.&lt;/p&gt;

&lt;p&gt;On top of the monthly updates, we can now fast track many “considered safe” updates just by merging the pull requests to the staging (develop) branch.&lt;/p&gt;

&lt;p&gt;As we run a staging and production setup for our sites, we can with good confidence simply merge updates for some of the more common plugins.&lt;/p&gt;

&lt;p&gt;If a problem with one of these updates is found in staging, no harm no foul we can just revert and investigate.&lt;/p&gt;

&lt;p&gt;We only do this for updates with a proven track record, honourable mentions include: Jetpack, Yoast SEO, Advanced Custom Fields, Post SMTP.&lt;/p&gt;

&lt;p&gt;Technically there is no time saved over the usual savings referenced above, but the benefit here isn’t time.&lt;/p&gt;

&lt;p&gt;At Gambit Nash our clients play an active role in using the staging sites, the staging area is for them as much as it is for us.&lt;/p&gt;

&lt;p&gt;These fast track updates demonstrate our active involvement day to day with their project.&lt;/p&gt;

&lt;p&gt;The admin areas aren’t an ever increasing update notification hell that resets once a month.&lt;/p&gt;

&lt;p&gt;If we need to release a new production deployment outside of the usual schedule, we can also rely on updates which might not have been completed till the next maintenance schedule being present.&lt;/p&gt;

&lt;p&gt;Simple patch release updates for security issues with plugins or themes can be notified, merged, tested and deployed within a day.&lt;/p&gt;

&lt;p&gt;These benefits outweigh any time saving or process optimisations.&lt;/p&gt;

&lt;h4&gt;
  
  
  WP Git Updater allows us to provide a faster, ever improving experience for our clients. And this is the real value from the automated plugin and theme updates WP Git Updater provides.
&lt;/h4&gt;

</description>
      <category>wordpress</category>
      <category>github</category>
    </item>
    <item>
      <title>Laravel Nova Resource Stubs For Spark Teams</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Mon, 04 Jan 2021 21:41:19 +0000</pubDate>
      <link>https://dev.to/leemason/laravel-nova-resource-stubs-for-spark-teams-4bk</link>
      <guid>https://dev.to/leemason/laravel-nova-resource-stubs-for-spark-teams-4bk</guid>
      <description>&lt;p&gt;At &lt;a href="https://gambitnash.co.uk"&gt;Gambit Nash&lt;/a&gt; we are on the verge of releasing &lt;a href="https://wpgitupdater.dev"&gt;WP Git Updater&lt;/a&gt;. WP Git Updater provides automated source control updates for WordPress websites stored in git. Very much like &lt;a href="https://dependabot.com"&gt;dependabot&lt;/a&gt; but for WordPress sites not using composer version management.&lt;/p&gt;

&lt;p&gt;As this is a service which has a monthly subscription. The obvious choice was to implement &lt;a href="https://spark.laravel.com"&gt;Laravel Spark&lt;/a&gt; and let it take care of the subscription management. As an added bonus it provided us with a base to work from for the rest of the site (layouts, style, etc).&lt;/p&gt;

&lt;p&gt;Im going to review Spark in a broader post, so I’m not going to go too far into a review, but suffice to say the kiosk feature is “ok”, but its not on the level of &lt;a href="https://nova.laravel.com"&gt;Laravel Nova&lt;/a&gt; (and nor should it be). So we took the decision to build a nova backend for the site too.&lt;/p&gt;

&lt;p&gt;I was quite surprised to find scarce examples or packages to display Spark information in Nova. Might even be worth Spark having some stubs included to show this info when Nova is also installed.&lt;/p&gt;

&lt;p&gt;We have used Spark through a team billing profile, so the following stubs don’t take into account user subscriptions, but I’m sure you could modify the resources to work for users if needed.&lt;/p&gt;

&lt;p&gt;Team Resource&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\Nova&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\Str&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\Nova\Fields\BelongsToMany&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\Nova\Fields\DateTime&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\Nova\Fields\HasMany&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Laravel\Nova\Fields\ID&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;Laravel\Nova\Fields\Select&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\Nova\Fields\Text&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\Spark\Spark&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;Team&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&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;static&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;App\Models\Team&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nv"&gt;$group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Spark'&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;static&lt;/span&gt; &lt;span class="nv"&gt;$search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'update_token'&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;static&lt;/span&gt; &lt;span class="nv"&gt;$title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'name'&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;fields&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;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="no"&gt;ID&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;'ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sortable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Name'&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="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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'max:255'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Slug'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'slug'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Update Token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'update_token'&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;readonly&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Stripe ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stripe_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;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Current Plan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'current_billing_plan'&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="nc"&gt;DateTime&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;'Trial Ends'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'trial_ends_at'&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="nc"&gt;DateTime&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;'Created At'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'created_at'&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;readonly&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;hideFromIndex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;DateTime&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;'Updated At'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'updated_at'&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;readonly&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;hideFromIndex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;HasMany&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;'Subscriptions'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'subscriptions'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TeamSubscription&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="nc"&gt;BelongsToMany&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;'Team Members'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'users'&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fields&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="nc"&gt;Select&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;'Role'&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;options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="nb"&gt;array_merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                    &lt;span class="nc"&gt;Spark&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                    &lt;span class="p"&gt;[&lt;/span&gt;
                                        &lt;span class="c1"&gt;//default role&lt;/span&gt;
                                        &lt;span class="nc"&gt;Spark&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;defaultRole&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;ucfirst&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Spark&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;defaultRole&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                                        &lt;span class="c1"&gt;//owner role&lt;/span&gt;
                                        &lt;span class="s1"&gt;'owner'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Owner'&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;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;Team Subscription Resource&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\Nova&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\Nova\Fields\BelongsTo&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\Nova\Fields\DateTime&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\Nova\Fields\HasMany&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Laravel\Nova\Fields\ID&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;Laravel\Nova\Fields\Number&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\Nova\Fields\Text&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;TeamSubscription&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&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;static&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Laravel\Spark\TeamSubscription&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nv"&gt;$group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Spark'&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;static&lt;/span&gt; &lt;span class="nv"&gt;$search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'stripe_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'stripe_plan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'stripe_status'&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;static&lt;/span&gt; &lt;span class="nv"&gt;$title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'name'&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;fields&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;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="no"&gt;ID&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;sortable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Name'&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="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="nc"&gt;Text&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;'Stripe ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stripe_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;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Stripe Plan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stripe_plan'&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="nc"&gt;Text&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;'Stripe Status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stripe_status'&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="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;span class="nc"&gt;Number&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;'QTY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'quantity'&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="nc"&gt;DateTime&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;'Trial Ends'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'trial_ends_at'&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="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;span class="nc"&gt;DateTime&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;'Ends At'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ends_at'&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="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;span class="nc"&gt;DateTime&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;'Created'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'created_at'&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;readonly&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;hideFromIndex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;DateTime&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;'Updated At'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'updated_at'&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;readonly&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;hideFromIndex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;BelongsTo&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;'Team'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'team'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Team&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;searchable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;HasMany&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;'Subscription Items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'items'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TeamSubscriptionItem&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Team Subscription Item Resource&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\Nova&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\Nova\Fields\BelongsTo&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\Nova\Fields\DateTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Laravel\Nova\Fields\ID&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;Laravel\Nova\Fields\Number&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\Nova\Fields\Text&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;TeamSubscriptionItem&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&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;static&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Laravel\Spark\TeamSubscriptionItem&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nv"&gt;$group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Spark'&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;static&lt;/span&gt; &lt;span class="nv"&gt;$search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'stripe_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'stripe_plan'&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;fields&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;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="no"&gt;ID&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;sortable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Stripe ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stripe_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;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;Text&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;'Stripe Plan'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'stripe_plan'&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="nc"&gt;Number&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;'QTY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'quantity'&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="nc"&gt;DateTime&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;'Created'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'created_at'&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;readonly&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;hideFromIndex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;DateTime&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;'Updated At'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'updated_at'&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;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;readonly&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;hideFromIndex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nc"&gt;BelongsTo&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;'Team Subscription'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'subscription'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TeamSubscription&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;searchable&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;



</description>
      <category>laravel</category>
    </item>
    <item>
      <title>Our Display Git Status plugin has just been approved on the WordPress.org plugin marketplace</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Sat, 19 Dec 2020 00:14:06 +0000</pubDate>
      <link>https://dev.to/wpgitupdater/our-display-git-status-plugin-has-just-been-approved-on-the-wordpress-org-plugin-marketplace-1p3b</link>
      <guid>https://dev.to/wpgitupdater/our-display-git-status-plugin-has-just-been-approved-on-the-wordpress-org-plugin-marketplace-1p3b</guid>
      <description>&lt;p&gt;&lt;a href="https://wordpress.org/plugins/display-git-status/"&gt;Display Git Status&lt;/a&gt; is a utility plugin to provide basic git status information right in the WordPress admin bar.&lt;/p&gt;

&lt;p&gt;We have created this plugin as a complimentary and free plugin for our users. Given our service offers Git source controlled plugin and theme updates. It's obvious such a feature would be beneficial right within the admin area. And we were surprised there wasn't already a solution.&lt;/p&gt;

&lt;p&gt;This plugin is great as a gentle reminder or flag to see if your staging or even production site becomes out of sync with your repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RWSsGnJr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://wpgitupdater.dev/storage/uploads/display-git-status-screenshot.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RWSsGnJr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://wpgitupdater.dev/storage/uploads/display-git-status-screenshot.jpg" alt="Display Git Status Settings Page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once activated just look out for the admin bar icon link turning to a red background, which indicates there are changes you probably need to investigate.&lt;/p&gt;

&lt;p&gt;We have added a new page to the documentation for our plugin (soon to be plugins) &lt;a href="https://wpgitupdater.dev/docs/latest/plugins#display-git-status"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>git</category>
    </item>
    <item>
      <title>WP Git Updater now supports theme updates</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Thu, 17 Dec 2020 20:07:39 +0000</pubDate>
      <link>https://dev.to/wpgitupdater/wp-git-updater-now-supports-theme-updates-323m</link>
      <guid>https://dev.to/wpgitupdater/wp-git-updater-now-supports-theme-updates-323m</guid>
      <description>&lt;p&gt;That's right! You can now update WordPress plugins and themes using WP Git Updater.&lt;/p&gt;

&lt;p&gt;The primary goal of WP Git Updater is to optimize and automate updates on your Git controlled WordPress websites. We started with plugins but early on it became clear theme updates were a must have. We felt this was such a critical feature we needed to make sure it was available before we officially released WP Git Updater.&lt;/p&gt;

&lt;p&gt;Additionally we have been able to retain the same configuration format with some new additions. This is down to some forward thinking knowing theme features were coming in the future.&lt;/p&gt;

&lt;p&gt;In short just adding:&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;themes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;themes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To your config file is sufficient to enable theme updates.&lt;/p&gt;

&lt;p&gt;There are of course further customizations available if required. Full details can be found in the &lt;a href="https://wpgitupdater.dev/docs/latest/configuration"&gt;Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just like plugins you can now automate theme branch and pull request creation on infrastructure and terms you define.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>github</category>
      <category>git</category>
    </item>
    <item>
      <title>Laravel On Shared Hosting</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Tue, 15 Dec 2020 15:42:46 +0000</pubDate>
      <link>https://dev.to/leemason/laravel-on-shared-hosting-51g2</link>
      <guid>https://dev.to/leemason/laravel-on-shared-hosting-51g2</guid>
      <description>&lt;p&gt;Before we start its worth caveating this post with the message: &lt;strong&gt;It’s always more desirable and recommended to serve Laravel apps from bespoke hosting environment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are plenty to choose, from first party tools like &lt;a href="https://forge.laravel.com/"&gt;Forge&lt;/a&gt; and &lt;a href="https://vapor.laravel.com/"&gt;Vapour&lt;/a&gt; to more general providers like &lt;a href="https://www.digitalocean.com/"&gt;Digital Ocean&lt;/a&gt;, &lt;a href="https://aws.amazon.com/"&gt;AWS&lt;/a&gt; and &lt;a href="https://cloud.google.com/"&gt;GCP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However as i find myself in a similar situation there may be times you have to deploy on a CPanel environment.&lt;/p&gt;

&lt;p&gt;I am going to cover the usual quite simple redirect you see across the web, but more importantly a second step thats usually left out. This second step can have a massive impact on your SEO.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Redirect
&lt;/h2&gt;

&lt;p&gt;As you may of found elsewhere theres a simple redirect you can put in the root folder of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;# Internally rewrite all top-level requests to the public directory
&lt;span class="nt"&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class="na"&gt;mod_rewrite.c&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    RewriteEngine On
    RewriteRule ^(.*)$ public/$1 [L]
&lt;span class="nt"&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s very simple and just redirects everything from the root of your site &lt;code&gt;public_html&lt;/code&gt; down into the &lt;code&gt;public&lt;/code&gt; folder of your Laravel project.&lt;/p&gt;

&lt;p&gt;From there the &lt;code&gt;.htaccess&lt;/code&gt; provided with Laravel will take over and route incoming requests.&lt;/p&gt;

&lt;p&gt;Job done? Maybe not…&lt;/p&gt;

&lt;h2&gt;
  
  
  The SEO Problem
&lt;/h2&gt;

&lt;p&gt;The problem with the above redirect is that it doesn’t prevent access to ALL of your urls with a &lt;code&gt;/public/&lt;/code&gt; prefix. So both &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;/public/&lt;/code&gt; will server your applications homepage!&lt;/p&gt;

&lt;p&gt;This is obviously bad for any SEO efforts your planning to make to the site as you now have duplicate pages and on top of that if someone stumbles upon those urls its not great for users.&lt;/p&gt;

&lt;p&gt;I spent sometime trying out redirects to prevent this from the &lt;code&gt;.htaccess&lt;/code&gt; file itself but came up blank. In my particular case this is a CPanel server with Litespeed installed. And i know from experience its not quite 100% like for like with Apache.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;.htaccess&lt;/code&gt; approach coming up blank i went with a Middleware solution. Again this isn’t ideal as its not really the applications responsibility but we are not in an ideal situation anyway.&lt;/p&gt;

&lt;p&gt;It’s a pretty simple middleware, it looks for the public prefix and returns a 301 redirect if found. This does mean you cannot have urls that start with public but in our case this wasn’t a problem:&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\Middleware&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;Closure&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RedirectPublicFolderRequests&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */&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;handle&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="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&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="nb"&gt;strpos&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;getRequestUri&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'/public'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&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;redirect&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;to&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;getSchemeAndHttpHost&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;substr&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;getRequestUri&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;8&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;setStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;);&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;$next&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again this isn’t ideal but works for our purposes.&lt;/p&gt;

&lt;p&gt;Add this to your global middleware stack before anything else.&lt;/p&gt;

&lt;p&gt;Note how we use the &lt;code&gt;$request-&amp;gt;getRequestUri()&lt;/code&gt; method and not simply &lt;code&gt;$request-&amp;gt;path()&lt;/code&gt;. As the path will be effected by the servers rewrites.&lt;/p&gt;

&lt;p&gt;Now any requests for &lt;code&gt;public&lt;/code&gt; will redirect with a 301 and you wont be serving duplicate pages.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>A look at WordPress source control strategies</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Mon, 14 Dec 2020 22:09:22 +0000</pubDate>
      <link>https://dev.to/wpgitupdater/a-look-at-wordpress-source-control-strategies-4fa3</link>
      <guid>https://dev.to/wpgitupdater/a-look-at-wordpress-source-control-strategies-4fa3</guid>
      <description>&lt;p&gt;In this post we are going to look at the benefits, available tools and a few common patterns to use source control with a WordPress powered website.&lt;/p&gt;

&lt;p&gt;Source control is quite a generalisation. There are many source control implementations. The two biggest are &lt;strong&gt;GIT&lt;/strong&gt; and &lt;strong&gt;SVN&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the purposes of this post we are going to be talking about &lt;strong&gt;GIT&lt;/strong&gt; as a source control tool, after all it’s in our name.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the benefits
&lt;/h2&gt;

&lt;p&gt;If you are a single administrator of a WordPress website, source control allows you to maintain a single source of truth for your website’s files. In case of problems it provides the ability to “revert/rollback” changes and it allows you to review the history of file changes.&lt;/p&gt;

&lt;p&gt;Additionally with branches, tags and commits you have fine grain control and authority on changes to be made.&lt;/p&gt;

&lt;p&gt;For mission critical Websites with development/staging environments, you can selectively apply changes and review before “merging” into the production codebase.&lt;/p&gt;

&lt;p&gt;Source control really shines when working in larger teams. It enables easier collaboration and mechanisms for applying outside changes to your own work. Conflict resolution is provided with diffing tools to review and apply.&lt;/p&gt;

&lt;p&gt;When twinned with a source control provider like Github, Gitlab, or Bitbucket you have additional features like Pull Requests and issue trackers to cross reference changes with bugs whilst giving project maintainers control over which updates are applied to main branches and when.&lt;/p&gt;

&lt;p&gt;For WordPress specifically, source control can remove the variability of WordPress core, plugin and theme updates. For most WordPress websites there are third party files operating your website, and depending on your update strategy these changes may be out of your control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Existing tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  VersionPress
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://versionpress.com"&gt;VersionPress&lt;/a&gt; treats your site file and its database as one and source controls everything. This takes the concept of GIT and brings it into the WordPress admin area. Instead of GIT being used as a background process to manage the state of your website files, VersionPress turns your whole website including content and settings into a GIT repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  WP Pusher
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://wppusher.com"&gt;WP Pusher&lt;/a&gt; is more traditional and looks specifically at individual plugins or themes. It provides the ability to install or update directly from a GIT repository, with features to choose a specific branch to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  WP Rollback
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/wp-rollback"&gt;WP Rollback&lt;/a&gt; takes a defensive approach and utilises source control to rollback plugin or theme updates directly from WordPress repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common source control approaches
&lt;/h2&gt;

&lt;p&gt;Although some of the tools mentioned above do more than source control your files, in this section we are going to focus on WordPress file source control.&lt;/p&gt;

&lt;p&gt;We are also going to look at these patterns from a “pull changes” perspective. This means the website is only updated from source control. The website doesn’t "push changes" back to GIT.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source control everything
&lt;/h3&gt;

&lt;p&gt;This pattern takes your website files, including WordPress itself and commits it to source.&lt;/p&gt;

&lt;p&gt;Using this approach, core, plugin and theme updates should be turned off within the administration area.&lt;/p&gt;

&lt;p&gt;The advantages of this are that everything, even down to the specific WordPress version, is static and controlled. For mission critical sites this could be the right approach as it leaves nothing to chance.&lt;/p&gt;

&lt;p&gt;Unfortunately this advantage is also its disadvantage. WordPress core updates can be applied automatically for good reason. Updates may include security patches to protect your website and without automatic updates, you would be responsible to ensure WordPress is always up to date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source control the &lt;code&gt;wp-content&lt;/code&gt; folder
&lt;/h3&gt;

&lt;p&gt;This is a common approach and provides an advantage over the first method. Using this approach WordPress core updates are usually turned on to ensure a secure system.&lt;/p&gt;

&lt;p&gt;This pattern also provides some flexibility. For example you may choose some highly trusted plugins or themes that can be automatically updated. For this to work without your Website and GIT becoming divergent you can “ignore” certain files and folders from source.&lt;/p&gt;

&lt;p&gt;Excluding any ignored files or folder (or uploads) everything within your &lt;code&gt;wp-content&lt;/code&gt; directory would be controlled. This would mean themes and plugins would only be updated when they are updated in GIT.&lt;/p&gt;

&lt;p&gt;A disadvantage of this approach is that updates would require manual intervention. You would need to apply the updates in source and pull them to your website.&lt;/p&gt;

&lt;p&gt;This approach provides protection against incompatibilities between plugins and themes. It doesn't safeguard against WordPress core updates. However, WordPress has a long and positive history on unknown breaking changes for core updates, so it is suitable for most scenarios. &lt;/p&gt;

&lt;h3&gt;
  
  
  Source control per plugin/theme
&lt;/h3&gt;

&lt;p&gt;This approach does not source control your website in any way it controls a particular plugin or theme. You could use this approach along with some of the tools mentioned earlier to manage individual parts of your website.&lt;/p&gt;

&lt;p&gt;This approach won't provide you any safeguards against future update compatibilities as the pattern is concerned only with the plugin or theme it controls.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about the &lt;code&gt;uploads&lt;/code&gt; folder?
&lt;/h3&gt;

&lt;p&gt;In all of the patterns above you would not source control the &lt;code&gt;uploads&lt;/code&gt; folder. Unless you're using a tool like VersionPress, this content is directly related to the contents of your database. This content is also ever changing via administrators so whenever new uploads are created your website and source control would be out of sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does WP Git Updater fit in?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://wpgitupdater.dev"&gt;WP GIT Updater&lt;/a&gt; is designed to make the first two patterns (source control everything, source control the &lt;code&gt;wp-content&lt;/code&gt; folder) much easier to manage and maintain.&lt;/p&gt;

&lt;p&gt;WP GIT Updater will automate branch and pull request creation for any updates available, from the WordPress plugins or themes repository.&lt;/p&gt;

&lt;p&gt;It can automate this process via manual invocation, or via a schedule defined within a continuous integration environment (for example Github Actions).&lt;/p&gt;

&lt;p&gt;What this means in practise is once in place, every day (or a schedule you define) you can take advantage of your CI system to reduce developer hours performing updates.&lt;/p&gt;

&lt;p&gt;To "make an update" on a source controlled website the steps usually involved are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull down the source&lt;/li&gt;
&lt;li&gt;Review each plugin/theme for updates (either via a local environment, or manual searching)&lt;/li&gt;
&lt;li&gt;Perform each update&lt;/li&gt;
&lt;li&gt;Add the update files&lt;/li&gt;
&lt;li&gt;Commit the changes&lt;/li&gt;
&lt;li&gt;Push the changes to a branch&lt;/li&gt;
&lt;li&gt;Create a pull request for review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps may take a seasoned developer only a few minutes for each update, but a website may have many updates, especially if the task is performed infrequently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wpgitupdater.dev"&gt;WP GIT Updater&lt;/a&gt; automates this process for you. What's more, each update branch and pull request will have consistent naming, version &lt;code&gt;from&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; details, and any author provided changelog added to the pull request for review, eliminating a low complexity but time consuming task.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My First Useful Tagged Template Literal</title>
      <dc:creator>Lee Mason</dc:creator>
      <pubDate>Mon, 14 Dec 2020 21:43:47 +0000</pubDate>
      <link>https://dev.to/leemason/my-first-useful-tagged-template-literal-479p</link>
      <guid>https://dev.to/leemason/my-first-useful-tagged-template-literal-479p</guid>
      <description>&lt;p&gt;Since their inclusion in Javascript &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates"&gt;tagged template literals&lt;/a&gt; have had developers on edge about possible use cases for them. There are the &lt;a href="https://viperhtml.js.org/"&gt;obvious&lt;/a&gt; &lt;a href="https://lit-html.polymer-project.org/"&gt;ones&lt;/a&gt;. For me they seemed soo useful. Yet I found myself looking for a problem to solve, instead of it being obvious they were the right tool for the job.&lt;/p&gt;

&lt;p&gt;Very recently i think i’ve happened upon a problem they solve elegantly.&lt;/p&gt;

&lt;p&gt;So the background story is I am working on a large scale SPA project. This project interfaces with an api server. Nothing unusual there.&lt;/p&gt;

&lt;p&gt;What is unusual is the &lt;code&gt;id&lt;/code&gt; for a lot of these resources is actually user submitted text strings. They have to be unique, not include certain characters etc, but the key is “its user submitted text”. We had no option to change this (at least for now).&lt;/p&gt;

&lt;p&gt;This creates what is at least for me an unusual situation. The api resource urls can contain certain “must be encoded” characters (spaces, commas, full stops, accents).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Existing Solution
&lt;/h2&gt;

&lt;p&gt;Another lead developer on the project created a solution i’ve seen before to construct the url, and escape the correct variables using indexed replacement from an array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/{0}/{1}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;varName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;anotherVar&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This solution works and we have been using it successfully for a while now.&lt;/p&gt;

&lt;p&gt;Alongside this a VueJs mixin was created which did some further magic to make replacements directly on a string using something similar to literal syntax: &lt;code&gt;/api/v1/{varName}&lt;/code&gt;. Where &lt;code&gt;varName&lt;/code&gt; could be accessed directly off the Vue instance.&lt;/p&gt;

&lt;p&gt;The problem with these 2 methods are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The array syntax feels awkward&lt;/li&gt;
&lt;li&gt;Option 2 (the clearer option) can only be used inside a component instance, not for example in a store module&lt;/li&gt;
&lt;li&gt;A limitation of option 2 is that it requires the “vars” used in the string be accessible on &lt;code&gt;this&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Option 2 caught a few members of the team out, thinking it was actually a string literal when they started to reuse from examples&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The (possible) Tagged Template Literal Solution
&lt;/h3&gt;

&lt;p&gt;So i had a bit of an idea, it takes the existing concept further and addresses some of the issues.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It does use a template literal with a tagged function&lt;/li&gt;
&lt;li&gt;Its clearer than the array syntax&lt;/li&gt;
&lt;li&gt;The vars can be anything you have access to, as its a literal any javascript inside placeholders &lt;code&gt;${}&lt;/code&gt; works.&lt;/li&gt;
&lt;li&gt;It can be used from anywhere&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usage is very much the same as before (via the mixin), you just convert the string to a literal, prepend the tagged function &lt;code&gt;url&lt;/code&gt; and you use real placeholder syntax &lt;code&gt;${}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Existing solution (result not implementation)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingWay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theUrlConstructor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/resource/{0}/{1}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my string, with unsafe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url +characters+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;// /api/v1/resource/my%20string%2C%20with%20unsafe/url%20%2Bcharacters%2B&lt;/span&gt;
&lt;span class="c1"&gt;// New solution&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;compiled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&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;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my string, with unsafe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url +characters+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newWay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="s2"&gt;`/api/v1/resource/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="c1"&gt;// /api/v1/resource/my%20string%2C%20with%20unsafe/url%20%2Bcharacters%2B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve introduced this to the team, but haven’t yet had any real feedback, let me know what you think.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
  </channel>
</rss>
