# Project Module Documentation

## Overview

The Project module provides a complete CRUD (Create, Read, Update, Delete) system for managing projects. Each project has a name, description, image, and relationships with clients and sectors. The module includes a service layer for business logic, a controller for handling HTTP requests, and proper route definitions.

## Architecture

### Components

1. **Model**: `app/Models/Project.php`
   - Handles database interactions
   - Uses soft deletes
   - Includes relationships for clients, sectors, and audit tracking

2. **Service**: `app/Services/ProjectService.php`
   - Contains all business logic
   - Manages file uploads and deletions
   - Handles create, read, update, delete operations
   - Provides filtering by client and sector

3. **Controller**: `app/Http/Controllers/ProjectController.php`
   - Handles HTTP requests/responses
   - Validates input data
   - Delegates business logic to the service

4. **Routes**: `routes/projects.php`
   - Defines all API endpoints
   - Separates public (read-only) from protected routes

## Database Schema

```sql
CREATE TABLE projects (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    description VARCHAR(255) NULL,
    image VARCHAR(255) NULL,
    client_id BIGINT UNSIGNED NULL,
    sector_id BIGINT UNSIGNED NULL,
    added_by BIGINT UNSIGNED NULL,
    updated_by BIGINT UNSIGNED NULL,
    deleted_at TIMESTAMP NULL,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,
    
    FOREIGN KEY (client_id) REFERENCES clients(id),
    FOREIGN KEY (sector_id) REFERENCES sectors(id),
    FOREIGN KEY (added_by) REFERENCES users(id),
    FOREIGN KEY (updated_by) REFERENCES users(id)
);
```

### Fields

- `id`: Primary key
- `name`: Project name (required, max 255 characters)
- `description`: Project description (optional, max 255 characters)
- `image`: Path to project image stored in `storage/app/public/projects/` (optional)
- `client_id`: Foreign key to clients table (optional)
- `sector_id`: Foreign key to sectors table (optional)
- `added_by`: User ID who created the project
- `updated_by`: User ID who last updated the project
- `deleted_at`: Soft delete timestamp
- `created_at`: Record creation timestamp
- `updated_at`: Record last update timestamp

## Relationships

### Project Belongs To:
- **Client**: A project can belong to a client (`client_id`)
- **Sector**: A project can belong to a sector (`sector_id`)
- **Added By User**: Tracks who created the project
- **Updated By User**: Tracks who last modified the project

## API Endpoints

### Public Endpoints (No Authentication Required)

#### 1. Get All Projects
```http
GET /api/projects
```

**Response:**
```json
[
  {
    "id": 1,
    "name": "Website Redesign",
    "description": "Complete website redesign",
    "image": "http://localhost:8000/storage/projects/abc123.jpg",
    "client_id": 1,
    "sector_id": 2,
    "client": {
      "id": 1,
      "name": "Acme Corp"
    },
    "sector": {
      "id": 2,
      "name": "Technology"
    },
    "added_by": {...},
    "updated_by": {...}
  }
]
```

#### 2. Get Project by ID
```http
GET /api/projects/{id}
```

#### 3. Get Project by Name
```http
GET /api/projects/name/{name}
```

**Example:**
```http
GET /api/projects/name/Website Redesign
```

#### 4. Get Projects by Client ID
```http
GET /api/projects/client/{clientId}
```

Returns all projects belonging to a specific client.

**Example:**
```http
GET /api/projects/client/1
```

#### 5. Get Projects by Sector ID
```http
GET /api/projects/sector/{sectorId}
```

Returns all projects belonging to a specific sector.

**Example:**
```http
GET /api/projects/sector/2
```

### Protected Endpoints (Require Authentication)

All protected endpoints require a Sanctum authentication token:
```http
Authorization: Bearer YOUR_TOKEN_HERE
```

#### 6. Create Project
```http
POST /api/projects
Authorization: Bearer {token}
Content-Type: multipart/form-data OR application/json
```

**Request Body (JSON):**
```json
{
  "name": "Website Redesign",
  "description": "Complete website redesign with modern UI/UX",
  "client_id": 1,
  "sector_id": 2
}
```

**Request Body (Form Data with image):**
```
name: Website Redesign
description: Complete website redesign
client_id: 1
sector_id: 2
image: [file upload]
```

**Validation Rules:**
- `name`: required, string, max 255 characters
- `description`: optional, string
- `client_id`: optional, must exist in clients table
- `sector_id`: optional, must exist in sectors table
- `image`: optional, must be an image file (jpeg, png, jpg, gif, webp), max 2MB

