This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

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

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

DirectoryPurpose
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

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 fixes
  • Feature: - New features
  • Refactor: - Code improvements
  • Docs: - Documentation updates
  • Test: - Test additions/fixes
  • Chore: - Build/tooling updates

Create Pull Request

  1. Go to GitHub - Your fork’s repository
  2. Click “Compare & pull request”
  3. 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
  4. Request reviews - Tag 2 reviewers (optimal per research)
  5. Link issue - Use “Fixes #123” in description

Pull Request Checklist

  • Code follows Drupal coding standards

  • Added/updated tests if applicable

  • Updated documentation

  • No merge conflicts

  • Passes automated tests

  • Screenshots for UI changes

  • Tested on multiple browsers/devices


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 More
Theme Development

Customize YMCA themes or create your own with Twig templates.

View Guide
Testing

Write unit tests, integration tests, and end-to-end tests.

Test Guide

Essential 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:

  1. Settings โ†’ PHP โ†’ Code Sniffer
  2. Configuration: /path/to/vendor/bin/phpcs
  3. Coding standard: Drupal

Drupal settings:

  1. Settings โ†’ PHP โ†’ Drupal
  2. Enable Drupal integration
  3. 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:

  1. Automated checks - Tests run automatically (5-10 minutes)
  2. Peer review - 2 reviewers will check your code (2-4 days typical)
  3. Feedback - You may be asked to make changes
  4. Approval - Once approved by 2+ reviewers
  5. 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

Community Guidelines

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

YUSA Open Y 11.3.1.0: Breaking Changes & Performance Architecture

โš ๏ธ 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

๐Ÿš€ Performance Optimizations to Understand

1. MenuTreeStorage Query Optimization

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

๐Ÿท๏ธ New Media Tags Architecture

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:

1. Media field usage

// 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

3. Form alterations

// 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

๐Ÿ“Š Performance Profiling

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

๐Ÿ” Debugging Tools

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/