A comprehensive RESTful API for managing projects with dynamic attributes using an Entity-Attribute-Value (EAV) data model, built with Laravel 12 and PHP 8.2.
- Features
- System Architecture
- Setup Instructions
- API Documentation
- Filtering & Pagination
- Example Requests/Responses
- Test Credentials
- Error Handling
- Security Considerations
- Authentication: Secure user authentication with Laravel Passport
- EAV Model: Dynamic attributes for flexible project management
- Advanced Filtering: Comprehensive filtering system across all resources
- Role-Based Access: Admin and regular user permissions
- Timesheet Management: Track time spent on projects
- Pagination: Control data response size
- Data Validation: Request validation to ensure data integrity
The API implements the Entity-Attribute-Value (EAV) pattern for dynamic attributes:
- Entities: Projects are the primary entities
- Attributes: Define metadata fields (name, type)
- Values: Store the actual data values
This design allows for flexible attribute management without database schema changes.
- PHP 8.2 or higher
- Composer
- MySQL or PostgreSQL
- Laravel 12.x
- Passport 12.x
Quickstart with Using MySQL Command Line sqldump file located in the root -> assessment_folders 1- mysql -u [username] -p [database_name] < astudiotaskdb.sql 2- Configure .env with your database credentials 3- php artisan passport:keys Check MySQL Options.
Note 1: It is better to use the full path of the SQL file file.sql.
Note 2: Use -R and --triggers with mysqldump to keep the routines and triggers of the original database. They are not copied by default.
Note 3 You may have to create the (empty) database from MySQL if it doesn't exist already and the exported SQL doesn't contain CREATE DATABASE (exported with --no-create-db or -n option) before you can import it.
-
Clone the repository:
bash git clone https://github.com/AbdoHany98/astudio-practical-assessment.git cd project-management-api -
Install dependencies:
bash composer install -
Configure environment:
bash cp .env.example .envEdit the.envfile with your database credentials and application settings. -
Generate application key:
bash php artisan key:generate -
Run migrations and seeders:
bash php artisan migrate --seed -
Install Passport:
bash php artisan passport:install -
Start the development server:
bash php artisan serve
All API endpoints return JSON responses with consistent structures:
{
"success": true|false,
"message": "Operation status message (when applicable)",
"data": { ... }
}| Method | Endpoint | Description | Required Fields | Response |
|---|---|---|---|---|
| POST | /api/register | Register a new user | name, email, password, password_confirmation | User data + API token |
| POST | /api/login | Login with credentials | email, password | User data + API token |
| POST | /api/logout | Logout (invalidate token) | - | Success message |
| Method | Endpoint | Description | Query Parameters | Authorization |
|---|---|---|---|---|
| GET | /api/projects | List projects with pagination | filters, paginate | Required |
| POST | /api/projects | Create new project | - | Admin Required |
| GET | /api/projects/{id} | Get specific project | - | Required |
| PUT | /api/projects/{id} | Update project | - | Admin Required |
| DELETE | /api/projects/{id} | Delete project | - | Admin Required |
| Method | Endpoint | Description | Query Parameters | Authorization |
|---|---|---|---|---|
| GET | /api/attributes | List attributes | name, type, created_from, created_to, paginate, sort_by, sort_dir | Required |
| POST | /api/attributes | Create new attribute | - | Admin Required |
| GET | /api/attributes/{id} | Get attribute with values | - | Required |
| PUT | /api/attributes/{id} | Update attribute | - | Admin Required |
| DELETE | /api/attributes/{id} | Delete attribute | - | Admin Required |
| Method | Endpoint | Description | Query Parameters | Authorization |
|---|---|---|---|---|
| GET | /api/attribute-values | List attribute values | attribute_id, entity_id, value, exact_match, attribute_name, attribute_type, created_from, created_to, paginate, sort_by, sort_dir | Required |
| POST | /api/attribute-values | Create new attribute value | - | Admin Admin Required Or Project User |
| GET | /api/attribute-values/{id} | Get attribute value | - | Required |
| PUT | /api/attribute-values/{id} | Update attribute value | - | Admin Required Or Project User |
| DELETE | /api/attribute-values/{id} | Delete attribute value | - | Admin Required Or Project User |
| Method | Endpoint | Description | Query Parameters | Authorization |
|---|---|---|---|---|
| GET | /api/timesheets | List timesheets | user_id, project_id, date_from, date_to | Required |
| POST | /api/timesheets | Create timesheet | - | Admin Required Or Project User |
| GET | /api/timesheets/{id} | Get timesheet | - | Admin Required Or Project User |
| PUT | /api/timesheets/{id} | Update timesheet | - | Admin Required Or Project User |
| DELETE | /api/timesheets/{id} | Delete timesheet | - | Admin Required Or Project User |
Projects can be filtered using the filters query parameter:
- Standard filtering:
?filters[name]=ProjectName - Operator filtering:
?filters[name:like]=Project
Available operators:
=(default): Exact matchlike: Partial match (case-insensitive)>,<,>=,<=: Comparison operators for numeric fields
Attributes can be filtered using specific query parameters:
?name=client- Filter by name (partial match)?type=text- Filter by type (exact match)?created_from=2023-01-01&created_to=2023-12-31- Date range filtering
All list endpoints support pagination using:
?paginate=10- Number of results per page
Add sorting capabilities:
?sort_by=name&sort_dir=asc- Sort by field in ascending or descending order
Request:
POST /api/projects
Content-Type: application/json
Accept: application/json
Authorization: Bearer {your_token}
{
"name": "Website Redesign",
"status": "active",
"users": [1, 2],
"attributes": [
{
"attribute_id": 1,
"value": "Company XYZ"
},
{
"attribute_id": 2,
"value": "10000"
}
]
}Response:
{
"data": {
"id": 1,
"name": "Website Redesign",
"status": "active",
"attributes": [
{
"id": 1,
"name": "client",
"type": "text",
"value": "Company XYZ"
},
{
"id": 2,
"name": "budget",
"type": "number",
"value": "10000"
}
],
"users": [
{
"id": 1,
"name": "John Doe"
},
{
"id": 2,
"name": "Jane Smith"
}
],
"created_at": "2023-01-01T00:00:00.000000Z",
"updated_at": "2023-01-01T00:00:00.000000Z"
}
}Request:
GET /api/projects?filters[client:like]=XYZ
Accept: application/json
Authorization: Bearer {your_token}Response:
{
"data": [
{
"id": 1,
"name": "Website Redesign",
"status": "active",
"attributes": [
{
"id": 1,
"name": "client",
"type": "text",
"value": "Company XYZ"
},
{
"id": 2,
"name": "budget",
"type": "number",
"value": "10000"
}
],
"users": [...],
"created_at": "2023-01-01T00:00:00.000000Z",
"updated_at": "2023-01-01T00:00:00.000000Z"
}
],
"links": {...},
"meta": {...}
}For testing purposes, the following credentials are available after running the seeders:
Admin User:
- Email: admin@example.com
- Password: password
Regular User:
- Email: user@example.com
- Password: password
The API returns appropriate HTTP status codes and error messages:
200 OK: Successful operation201 Created: Resource created successfully400 Bad Request: Invalid input data401 Unauthorized: Authentication failure403 Forbidden: Permission denied404 Not Found: Resource not found422 Unprocessable Entity: Validation errors500 Internal Server Error: Server error
- All API requests (except authentication) require a valid Bearer token
- Password hashing is handled by Laravel's built-in mechanisms
- CORS is configured to allow specific origins only
- Input validation is applied to all endpoints
- Resource permissions are checked before operations
Note: All API requests require the following headers:
Content-Type: application/jsonAccept: application/jsonAuthorization: Bearer {your_token}(except for login/register)
For more detailed examples, please refer to the Postman collection located in the root -> assessment_files.