Skip to content

Instantly share code, notes, and snippets.

@doubleedesign
Last active July 5, 2025 03:44
Show Gist options
  • Save doubleedesign/d33856c7e3ce1b8f1b883f704a7a2ba8 to your computer and use it in GitHub Desktop.
Save doubleedesign/d33856c7e3ce1b8f1b883f704a7a2ba8 to your computer and use it in GitHub Desktop.
Local by Flywheel + Laravel Herd, best of both worlds!

Local by Flywheel + Laravel Herd

Best of both worlds! Use Local for pushing and pulling and Herd for local development (so you can use the Dumps window and test emails).

If you put these in your herd bin folder, you can run "local-to-herd" from anywhere to transfer the site's database and link the install to Herd's server, and "local-export-db" to copy the db back from Herd to Local.

@echo off
powershell -ExecutionPolicy Bypass -File "%~dp0local-export-db.ps1" %*
# This is a custom script that exports the database from Herd to the local.sql file where Local by Flywheel can use it.
# When placed in your user directory -> .config/herd/bin alongside a .bat file to run it,
# you can run it from anywhere using the command "local-export-db" in PowerShell.
param(
[string]$SiteName
)
# Function to check if a command exists
function Test-Command {
param([string]$Command)
$null = Get-Command $Command -ErrorAction SilentlyContinue
return $?
}
# Common variables
$username = $env:USERNAME
# Note: If these are in the system environment PATH, you can use just "mysql" and "mysqldump" instead of these full paths.
# Just check first that the aliases are resolving to the correct MySQL instances using "Get-Command mysql" and "Get-Command mysqldump".
$herdMySQLPath = "C:\Users\$username\.config\herd\bin\services\mysql\8.0.36\bin\mysql.exe"
$herdMySQLDumpPath = "C:\Users\$username\.config\herd\bin\services\mysql\8.0.36\bin\mysqldump.exe"
Write-Host "`n========== Herd to Local Database Export Script ==========" -ForegroundColor Green
Write-Host "`n Warnings:"
Write-Host "`t Ensure Herd and Local are running compatible versions of MySQL."
Write-Host "This script assumes that:"
Write-Host "`t - Herd's MySQL service is running on port 3309 [NOT THE STANDARD 3306]"
Write-Host "`t - You have set Laravel Herd to use .local as the TLD (if not, you will need to do a find and replace in the database)"
Write-Host "`t - Your Herd MySQL username is 'root' and the password is empty"
Write-Host "========================================================" -ForegroundColor Green
try {
# First, check prerequisites
$prerequisites = @(
"herd",
$herdMySQLPath,
$herdMySQLDumpPath
)
foreach ($prerequisite in $prerequisites) {
if (-not (Test-Command $prerequisite)) {
throw "Error: Required command or executable path '$prerequisite' not found."
}
}
# Get site name from the user if not provided in the initial command
if (-not $SiteName) {
$SiteName = Read-Host "Enter the site name as it appears in Local (probably snake-case)"
}
if ([string]::IsNullOrEmpty($SiteName)) {
throw "Site name cannot be empty"
}
# Check if the database exists in Herd's MySQL service
$herdDatabaseName = "$SiteName" + "_local"
$checkCommand = "SHOW DATABASES LIKE '$herdDatabaseName';"
$checkResult = & $herdMySQLPath -u root -P 3309 -e $checkCommand
if ($LASTEXITCODE -ne 0) {
throw $checkResult
}
$sqlFilePath = "C:\Users\$username\LocalSites\$SiteName\app\sql\local.sql"
# Rename the existing app/sql/local.sql file if it exists, so we don't overwrite it
if (Test-Path $sqlFilePath) {
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$backupFilePath = "$sqlFilePath.bk-$timestamp.sql"
Rename-Item -Path $sqlFilePath -NewName $backupFilePath
Write-Host "Existing Local SQL file renamed to: $backupFilePath" -ForegroundColor Yellow
}
# Export the database from Herd to the local.sql file in the same location as $sqlFilePath was before renaming
$exportResult = & $herdMySQLDumpPath -u root -P 3309 $herdDatabaseName > $sqlFilePath
if ($LASTEXITCODE -ne 0) {
throw $exportResult
}
else {
Write-Host $exportResult
Write-Host "Database exported successfully to: $sqlFilePath" -ForegroundColor Green
}
}
catch {
Write-Host "Error: $_" -ForegroundColor Red
exit 1
}
finally {
Set-Location $sitePath
}
@echo off
powershell -ExecutionPolicy Bypass -File "%~dp0local-to-herd.ps1" %*
# This is a custom script that automates the transition of a WordPress site
# from Local by Flywheel to Laravel Herd for local development.
# When placed in your user directory -> .config/herd/bin alongside a .bat file to run it,
# you can run it from anywhere using the command "local-to-herd" in PowerShell.
param(
[string]$SiteName
)
# Function to check if a command exists
function Test-Command {
param([string]$Command)
$null = Get-Command $Command -ErrorAction SilentlyContinue
return $?
}
# Common variables
$username = $env:USERNAME
# Note: If this is in the system environment PATH, you can use just "mysql" instead of this full path.
# Just check first that the alias is resolving to the correct MySQL instance using "Get-Command mysql".
$herdMySQLPath = "C:\Users\$username\.config\herd\bin\services\mysql\8.0.36\bin\mysql.exe"
Write-Host "`n=========== Local to Herd Site Setup Script ===========" -ForegroundColor Green
Write-Host "Warnings:" -ForegroundColor Yellow
Write-Host "`t - This script will close Local by Flywheel if it is running." -ForegroundColor Yellow
Write-Host "`t - If you have used this before for this site, this script will overwrite the previous version of the database in Herd's MySQL server" -ForegroundColor Yellow
Write-Host "`t - Ensure Herd and Local are running compatible versions of MySQL before proceeding, `n`t or you may have problems transferring the database back to Local `n`t and more importantly, to your live site if it is on Flywheel/WP Engine." -ForegroundColor Yellow
Write-Host "This script assumes that:"
Write-Host "`t - You have pulled down your site from Local and the database in the app/sql directory is the one you want to use"
Write-Host "`t Note: To ensure this is the latest version, stop your site in Local before proceeding"
Write-Host "`t - Your Local sites are located in C:\Users\$username\LocalSites\"
Write-Host "`t Note: This script will not move any site files. `n`t This script configures Herd to serve your site from the same directory as Local."
Write-Host "`t - Herd's MySQL service is running on port 3309 [NOT THE STANDARD 3306]"
Write-Host "`t - You have set Laravel Herd to use .local as the TLD (if not, you will need to do a find and replace in the database)"
Write-Host "`t - Your Herd MySQL username is 'root' and the password is empty"
Write-Host "========================================================" -ForegroundColor Green
try {
# First, check prerequisites
$prerequisites = @(
"herd",
$herdMySQLPath
)
foreach ($prerequisite in $prerequisites) {
if (-not (Test-Command $prerequisite)) {
throw "Error: Required command or executable path '$prerequisite' not found."
}
}
# Make sure Local is not running
$localProcess = Get-Process -Name "Local" -ErrorAction SilentlyContinue
if ($localProcess) {
Stop-Process -Name "Local" -Force
}
# Get site name from the user if not provided in the initial command
if (-not $SiteName) {
$SiteName = Read-Host "Enter the site name as it appears in Local (probably snake-case)"
}
if ( [string]::IsNullOrEmpty($SiteName)) {
throw "Site name cannot be empty"
}
# Get Local database from app/sql
$sqlFilePath = "C:\Users\$username\LocalSites\$SiteName\app\sql\local.sql"
if (-not (Test-Path $sqlFilePath)) {
throw "SQL file does not exist: $sqlFilePath"
}
# Import it into Herd's MySQL server
$newDatabaseName = "$SiteName" + "_local"
try {
$createDbCommand = "CREATE DATABASE IF NOT EXISTS ``$newDatabaseName``;"
$result = & $herdMySQLPath -u root -P 3309 -e $createDbCommand
Write-Host "Database '$newDatabaseName' created successfully, or was already there." -ForegroundColor Green
}
catch {
throw "Error creating database: $_"
}
try {
Write-Host "Importing SQL file from Local into Herd database '$newDatabaseName'..." -ForegroundColor Cyan
$mysqlImportArgs = @("-u", "root", "-P", "3309", $newDatabaseName)
$importResult = Get-Content $sqlFilePath | & $herdMySQLPath $mysqlImportArgs
if ($LASTEXITCODE -ne 0) {
throw "Database import error: $importResult"
}
else {
Write-Host $importResult -ForegroundColor Green
}
}
catch {
throw "Error importing SQL file: $_"
}
# Construct site path
$sitePath = "C:\Users\$username\LocalSites\$SiteName\app\public"
# Check if site directory exists
if (-not (Test-Path $sitePath)) {
throw "Site directory does not exist: $sitePath"
}
# Navigate to site directory
Set-Location $sitePath
# Run herd link
$linkResult = & herd link $SiteName
if ($LASTEXITCODE -ne 0) {
throw $linkResult
}
else {
Write-Host $linkResult -ForegroundColor Green
}
# Run herd secure
$secureResult = & herd secure
if ($LASTEXITCODE -ne 0) {
Write-Warning "Herd secure command returned non-zero exit code: $secureResult"
}
else {
Write-Host $secureResult
}
# Setup for wp-config-local.php
$wpConfigPath = "C:\Users\$username\LocalSites\$SiteName\app\public"
$wpConfigFile = Join-Path $wpConfigPath "wp-config-local.php"
$getPrefixCommand = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$newDatabaseName' AND TABLE_NAME LIKE '%_postmeta'"
# Get the table prefix from the database by getting the postmeta table name
$tablePrefixResult = & $herdMySQLPath -u root -P 3309 -D $newDatabaseName -e $getPrefixCommand
if ($LASTEXITCODE -ne 0) {
throw "Error retrieving table prefix: $tablePrefixResult"
}
# Extract the table prefix from the result and prepare it for the wp-config-local.php file
$tablePrefix = $tablePrefixResult[1]
$tablePrefix = $tablePrefix -replace 'postmeta$', '' # Remove postmeta from the result
$tablePrefix = $tablePrefix.trim() # Trim any remaining whitespace
$tablePrefix = "`$table_prefix = '$tablePrefix';"
# Make a request to https://api.wordpress.org/secret-key/1.1/salt/ and put the returned keys in a variable
$saltKeysUrl = "https://api.wordpress.org/secret-key/1.1/salt/"
$saltKeys = Invoke-RestMethod -Uri $saltKeysUrl -UseBasicParsing
if (-not $saltKeys) {
throw "Failed to retrieve WordPress salt keys from $saltKeysUrl"
}
# Prepare the wp-config-local.php content
$wpConfigContent = @"
<?php
/**
* Local WordPress configuration
* Generated by Herd Setup Script
*/
// Database settings
define('DB_NAME', '$newDatabaseName');
define('DB_USER', 'root');
define('DB_PASSWORD', '');
define('DB_HOST', '127.0.0.1:3309');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
$saltKeys
// Debug settings for local development
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('WP_ENVIRONMENT_TYPE', 'local');
// Globally installed Composer packages - enables Symfony Vardumper if it's not installed in the project and you have installed it globally
// If not, composer global require symfony/var-dumper
require_once('C:\Users\$username\AppData\Roaming\Composer\vendor\autoload.php');
$tablePrefix
/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
// Additional local configuration can be added here
"@
# Write the content to wp-config-local.php
try {
Set-Content $wpConfigFile -Value $wpConfigContent -Encoding UTF8
Write-Host "wp-config-local.php created successfully at: $wpConfigFile" -ForegroundColor Green
}
catch {
throw "Error creating wp-config-local.php: $_"
}
# Add redirect at the top of the existing wp-config.php file if it isn't already there
$wpConfigFilePath = Join-Path $wpConfigPath "wp-config.php"
if (Test-Path $wpConfigFilePath) {
$redirectContent = @"
<?php
if (file_exists(__DIR__ . '/wp-config-local.php')) {
require_once __DIR__ . '/wp-config-local.php';
}
"@
try {
# Read existing content
$existingContent = Get-Content $wpConfigFilePath -Raw
# Check if the redirect already exists
if ($existingContent -match "require_once __DIR__ . '/wp-config-local.php';") {
Write-Host "Redirect already exists in wp-config.php, skipping addition." -ForegroundColor Yellow
}
else {
# If it doesn't exist, prepend the redirect content
# Remove the first <?php tag if it exists, because we're adding it back in the redirect content
if ($existingContent -match '^\s*<\?php') {
$existingContent = $existingContent -replace '^\s*<\?php', ''
}
Set-Content $wpConfigFilePath -Value ($redirectContent + $existingContent) -Encoding UTF8
Write-Host "Local config redirect added to wp-config.php successfully." -ForegroundColor Green
}
}
catch {
throw "Error adding to wp-config.php: $_"
}
}
# Summary
Write-Host ""
Write-Host "==================== Setup Complete ====================" -ForegroundColor Green
Write-Host "URL: https://$SiteName.local"
Write-Host "To export your database back to Local, run 'local-export-db'."
Write-Host "========================================================" -ForegroundColor Green
}
catch {
Write-Host "Error: $_" -ForegroundColor Red
exit 1
}
finally {
Set-Location $sitePath
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment