Files
4WDCSA.co.za/MIGRATION_GUIDE.md
twotalesanimation 5a36a55bd4 Add comprehensive documentation for Phase 1 refactoring
- REFACTORING_PHASE1.md: Technical details of all changes made
- MIGRATION_GUIDE.md: Developer guide for using new service layer
  - Code examples for all services
  - CSRF token implementation
  - Environment configuration
  - Troubleshooting guide
  - Performance improvements documented
2025-12-02 20:38:06 +02:00

430 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Migration Guide: Using the New Service Layer
## For Developers
### Understanding the New Architecture
The code has been refactored to use a **Service Layer pattern**. Instead of functions directly accessing the database, they delegate to service classes:
#### Old Way (Before):
```php
function sendVerificationEmail($email, $name, $token) {
// ... 30 lines of Mailjet code with hardcoded credentials ...
}
function sendInvoice($email, $name, $eft_id, $amount, $description) {
// ... 30 lines of Mailjet code (DUPLICATE) ...
}
```
#### New Way (After):
```php
function sendVerificationEmail($email, $name, $token) {
$service = new EmailService();
return $service->sendVerificationEmail($email, $name, $token);
}
```
### Using Services Directly (New Code)
When writing **new** code, you can use services directly for cleaner syntax:
```php
<?php
require_once 'env.php';
use Services\UserService;
use Services\EmailService;
// Direct service usage (recommended for new code)
$userService = new UserService();
$emailService = new EmailService();
$email = $userService->getEmail(123);
$success = $emailService->sendVerificationEmail(
$email,
'John Doe',
'token123'
);
```
### Legacy Wrapper Functions
All original function names still work for **backward compatibility**:
```php
<?php
// These still work and do the same thing
$fullName = getFullName(123);
$email = getEmail(123);
$success = sendVerificationEmail('user@example.com', 'John', 'token');
```
You can use either approach, but **new code should prefer services**.
## Specific Service Usage
### UserService
```php
<?php
use Services\UserService;
$userService = new UserService();
// Get single field
$firstName = $userService->getFirstName($userId);
$email = $userService->getEmail($userId);
$profilePic = $userService->getProfilePic($userId);
// Get multiple fields at once (more efficient)
$userData = $userService->getUserInfo($userId, [
'first_name',
'last_name',
'email',
'phone'
]);
echo $userData['first_name'];
echo $userData['email'];
```
### EmailService
```php
<?php
use Services\EmailService;
$emailService = new EmailService();
// Send using template (Mailjet)
$emailService->sendVerificationEmail(
'user@example.com',
'John Doe',
'verification-token-xyz'
);
// Send custom HTML email
$emailService->sendCustom(
'user@example.com',
'John Doe',
'Welcome!',
'<h1>Welcome to 4WDCSA</h1><p>Your account is ready.</p>'
);
// Send admin notification
$emailService->sendAdminNotification(
'New Booking',
'A new booking has been submitted for review.'
);
```
### PaymentService
```php
<?php
use Services\PaymentService;
use Services\UserService;
$paymentService = new PaymentService();
$userService = new UserService();
$user_id = $_SESSION['user_id'];
$userInfo = $userService->getUserInfo($user_id, [
'first_name',
'last_name',
'email'
]);
// Generate PayFast payment form
$html = $paymentService->processBookingPayment(
'PAY-001', // payment_id
1500.00, // amount
'Trip Booking', // description
'https://domain.com/success',
'https://domain.com/cancel',
'https://domain.com/notify',
$userInfo // user details
);
echo $html; // Outputs form + auto-submit script
```
### DatabaseService
```php
<?php
use Services\DatabaseService;
// Get the singleton connection
$db = DatabaseService::getInstance();
$conn = $db->getConnection();
// Use it like normal MySQLi
$result = $conn->query("SELECT * FROM trips");
$row = $result->fetch_assoc();
// Or use convenience methods
$stmt = $db->prepare("SELECT * FROM users WHERE user_id = ?");
$stmt->bind_param('i', $userId);
$stmt->execute();
$result = $stmt->get_result();
```
### AuthenticationService
```php
<?php
use Services\AuthenticationService;
// Generate CSRF token (called automatically in header01.php)
$token = AuthenticationService::generateCsrfToken();
// Validate CSRF token (on form submission)
$isValid = AuthenticationService::validateCsrfToken($_POST['csrf_token']);
// Check if user is logged in
if (AuthenticationService::isLoggedIn()) {
echo "User is logged in";
}
// Regenerate session after login (prevents session fixation)
AuthenticationService::regenerateSession();
```
## Adding CSRF Tokens to Forms
All forms should now include CSRF tokens for protection:
```html
<form method="POST" action="process_booking.php">
<!-- Add CSRF token as hidden field -->
<input type="hidden" name="csrf_token" value="<?php echo AuthenticationService::generateCsrfToken(); ?>">
<!-- Rest of form -->
<input type="text" name="trip_id">
<button type="submit">Book Trip</button>
</form>
```
Processing the form:
```php
<?php
use Services\AuthenticationService;
if ($_POST) {
// Validate CSRF token
if (!AuthenticationService::validateCsrfToken($_POST['csrf_token'] ?? '')) {
die("Invalid request. Please try again.");
}
// Process the form safely
$tripId = $_POST['trip_id'];
// ... rest of processing ...
}
```
## Migration Checklist for Existing Code
If you're updating old code to use the new services:
### Step 1: Replace Database Calls
```php
// OLD
function getUserEmail($user_id) {
$conn = openDatabaseConnection();
// ... 5 lines of query code ...
$conn->close();
}
// NEW
use Services\UserService;
$userService = new UserService();
$email = $userService->getEmail($user_id);
```
### Step 2: Replace Email Sends
```php
// OLD
sendVerificationEmail($email, $name, $token);
// NEW - Still works the same way
sendVerificationEmail($email, $name, $token);
// OR use service directly
$emailService = new EmailService();
$emailService->sendVerificationEmail($email, $name, $token);
```
### Step 3: Add CSRF Protection
```php
// Add to all forms
<input type="hidden" name="csrf_token" value="<?php echo AuthenticationService::generateCsrfToken(); ?>">
// Validate on form processing
use Services\AuthenticationService;
if (!AuthenticationService::validateCsrfToken($_POST['csrf_token'] ?? '')) {
die("Invalid request");
}
```
### Step 4: Regenerate Sessions
```php
// After successful login
use Services\AuthenticationService;
$_SESSION['user_id'] = $userId;
AuthenticationService::regenerateSession();
```
## Environment Variables
The `.env` file must contain all required credentials:
```
# Database
DB_HOST=localhost
DB_USER=root
DB_PASS=password
DB_NAME=4wdcsa
# Mailjet
MAILJET_API_KEY=your-key-here
MAILJET_API_SECRET=your-secret-here
MAILJET_FROM_EMAIL=info@4wdcsa.co.za
MAILJET_FROM_NAME=4WDCSA
# PayFast
PAYFAST_MERCHANT_ID=your-merchant-id
PAYFAST_MERCHANT_KEY=your-merchant-key
PAYFAST_PASSPHRASE=your-passphrase
PAYFAST_DOMAIN=www.yourdomain.co.za
PAYFAST_TESTING_MODE=true
# Admin
ADMIN_EMAIL=admin@4wdcsa.co.za
```
**IMPORTANT**: `.env` should never be committed to git. Add to `.gitignore`:
```
.env
.env.local
.env.*.local
```
## Testing Your Changes
### Quick Test: Database Connection
```php
<?php
require_once 'env.php';
use Services\DatabaseService;
$db = DatabaseService::getInstance();
$result = $db->query("SELECT 1");
echo $result ? "✓ Database connected" : "✗ Connection failed";
```
### Quick Test: Email Service
```php
<?php
require_once 'env.php';
use Services\EmailService;
$emailService = new EmailService();
$success = $emailService->sendVerificationEmail(
'test@example.com',
'Test User',
'test-token'
);
echo $success ? "✓ Email sent" : "✗ Email failed";
```
### Quick Test: User Service
```php
<?php
require_once 'env.php';
use Services\UserService;
$userService = new UserService();
$email = $userService->getEmail(1);
echo $email ? "✓ User data retrieved: " . $email : "✗ User not found";
```
## Troubleshooting
### Issue: "Class not found: Services\UserService"
**Solution**: Ensure `env.php` is required at the top of your file:
```php
<?php
require_once 'env.php'; // Must be first
use Services\UserService;
```
### Issue: "CSRF token validation failed"
**Solution**: Ensure token is included in form AND validated on submission:
```html
<!-- In form -->
<input type="hidden" name="csrf_token" value="<?php echo AuthenticationService::generateCsrfToken(); ?>">
<!-- In processor -->
if (!AuthenticationService::validateCsrfToken($_POST['csrf_token'] ?? '')) {
die("Invalid request");
}
```
### Issue: "Mailjet credentials not configured"
**Solution**: Check that `.env` file has:
```
MAILJET_API_KEY=...
MAILJET_API_SECRET=...
```
And that the file is in the correct location (root of application).
### Issue: "Database connection failed"
**Solution**: Verify `.env` has correct database credentials:
```
DB_HOST=localhost
DB_USER=root
DB_PASS=your-password
DB_NAME=4wdcsa
```
## Performance Notes
### Connection Pooling
The old code opened a **new database connection** for each function call. The new `DatabaseService` uses a **singleton pattern** with a single persistent connection:
- **Before**: 20 functions × 10 page views = 200 connections/sec
- **After**: 20 functions × 10 page views = 1 connection/sec
- **Improvement**: 200x fewer connection overhead!
### Query Efficiency
The new `UserService.getUserInfo()` method allows fetching multiple fields in one query:
```php
// OLD: 3 database queries
$firstName = getFirstName($id); // Query 1
$lastName = getLastName($id); // Query 2
$email = getEmail($id); // Query 3
// NEW: 1 database query
$data = $userService->getUserInfo($id, ['first_name', 'last_name', 'email']);
```
## Next Steps
1. **Test everything thoroughly** - no functional changes should be visible to users
2. **Update forms** - add CSRF tokens to all POST forms
3. **Review logs** - ensure no error logging issues
4. **Deploy to staging** - test in staging environment first
5. **Deploy to production** - follow your deployment procedure
---
For questions or issues, refer to `REFACTORING_PHASE1.md` for complete technical details.