A comprehensive Laravel-based web application that automatically sends personalized birthday SMS messages with custom digital birthday cards to your contacts. Built for SITC Campus, this system features automated scheduling, contact management, activity logging, and an interactive birthday card experience.
- Features
- Demo
- Tech Stack
- Architecture
- Prerequisites
- Installation
- Configuration
- Usage
- Scheduling
- API Reference
- Database Schema
- Project Structure
- Development
- Testing
- Deployment
- Troubleshooting
- Contributing
- Security
- License
- Acknowledgments
- π€ Automated Birthday Detection - Automatically identifies and processes birthdays daily via scheduled tasks
- π± SMS Integration - Sends personalized birthday messages via HTTP SMS gateway API
- π¨ Dynamic Birthday Cards - Generates custom digital birthday cards with recipient names
- π Public Card URLs - Each contact receives a unique, secure link to view their birthday card
- π Interactive Card Experience - Animated confetti effects and downloadable cards
- β CRUD Operations - Full Create, Read, Update, Delete functionality for contacts
- π CSV Import/Export - Bulk import contacts from CSV files and export to CSV
- π Advanced Search - Search contacts by name or phone number
- π Sorting & Pagination - Sort by multiple columns with persistent query parameters
- β Bulk Actions - Select multiple contacts for batch deletion
- ποΈ Delete All - Clear entire contact database with confirmation
- π Simple Admin Authentication - Session-based login system with environment credentials
- π Activity Logging - Comprehensive logging of all system actions (SMS sent, imports, deletions, etc.)
- π Log Viewer - Paginated activity logs with detailed payload inspection
- π― Manual Triggers - Force birthday message sending for testing or special occasions
- π§ Individual Manual Send - Send birthday messages to specific contacts on-demand
- π± Responsive Design - Mobile-first UI built with Tailwind CSS 4.x
- π Clean Interface - Modern, intuitive admin dashboard
- β‘ Fast Performance - Optimized queries and asset compilation with Vite
- π Gift Reveal Animation - Engaging "tap to open" experience for birthday recipients
- πΎ Card Download - Recipients can download their personalized birthday cards
- π Duplicate Prevention - Tracks last message year to prevent duplicate sends
- π‘οΈ Secure Tokens - Random public tokens for secure, shareable card URLs
- πΌοΈ Dynamic Image Generation - Server-side image manipulation with Intervention Image
- π Smart Name Handling - Automatic name truncation and formatting for long names
- π¨ Custom Fonts - Beautiful typography using custom font files
- π SweetAlert2 Integration - Professional confirmation dialogs and notifications
The admin panel provides a comprehensive interface for managing contacts:
- Dashboard: View, search, sort, and filter all contacts
- Import: Bulk upload contacts via CSV files
- Export: Download contact database as CSV
- Manual Triggers: Send birthday messages on-demand
- Activity Logs: Monitor all system activities with detailed payloads
Recipients receive an SMS with a unique URL that leads to:
- Surprise Screen: "You have a surprise! π" with a tap-to-open button
- Card Reveal: Animated confetti celebration with personalized birthday card
- Download Option: Button to save the card image to their device
HAPPY BIRTHDAY JOHN DOE!
SITC Campus wishes you a year filled with success, knowledge and new opportunities.
Your Birthday Card: http://yourapp.com/birthday/abc123xyz
Keep learning, growing and shining bright!
- Framework: Laravel 10.x
- Language: PHP 8.1+
- Database: SQLite (easily swappable to MySQL/PostgreSQL)
- Queue System: Database driver
- Session Storage: Database driver
- Task Scheduler: Laravel Scheduler (Cron)
- CSS Framework: Tailwind CSS 4.x
- Build Tool: Vite 7.x
- JavaScript: Vanilla JS with modern ES6+ features
- Confetti Animation: canvas-confetti library
- Alerts: SweetAlert2
- Image Processing: Intervention Image 3.x (GD Driver)
- HTTP Client: Guzzle HTTP
- Date Handling: Carbon (included with Laravel)
- Development: Laravel Sail, Laravel Pint, PHPUnit
- SMS Gateway: HTTP-based SMS API (configurable)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Laravel Scheduler β
β (Runs hourly: birthday:send) β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SendBirthdayMessages Command β
β - Queries contacts with today's birthday β
β - Filters out already-sent-this-year β
β - Generates unique tokens if missing β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SmsService β
β - Sends HTTP request to SMS Gateway API β
β - Logs success/failure to ActivityLog β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Contact Receives SMS β
β - Message includes unique URL β
β - URL: /birthday/{public_token} β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BirthdayController::show β
β - Displays surprise screen with "Tap to Open" β
β - JavaScript reveals card with confetti animation β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BirthdayController::card (Image) β
β - Loads base birthday card image β
β - Overlays contact's name using custom font β
β - Returns JPEG image dynamically β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
app/
βββ Console/
β βββ Commands/
β β βββ SendBirthdayMessages.php # Birthday SMS sending command
β βββ Kernel.php # Task scheduling configuration
βββ Http/
β βββ Controllers/
β β βββ ActivityLogController.php # Log viewing
β β βββ BirthdayController.php # Public birthday card views
β β βββ ContactController.php # Contact CRUD & bulk operations
β β βββ LoginController.php # Admin authentication
β βββ Middleware/
β βββ AdminAuthMiddleware.php # Session-based auth guard
βββ Models/
β βββ ActivityLog.php # Activity logging model
β βββ Contact.php # Contact model with DOB handling
β βββ User.php # Standard Laravel user model
βββ Services/
βββ LoggerService.php # Centralized activity logging
βββ SmsService.php # SMS gateway integration
database/
βββ migrations/
β βββ 2025_12_15_080530_create_contacts_table.php
β βββ 2025_12_15_084958_add_public_token_to_contacts_table.php
β βββ 2025_12_18_063949_create_activity_logs_table.php
βββ seeders/
βββ DatabaseSeeder.php
resources/
βββ css/
β βββ app.css # Tailwind CSS imports
βββ js/
β βββ app.js # Main JavaScript entry
β βββ bootstrap.js # Axios configuration
βββ views/
βββ auth/
β βββ login.blade.php # Admin login page
βββ birthday/
β βββ show.blade.php # Public birthday card page
βββ contacts/
β βββ index.blade.php # Contact listing & management
β βββ create.blade.php # Add new contact form
β βββ edit.blade.php # Edit contact form
βββ logs/
β βββ index.blade.php # Activity log viewer
βββ layouts/
βββ app.blade.php # Base layout template
public/
βββ fonts/
β βββ EmilysCandy-Regular.ttf # Custom font for birthday cards
βββ images/
βββ SITC Birthday Card.jpg # Base birthday card template
routes/
βββ web.php # Web routes (admin & public)
βββ api.php # API routes (unused)
βββ console.php # Console routes/closures
Before you begin, ensure you have met the following requirements:
- PHP: >= 8.1 with extensions:
- OpenSSL
- PDO
- Mbstring
- Tokenizer
- XML
- Ctype
- JSON
- BCMath
- Fileinfo
- GD (for image manipulation)
- Composer: >= 2.0
- Node.js: >= 18.x
- npm: >= 9.x or Yarn: >= 1.22
- SQLite: >= 3.8.8 (or MySQL/PostgreSQL for production)
- Git: For version control
- PHP 8.1/8.2/8.3: Any version >= 8.1 is supported
- Laravel Sail: For Docker-based development environment
- Memory: Minimum 512MB RAM
- Disk Space: Minimum 500MB free space
- Operating System: Linux, macOS, or Windows 10+
# Clone the repository
git clone https://github.com/codezelat/laravel-automated-birthday-message-sender.git
cd laravel-automated-birthday-message-sender
# Run the automated setup script
composer run setupThis will:
- Install PHP dependencies
- Copy
.env.exampleto.env - Generate application key
- Run database migrations
- Install Node.js dependencies
- Build frontend assets
If you prefer manual setup or the automated script fails:
git clone https://github.com/codezelat/laravel-automated-birthday-message-sender.git
cd laravel-automated-birthday-message-sendercomposer install# Copy the example environment file
cp .env.example .env
# Generate application key
php artisan key:generate# Create SQLite database file (if using SQLite)
touch database/database.sqlite
# Run migrations
php artisan migratenpm install# For development
npm run dev
# For production
npm run build# Ensure storage and cache directories are writable
chmod -R 775 storage bootstrap/cacheEdit your .env file with the following configurations:
APP_NAME="Birthday Automator"
APP_ENV=local # Change to 'production' for live
APP_KEY=base64:... # Generated automatically
APP_DEBUG=true # Set to false in production
APP_URL=http://localhost:8000 # Your application URLADMIN_USERNAME=admin # Change to secure username
ADMIN_PASSWORD=secure_password # Change to strong passwordFor SQLite (Default):
DB_CONNECTION=sqlite
# DB_DATABASE will default to database/database.sqliteFor MySQL:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=birthday_sender
DB_USERNAME=root
DB_PASSWORD=your_passwordFor PostgreSQL:
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=birthday_sender
DB_USERNAME=postgres
DB_PASSWORD=your_passwordSMS_USERNAME=your_sms_gateway_username
SMS_PASSWORD=your_sms_gateway_password
SMS_SOURCE=SITC # Sender ID (alphanumeric)
SMS_API_URL=https://your-sms-gateway-api-url.com/api/sendQUEUE_CONNECTION=database # Use 'redis' for better performance
SESSION_DRIVER=database # Store sessions in database
SESSION_LIFETIME=120 # Session timeout in minutesMAIL_MAILER=log # Use 'smtp' for actual email sending
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"This application uses a generic HTTP-based SMS gateway. Configure your SMS provider settings in the SmsService class if needed.
username: API usernamepassword: API passwordsrc: Sender ID/sourcedst: Destination phone numbermsg: Message contentdr: Delivery report (1 = enabled)
To integrate a different SMS provider, modify app/Services/SmsService.php:
// Example: Twilio Integration
public function send($phone, $message)
{
$response = Http::withBasicAuth($this->username, $this->password)
->asForm()
->post('https://api.twilio.com/2010-04-01/Accounts/YOUR_SID/Messages.json', [
'From' => $this->source,
'To' => $phone,
'Body' => $message,
]);
// ... handle response
}Replace [public/images/SITC Birthday Card.jpg](public/images/SITC Birthday Card.jpg) with your custom birthday card image.
Replace public/fonts/EmilysCandy-Regular.ttf with your preferred font file.
Edit app/Http/Controllers/BirthdayController.php in the getBirthdayCardImage() method:
private function getBirthdayCardImage($contact)
{
$manager = new ImageManager(new Driver());
$image = $manager->read(public_path('images/SITC Birthday Card.jpg'));
$name = strtoupper($contact->short_name);
// Customize text properties
$image->text($name, $x, $y, function ($font) {
$font->filename(public_path('fonts/YourFont.ttf'));
$font->color('#FF0000'); // Change color
$font->size(60); // Change size
$font->align('center');
$font->valign('middle');
});
return $image;
}Edit the message template in app/Console/Commands/SendBirthdayMessages.php:
$message = "HAPPY BIRTHDAY {$name}!\n\n"
. "Your custom message here.\n\n"
. "Your Birthday Card: {$url}\n\n"
. "Custom footer text!";# Start Laravel development server
php artisan serve
# In a separate terminal, start Vite dev server (for hot reloading)
npm run dev
# Or use the all-in-one development command
composer run devThe application will be available at http://localhost:8000
For processing background jobs (recommended):
php artisan queue:listen- Navigate to
http://localhost:8000 - You'll be redirected to the login page
- Enter your admin credentials (from
.env):- Default username:
admin - Default password:
secure_password(β οΈ change this!)
- Default username:
- Click "Add Contact" button
- Fill in the form:
- Name: Full name of the contact
- Phone: Phone number (include country code, e.g., +94771234567)
- Date of Birth: YYYY-MM-DD format
- Click "Create"
-
Prepare your CSV file with the following format:
Name,Phone,DOB John Doe,+94771234567,1990-05-15 Jane Smith,+94777654321,1985-08-22
-
Click "Import CSV" in the import section
-
Select your CSV file
-
Click "Import"
- Use the search bar to find contacts by name or phone number
- Click column headers to sort (Name, Phone, DOB, Last Message)
- Sorting persists across page refreshes
- Check the checkbox next to contacts you want to delete
- Click "Delete Selected" button
- Confirm the action
- Click "Delete All" button
- Confirm the dangerous action
- All contacts will be permanently removed
- Click "Export CSV" button
- Your browser will download
contacts.csvwith all contact data
Birthday messages are sent automatically based on your scheduler configuration (see Scheduling section).
- Click "Send Today's Messages" button
- Confirm the action
- The system will immediately check for today's birthdays and send messages
- Check the Activity Logs for results
- Find a contact in the list
- Click "Send Manual" next to their name
- Confirm the action
- An SMS will be sent immediately (bypasses year check)
- Click "View Logs" button
- Browse paginated log entries
- Click "View" on any log entry to see detailed information:
- Timestamp
- Action type
- Description
- Status (success/error/info)
- Full payload (JSON data)
When a recipient clicks the URL from their SMS:
- Landing Page: Shows "You have a surprise! π" with a button
- Tap to Open: Recipient clicks/taps the button
- Card Reveal: Animated confetti fills the screen, personalized card appears
- Download: Recipient can download the card image by clicking "Download Card"
The application uses Laravel's task scheduler to automatically send birthday messages. The scheduler needs to run continuously via a system cron job or process manager.
-
Open your crontab:
crontab -e
-
Add this line:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
-
Replace
/path-to-your-projectwith your actual project path
- Open Task Scheduler
- Create a new task:
- Trigger: Every 1 minute
- Action: Start a program
- Program:
C:\path\to\php.exe - Arguments:
artisan schedule:run - Start in:
C:\path\to\your\project
The scheduler is configured in app/Console/Kernel.php:
protected function schedule(Schedule $schedule): void
{
$schedule->command('birthday:send')->hourly();
}Default: Runs every hour to check for birthdays and send messages.
You can modify the frequency in app/Console/Kernel.php:
// Run every hour (default)
$schedule->command('birthday:send')->hourly();
// Run daily at midnight
$schedule->command('birthday:send')->dailyAt('00:00');
// Run every 30 minutes
$schedule->command('birthday:send')->everyThirtyMinutes();
// Run daily at 8 AM
$schedule->command('birthday:send')->dailyAt('08:00');
// Run on specific days
$schedule->command('birthday:send')
->dailyAt('09:00')
->days([1, 2, 3, 4, 5]); // Monday to Friday onlyYou can manually trigger the birthday check command:
php artisan birthday:sendThis will:
- Find all contacts with today's birthday
- Filter out those who already received a message this year
- Send SMS to eligible contacts
- Update
last_message_sent_yearto current year - Log all activities
The system automatically prevents sending duplicate messages:
- Tracks
last_message_sent_yearfor each contact - Skips contacts who already received a message in the current year
- Manual sends bypass this check (for testing or special occasions)
GET /birthday/{token}
Display the birthday surprise page with confetti animation.
Parameters:
token(string, required): Unique public token for the contact
Response: HTML page with interactive birthday card experience
GET /birthday/{token}/card
Generate and return the birthday card image with the contact's name.
Parameters:
token(string, required): Unique public token for the contact
Response: JPEG image (Content-Type: image/jpeg)
GET /birthday/{token}/download
Download the birthday card image as a file.
Parameters:
token(string, required): Unique public token for the contact
Response: JPEG image download (filename: birthday-card.jpg)
POST /login
Authenticate admin user.
Body:
{
"username": "admin",
"password": "secure_password"
}Response: Redirect to contacts page or error message
POST /logout
End admin session.
Response: Redirect to login page
GET /contacts
Display paginated list of contacts with search and sort.
Query Parameters:
search(string, optional): Search term for name/phonesort_by(string, optional): Column to sort by (name, phone, dob, last_message_sent_year, created_at)sort_direction(string, optional): Sort direction (asc, desc)page(integer, optional): Page number
Response: HTML page with contact listing
POST /contacts
Add a new contact.
Body:
{
"name": "John Doe",
"phone": "+94771234567",
"dob": "1990-05-15"
}Response: Redirect to contacts list with success message
PUT /contacts/{contact}
Update an existing contact.
Parameters:
contact(integer, required): Contact ID
Body:
{
"name": "John Doe Updated",
"phone": "+94771234567",
"dob": "1990-05-16"
}Response: Redirect to contacts list with success message
DELETE /contacts/{contact}
Delete a single contact.
Parameters:
contact(integer, required): Contact ID
Response: Redirect to contacts list with success message
POST /contacts/import
Bulk import contacts from CSV file.
Body: multipart/form-data
file(file, required): CSV file (max 2MB)
CSV Format:
Name,Phone,DOB
John Doe,+94771234567,1990-05-15Response: Redirect to contacts list with success message
GET /contacts/export/csv
Export all contacts to CSV file.
Response: CSV file download
POST /contacts/send-today
Manually trigger birthday message sending for today's birthdays.
Response: Redirect to contacts list with success message
POST /contacts/bulk-delete
Delete multiple contacts at once.
Body:
{
"ids": [1, 2, 3, 4]
}Response: Redirect to contacts list with success message
POST /contacts/delete-all
Delete all contacts from database.
Response: Redirect to contacts list with success message
POST /contacts/{contact}/send-manual
Send birthday message to a specific contact immediately.
Parameters:
contact(integer, required): Contact ID
Response: Redirect back with success/error message
GET /logs
Display paginated activity logs.
Response: HTML page with log entries
Stores contact information and birthday data.
| Column | Type | Description |
|---|---|---|
id |
BIGINT | Primary key |
name |
VARCHAR(255) | Full name of contact |
phone |
VARCHAR(20) | Phone number |
public_token |
VARCHAR(64) | Unique token for public card URL |
dob |
DATE | Date of birth |
last_message_sent_year |
INTEGER | Year when last birthday SMS was sent |
created_at |
TIMESTAMP | Record creation timestamp |
updated_at |
TIMESTAMP | Record update timestamp |
Indexes:
- Primary key on
id - Unique index on
public_token
Stores all system activity for auditing and debugging.
| Column | Type | Description |
|---|---|---|
id |
BIGINT | Primary key |
action |
VARCHAR(255) | Action type (e.g., "SMS Sent", "Import") |
description |
TEXT | Detailed description of the action |
status |
VARCHAR(255) | Status (success, error, info, warning) |
payload |
JSON | Additional data (contact details, responses) |
created_at |
TIMESTAMP | When the activity occurred |
updated_at |
TIMESTAMP | Record update timestamp |
Indexes:
- Primary key on
id - Index on
created_atfor efficient sorting
Standard Laravel users table (for future extensions).
| Column | Type | Description |
|---|---|---|
id |
BIGINT | Primary key |
name |
VARCHAR(255) | User's full name |
email |
VARCHAR(255) | Email address (unique) |
email_verified_at |
TIMESTAMP | Email verification time |
password |
VARCHAR(255) | Hashed password |
remember_token |
VARCHAR(100) | Remember me token |
created_at |
TIMESTAMP | Account creation time |
updated_at |
TIMESTAMP | Record update timestamp |
Indexes:
- Primary key on
id - Unique index on
email
- Contacts: No explicit relationships (self-contained)
- ActivityLogs: No explicit relationships (self-contained)
- Users: Not currently used in authentication (session-based admin login instead)
laravel-automated-birthday-message-sender/
βββ app/ # Application core code
β βββ Console/
β β βββ Commands/ # Artisan commands
β β β βββ SendBirthdayMessages.php
β β βββ Kernel.php # Scheduler configuration
β βββ Exceptions/ # Exception handlers
β β βββ Handler.php
β βββ Http/
β β βββ Controllers/ # Request handlers
β β β βββ ActivityLogController.php
β β β βββ BirthdayController.php
β β β βββ ContactController.php
β β β βββ Controller.php
β β β βββ LoginController.php
β β βββ Middleware/ # HTTP middleware
β β β βββ AdminAuthMiddleware.php
β β βββ Kernel.php # HTTP kernel
β βββ Models/ # Eloquent models
β β βββ ActivityLog.php
β β βββ Contact.php
β β βββ User.php
β βββ Providers/ # Service providers
β β βββ AppServiceProvider.php
β β βββ AuthServiceProvider.php
β β βββ EventServiceProvider.php
β β βββ RouteServiceProvider.php
β βββ Services/ # Business logic services
β βββ LoggerService.php
β βββ SmsService.php
βββ bootstrap/ # Framework bootstrap files
β βββ app.php
β βββ cache/
βββ config/ # Configuration files
β βββ app.php
β βββ database.php
β βββ queue.php
β βββ services.php
β βββ ...
βββ database/ # Database files
β βββ database.sqlite # SQLite database (created on install)
β βββ factories/ # Model factories
β β βββ UserFactory.php
β βββ migrations/ # Database migrations
β β βββ 2025_12_15_080530_create_contacts_table.php
β β βββ 2025_12_15_084958_add_public_token_to_contacts_table.php
β β βββ 2025_12_18_063949_create_activity_logs_table.php
β βββ seeders/ # Database seeders
β βββ DatabaseSeeder.php
βββ public/ # Public web root
β βββ build/ # Compiled assets (Vite output)
β βββ fonts/ # Custom fonts
β β βββ EmilysCandy-Regular.ttf
β βββ images/ # Static images
β β βββ SITC Birthday Card.jpg
β βββ index.php # Entry point
β βββ robots.txt
βββ resources/ # Raw assets and views
β βββ css/
β β βββ app.css # Tailwind CSS entry
β βββ js/
β β βββ app.js # JavaScript entry
β β βββ bootstrap.js # Axios config
β βββ views/ # Blade templates
β βββ auth/
β β βββ login.blade.php
β βββ birthday/
β β βββ show.blade.php
β βββ contacts/
β β βββ index.blade.php
β β βββ create.blade.php
β β βββ edit.blade.php
β βββ logs/
β β βββ index.blade.php
β βββ layouts/
β βββ app.blade.php
βββ routes/ # Route definitions
β βββ api.php # API routes
β βββ console.php # Console routes
β βββ web.php # Web routes
βββ storage/ # Storage files
β βββ app/ # Application storage
β βββ framework/ # Framework storage
β βββ logs/ # Application logs
βββ tests/ # Automated tests
β βββ Feature/
β βββ Unit/
βββ vendor/ # Composer dependencies
βββ .env # Environment configuration (create from .env.example)
βββ .env.example # Example environment file
βββ artisan # Artisan CLI
βββ composer.json # PHP dependencies
βββ composer.lock # PHP dependency lock
βββ package.json # Node.js dependencies
βββ package-lock.json # Node.js dependency lock
βββ phpunit.xml # PHPUnit configuration
βββ README.md # This file
βββ vite.config.js # Vite build configuration
# Start all development servers simultaneously
composer run devThis command starts:
- Laravel development server (port 8000)
- Queue worker
- Vite dev server (hot reload)
Or run each separately:
# Terminal 1: Laravel server
php artisan serve
# Terminal 2: Queue worker
php artisan queue:listen --tries=1
# Terminal 3: Vite dev server
npm run dev# Format all code to Laravel standards
./vendor/bin/pint
# Check without fixing
./vendor/bin/pint --test# Interactive PHP shell with loaded application
php artisan tinker
# Examples:
>>> App\Models\Contact::count()
>>> App\Models\Contact::whereMonth('dob', now()->month)->get()
>>> \App\Services\SmsService::send('+94771234567', 'Test message')# Clear all caches
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
# Or clear everything at once
php artisan optimize:clear# Run migrations
php artisan migrate
# Rollback last migration
php artisan migrate:rollback
# Rollback and re-run all migrations
php artisan migrate:refresh
# Rollback all and re-seed
php artisan migrate:fresh --seed
# Check migration status
php artisan migrate:statusApplication logs are stored in storage/logs/:
# View real-time logs
tail -f storage/logs/laravel.log
# Clear log file
echo "" > storage/logs/laravel.log# Run all tests
php artisan test
# Or using PHPUnit directly
./vendor/bin/phpunit
# Run specific test file
php artisan test tests/Feature/ContactTest.php
# Run with coverage (requires Xdebug)
php artisan test --coverageCreate new tests:
# Feature test (HTTP, database, etc.)
php artisan make:test ContactControllerTest
# Unit test (pure PHP logic)
php artisan make:test ContactServiceTest --unitExample test structure:
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\Contact;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ContactControllerTest extends TestCase
{
use RefreshDatabase;
public function test_can_list_contacts()
{
Contact::factory()->count(5)->create();
$response = $this->get('/contacts');
$response->assertStatus(200);
$response->assertViewHas('contacts');
}
}Example GitHub Actions workflow (.github/workflows/tests.yml):
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
- name: Install Dependencies
run: composer install
- name: Run Tests
run: php artisan testBefore deploying to production:
- Change
APP_ENV=productionin.env - Set
APP_DEBUG=falsein.env - Generate new
APP_KEYwithphp artisan key:generate - Update
APP_URLto your production domain - Change admin username and password
- Configure SMS gateway credentials
- Use MySQL/PostgreSQL instead of SQLite
- Set up Redis for cache and queue (recommended)
- Configure proper mail driver
- Enable HTTPS/SSL
- Set up proper file permissions
- Configure backup strategy
- Set up monitoring and error tracking
Web Server Options:
- Apache 2.4+ with mod_rewrite
- Nginx 1.18+
- Caddy 2.0+
PHP Extensions Required:
- OpenSSL
- PDO
- Mbstring
- Tokenizer
- XML
- Ctype
- JSON
- BCMath
- Fileinfo
- GD
# Update system
sudo apt update && sudo apt upgrade -y
# Install PHP 8.1
sudo apt install php8.1 php8.1-fpm php8.1-cli php8.1-mbstring \
php8.1-xml php8.1-curl php8.1-zip php8.1-gd php8.1-sqlite3 \
php8.1-mysql -y
# Install Composer
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install nodejs -y# Clone repository
cd /var/www
git clone https://github.com/codezelat/laravel-automated-birthday-message-sender.git
cd laravel-automated-birthday-message-sender
# Install dependencies
composer install --optimize-autoloader --no-dev
npm install
npm run build
# Set permissions
sudo chown -R www-data:www-data /var/www/laravel-automated-birthday-message-sender
sudo chmod -R 775 storage bootstrap/cache
# Environment configuration
cp .env.example .env
nano .env # Edit configuration
php artisan key:generate
php artisan migrate --forceNginx Configuration (/etc/nginx/sites-available/birthday-sender):
server {
listen 80;
server_name yourdomain.com;
root /var/www/laravel-automated-birthday-message-sender/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}Enable the site:
sudo ln -s /etc/nginx/sites-available/birthday-sender /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Obtain certificate
sudo certbot --nginx -d yourdomain.com
# Auto-renewal is set up automaticallyFor queue workers, create /etc/supervisor/conf.d/birthday-sender-worker.conf:
[program:birthday-sender-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/laravel-automated-birthday-message-sender/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/laravel-automated-birthday-message-sender/storage/logs/worker.log
stopwaitsecs=3600Activate:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start birthday-sender-worker:*Add to crontab (sudo crontab -e):
* * * * * cd /var/www/laravel-automated-birthday-message-sender && php artisan schedule:run >> /dev/null 2>&1Create Dockerfile:
FROM php:8.1-fpm
# Install dependencies
RUN apt-get update && apt-get install -y \
git curl zip unzip libpng-dev libonig-dev libxml2-dev
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www
# Copy application
COPY . /var/www
# Install dependencies
RUN composer install --optimize-autoloader --no-dev
RUN npm install && npm run build
# Set permissions
RUN chown -R www-data:www-data /var/www
RUN chmod -R 775 storage bootstrap/cache
CMD php artisan serve --host=0.0.0.0 --port=8000Build and run:
docker build -t birthday-sender .
docker run -d -p 8000:8000 birthday-sender# Cache configuration
php artisan config:cache
# Cache routes
php artisan route:cache
# Cache views
php artisan view:cache
# Optimize autoloader
composer install --optimize-autoloader --no-dev
# Enable OPcache (php.ini)
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000# Fix storage and cache permissions
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache- SQLite: Ensure
database/database.sqliteexists and is writable - MySQL: Verify credentials in
.envand ensure database exists - Test connection:
php artisan migrate:status
- Verify cron job is active:
crontab -l
- Check cron logs:
grep CRON /var/log/syslog
- Test scheduler manually:
php artisan schedule:run
- Check SMS credentials in
.env - Review logs:
storage/logs/laravel.log - Check Activity Logs in admin panel for detailed error messages
- Test SMS service manually:
php artisan tinker >>> $sms = new \App\Services\SmsService(); >>> $sms->send('+94771234567', 'Test message');
- Ensure GD extension is installed:
php -m | grep -i gd - Check image and font files exist:
ls -lh public/images/ ls -lh public/fonts/
- Verify file permissions:
chmod 644 public/images/* public/fonts/*
This occurs when assets aren't compiled:
npm install
npm run build- Clear sessions:
php artisan session:flush
- Check session driver in
.env(should bedatabase) - Ensure sessions table exists:
php artisan migrate
Enable debug mode for detailed error messages (development only):
APP_DEBUG=trueView detailed error traces in storage/logs/laravel.log
If you encounter issues not covered here:
- Check application logs:
storage/logs/laravel.log - Check web server logs:
- Nginx:
/var/log/nginx/error.log - Apache:
/var/log/apache2/error.log
- Nginx:
- Enable query logging to debug database issues:
// In app/Providers/AppServiceProvider.php boot() method \DB::listen(function($query) { \Log::info($query->sql, $query->bindings); });
- Use
php artisan tinkerto test code interactively - Check the Laravel Documentation
- Open an issue on GitHub (provide error logs and steps to reproduce)
Contributions are welcome! Please follow these guidelines:
-
Fork the Repository
git clone https://github.com/codezelat/laravel-automated-birthday-message-sender.git cd laravel-automated-birthday-message-sender -
Create a Feature Branch
git checkout -b feature/your-feature-name
-
Make Your Changes
- Write clean, documented code
- Follow Laravel coding standards (use Laravel Pint)
- Add tests for new features
- Update documentation as needed
-
Run Code Quality Checks
./vendor/bin/pint # Format code php artisan test # Run tests
-
Commit Your Changes
git add . git commit -m "feat: add your feature description"
Use Conventional Commits:
feat:- New featuresfix:- Bug fixesdocs:- Documentation changesstyle:- Code style changes (formatting)refactor:- Code refactoringtest:- Test additions/changeschore:- Maintenance tasks
-
Push to Your Fork
git push origin feature/your-feature-name
-
Open a Pull Request
- Go to the original repository
- Click "New Pull Request"
- Select your feature branch
- Describe your changes clearly
- π Multi-language support - Internationalization (i18n)
- π§ Email integration - Send emails alongside SMS
- π Analytics dashboard - Visualize birthday statistics
- π WhatsApp integration - Send messages via WhatsApp Business API
- π₯ Multiple admin users - User management system
- π± Mobile app - React Native or Flutter companion app
- π¨ Template system - Multiple birthday card designs
- π API endpoints - RESTful API for external integrations
- β° Custom timing - Per-contact preferred send time
- π Custom messages - Per-contact personalized messages
- Be respectful and inclusive
- Provide constructive feedback
- Focus on the code, not the person
- Help others learn and grow
If you discover a security vulnerability, please DO NOT open a public issue. Instead:
- Email: info@codezela.com
- Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
We will respond within 48 hours and work with you to resolve the issue.
ADMIN_USERNAME=your_secure_username
ADMIN_PASSWORD=your_very_strong_password_here- Never commit
.envfile to version control - Keep
.envfile permissions restrictive:chmod 600 .env - Use strong, unique passwords for all services
- Use prepared statements (Laravel does this automatically)
- Sanitize user input (Laravel validators handle this)
- Use encrypted connections for remote databases
- Always use HTTPS in production
- Redirect HTTP to HTTPS
- Use HSTS headers
Add rate limiting to prevent abuse:
// In routes/web.php
Route::middleware(['throttle:60,1'])->group(function () {
// Your routes
});Always validate and sanitize user input (already implemented):
- Contact forms use Laravel validation
- CSRF protection on all forms
- XSS protection via Blade templating
- Validate file types and sizes (CSV import does this)
- Store uploaded files outside public directory
- Scan for malware if handling user uploads at scale
- Keep Laravel and dependencies updated
- Monitor security advisories:
composer audit - Update PHP version regularly
This project is licensed under the MIT License.
Copyright (c) 2025 SITC Campus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This project uses the following open-source packages:
- Laravel Framework - MIT License
- Intervention Image - MIT License
- Tailwind CSS - MIT License
- SweetAlert2 - MIT License
- Canvas Confetti - ISC License
- Guzzle HTTP - MIT License
See individual package licenses in the vendor/ and node_modules/ directories.
- Laravel - The PHP Framework for Web Artisans
- Tailwind CSS - A utility-first CSS framework
- Intervention Image - Image handling and manipulation library
- Vite - Next generation frontend tooling
- SweetAlert2 - Beautiful, responsive, customizable alerts
- Canvas Confetti - Confetti animation library
This project was created to automate birthday greetings for SITC Campus, bringing joy to students and staff on their special day.
Special thanks to all contributors who help improve this project!
- Project Maintainer: SITC Campus Development Team
- Email: info@sitc.lk
- Website: https://sitc.lk
- Version: 1.0.0
- Status: Active Development
- Last Updated: January 2026
- Supported Laravel Version: 10.x
- Supported PHP Version: 8.1+
- Documentation: Read this README thoroughly
- Issue Tracker: GitHub Issues
- Discussions: GitHub Discussions
- Email: info@sitc.lk
Q: Can I use this for commercial purposes?
A: Yes, the MIT License allows commercial use.
Q: Does this support multiple languages?
A: Not yet, but it's planned for a future release.
Q: Can I customize the birthday card design?
A: Yes, replace the image in public/images/ and modify the controller.
Q: How do I change the message template?
A: Edit the message in app/Console/Commands/SendBirthdayMessages.php.
Q: Can I use MySQL instead of SQLite?
A: Yes, just update your .env database configuration.
Q: Is there a limit to how many contacts I can have?
A: No hard limit, but performance depends on your server resources.
Made with β€οΈ by SITC Campus
β Star this repository if you found it helpful! β