Skip to content

Lando and WordPress Plugin Development

Dear Reader,Lando logo

So I’m working on my next book “Extending the WordPress REST API”. It’s the companion volume to “Using the WordPress REST API”. In this book, I actually have a working WordPress REST API controller and series of endpoints that I am showcasing. This means I’ve got development to do.

One of the hardest things I do in software development is WordPress plugin development. It takes so much to get things stood up and ready that it’s a pain just to start a project. My last few, I would spin up a site on an unused domain I own – don’t look at me funny…you’ve got them too – and then use VS Code to edit the files remotely. Not exactly the best solution but it’s a great excuse for not working when your not connected. (Scuba boat, airplane in times past, etc.)

Now though, that excuse has been taken away from me thanks to Lando. :)

Installing Lando

Installing Lando is not difficult. One of the great things about Lando it’s copious documentation. Anyone familiar with the basics of software development should be able to install Lando in no time regardless of their operating system. Thanks to Microsoft’s new WSL2 and some huge improvements in the file system, Lando now works better under WSL2 than it does on macOS. (I’ve done both)

I wrote up my thoughts on how to get Lando installed, up and running on WSL2 in this post, Making Lando work inside WSL2.

Developing a Plugin

Once you have Lando installed, the next thing we need is a plugin. You can use any plugin you are wanting to do development on. I’m using my wp_podcast_api plugin that I wrote for Voices of the ElePHPant, and that is the sample code for my book.

Once you are in your plugin’s directory, you have to create a .lando.yml file. Lando makes this part easy. Straight from the Lando Docs,

1
2
3
4
5
6
lando init \
--source remote \
--remote-url https://wordpress.org/latest.tar.gz \
--recipe wordpress \
--webroot wordpress \
--name my-first-wordpress-app

Of course you want something more clever than “my-first-wordpress-app” but the rest is exactly what you need. This will create you a basic .lando.yml file. This also downloads and uncompresses the latest version of WordPress. This is necessary for when we fire things up.

Now we start tinkering. You can find the finished product here, in case you are too impatient to read this.

The Basics

The basic .lando.yml that the init process created for us is just that, very basic.

1
2
3
4
name: my-first-wordpress-app
  recipe: wordpress
  config:
  webroot: wordpress

That’s it. It just defines our webroot. By the way, you CAN change that if you want but there’s very little value to be gained unless you’ve got something hard-coded to public.

Config

For our working environment, we want a little more than just the basics. We want ti decked out exactly the way we like to develop, otherwise, whats the point in using Lando to begin with? So I’m going to walk through each part of the finished .lando.yml file explaining why the lines exist and why I set them this way.

1
2
3
4
5
6
7
config:
  via: apache
  php: 7.4
  webroot: wordpress
  ssl: true
  xdebug: false
  database: mariadb

I am now and have always been an Apache fan. So I build with Apache. If you like nginx, that’s cool you can specify that. I’m developing using PHP 7.4 like every good PHP developer should. Also, I like MariaDB over MySQL. I don’t currently use xDebug on this project but I can easily turn it on and then lando rebuild to use it. Of course, everyone should use ssl. It’s not that important for development so it’s fine to leave that off if you like.

Services – Database

Services one gets a bit long so I will break it up into smaller chunks.

1
2
database:
  portforward: true

I want to be able to access my database from outside of the Lando container, so I tell it to port forward. Here’s the thing about Lando and port forwarding you can turn it on or off, but it’s best if you let Lando decide the port to forward. Let me quote the Lando manual for you.

portforward will allow you to access this service externally by assigning a port directly on your host’s localhost. Note that portforward can be set to either true or a specific port but we highly recommend you set it to true unless you have pretty good knowledge of how port assignment works or you have a very compelling reason for needing a locked down port.

portforward: true will prevent inevitable port collisions and provide greater reliability and stability across Lando apps. That said, one downside of portforward: true is that Docker will assign a different port every time you restart your application. You can read more about accessing services externally over here.

I mean seriously, I could expound on it, but why. They said it best.

Services – Appserver (Apache)

This is the big section so let’s break it up into even smaller chunks.

The first is the overrides section. This is where the magic happens for WordPress plugin developers happens. That volume mapping below where we map . to /app/wordpress/wp-content/plugins/wp_podcast_api, that is the secret sauce. See, the .lando.ymlgoes in the root of your plugin directory. This way everything is kept together.

You cd in to your plugin’s working directory (on my laptop I have a directory named Projectsand all of my projects, including all of my plugins, have their own dir there.) and then you lando start. Everything stays together, everything in a single git repo, all nice and tidy. That mapping makes this possible.

