A minimal REST API showcasing Symfony features such as attribute routing, dependency injection, and Doctrine ORM with SQLite. Designed for WordPress developers exploring modern PHP frameworks, it introduces basic Symfony concepts and helps users understand the key similarities and differences between WordPress and Symfony workflows.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users | List all users |
| GET | /api/users/{id} | Get single user |
| POST | /api/users | Create new user |
| PUT | /api/users/{id} | Update user |
| DELETE | /api/users/{id} | Delete user |
Before setting up this project, you need PHP, Composer, and optionally the Symfony CLI.
Windows:
- Go to https://windows.php.net/download/
- Download VS16 x64 Thread Safe ZIP (e.g., php-8.1.x-Win32-vs16-x64.zip)
- Extract to
C:\php - Add
C:\phpto your system PATH:- Press
Win + R, typesysdm.cpl, press Enter - Click Advanced → Environment Variables
- Under System variables, find Path, click Edit
- Click New, add
C:\php, click OK
- Press
- Copy
php.ini-developmenttophp.ini - Verify: Open new Command Prompt, type:
php -v
Mac:
brew install php
php -vLinux:
sudo apt update
sudo apt install php8.1 php8.1-cli php8.1-xml php8.1-mbstring php8.1-sqlite3
php -vWindows:
- Download and run https://getcomposer.org/Composer-Setup.exe
- Follow installer
- Verify in new Command Prompt:
composer -V
Mac:
brew install composer
composer -VLinux:
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
sudo mv composer.phar /usr/local/bin/composer
composer -VThe Symfony CLI provides a local web server with HTTPS support.
Windows (PowerShell as Admin):
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm https://get.symfony.com/cli/installer | iexClose and reopen Terminal, then verify: symfony -v
Mac:
curl -sS https://get.symfony.com/cli/installer | bash
# Follow the output to add to PATH
symfony -vLinux:
curl -sS https://get.symfony.com/cli/installer | bash
sudo mv ~/.symfony*/bin/symfony /usr/local/bin/
symfony -vphp -v # PHP 8.1+
composer -V # Composer 2.x
symfony -v # OptionalSymfony projects are created using Composer or the Symfony CLI. We'll create a fresh Symfony project with default files, then add our custom files.
# Navigate to your projects folder
cd ~/projects # Mac/Linux
cd C:\projects # Windows
# Option A: Using Symfony CLI (recommended)
symfony new userapi-symfony --version="6.4.*" --webapp
# Option B: Using Composer
composer create-project symfony/skeleton:"6.4.*" userapi-symfony
cd userapi-symfony
composer require webappWait 2-5 minutes for installation.
cd userapi-symfonyYou should see default folders like bin/, config/, src/, public/, etc.
composer require symfony/orm-packOpen the .env file in a text editor. Find the DATABASE_URL line and modify it:
# Comment out the existing DATABASE_URL by adding # at the start
# DATABASE_URL="postgresql://...
# Add this line for SQLite
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"Download the files from this repo to your Computer. Then, create the necessary folders (if they don't exist):
Windows:
mkdir src\Entity
mkdir src\Repository
mkdir src\Controller
Mac/Linux:
mkdir -p src/Entity src/Repository src/ControllerCopy the downloaded project files:
User.php→src/Entity/User.phpUserRepository.php→src/Repository/UserRepository.phpUserController.php→src/Controller/UserController.php
Run these commands in order:
# Create the SQLite database file
php bin/console doctrine:database:create
# Create the users table from the Entity
php bin/console doctrine:schema:createYou should see success messages like "Created database" and "Executing 1 query".
Option A - Using Symfony CLI:
symfony serveOpens at 127.0.0.1:8000
Option B - Using PHP built-in server:
php -S localhost:8000 -t publicOpens at localhost:8000
Test the API: Open browser: localhost:8000/api/users
You should see: [] (empty array, no users yet)
GET /api/users
Response 200:
[
{"id": 1, "name": "John Doe", "email": "john@example.com", "createdAt": "2025-02-22 10:30:00"},
{"id": 2, "name": "Janifer", "email": "jane@example.com", "createdAt": "2025-02-22 11:00:00"}
]
GET /api/users/1
Response 200:
{"id": 1, "name": "John Doe", "email": "john@example.com", "createdAt": "2025-02-22 10:30:00"}
Response 404 (not found):
{"error": "User not found"}
POST /api/users
Content-Type: application/json
{"name": "John Doe", "email": "john@example.com"}
Response 201:
{"id": 1, "name": "John Doe", "email": "john@example.com", "createdAt": "2025-02-22 10:30:00"}
Response 400 (validation error):
{"error": "Name and email are required"}
PUT /api/users/1
Content-Type: application/json
{"name": "John Updated", "email": "john.new@example.com"}
Response 200:
{"id": 1, "name": "John Updated", "email": "john.new@example.com", "createdAt": "2025-02-22 10:30:00"}
DELETE /api/users/1
Response 204: (no content)
A Postman collection is included: userapi.postman_collection.json
To import:
- Open Postman
- Click Import button (top left)
- Drag and drop the JSON file or click Upload Files
- The "UserAPI" collection appears in your sidebar
- Before testing, make sure your server is running
userapi-symfony/
├── bin/
│ └── console # Symfony CLI commands
├── config/ # Configuration files
├── public/
│ └── index.php # Entry point
├── src/
│ ├── Controller/
│ │ └── UserController.php # REST API endpoints
│ ├── Entity/
│ │ └── User.php # Doctrine entity
│ └── Repository/
│ └── UserRepository.php # Database queries
├── var/
│ └── data.db # SQLite database (created automatically)
├── vendor/ # Dependencies
├── .env # Environment config
├── composer.json
└── README.md
# Install production dependencies only
composer install --no-dev --optimize-autoloader
# Clear and warm cache
php bin/console cache:clear --env=prodEdit .env:
APP_ENV=prod
APP_SECRET=generate-a-random-32-char-string
Upload via FTP to your hosting (e.g., /public_html/userapi/).
Important: The var/ folder needs write permissions:
chmod 755 var/
# or
chmod 775 var/Point your domain/subdomain to the /public folder.
Or access via: yourdomain.com/userapi/public/api/users
SSH into server (or use hosting control panel terminal):
php bin/console doctrine:database:create
php bin/console doctrine:schema:createVisit: yourdomain.com/api/users
Should see: []
- Attribute Routing:
#[Route('/api/users')]defines URLs directly on controller methods - Dependency Injection: Controller receives
EntityManagerInterfaceandUserRepositorythrough constructor - Symfony's service container autowires these automatically - Service Container: All services (EntityManager, repositories, controllers) are managed by Symfony's DI container, configured via
config/services.yaml - Middleware Concepts: Symfony's HttpKernel uses event listeners and kernel events (Request → Controller → Response) which function like middleware - the framework handles CORS, content negotiation, and exception handling through this pipeline
- JsonResponse: Returns JSON with proper Content-Type headers
- Doctrine ORM: Entity classes map to database tables, no raw SQL needed
- HTTP Status Codes: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 404 Not Found
Symfony fundamentally differs from WordPress in architecture:
-
Attribute Routing vs Hooks: Instead of
add_action('rest_api_init', ...), Symfony uses#[Route]attributes directly on controller methods. The URL structure is explicit and visible. -
Dependency Injection vs Globals: WordPress relies on global functions (
get_option(),$wpdb). Symfony injects dependencies through constructors, making code easier to test and understand. -
Doctrine ORM vs $wpdb: Instead of raw SQL queries, Doctrine uses Entity objects. You work with PHP objects (
$user->setName('John')) rather than SQL strings. -
Structured Response vs echo: Symfony's
JsonResponsehandles JSON encoding, headers, and status codes. No manualheader('Content-Type: application/json')needed. -
File Structure: Symfony enforces separation (Entity, Repository, Controller) while WordPress mixes everything in
functions.phpor plugin files.