**Success Response (201):**
```json
{
  "message": "Project created successfully",
  "project": {
    "id": 1,
    "name": "Website Redesign",
    "description": "Complete website redesign",
    "client": {...},
    "sector": {...},
    ...
  }
}
```

#### 7. Update Project by ID
```http
POST /api/projects/{id}
Authorization: Bearer {token}
Content-Type: multipart/form-data OR application/json
```

**Request Body (all fields optional):**
```json
{
  "name": "Website Redesign v2",
  "description": "Updated description",
  "client_id": 2,
  "sector_id": 3
}
```

**Note:** When updating with a new image, the old image will be automatically deleted.

#### 8. Delete Project by ID
```http
DELETE /api/projects/{id}
Authorization: Bearer {token}
```

**Note:** Deleting a project will also delete its associated image file.

#### 9. Upsert Project by Name
```http
POST /api/projects/name/{name}
Authorization: Bearer {token}
Content-Type: multipart/form-data OR application/json
```

Creates a new project if the name doesn't exist, or updates the existing one.

**Example:**
```http
POST /api/projects/name/Marketing Campaign
```

#### 10. Delete Project by Name
```http
DELETE /api/projects/name/{name}
Authorization: Bearer {token}
```

## Service Methods

### ProjectService Methods

```php
// Get all projects with relationships
listAll(): Collection

// Find project by ID
findById(int $id): ?Project

// Find project by name
findByName(string $name): ?Project

// Get projects by client ID
findByClientId(int $clientId): Collection

// Get projects by sector ID
findBySectorId(int $sectorId): Collection

// Create new project
create(array $data, ?UploadedFile $image = null): Project

// Update existing project
update(int $id, array $data, ?UploadedFile $image = null): ?Project

// Delete project by ID
delete(int $id): bool

// Create or update project by name
upsertByName(string $name, array $data, ?UploadedFile $image = null): Project

// Delete project by name
deleteByName(string $name): bool
```

## Usage Examples

### Example 1: Create Project with Full Details using cURL

```bash
curl -X POST http://localhost:8000/api/projects \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "name=Website Redesign" \
  -F "description=Complete website overhaul" \
  -F "client_id=1" \
  -F "sector_id=2" \
  -F "image=@/path/to/project.jpg"
```

### Example 2: Get All Projects for a Client

```bash
curl -X GET http://localhost:8000/api/projects/client/1
```

### Example 3: Get All Projects in a Sector

```bash
curl -X GET http://localhost:8000/api/projects/sector/2
```

### Example 4: Update Project

```bash
curl -X POST http://localhost:8000/api/projects/1 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Website Redesign v2",
    "description": "Updated scope",
    "client_id": 2
  }'
```

### Example 5: Using the Service in Code

```php
use App\Services\ProjectService;

class YourController extends Controller
{
    protected $projectService;
    
    public function __construct(ProjectService $projectService)
    {
        $this->projectService = $projectService;
    }
    
    public function example(Request $request)
    {
        // Create a project
        $project = $this->projectService->create([
            'name' => 'Website Redesign',
            'description' => 'Complete overhaul',
            'client_id' => 1,
            'sector_id' => 2
        ], $request->file('image'));
        
        // Get all projects
        $projects = $this->projectService->listAll();
        
        // Get projects for a client
        $clientProjects = $this->projectService->findByClientId(1);
        
        // Get projects in a sector
        $sectorProjects = $this->projectService->findBySectorId(2);
        
        // Update
        $project = $this->projectService->update(1, [
            'name' => 'Updated Name',
            'client_id' => 2
        ], $request->file('image'));
        
        // Delete
        $this->projectService->delete(1);
    }
}
```

## Model Features

### Relationships

```php
// Get the client
$project->client; // Returns Client model or null

// Get the sector
$project->sector; // Returns Sector model or null

// Get the user who created the project
$project->addedBy; // Returns User model

// Get the user who last updated the project
$project->updatedBy; // Returns User model
```

### Image Accessor

The image attribute automatically converts the storage path to a full URL:

```php
// Database: projects/abc123.jpg
// Accessor returns: http://localhost:8000/storage/projects/abc123.jpg
$project->image;
```

### Soft Deletes

Projects use soft deletes, meaning deleted records are not permanently removed:

```php
// Soft delete
$project->delete();

// Get trashed projects
Project::onlyTrashed()->get();

// Restore a soft-deleted project
$project->restore();

// Permanently delete
$project->forceDelete();
```