1
2
3
4
appserver:
  overrides:
    volumes:
      - '.:/app/wordpress/wp-content/plugins/wp_podcast_api'

Now when lando starts up, you get a directory in /app/wordpress/wp-content/plugins/wp_podcast_apithat is your plugin, just like you need it. Since it’s a mapping, you can still edit the files in the root of your project and your webserver sees those changes.

Literally, after lando startmy next command is code . and I have my editor up and running ready to start building some awesome.

If this were a simple plugin, that would be all I would need. However, this particular example depends on other plugins. I don’t want to have to re-install them every time I lando destroy -y && lando start so let’s use a combination of composer and wp-cli to put them in place and configure them. Of course we don’t do this manually, let let Lando deal with it. :)

Services – Appserver Build and Run

Lando’s .lando.yml has 4 sections under each service where you can define things that are to be executed inside the container.

  • build_as_root
  • build
  • run_as_root
  • run

The *_as root should be obvious from the names, in the case of this sample plugin, we don’t use them, just build and root. These execute every time you lando start from scratch, you lando rebuild, or you lando destroy -y and then lando start. If you just lando stop and then lando start again (or lando restart) these steps do not execute.

build
Build executes before the services have been started. If you need to download stuff, tinker w/config files, etc, build is your friend. (same for build_as_root)

1
2
build:
  - wp core download --force --skip-content

Now I know what you are thinking…WE JUST DOWNLOAD CORE! Yes, we did, for the init. However, if you want to start totally from scratch. (commit this to a repo, delete the dir, clone the rep anew and rebuild it with Lando) then you need this command.

run

run executes after the services have started. If you needs the services up and running to do a task, you want those tasks in run. In our case, we do several tasks here in run.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
run:
  - composer install
  - wp config create --dbname=wordpress --dbuser=wordpress --dbpass=wordpress --dbhost=database --skip-check --force
  - wp core install --url=podcast.lndo.site --title=Podcast --admin_user=admin --admin_password=admin --admin_email=cal@example.com
  - cp assets/.htaccess wordpress
  - perl -p -i -e "s/\/\* That's all, stop editing! Happy publishing. \*\//\ndefine('JWT_AUTH_SECRET_KEY', 'bite-me');\n\/\* That's all, stop editing! Happy publishing. \*\//" wordpress/wp-config.php
  - wp theme install twentytwenty --activate
  - wp post delete 1
  - wp rewrite structure '/%postname%/' --hard
  - wp plugin install classic-editor --activate
  - wp plugin install jwt-auth --activate
  - wp plugin install wordpress-importer --activate
  - wp plugin install wordpress-seo --activate
  - wp plugin install powerpress --activate
  - wp plugin activate wp_podcast_api
  - wp import assets/voicesoftheelephpant.wordpress.2020-07-23.000.xml --authors=create
  - wp user update calevans --rich_editing=false
  - wp user update admin --rich_editing=false

Everything we do falls into two buckets.

  1. composer tasks (exactly 1 of these)
  2. wp-cli tasks

Lando was built for devs. It knows we love our tools. So when you told it you wanted to start with a WordPress recipe, it figured out that you need composer, wp-cli and a few other must-have tools ready to go. So by the time you get to run, they are there and waiting on you.

In out run section we make use of both composer and wp-cli these tools. This plugin requires a couple of other plugins to be installed. We use composer and wpackagist to get those installed properly. After they are installed, we use wp-cli to activate them as well the TwentyTwenty theme.

I’ve also got a few config options in there and yes, I install classic editor and turn off rich-text editing, I’m old skool.

Side note, while I use composer because it’s the tool I am comfortable with, I could have just as easily used wp-cli to download, install, and activate the plugins I a using because they all come from the main WordPress plugin repository. If you are using custom plugins from a private Packagist.com, composer is your best route. If you know how to use both then you can figure out the best tool for the job each time.

Services – mailhog

Finally, one of my favorite things about Lando, I tell it to setup mailhog. This is a real simple email testing service. When you spin up Lando with mailhog it gives you a URL to go to that shows you any email that was sent to anyone. The mails are not sent out over the net but just captured for you to examine.

1
2
3
4
5
mailhog:
  type: mailhog
  portforward: false
  hogfrom:
    - appserver

Since WordPress can send a lot of emails and we want to make sure they are correct, mailhog is a great way to monitor them.

Bring it all together

Ok, if you’ve followed along so far and you’ve tweaked your .lando.yml file, you can now spin it up.

1
$ lando start

That’s all it takes.

