# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://piku235.gitbook.io/jungiframeworkextrabundle/attributes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
