
I have built a complete Drupal 11.2 platform for this test but apprecaite the module was all that was required.
Here is the link to the full Drupal repository: https://github.com/zarexogre/oyw
Here is the link to just the module: https://github.com/zarexogre/oyw/tree/main/web/modules/custom/oyw_salesforce
Setting up the Drupal 11 site locally.

I have a custom built Drupal construction bash script called bedrock. It allows me to spin up a new Drupal site locally using ddev in seconds, but also has some important custom site setup features which I prefer like setting up the repo in github, adding a dotenv file for environmental variables, configuration options for local, staging and production and some custom modules/ themes installed and setup (I know I can use profiles for this but prefer to control it all via 1 simple build script).
Here is it building the platform ------>
I then installed the salesforce module, there is a detail installation process to setup the full salesforce integration with push/pull for entities etc but this seemed overkill for this module so just ran these commands.
ddev composer require drupal/salesforce
ddev drush en salesforce -yOYW Salesforce Module
What each file does
oyw_salesforce.info.yml
This just tells Drupal that my module exists and that it depends on the Salesforce module.
oyw_salesforce.services.yml
This wires all my services together:
- My custom Salesforce client
- The class that builds the dropdown values
- A custom logging channel for Salesforce activity
src/Client/OywSalesforceClient.php
This file is responsible for talking to Salesforce.
- Builds the Salesforce API URL
- Sends the API request
- Logs what endpoint was called
- Logs how many values came back
src/SalesforceFieldValues.php
This file contains the main logic:
- Checks if the current field is field_gender
- Looks in cache first
- If nothing is cached, it calls Salesforce
- Converts the response into Drupal dropdown options
- Saves the result in cache and returns it
oyw_salesforce.module
This file contains the Drupal hook that runs when Drupal needs to build a dropdown.
It simply passes control to my service class.
tests/src/Unit/*
These are my PHPUnit unit tests:
- Fake Salesforce responses
- Make sure the API path is correct
- Make sure values are converted properly
- Make sure caching works as expected

.githooks/pre-commit
#!/bin/sh
ddev phpunit web/modules/custom/oyw_salesforce/tests/src/Unit
RESULT=$?
if [ $RESULT -ne 0 ]; then
echo "PHPUnit failed. Commit aborted."
exit 1
fi
exit 0This automatically runs the tests every time I commit code.
If the tests fail, the commit is blocked. Dev need to run this for pre commit hook to work.
git config core.hooksPath .githooksExtras
deployment
since your reading this I have succsefully deployed this to my home server which is also all setup for a project, via bedrock. I have a custom github runner which I trigger via a github action script:
name: Deploy and test oyw platform.
on:
push:
branches:
- main
jobs:
deploy-runner:
runs-on: ubuntu-latest
steps:
- name: SSH into server and execute deploy runner
uses: appleboy/ssh-action@master
with:
host: drupaljedimaster.co.uk
username: ******
password: ******
port: 22
script: cd /root/Projects/github_runner && git pull origin main && ./deploy.sh oyw --skip-db
This GitHub Action automatically deploys the latest version of the project:
- Pulls the latest code from the repository
- Builds a new Docker image using a custom bulit Drupal/LEMP Dockerfile
- Tags the image with the latest version
- Pushes the image to my custom container registry
- Tells my kubernetes container cluster K3S to pull the new image
- K3S redeploys the application using the latest image
This setup ensures that every code change (merged pull request) is automatically built and deployed without any manual steps, and no down time. Pretty pointless for this module but allowed me to build this article and share it with you, and demonstatres I love devops! One slight headache with conatiner cluster deployment and Drupal is the file system, but I used the Amazon S3 module to solve this, so all files are store in S3 (as are the images on this page).
comments
I've gone well over the top with comments, usually I like to write clean code which doesnt need too many comments, I always have clean doc comments and extra comments for more complex code, but for this wanted to added tonnes so when you view the code you can see what I was going for.
logging
you didnt ask for this but after building a few of these API layers, I figured its valuable.
phpcs
I used this extension in vscode so my drupal code is always inline with Drupal coding standards
caching
always useful and pretty simple to implement so thought I'd add this in too.
vscode
I have a .vscode/tasks.json and .vscode/launch.json setup to run xdebug so I can use xdebug if I need to direct from vscode. I also have ddev running automatically when I open the project, and sass compliation running (no point right now) so any sass changes I make are auto compiled in background.
Future enhancements
- bulid custom log module with advanced logging to its own table
- configure the salesforce module to make drupal entities store in salesforce
- build automated cypress tests for frontend builds
- deploy to upsun