## File Storage

### Image Storage Location
- Images are stored in: `storage/app/public/projects/`
- Publicly accessible via: `http://localhost:8000/storage/projects/{filename}`

### Image Processing
- Accepted formats: JPEG, PNG, JPG, GIF, WEBP
- Maximum file size: 2MB
- Files are stored with random 40-character filenames to prevent conflicts
- Old images are automatically deleted when updating or deleting projects

## Security Features

1. **Authentication**: All write operations (create, update, delete) require authentication via Laravel Sanctum
2. **Validation**: Input data is validated before processing
3. **Foreign Key Validation**: Client and sector IDs are validated to exist
4. **File Upload Security**: 
   - Only image files are accepted
   - File size limited to 2MB
   - Files stored with random names to prevent path injection
5. **Soft Deletes**: Accidental deletions can be recovered
6. **Audit Trail**: `added_by` and `updated_by` fields track user actions

## Error Handling

### Common Error Responses

**400 Bad Request - Validation Error:**
```json
{
  "message": "The given data was invalid.",
  "errors": {
    "name": ["The name field is required."],
    "client_id": ["The selected client id is invalid."]
  }
}
```

**401 Unauthorized:**
```json
{
  "message": "Unauthenticated."
}
```

**404 Not Found:**
```json
{
  "message": "Project not found"
}
```

**422 Unprocessable Entity:**
```json
{
  "message": "The given data was invalid.",
  "errors": {
    "image": ["The image must be an image file."]
  }
}
```

## Testing

Run the tests with:
```bash
php artisan test --filter ProjectTest
```

The test suite includes 25+ test cases covering:
- CRUD operations
- Authentication requirements
- Validation rules
- Relationship loading
- Image management
- Filtering by client and sector
- Error handling

## Integration Examples

### Get All Projects for a Specific Client

```javascript
// Frontend example
fetch('/api/projects/client/1')
  .then(response => response.json())
  .then(projects => {
    console.log('Client projects:', projects);
  });
```

### Get All Projects in a Sector

```javascript
fetch('/api/projects/sector/2')
  .then(response => response.json())
  .then(projects => {
    console.log('Sector projects:', projects);
  });
```

### Create Project with Client and Sector

```javascript
const formData = new FormData();
formData.append('name', 'New Project');
formData.append('description', 'Project description');
formData.append('client_id', 1);
formData.append('sector_id', 2);
formData.append('image', imageFile);

fetch('/api/projects', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + token
  },
  body: formData
})
.then(response => response.json())
.then(data => {
  console.log('Project created:', data.project);
});
```

## Best Practices

1. **Always use the Service Layer**: Don't bypass the service and interact directly with the model in controllers
2. **Handle Images Properly**: The service automatically manages image uploads and deletions
3. **Use Eager Loading**: When fetching projects, use `with(['client', 'sector', 'addedBy', 'updatedBy'])` to avoid N+1 queries
4. **Validate Input**: Always validate user input in the controller before passing to the service
5. **Error Handling**: Check for null returns from service methods that might not find records
6. **Client/Sector Validation**: Always validate that client_id and sector_id exist before creating/updating projects

## Relationships with Other Modules

### Client Module
- Projects belong to clients
- Use `GET /api/projects/client/{id}` to get all projects for a client
- When a client is deleted, consider what happens to associated projects

### Sector Module
- Projects belong to sectors
- Use `GET /api/projects/sector/{id}` to get all projects in a sector
- Sectors categorize projects by industry or type

## Future Enhancements

Potential improvements for the Project module:

1. Add start and end date fields
2. Implement project status (active, completed, on-hold)
3. Add project team members (many-to-many with users)
4. Add project gallery (multiple images)
5. Implement project milestones
6. Add budget and cost tracking
7. Add project tags for better categorization
8. Implement project timeline/Gantt chart
9. Add project documents/attachments
10. Implement project comments/notes

## Troubleshooting

### Images not displaying
- Ensure the storage link is created: `php artisan storage:link`
- Check file permissions on `storage/app/public/projects/`
- Verify the APP_URL in `.env` is correct

### Foreign key constraint errors
- Ensure referenced clients and sectors exist
- Check that migrations have been run in the correct order
- Verify client_id and sector_id are valid integers

### Upload fails
- Check `php.ini` settings: `upload_max_filesize` and `post_max_size`
- Ensure the `storage/app/public/projects/` directory exists and is writable
- Verify image file is under 2MB
