• Development

    Using “php artisan serve” with xdebug and PHPStorm

    This is more of a person note for myself. This posts assumes some knowledge of php, Laravel, artisan, Homebrew, and xdebug.

    I’ve been using php artisan serve to serve Laravel applications locally. It’s quick and it works well.

    It’s probably not a great solution if you’re working with other people on a project, or if you want to implement a good CD/CI workflow. Having said that, it’s what I’m using today, so I figured I’d document how I got xdebug working with it (on my Mac).

  • Development

    Using Carbon to Generate Random Dates and Times

    There are a number of ways to generate random dates/times in PHP. Here are some examples using the Carbon library:

  • Development

    Adding a Global Function to Cypress for a Date {num} Months Ago

    I’m working on a COVID-19 booster shot registration form. On this form I ask the user when they received their last COVID-19 vaccine shot. This is three simple fields (the month values are 01, 02, 03, etc.):

    Date of last covid shot

    Each vaccine brand (Pfizer, Moderna, J&J) has different eligibility criteria as it relates to the time since the individual received their original vaccine series. Pfizer, for example, is 6 months. My form needs to reject a user who says they received Pfizer less than 6 months ago.

    Using Cypress I wanted some test coverage of this functionality. Original I had hard-coded a date value of 5 months ago:

    As you can imagine this would work well for a while. Fast-forward a few months from now… the time ago will eventually be more than 6 months ago. I don’t want to have to keep updating this code with a recent date. The obvious solution is to dynamically choose the dates based on the current date. A global helper function would be ideal as I need to use this in many tests. Here’s a simple solution:

    Step 1: Define the Function in index.js

    The support/index.js file is loaded before each Cypress run. Creating a global function here is a quick way to introduce a reusable piece of code.

    There are many solutions to handle getting a date from {num} months ago. If you start looking around for “What is a month, really?” you will find many different opinions. The code I’m using above responds like most humans do: give the same day of the month from {num} months ago. With a {num} of 5 (five months ago), on October 25, 2021 the result would be May 25, 2021.

    WARNING: Please note the following:

    Step 2: Use the Function

     

  • Development,  Tech Tips

    PERT Estimates – Using Three-Point Estimation to Improve Accuracy

    Background & Method

    I’ve long been a promoter of using three-point estimates for improved accuracy when estimating tasks.

    When I estimate how long a task will take to execute, I imagine three separate circumstances in an effort to improve the accuracy of my estimate:

    1. Optimistic: How quickly could I get this task done if everything went perfectly. What if I realize some unexpected “wins” that get me to the finish line faster?
    2. Realistic: When I think about my performance executing similar tasks in the past, what’s my best guess at an average amount of time needed?
    3. Pessimistic: Sometimes nothing seems to go well. If things do not come together as expected, what’s the longest (within reason) you can imagine this task taking?

    If I’ve truly thought about each of these, I can use them as variables in a simple formula, and can get a more accurate time estimate as a result.

    This formula, (Best+(Real*2)+(Worst*3))/6 , puts more emphasis on the worst-case scenario (#3 above) as things seem to always take longer than expected to execute. Sometimes I use two versions of the formula (one that puts even more emphasis on pessimistic).

    One more thing worth noting: You’ll see I used “I” and “my” a lot above; I do not recommend estimating tasks you are not executing, personally. I recommend having the responsible party estimate the task whenever possible. Explain the PERT process to them.

    Usage

    While this formula is simple, and adjustable (alter the weights as you see fit), it would take some time to manually calculate the result for each task.

    I utilize a few tools to generate PERT estimates at the speed of thought.

    First, I’ve written an Alfred plugin to make the process simple. This is my most used method of generating PERT estimates. Here’s a screenshot to give you a taste:

    I’ve also created many spreadsheets over the years. These often look similar to this:

    Here are a few templates you can use: XLSX | ODS

  • Development

    Simple OAuth Token Handling with Cypress

    Here’s a quick (and dirty?) way to handle requesting an access token and using it in a subsequent request.

    You should probably pull the client_secret from an environment variable (not shown below).

    commands.js

     

    some-tests.spec.js

     

  • Development

    Remote PHP Debugging with Xdebug + PHPStorm or VSCode on Cloudways

    Here’s a quick breakdown of the steps required to debug a PHP site on a remote Cloudways server.

    Step 1:

    Enable xdebug for the whole Cloudways server:

    Server » Settings & Packages » Advanced » XDEBUG: Enabled

    Step 2:

    For the specific application in Cloudways, add some PHP settings:

    Application » Application Settings » PHP FPM Settings:

    Step 3: Setup SSH config for an ssh tunnel to the server:

    1. Edit ~/.ssh/config

    Step 4 (PHPStorm):

    Configure PHPStorm Preferences

    1. PHP » Debug » Xdebug » Debug port = 9003
    2. PHP » Debug » XdebugCheck all four boxes:
      • Can accept external connections
      • Resolve breakpoint if it’s not available on the current line
      • Force break at first line when no path mapping specified
      • Force break at first line when a script is outside the project
    3. You shouldn’t need to configure servers, PHP cli, deployment locations, or anything similar…

    Try It

    1. Start an SSH tunnel: ssh mhd1_xdebug
    2. Drop a breakpoint in index.php (or whatever will surely execute)
    3. Visit the site, then wait for PHPStorm to prompt you for which file to connect to. Choose accordingly. It should connect and pause. You only have to do this once; it’ll remember the settings and path mapping in PHP » Servers.

    Step 4 (VSCode)

    Configure VSCode Preferences

    1. Install https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug
    2. Click the Run and Debug sidebar icon (⌘⇧D)
    3. SSH into the server to find the exact path to the root of the site (which should match the root of your repo locally, your workspace folder)
    4. Add new configuration (you can disable the log option if things work well right away)

    Try it

    1. Start an SSH tunnel: ssh mhd1_xdebug
    2. Click Listen for Xdebug in the Run and Debug screen
    3. Drop a breakpoint in index.php (or whatever will surely execute)
    4. Visit the site
  • Development

    Setting a Specific External Port for MySQL in Lando

    Lando sets an external MySQL port dynamically by default. This means every time you (re)start a lando app it gets (potentially) a different external MySQL port. This is an annoyance as you have to change your port in TablePlus, SequelPro, or whatever MySQL GUI you’re using.

    There is a simple fix: use a local lando file to override the forwarded MySQL port. These steps assume you already have a working .lando.yml file.

    Step 1: Create a local lando file: .lando.local.yml

    Step 2: Run lando rebuild if you’ve already started the site in the past

    Step 3: Verify the settings by running lando info

    Step 4: Add .lando.local.yml to your .gitignore  file

    This site will always get the port you specified if it’s available when you start the app. You can use this port in your MySQL gui, scripts, etc.

  • Development

    Setting a LIMIT on an UPDATE query in Drupal 8 / 9

    Here’s a quick tip regarding Drupal database manipulation. Specifically, I needed a way to flip a boolean value (from 1 to 0) on the first N rows in a database table that matched a specific set of conditions.

    The Problem

    It seems you cannot enforce a LIMIT on an UPDATE query using a static query in Drupal.

    Here’s what I was trying to do:

    This throws an error. I believe the issue is caused by the variable substitution wrapping the limit value in quotes. The error message starts with:

    If you run this query manually in MySQL, it works fine with LIMIT 10 but doesn’t work with LIMIT '10' (it throws the same error shown above).

    The Solution

    Given dynamic queries are favored over static queries, and that this static query doesn’t work, I ended up getting to the finish line with two dynamic queries.

    I have tested the solution on 10,000 records and it took < 6 seconds to process the entire request on my local dev lando site.

    “code” is a unique identifier column in the database table.

  • Development

    Missing Titles on Drupal 8 ECK Content Listings

    UPDATE: I discovered the solution below was only working when the Title base field was enabled for the entity type. If you enable the title field on the entity type it works, but then the title is required to save a new record. I will look for an alternative solution to avoid using the Title field altogether. Also, I’ll look into https://www.drupal.org/project/eck/issues/2785297.


    I’ve recently run into an issue with an ECK-created custom entity type. There was no reason for me to use the “Title” field on this particular entity type. Unfortunately, when you don’t have a title field several of the built-in entity listings and displays seem broken. Here’s what the Content List screen looks like, for example:

  • Development

    Adding Custom Fields to the Field Edit Screen in Drupal 8 / 9

    Introduction

    I’m building an API with Drupal 8/9. There is additional information I want to track against each field instance in each content type (or custom entity type) in the system. This information includes where the field’s data comes from, who is responsible for maintaining it, etc. This is useful for the site administrator and developers to track where things are coming from. Drupal’s Third Party Settings functionality makes this easy.

    Result

    This is what you see when you edit the First name field:

    When you click Save settings the data is saved to this specific field’s instance configuration (meaning if you reuse this field you can fill the info out differently per instance).

    Solution

    There are two ways to achieve this. In both cases, we use hook_form_FORM_ID_alter() to introduce the new fields. I am doing this within a custom module called pdms. The code lives in pdms.module.

    Method #1:

    With this method, the system automatically handles storing the value into the field configuration’s third party settings.

    This happens because we use the  third_party_settings  key.

    The issue I had with this approach is that I could not get it to work if I put the fields in a fieldset (as shown in the screenshot).

    I think it’d be a matter of getting $entity->getThirdPartySetting()  to look into the fieldset somehow. If you know how to do this, please let me know!

    Method #2:

    This method gives us more control over what happens when the form is submitted.

    Because we have more control we’re able to traverse the fieldset easily.

    You can see, too, how I unset the third party setting if the value is empty. I am not sure if I’ll keep this in place; we’ll see if it causes any issues when I attempt to build a report showing all of the fields and their info.