# Attributes

The core feature of this bundle, the below attributes mainly exist to facilitate handling requests and responses.

## `RequestBody`

This attribute is one with the most features in it. It decodes the request message body and passes it to a method argument in a controller. It supports scalars, objects, collections, and file-based method arguments.

### Scalars

For scalar method arguments the `ConverterManager` is used if necessary.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\RequestBody;
use Symfony\Component\Routing\Attribute\Route;

class MessageController
{
    #[Route('/messages', methods: ['POST'])]
    public function sendMessage(#[RequestBody] string $message)
    {
        // ..
    }
}
```

### Objects

On the contrary to the scalar types, it uses the `MessageBodyMapperManager` to decode the request message body.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\RequestBody;
use Symfony\Component\Routing\Attribute\Route;

class UserController
{
    #[Route('/users', methods: ['POST'])]
    public function registerUser(#[RequestBody] UserRegistrationData $data)
    {
        // ..
    }
}
```

### Collections

Unfortunately, at the current state, PHP does not support declaring a collection with the desired type. In order to achieve that, the `type` parameter has been provided. It accepts types of well-known syntax `type[]`.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\RequestBody;
use Symfony\Component\Routing\Attribute\Route;

class RoleController
{
    #[Route('/roles', methods: ['POST'])]
    public function createRoles(#[RequestBody('RoleCreationData[]')] array $rolesData)
    {
        // ..
    }
}
```

### Files

When the request message body contains a file, you can declare your method argument as `UploadedFile`, `File`, `SplFileInfo` or `SplFileObject`. Thanks to the `TmpFileUtils` you can query those objects for any public method like `getFilename()`, `getPathname()` and so on.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\RequestBody;
use Symfony\Component\Routing\Attribute\Route;

class PictureController
{
    #[Route('/pictures', methods: ['POST'])]
    public function savePicture(#[RequestBody] \SplFileInfo $file)
    {
        // ...
    }
}
```

#### UploadedFile

It's a bit a special one, it covers all public methods like `getClientOriginalName()` or `getClientMimeType()`.

The `getClientOriginalName()` uses the `Content-Disposition` header. In general, this header is used in responses and in `multipart/form-data` requests. For this to work, the `Content-Disposition` must be set to `inline`, and the `filename` parameter must be provided.

On the contrary, the `getClientMimeType()` uses the `Content-Type` header.

```
POST /pictures HTTP/1.1
Content-Type: image/png
Content-Disposition: inline; filename=foo.png
```

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\RequestBody;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Routing\Attribute\Route;

class PictureController
{
    #[Route('/pictures', methods: ['POST'])]
    public function savePicture(#[RequestBody] UploadedFile $file)
    {
        $filename = $file->getClientOriginalName(); // foo.png
        $mimeType = $file->getClientMimeType(); // image/png
        $path = $file->getPathname();
        // ...
    }
}
```

## `RequestParam`

It binds a request parameter to a method argument in a controller. It supports `multipart/form-data` requests and that means you can even access a file through this attribute.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\RequestParam;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Routing\Attribute\Route;

class PictureController
{
    #[Route('/pictures', methods: ['POST'])]
    public function savePicture(
        #[RequestParam] UploadedFile $file, 
        #[RequestParam('desc')] string $description
    ) {
        // ...
    }
}
```

{% hint style="info" %}
If a method argument cannot be nullable, and a request parameter cannot be found, a **400** HTTP response is returned.
{% endhint %}

## `QueryParam`

It binds a query parameter to a method argument in a controller.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\QueryParam;
use Symfony\Component\Routing\Attribute\Route;

class UserController
{
    #[Route('/users', methods: ['GET'])]
    public function getUsers(
        #[QueryParam('q')] ?string $searchQuery, 
        #[QueryParam] ?int $limit
    ) {
        // ...
    }
}
```

{% hint style="info" %}
If a method argument cannot be nullable, and a query parameter cannot be found, a **400** HTTP response is returned.
{% endhint %}

## `QueryParams`

It is similar to the `@QueryParam`, but instead of a single query parameter, it takes all query parameters to pass them as a single method argument in a controller. This attribute requires an argument to be of an object type.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\QueryParams;
use Symfony\Component\Routing\Attribute\Route;

class UserController
{
    #[Route('/users', methods: ['GET'])]
    public function getUsers(#[QueryParams] UserQueryData $queryData)
    {
        // ...
    }
}
```

## `RequestCookie`

It binds a cookie value to a method argument in a controller.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\RequestCookie;
use Symfony\Component\Routing\Attribute\Route;

class SessionController
{
    #[Route('/sessions/current', methods: ['GET'])]
    public function getCurrentSessionInformation(#[RequestCookie] string $sessionId)
    {
        // ...
    }
}
```

{% hint style="info" %}
If a method argument cannot be nullable, and a request cookie cannot be found, a **400** HTTP response is returned.
{% endhint %}

## `RequestHeader`

It binds a header value to a method argument in a controller.

```php
namespace App\Controller;

use Jungi\FrameworkExtraBundle\Attribute\RequestHeader;
use Symfony\Component\Routing\Attribute\Route;

class ReportController
{
    #[Route('/reports/monthly-revenue', methods: ['GET'])]
    public function getMonthlyRevenueReport(#[RequestHeader('Accept-Language')] string $acceptableLanguage)
    {
        // ...
    }

    #[Route('/reports/monthly-visitors', methods: ['GET'])]
    public function getMonthlyVisitorsReport(#[RequestHeader('Accept')] array $acceptableMediaTypes)
    {
        // ...
    }
}
```

{% hint style="info" %}
If a method argument cannot be nullable, and a request header cannot be found, a **400** HTTP response is returned.
{% endhint %}

As you can see in the example when a method argument is declared as `array` it will pass all values of the header to it.
