Developer Hub
Build features, contribute code, and customize YMCA Website Services.
Welcome, Developers! ๐ป
Tools, documentation, and resources for building with YMCA Website Services.
New here? Start with our Getting Started Guide for Developers.
Getting Started for Developers
Set up your development environment, understand contribution workflows, and submit your first pull request.
You'll learn:- Local development setup (DDEV)
- Git workflow and branching strategy
- Coding standards and best practices
- Submitting and reviewing pull requests
Tech Stack
Understand YMCA Website Services architecture: Drupal 11, Composer workflows, Docker environments, and development tools.
You'll learn:- Drupal 11 core and modules
- Composer dependency management
- Local development setup (DDEV)
- CI/CD pipeline
Quick Reference
Drush commands, Git workflows, debugging techniques, and developer shortcuts. Essential commands at your fingertips.
You'll learn:- Common Drush commands
- Configuration import/export
- Cache clearing and rebuilding
- Database updates
Troubleshooting
Debug WSOD, permission errors, Composer conflicts, and performance issues. Comprehensive error solutions.
You'll learn:- Debugging White Screen of Death
- Fixing Composer dependencies
- Resolving permission errors
- Performance optimization
Contributing
Submit pull requests, follow code standards, and join the YMCA Website Services community. Your contributions matter!
You'll learn:- Contribution workflow
- Code review standards
- Pull request guidelines
- Community communication
1 - Getting Started for Developers
Set up your local environment, contribute code, and build custom features for YMCA Website Services.
Welcome! This guide will walk you through setting up a local development environment and making your first contribution to YMCA Website Services.
What You’ll Learn
By the end of this guide, you’ll be able to:
- โ
Set up a local development environment with DDEV
- โ
Clone and install YMCA Website Services locally
- โ
Understand the codebase structure and architecture
- โ
Create a custom module or theme modification
- โ
Submit your first pull request
Estimated time: 60-90 minutes
Step 1: Set Up Your Local Development Environment
Install DDEV (Recommended) โญ
Recommended: DDEV is the official local development tool for Drupal since June 2024. It provides a consistent, containerized environment that works across all platforms.
Why DDEV?
- Official Drupal recommendation
- Cross-platform (Mac, Windows, Linux)
- Pre-configured for Drupal
- Includes database, web server, PHP, and tooling
- Matches production environment
Installation Steps
macOS:
# Install using Homebrew
brew install ddev/ddev/ddev
# Start Docker Desktop (required)
# Download from https://www.docker.com/products/docker-desktop
Windows:
# Install using Chocolatey
choco install ddev
# Or download installer from:
# https://github.com/ddev/ddev/releases
Linux:
# Install script
curl -fsSL https://ddev.com/install.sh | bash
Verify Installation
# Check DDEV version
ddev version
# Expected output: ddev version v1.23.0 or higher
Step 2: Clone and Install YMCA Website Services
Choose Your Starting Point
For Contributing to Core:
# 1. Fork the repository on GitHub
# Visit: https://github.com/YCloudYUSA/yusaopeny
# 2. Clone YOUR fork
git clone git@github.com:YOUR_USERNAME/yusaopeny.git ymca-dev
cd ymca-dev
# 3. Add upstream remote
git remote add upstream git@github.com:YCloudYUSA/yusaopeny.git
# 4. Create a new branch
git checkout -b feature/my-contribution
For Custom Development:
# 1. Create a new project
composer create-project ycloudyusa/yusaopeny-project ymca-dev --no-interaction
cd ymca-dev
Install with DDEV
# 1. Configure DDEV
ddev config --project-type=drupal --docroot=web --create-docroot
# 2. Start DDEV
ddev start
# 3. Install Drupal
ddev drush site:install openy \
--account-name=admin \
--account-pass=admin \
--site-name='YMCA Dev'
# 4. Get login link
ddev drush uli
๐ Success! Your local YMCA Website Services site is running at the URL shown by ddev describe.
Essential DDEV Commands
# Start environment
ddev start
# Stop environment
ddev stop
# Run Drush commands
ddev drush cr
# Run Composer commands
ddev composer require drupal/module_name
# SSH into container
ddev ssh
# View site URL and credentials
ddev describe
# Import database
ddev import-db --file=dump.sql.gz
# Export database
ddev export-db --file=dump.sql.gz
Step 3: Understand the Codebase
Project Structure
yusaopeny/
โโโ web/ # Drupal root
โ โโโ modules/
โ โ โโโ contrib/ # Downloaded modules
โ โ โโโ custom/ # Your custom modules
โ โโโ themes/
โ โ โโโ contrib/ # Downloaded themes
โ โ โโโ custom/ # Your custom themes
โ โโโ profiles/
โ โ โโโ openy/ # YMCA installation profile
โ โโโ sites/default/ # Site-specific files
โโโ config/ # Configuration management
โ โโโ sync/ # Exported configuration
โโโ vendor/ # Composer dependencies
โโโ composer.json # Project dependencies
โโโ .ddev/ # DDEV configuration
Key Directories
| Directory | Purpose |
|---|
web/modules/custom/ | Custom modules for YMCA features |
web/themes/custom/ | Custom themes (Carnation) |
config/sync/ | Configuration files (version controlled) |
web/profiles/openy/ | Installation profile and defaults |
YMCA-Specific Modules
Familiarize yourself with these core YMCA modules:
- openy_activity_finder - Program search integration (Daxko, ActiveNet, Personify)
- openy_schedules - Class schedule displays
- openy_loc_branch - Branch/location functionality
- openy_node_ - Content type modules (landing_page, event, etc.)
- openy_prgf_ - Paragraphs components (legacy)
- lb_ - Layout Builder components (modern)
Step 4: Create Your First Contribution
Find a Good First Issue
Pro Tip: Start with issues labeled
"good first issue" to get familiar with the contribution process.
Read the Contribution Guidelines โญ
Before contributing, review:
โ Contribution Guidelines - Required reading
Key points:
- Code of Conduct expectations
- Branching strategy (feature branches from
main) - Commit message conventions
- Pull request process
- Code review expectations
Create a Custom Module Example
Let’s create a simple “Hello YMCA” module to understand the structure:
# 1. Create module directory
mkdir -p web/modules/custom/hello_ymca
# 2. Create .info.yml file
cat > web/modules/custom/hello_ymca/hello_ymca.info.yml << 'EOF'
name: Hello YMCA
type: module
description: 'A simple example module for YMCA Website Services'
package: YMCA Custom
core_version_requirement: ^10 || ^11
dependencies:
- drupal:node
EOF
# 3. Create a simple controller
mkdir -p web/modules/custom/hello_ymca/src/Controller
Controller file (web/modules/custom/hello_ymca/src/Controller/HelloController.php):
<?php
namespace Drupal\hello_ymca\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
* Returns responses for Hello YMCA routes.
*/
class HelloController extends ControllerBase {
/**
* Builds the response.
*/
public function content() {
$build['content'] = [
'#type' => 'markup',
'#markup' => $this->t('Hello from YMCA Website Services!'),
];
return $build;
}
}
Routing file (web/modules/custom/hello_ymca/hello_ymca.routing.yml):
hello_ymca.hello:
path: '/hello-ymca'
defaults:
_controller: '\Drupal\hello_ymca\Controller\HelloController::content'
_title: 'Hello YMCA'
requirements:
_permission: 'access content'
Enable and test:
# Enable module
ddev drush en hello_ymca -y
# Clear cache
ddev drush cr
# Visit /hello-ymca in your browser
Step 5: Submit Your First Pull Request
Git Workflow (Feature Branch)
# 1. Create feature branch from main
git checkout main
git pull upstream main
git checkout -b feature/issue-123-fix-description
# 2. Make your changes
# Edit files...
# 3. Stage and commit
git add .
git commit -m "Fix: Description of what you fixed (#123)
- Detailed explanation of changes
- Why the change was needed
- Any related issues
Fixes #123"
# 4. Push to YOUR fork
git push origin feature/issue-123-fix-description
Commit Message Best Practices
Format:
Type: Short description (#issue-number)
- Detailed bullet points
- Explaining the change
- And the reasoning
Fixes #123
Types:
Fix: - Bug fixesFeature: - New featuresRefactor: - Code improvementsDocs: - Documentation updatesTest: - Test additions/fixesChore: - Build/tooling updates
Create Pull Request
- Go to GitHub - Your fork’s repository
- Click “Compare & pull request”
- Fill in the template:
- Title: Clear, concise description
- Description: What, why, how
- Screenshots: If UI changes
- Testing steps: How reviewers can test
- Checklist: Complete all items
- Request reviews - Tag 2 reviewers (optimal per research)
- Link issue - Use “Fixes #123” in description
Pull Request Checklist
Next Steps
Now that you’ve set up your environment and made your first contribution, explore these advanced topics:
Continue Learning
Module Development
Deep dive into custom module development, services, and dependency injection.
Learn MoreTheme Development
Customize YMCA themes or create your own with Twig templates.
View GuideTesting
Write unit tests, integration tests, and end-to-end tests.
Test GuideEssential Developer Resources
Advanced Topics
Quick Reference
Common Git Commands
# Update your fork's main branch
git checkout main
git pull upstream main
git push origin main
# Create feature branch
git checkout -b feature/issue-123-description
# View status
git status
# View diff
git diff
# Stage changes
git add file.php
# Commit
git commit -m "Fix: Description (#123)"
# Push to your fork
git push origin feature/issue-123-description
# Rebase on latest main
git checkout feature/issue-123-description
git rebase main
# Squash commits (interactive)
git rebase -i HEAD~3
Common Drush Commands
# Clear cache
ddev drush cr
# Enable module
ddev drush en module_name -y
# Uninstall module
ddev drush pmu module_name -y
# Export configuration
ddev drush config:export -y
# Import configuration
ddev drush config:import -y
# Update database
ddev drush updb -y
# Generate login link
ddev drush uli
# Watch logs
ddev drush watchdog:tail
# Run cron
ddev drush cron
Common Composer Commands
# Require new module
ddev composer require drupal/module_name
# Remove module
ddev composer remove drupal/module_name
# Update all packages
ddev composer update
# Update specific package
ddev composer update drupal/core --with-all-dependencies
# Show outdated packages
ddev composer outdated
# Validate composer.json
ddev composer validate
IDE Setup (PhpStorm)
Recommended plugins:
- Drupal Symfony Bridge
- PHP Annotations
- Twig
- Composer.json support
Code style:
- Settings โ PHP โ Code Sniffer
- Configuration:
/path/to/vendor/bin/phpcs - Coding standard: Drupal
Drupal settings:
- Settings โ PHP โ Drupal
- Enable Drupal integration
- Set Drupal installation path:
web/
Code Quality and Standards
Drupal Coding Standards
All contributions must follow Drupal coding standards:
# Install PHP_CodeSniffer
ddev composer require --dev drupal/coder
ddev composer require --dev dealerdirect/phpcodesniffer-composer-installer
# Check your code
ddev exec phpcs --standard=Drupal web/modules/custom/your_module/
# Auto-fix issues
ddev exec phpcbf --standard=Drupal web/modules/custom/your_module/
Modern Drupal Best Practices
โ
DO:
- Use dependency injection for services
- Use Events and Subscribers (not hooks when possible)
- Type-hint function parameters
- Document with PHPDoc blocks
- Use configuration entities for exportable config
- Write tests for new functionality
โ DON’T:
- Use
drupal_set_message() (use Messenger service) - Use
db_query() (use Database API) - Use
l() or url() (use Url and Link classes) - Store data in variables (use State API or Config)
- Use
global $user (inject current_user service)
Security Best Practices
- โ
Always sanitize user input
- โ
Use
checkPlain(), Xss::filter(), or render arrays - โ
Check permissions before granting access
- โ
Use CSRF tokens for state-changing operations
- โ
Validate and sanitize database queries
- โ
Follow OWASP top 10 guidelines
Need Help?
Troubleshooting
- DDEV not starting? Check Docker is running (
docker ps) - Composer memory errors? Increase PHP memory:
ddev config --php-memory-limit=4G - Git conflicts? Rebase on latest main and resolve conflicts
- Code style issues? Run
phpcs and phpcbf to auto-fix
Get Support
Code Review Process
What to expect:
- Automated checks - Tests run automatically (5-10 minutes)
- Peer review - 2 reviewers will check your code (2-4 days typical)
- Feedback - You may be asked to make changes
- Approval - Once approved by 2+ reviewers
- Merge - Maintainer merges to main branch
How to get faster reviews:
- Keep PRs small and focused (< 400 lines ideal)
- Write clear commit messages
- Add tests for new functionality
- Respond promptly to feedback
- Test thoroughly before submitting
Best Practices
Git Workflow
- โ
Always branch from latest
main - โ
One feature/fix per branch
- โ
Keep commits focused and atomic
- โ
Write descriptive commit messages
- โ
Rebase on main before submitting PR
- โ
Keep PRs small (easier to review)
Development Workflow
- โ
Use DDEV for consistent environment
- โ
Export configuration after changes (
drush cex) - โ
Never commit
sites/default/files/ - โ
Test on multiple browsers
- โ
Check accessibility with screen readers
- โ
Test with real content
Code Organization
- โ
Keep modules small and focused
- โ
Use services for reusable logic
- โ
Leverage existing Drupal APIs
- โ
Follow Drupal file structure conventions
- โ
Document complex logic with comments
- โ
Write self-documenting code (clear naming)
Testing
- โ
Write tests for new features
- โ
Run tests locally before pushing
- โ
Test edge cases and error conditions
- โ
Test with JavaScript disabled
- โ
Test responsive layouts
- โ
Test with different user roles
Code of Conduct
YMCA Website Services follows the
Drupal Code of Conduct. All contributors must:
- โ
Be respectful and inclusive
- โ
Welcome newcomers
- โ
Assume good faith
- โ
Give constructive feedback
- โ
Focus on what’s best for the community
Getting Involved
Ways to contribute:
- ๐ Report bugs
- ๐ก Suggest features
- ๐ Improve documentation
- ๐ Review pull requests
- ๐ฌ Help others in Slack
- ๐ Share knowledge in blog posts
- ๐ค Present at community calls
Ready to code? Join us on
Slack, grab a
good first issue, and start contributing! ๐
2 - Release 11.3.1.0 for Developers
Technical details and breaking changes in YUSA Open Y 11.3.1.0
โ ๏ธ Breaking Changes - Action Required
1. Entity Browser Removal
// REMOVED - No longer available
use Drupal\entity_browser\...
// MIGRATE TO - Core media library
use Drupal\media_library\...
Impact:
- 34 modules migrated automatically
- Custom code using entity_browser WILL BREAK
- Form alterations referencing
entity_browser_entity_reference need updates
Migration pattern:
// Before
$form['field_media']['widget']['#type'] = 'entity_browser_entity_reference';
// After
$form['field_media']['widget']['#type'] = 'media_library_widget';
Required actions:
- Search codebase:
grep -r "entity_browser" custom/ - Update form displays programmatically altering media fields
- Remove entity_browser service dependencies
- Update tests mocking entity_browser interactions
2. PHP 8.4/8.5 Compatibility
Deprecated patterns eliminated (~256K warnings/day fixed):
// DEPRECATED - Will error in PHP 8.4+
function foo($bar = null, $required) { }
Request::get('param');
// CORRECT - PHP 8.4/8.5 compatible
function foo($required, $bar = null) { }
$request->query->get('param');
44 patterns fixed across 6 modules:
- Nullable parameter ordering
- Deprecated
Request static methods - Type hint compliance
- Return type declarations
Scan custom modules:
# Find deprecated patterns
vendor/bin/phpstan analyze custom/ --level=8
# Find Request static calls
grep -r "Request::" custom/
3. Google Analytics Module Removed
// REMOVED
\Drupal::service('google_analytics.tracking');
// MIGRATE TO
\Drupal::service('google_tag.tag_manager');
Update custom tracking:
- Replace google_analytics hooks with google_tag events
- Migrate custom dimensions to GA4 event parameters
- Update JavaScript tracking calls
4. Drupal Core 11.3.3 API Changes
New requirements:
- Drupal 11.1.9 โ 11.3.3 (review change records)
- Updated deprecation notices
- New APIs for Layout Builder, Media, Form systems
Check for:
# Find deprecated core API calls
composer require phpstan/phpstan-deprecation-rules
vendor/bin/phpstan analyze
What changed:
- 50% reduction in SELECT queries for menu rendering
- Optimized tree traversal algorithm
- Better caching strategy
If you have custom menu code:
// Review custom implementations using:
\Drupal::menuTree()->load()
MenuTreeStorage queries
Menu link plugins
2. Route Rebuild Optimization
What changed:
- Prevents unnecessary route rebuilds during module installation
- Reduces memory consumption during updates
- Faster deployment times
If you implement routes:
- Test module installation performance
- Verify dynamic routes still rebuild when needed
- Check route subscriber timing
3. Breadcrumb Caching Fix
What changed:
// Before: Breadcrumbs had max-age=0
#cache: ['max-age' => 0]
// After: Proper cache tags
#cache: [
'contexts' => ['route'],
'tags' => ['route_match'],
]
If you have custom breadcrumb builders:
- Review
getCacheTags() implementation - Ensure proper cache context declaration
- Test breadcrumb invalidation
Technical implementation:
Database structure:
-- New taxonomy vocabulary
taxonomy_term_data: media_tags
taxonomy_term_field_data: name, parent, weight
taxonomy_term_hierarchy: parent relationships
API usage:
// Load media tags
$tags = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadTree('media_tags', 0, NULL, TRUE);
// Tag media entity
$media->field_media_tags->appendItem(['target_id' => $tag_id]);
Migration from media_directories:
// Automatic migration in hook_update_N()
// Custom directories may need manual migration
// Check: /admin/structure/taxonomy/manage/media_tags
Frontend rendering:
- Badge/chip UI with hierarchical display
- Autocomplete multi-select widget
- Tree structure with depth indicators
๐๏ธ Trash Module Integration
Soft-delete pattern:
// Entity deletion now routes through trash
$entity->delete(); // Soft delete (moves to trash)
// Permanent deletion (bypass trash)
$trash_manager = \Drupal::service('trash.manager');
$storage = \Drupal::entityTypeManager()->getStorage('node');
$trash_manager->executeInTrashContext('inactive', function () use ($storage, $entity) {
$storage->delete([$entity->id() => $entity]);
});
// Restore from trash
\Drupal::service('trash.manager')->restore($entity);
Hooks available:
hook_trash_entity_predelete($entity)
hook_trash_entity_delete($entity)
hook_trash_entity_restore($entity)
Configuration:
// Get trash settings
$config = \Drupal::config('trash.settings');
$retention_days = $config->get('retention_days');
๐ฆ Removed Modules - Check Dependencies
{
"removed": [
"entity_browser",
"entity_browser_entity_reference",
"entity_embed",
"inline_entity_form",
"media_directories",
"google_analytics",
"history",
"groupexpro",
"groupexpro_auth",
"groupexpro_recurring_event",
"openy_node_session"
]
}
Composer updates:
# These will be removed automatically
# Check composer.json for custom dependencies
composer why drupal/entity_browser
composer why drupal/media_directories
๐ง Development Workflow Updates
Local development:
# Prerequisites
composer require yusaopeny/yusaopeny:11.3.1.0
# Enable AVIF support (required)
# macOS:
brew install gd --with-avif
# Ubuntu:
apt-get install php-gd libavif-dev
# Verify
php -r "var_dump(gd_info());" | grep AVIF
Database updates:
drush updatedb -y
# Watch for:
# - Entity browser cleanup
# - Media tags migration
# - GroupEx Pro removal
# - Permission cleanup
Testing checklist:
# Unit tests
vendor/bin/phpunit custom/
# Check deprecations
export SYMFONY_DEPRECATIONS_HELPER=weak
vendor/bin/phpunit
# Code standards
vendor/bin/phpcs custom/ --standard=Drupal
# Static analysis
vendor/bin/phpstan analyze custom/ --level=5
๐งช Testing Custom Code
High-risk areas to test:
// Test all custom code using:
$form['field_media']
EntityBrowserWidget
entity_browser_entity_reference
MediaLibraryState
2. Tracking code
// Test all custom:
Google Analytics hooks
Custom tracking JavaScript
Analytics event handlers
// Test forms with:
hook_form_alter() on media forms
Custom entity_browser configurations
Media upload workflows
4. Entity operations
// Test deletion workflows
$entity->delete()
Entity hooks (presave, delete, update)
Batch operations deleting entities
Benchmark these metrics:
// Installation time
time composer install && drush site:install
// Memory usage during updates
drush updatedb -y --verbose
// Menu rendering queries
drush debug:event kernel.response
// Route rebuild time
drush router:rebuild
Expected improvements:
- Installation: 537s โ 180s (67% faster)
- Memory: 4.38GB โ 326MB (93% reduction)
- Menu queries: ~50% reduction
- Route rebuilds: Fewer unnecessary rebuilds
New debugging approaches:
// Debug media library
\Drupal::service('media_library.opener_resolver')->get($state);
// Debug trash operations
\Drupal::service('trash.manager')->getTrashedEntities();
// Debug google_tag
\Drupal::service('google_tag.tag_manager')->getContainer();
Logging:
// Key subsystems to monitor
'media_library', 'trash', 'google_tag', 'route', 'menu'
๐จ Common Pitfalls
1. Entity browser assumptions
// DON'T assume entity_browser exists
if (\Drupal::moduleHandler()->moduleExists('entity_browser')) {
// This will be FALSE
}
2. Static Request calls
// DON'T use static methods
$param = Request::get('id'); // Deprecated
// DO inject request_stack
$request = \Drupal::requestStack()->getCurrentRequest();
$param = $request->query->get('id');
3. Nullable parameters
// DON'T put nullable before required
function foo($optional = null, $required) { } // Error in PHP 8.4+
// DO put required parameters first
function foo($required, $optional = null) { } // Correct
๐ Developer Resources
API documentation:
- Media Library:
core/modules/media_library/media_library.api.php - Trash: Custom module, check
trash.api.php - Google Tag:
modules/contrib/google_tag/google_tag.api.php
Change records:
Testing:
- 71 PRs merged (GitHub + drupal.org)
- 30+ production sites validated
- 64 releases published across packages
Code references:
- Entity Browser migration:
entity_browser โ media_library_widget - Media Tags:
modules/custom/*/media_tags/* - Trash implementation:
modules/contrib/trash/src/