If you look at the final lando.yml file, you’ll see that I use the wp importer tool to import a set of posts. You can also use lando db-importto populate your database from a .sql file. If you have awesome friends like Kim Cottrell then you’ve probably got a good companion tool like lando db-download that exports and downloads your production database for you.  Or you can do like I did for this one and just use XML. It doesn’t suck that bad. :)

When you are done, you can stop it with a simple

$ lando stop

Don’t destroy it with lando destroy -y unless you aren’t going to be back for a while. stopping and restarting is a lot faster and preserves you database.

As you can see from my sample plugin’s repo, I keep all of this, .lando.yml, composer.json, and the plugin code, in the same repo. This way, no mater what machine I am on, if Lando is installed, I can git pull and then lando start to be up and running with a full WordPress development site faster than light-speed. (Sorry, couldn’t resit) :)

 

Until next time,
I <3 |<
=C=

GIGO

Dear Reader,

Life is not toxic unless I let it be.

There is a lot of good in the world, but there’s no business model that derives profits from bring it to me.

The next time I am binging on social media, I need to remember that these platforms exist to manipulate me. Their business model is to get me fired up so I get a hit of dopamine and want more. That’s the “Garbage In”.

I want more from life than anger – the “Garbage Out”. There’s only one way to get that though, and that’s to make sure I am not constantly pouring garbage in.

Until next time,
I <3 |<

Training isn’t about finding, it’s about understanding

Dear Reader,scrabble tiles spelling out listen, understand, act

I get a lot of offers for training, mostly in the areas of  marketing and programming, two things that are passions of mine.  I pass over most of them with the attitude of “I’m not paying $99 for something I can find in a couple of online videos or blogs.

Contrast that with scuba diving – regular readers are tired of doing this – where I spend on average two weekends a month helping train people to “swim with the fishes and live to tell the tale”. Each of these people I’ve helped train have paid at a minimum $500 to be there and some paid much more.

As a programmer, I have taught myself much of what I know. A lot of it through trial and error. Yes, it’s easy to find things on the internet that will show me how to do something, but that’s not what training is for. Training helps me understand what I am doing. It’s only when I understand what I’m doing that I can apply that knowledge to other problems.

Trail and error is not only an inefficient way to learn scuba diving principals, it’s a deadly way to do it. I literally need to understand not only how but why.

I need to spend more time in professional training courses for software development. It’s a much faster way to learn how to do something and it has the added benefit of  helping me understand why I need to do it.

Until next time,
I <3 |<

 

 

Photo Credit Steven Shorrock
Listen, Understand, Act

Life doesn’t have to be great to be good

Dear Reader,

I’ll be the first to admin that I am not living the life I thought I would. Growing up I had dreams of being a millionaire and living the good life. Working when I wanted to and on what I wanted to. I’m probably not alone in this.

My life didn’t turn out that way. I’ve always been able to provide for my family, sometimes better than other times, but we never went hungry, but it’s not exactly la vida loca. :)

Instead of constantly longing for what I don’t have though, I’ve learned as I got older that I cab be happy with what I’ve got.

  • I am married to the most beautiful woman in the world.
  • I have two wonderful children that, thanks to their mother’s genes, are very smart and talented
  • I have a dog

Life is good.

Could it be better? I’m sure it could. At some point though, I began to realize that instead of spending my time constantly working and scheming to get more stuff, I could spend the time I have left on this planet just enjoying what I have.

Life is good. Yes, if I worked at it, I could make life great. But then I would have less time to enjoy it. So I’ll stick with good.

Until next time,
I<3 |<
=C=

Creativity is hard

Dear Reader,

I have a friend named Jeremy Kendall who is an outstanding photographer. One day I was taking a picture of an old clay pot I thought of Jeremy and something kinda fell into place for me.

I can look through the view finder and see the subject. I can compose a picture properly to showcase the subject. I can adjust the settings for the available light. I can frame it and I can take a picture.

Jeremy however, can look through the viewfinder and see beyond the clay pot, he can see the beauty of the shot that is waiting to be captured. Jeremy doesn’t take pictures, Jeremy makes art.

We both use a camera, we both push the shutter button, but only one of us makes art.

The next time I am tempted to dismiss someone’s creation as “simple” because it’s something I can do, I’ll think of Jeremy.

This includes the next time I look at a SaaS product and think “I’m not paying $75 a month for 250 lines of code and a cron job.” (Actually happened. Cost me 3 years of my weekends.)

“Code is Poetry” as Matt Mullenweg is fond of saying. Code is truly is art.

It doesn’t matter if it’s written in a language I approve of. It doesn’t matter if I like the platform it runs on. Art is art, even if I don’t recognize it.

I need to remember that code is art and that someone poured a lot of love into that code, even if I don’t immediately appreciate it.

Until next time
I <3 |<
=C=