diff --git a/pages/07.dependency-injection/02.the-di-container/docs.md b/pages/07.dependency-injection/02.the-di-container/docs.md index 00fea83f..0caee6d9 100644 --- a/pages/07.dependency-injection/02.the-di-container/docs.md +++ b/pages/07.dependency-injection/02.the-di-container/docs.md @@ -136,7 +136,7 @@ You'll notice that the callable used to create a `Logger` object takes two param ### Binding Interfaces -Earlier we discussed the benefits of using interfaces, as the constructor can accept any class that implement the correct interface: +Earlier we discussed the benefits of using interfaces, as the constructor can accept any class that implements the correct interface: ```php public function __construct(NestInterface $nest) // Accept both `Nest` and `ImprovedNest` diff --git a/pages/08.routes-and-controllers/01.introduction/docs.md b/pages/08.routes-and-controllers/01.introduction/docs.md index b24ce3cd..891a9b33 100644 --- a/pages/08.routes-and-controllers/01.introduction/docs.md +++ b/pages/08.routes-and-controllers/01.introduction/docs.md @@ -32,8 +32,8 @@ if (isset($_POST)) { This is what is commonly referred to as ***spaghetti code***. All of the logic and presentation for a feature is mixed up into a single file. There is little or no object-oriented design, and probably a lot of repetitive code from one feature to the next. It's also really difficult to write clean HTML when we're building it with `echo` statements and interpolating all sorts of PHP statements with the HTML content. -MVC organizes our application into three main domains - the **model**, which represents our [database](/database) entities and other types of encapsulated logic, the **view**, which generates the final output (often HTML) that the user receives, and the **controller**, which controls the flow of interaction between the model and the view (and may contain some logic of its own as well). +MVC organizes our application into three main domains - the **model**, which represents our [database](/database) entities and other types of encapsulated logic; the **view**, which generates the final output (often HTML) that the user receives; and the **controller**, which controls the flow of interaction between the model and the view (and may contain some logic of its own as well). -UserFrosting uses a templating engine called [Twig](/templating-with-twig) to handle the rendering of HTML output in the view. UserFrosting's model consists of a set of [Eloquent](https://laravel.com/docs/8.x/eloquent) models for handling interactions with the database, as well as a number of other accessory classes that perform most of heavy lifting for your application. We'll talk about both of these in later chapters. +UserFrosting uses a templating engine called [Twig](/templating-with-twig) to handle the rendering of HTML output in the view. UserFrosting's model consists of a set of [Eloquent](https://laravel.com/docs/10.x/eloquent) models for handling interactions with the database, as well as a number of other accessory classes that perform most of the heavy lifting for your application. We'll talk about both of these in later chapters. In this chapter, we discuss UserFrosting's **controller**, which is based around the [Slim 4](https://www.slimframework.com/docs/v4/) microframework. Whenever you are looking to add a new page or feature to your application, you probably want to start with the controller. diff --git a/pages/08.routes-and-controllers/02.REST/02.restful-responses/docs.md b/pages/08.routes-and-controllers/02.REST/02.restful-responses/docs.md index 1d79cea3..029168bd 100644 --- a/pages/08.routes-and-controllers/02.REST/02.restful-responses/docs.md +++ b/pages/08.routes-and-controllers/02.REST/02.restful-responses/docs.md @@ -20,7 +20,7 @@ The default status code used by the `Response` object. You should use this code You should use this whenever you permanently rename a route - especially for pages! You want the old route to automatically resolve to your new URL, otherwise this could hurt your search engine rankings. -Recommended practice is to create a new route definition class in your Sprinkle and keep your redirect routes together. The [redirect helper](https://www.slimframework.com/docs/v4/objects/routing.html#redirect-helper) can help to perform the actual link between the old and the new route : +Recommended practice is to create a new route definition class in your Sprinkle and keep your redirect routes together. Slim's [redirect helper](https://www.slimframework.com/docs/v4/objects/routing.html#redirect-helper) assists in performing the actual link between the old and the new route : ```php checkAccess($currentUser, 'uri_users')) { The default exception handler that handles `ForbiddenException`s will automatically generate an error message/page response with a 403 response code. -In some cases, you may not want to disclose to unauthorized users that the resource even _exists_. In this case, you can [override](/advanced/error-handling) the `ForbiddenExceptionHandler` with your own handler and have it return a 404 error instead. +In some cases, you may not want to disclose to unauthorized users that the resource even _exists_. In this case, you can [override](/advanced/error-handling#creating-a-custom-exception-handler) the `ForbiddenExceptionHandler` with your own handler and have it return a 404 error instead. ### 404 (Not Found) @@ -111,7 +111,7 @@ This code is automatically returned by the router when a route exists for a give ### 429 (Too Many Requests) -This code is returned by the [throttler](/routes-and-controllers/client-input/throttle), when a request's rate limit has been exceeded. +This code is returned by the [throttler](/routes-and-controllers/client-input/throttle) when a request's rate limit has been exceeded. ### 500 (Internal Server Error) @@ -123,4 +123,4 @@ By default when an exception is thrown and no registered exception handler is fo ### 503 (Service Unavailable) -You should return this code, for example, if you absolutely need to have your application down for a period of time (for example, for maintenance). +You should return this code when you absolutely need to have your application down for a period of time (for example, for maintenance). diff --git a/pages/08.routes-and-controllers/02.REST/docs.md b/pages/08.routes-and-controllers/02.REST/docs.md index f8687039..65bba808 100644 --- a/pages/08.routes-and-controllers/02.REST/docs.md +++ b/pages/08.routes-and-controllers/02.REST/docs.md @@ -17,7 +17,7 @@ When HTTP was first designed, it was meant to reflect the transactional nature o A url is simply a way of identifying a resource. The HTTP method then tells the server what the client wants to _do_ with that resource. You can think of this as the grammar of a natural language, with the method acting as the verb and the url as the object of a sentence. Together, a specific url and method are commonly referred to as an **endpoint**. -In the years since, there has been a tendency to build more abstractions on top of this very basic language. However, we have been seeing lately an effort to get back to the roots of HTTP as it was intended to be used - this is what people are commonly referring to when they talk about [REST](https://en.wikipedia.org/wiki/Representational_state_transfer). +In the years since, there has been a tendency to build more abstractions on top of this very basic language. However, we have been seeing lately an effort to get back to the roots of HTTP as it was intended to be used - this is what people are commonly referring to when they talk about [REST](https://en.wikipedia.org/wiki/REST). ## REST and PHP @@ -36,7 +36,7 @@ www/ Then you would be able to access the page at `http://example.com/myNewbieProject/owls/barn_owl.php`. Most web servers are configured to automatically map the portion of the url after the scheme (`http://example.com/`) to an actual file in the document root directory, where each slash represents a subdirectory and the last portion corresponds to the name of a PHP script. -This system is easy for newbies to understand, but it has a lot of limitations. First, it requires you to have a separate file for each web page that you want to generate. In a real application, you may want to have hundreds of thousands of very similar web pages, and it doesn't make sense to require a separate file for each page. Also, it couples the structure of your **code** to the structure of your **urls**. To generate semantically useful urls, we'd have to have a messy and complicated maze of directories on our server. +This system is easy to understand, but it has a lot of limitations. First, it requires you to have a separate PHP file for each web page that you want to generate. In a real application, you may want to have hundreds of thousands of very similar web pages, and it doesn't make sense to require a separate file for each page. Also, it couples the structure of your **code** to the structure of your **urls**. To generate semantically useful urls, we'd have to have a messy and complicated maze of directories on our server. Within each file, you'd also need control structures (if/else) to have it do different things depending on which HTTP method was used. All of this makes it very cumbersome to implement a RESTful design for your endpoints. @@ -44,6 +44,4 @@ Within each file, you'd also need control structures (if/else) to have it do dif UserFrosting, and most other modern frameworks and content management systems, use a [front controller](/routes-and-controllers/front-controller) to solve this problem. With a front controller, the web server is configured to pass all requests to a single script - `index.php`. From there, the request endpoint is interpreted, and a matching **route** is invoked. These routes do not need to be defined in PHP files that match the name of the url. Thus, we've **decoupled** the endpoints from the directory structure of our application. -Having done this, we are now free to choose any url and method for any request - whether it's a page, form submission, API request, or whatever. This allows us to design our endpoints according to the principles of REST. The next section explains how we should think when we're choosing the urls and methods that our application exposes to the client. - -In the [next section](/routes-and-controllers/front-controller), we'll talk about how and where routes are defined in UserFrosting, so you can start implementing your endpoints. +Having done this, we are now free to choose any url and method for any request - whether it's a page, form submission, API request, or whatever. This allows us to more easily design our endpoints according to the principles of REST. The next section explains how we should think when we're choosing the urls and methods that our application exposes to the client. diff --git a/pages/08.routes-and-controllers/03.front-controller/docs.md b/pages/08.routes-and-controllers/03.front-controller/docs.md index 8316d41e..165abf9a 100644 --- a/pages/08.routes-and-controllers/03.front-controller/docs.md +++ b/pages/08.routes-and-controllers/03.front-controller/docs.md @@ -8,7 +8,7 @@ taxonomy: The front controller is a collective term for the **routes** that your web application defines for its various **endpoints**. This is how UserFrosting links urls and methods to your application's code. -Sprinkles define their routes in classes and register them in their Recipe. Inside, there are two ways to define a route - as a closure, or as a reference to a [controller class](/routes-and-controllers/controller-classes) method. We will use a simple closure example here to understand the concepts, but for your application **you should create controller classes**. +Sprinkles define their routes in classes and register them in their Recipe. There are two ways to define a route - as a closure, or as a reference to a [controller class](/routes-and-controllers/controller-classes) method. We will use a simple closure example here to illustrate the concept, but for your application **you should create controller classes**. The following is an example of a `GET` route: @@ -28,12 +28,12 @@ $app->get('/api/users/u/{username}', function (string $username, Request $reques }); ``` -This is a very simplified example, but it illustrates the main features of a route definition. First, there is the call to `$app->get()`. The `get` refers to the HTTP method for which this route is defined. You may also define `post()`, `put()`, `delete()`, `options()`, and `patch()`, routes. +This is a very simplified example, but it illustrates the main features of a route definition. First, there is the call to `$app->get()`. The `get` refers to the HTTP method for which this route is defined. You may also define `post()`, `put()`, `delete()`, `options()`, and `patch()` routes. -The first parameter is the url for the route. Routes can contain placeholders, such as `{username}` to match arbitrary values in a portion of the url. These placeholders can even be matched according to regular expressions. See the [Slim documentation ](https://www.slimframework.com/docs/v4/objects/routing.html#route-placeholders) for a complete guide to url placeholders. +The first parameter is the url for the route. Routes can contain placeholders such as `{username}` to match arbitrary values in a portion of the url. These placeholders can even be matched according to regular expressions: see the [Slim documentation ](https://www.slimframework.com/docs/v4/objects/routing.html#route-placeholders) for a complete guide to url placeholders. -After the url comes the **closure**, where we place our actual route logic. In this example, the closure uses three parameters - the **placeholder** variable, the **request** object (which contains all the information from the client request) and the **response** object (which is used to build the response that the server sends back to the client). These parameters can vary from routes to routes. Behind the scenes, PHP-DI will intelligently inject the proper services and variables into the closure, more on that in a bit. +After the url comes the **closure**, where we place our actual route logic. In this example, the closure uses three parameters - a **placeholder** variable, the **request** object (which contains all the information from the client request) and the **response** object (which is used to build the response that the server sends back to the client). These parameters can vary from route to route. Behind the scenes, PHP-DI will intelligently inject the proper services and variables into the closure--more on that in a bit. -In the example above, we use the `username` placeholder to look up information for that user from the database. We then use the value of the `format` query parameter from the request, to decide what to put in the response. You'll notice that the closure writes to the body of the `$response` object before returning. Slim will return the response to the client, perhaps modifying it further through the use of [middleware](https://www.slimframework.com/docs/v4/concepts/middleware.html) first. +In the example above, we use the `username` placeholder to look up information for that user from the database. We then use the value of the `format` query parameter from the request to decide what to put in the response. You'll notice that the closure writes to the body of the `$response` object before returning. Slim will return the response to the client, perhaps first modifying it further through the use of [middleware](/advanced/middlewares). For a more detailed guide to routes, we highly recommend that you read the [Slim documentation](https://www.slimframework.com/docs/v4/objects/routing.html). diff --git a/pages/08.routes-and-controllers/04.controller-classes/docs.md b/pages/08.routes-and-controllers/04.controller-classes/docs.md index 41044ce2..60f38b7e 100644 --- a/pages/08.routes-and-controllers/04.controller-classes/docs.md +++ b/pages/08.routes-and-controllers/04.controller-classes/docs.md @@ -8,12 +8,12 @@ taxonomy: To keep your code organized, it is highly recommended to use **controller** or **action** classes. By separating your code in this way, you can easily see a list of the endpoints that a Sprinkle defines by looking at its route definitions. The implementation can then be tucked away in separate files. -[notice=tip]**Controller or Action?** Controller and Action classes are basically the same thing. The only difference is an **Action** handles one specific action (route) per class, while a **Controller** can handles many actions or routes. In other words, a controller class is composed of several actions, with one method per route. In this guide, we often use the term **Controller** for both implementation for simplicity, but we highly recommend you use the "one action = one class" method. +[notice=tip]**Controller or Action?** Controller and Action classes are basically the same thing. The only difference is an **Action** handles one specific action (route) per class, while a **Controller** can handle many actions or routes. In other words, a controller class is composed of several actions, with one method per route. In this guide, we often use the term **Controller** for both implementations for simplicity, but we highly recommend you use the "one action = one class" method. [/notice] ## Defining Controller Classes -A controller class doesn't require to implement any Interface or extends any other class. Their are usually standalone classes, located in `src/Controller/`: +A controller class doesn't require implementing any Interface or extending any other class. They are usually standalone classes, located in `src/Controller/`: ```php -[notice=tip]For this reason, if you plan to distribute your Sprinkle as a Community Sprinkle, it can be helpful to split your routes into multiple classes instead of a single big class. It will be easier for dependent sprinkle to cherry pick the routes they need or want to overwrites.[/notice] +Another workaround is to [override](/advanced/custom-models#overwriting-existing-map) the Action class called in the dependent Sprinkle's route. + +[notice=tip]For this reason, if you plan to distribute your Sprinkle as a Community Sprinkle, it can be helpful to split your routes into multiple classes instead of a single big class. It will be easier for an inheriting sprinkle to cherry pick the routes they want to keep or overwrite.[/notice] diff --git a/pages/08.routes-and-controllers/06.client-input/01.validation/docs.md b/pages/08.routes-and-controllers/06.client-input/01.validation/docs.md index 12e6e1cb..b349ca8b 100644 --- a/pages/08.routes-and-controllers/06.client-input/01.validation/docs.md +++ b/pages/08.routes-and-controllers/06.client-input/01.validation/docs.md @@ -14,7 +14,7 @@ Data from the outside world is the Achilles' heel of modern interactive web serv ### Server-side -Many new developers [fail to realize](http://security.stackexchange.com/questions/147216/hacker-used-picture-upload-to-get-php-code-into-my-site) that a malicious user could submit any type of request, with any content they like, to your server at any time. This is possible regardless of the forms and widgets that your web application presents to the client - it is a trivial matter to change their behavior using the [browser console](/troubleshooting/debugging), or bypass them completely using a command line tool such as [cURL](https://curl.haxx.se/docs/httpscripting.html). +Many new developers [fail to realize](http://security.stackexchange.com/questions/147216/hacker-used-picture-upload-to-get-php-code-into-my-site) that a malicious user could submit any type of request, with any content they like, to your server at any time. This is possible regardless of the forms and widgets that your web application presents to the client - it is a trivial matter to change their behavior using the [browser console](/troubleshooting/debugging#client-side-debugging), or bypass them completely using a command line tool such as [cURL](https://curl.haxx.se/docs/httpscripting.html). For this reason, it is **imperative** to validate user input on the server side - *after* the request has left the control of the submitter. @@ -95,7 +95,7 @@ use UserFrosting\Fortress\RequestSchema; $schema = new RequestSchema('schema://requests/contact.yaml'); ``` -Notice that we've used the `schema://` stream wrapper, rather than having to hardcode an absolute file path. This allows UserFrosting to automatically scan the `schema/` subdirectories of each loaded Sprinkle for `contact.yaml`, and using the version found in the most recently loaded Sprinkle. +Notice that we've used the `schema://` stream wrapper, rather than having to hardcode an absolute file path. This tells UserFrosting to automatically scan the `schema/` subdirectories of each loaded Sprinkle for `contact.yaml`, and use the version found in the most recently loaded Sprinkle. ### Generating Client-side Rules @@ -105,7 +105,7 @@ To automatically generate a set of client-side rules compatible with the [jQuery // This line goes at the top of your file use UserFrosting\Fortress\Adapter\JqueryValidationJsonAdapter; -// This assume $translator as been properly injected into the class or method. +// This assume $translator has been properly injected into the class or method. $validator = new JqueryValidationJsonAdapter($this->translator); ``` @@ -133,7 +133,7 @@ If you visit the page `/account/register` and use "View Source", you can see how To process data on the server, use the `RequestDataTransformer` and `ServerSideValidator` classes. -`RequestDataTransformer` will filter out any submitted fields that are not defined in the request schema (whitelisting), and perform any field [transformations](#transformations) as defined in your schema: +`RequestDataTransformer` will filter out any submitted fields that are not defined in the request schema (whitelisting), and perform any field [transformations](#transformations) defined in your schema: ```php // These lines goes at the top of your file @@ -161,7 +161,7 @@ It's worth pointing out that we do not do any sort of "sanitization" on submitte Once you have filtered and whitelisted the input data, you can perform validation using `ServerSideValidator`: ```php -// These lines goes at the top of your file +// These lines go at the top of your file use UserFrosting\Fortress\RequestSchema; use UserFrosting\Fortress\Transformer\RequestDataTransformer; use UserFrosting\Fortress\Validator\ServerSideValidator; @@ -193,7 +193,7 @@ A field consists of a unique **field name**, along with a set of attributes. The | ----------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `transformations` | Yes | The `transformations` attribute specifies an ordered list of **data transformations** to be applied to the field. | | `validators` | Yes | The `validators` attribute specifies an ordered list of **validators** to be applied to the field. | -| `default` | Yes | The `default` attribute specifies a default value to be used if the field has not been specified in the HTTP request. When a default value is applied, the data transformations and validators for the field shall be ignored. | +| `default` | Yes | The `default` attribute specifies a default value to be used if the field is not specified in the HTTP request. When a default value is applied, the data transformations and validators for the field shall be ignored. | **Example:** @@ -292,7 +292,7 @@ The following validators are available: | `not_matches` | Specifies that the value of the field must **not** be equivalent to the value of `field`. | | `not_member_of` | Specifies that the value of the field must **not** appear in the specified `values` array. | | `numeric` | Specifies that the value of the field must represent a numeric (floating-point or integer) value. | -| `range` | Specifies a numeric interval bound on the field's value. The `range` validator supports the following attributes: | +| `range` | Specifies a numeric interval bound on the field's value. | | `regex` | Specifies that the value of the field must match a specified Javascript- and PCRE-compliant regular expression. | | `required` | Specifies that the field is a required field. If the field is not present in the HTTP request, validation will fail unless a default value has been specified for the field. | | `telephone` | Specifies that the value of the field must represent a valid telephone number. | @@ -364,11 +364,11 @@ screech: message: You did not provide a valid screech. ``` -[notice=warning]Regular expressions should _not_ be wrapped in quotes in YAML. Also the jQuery Validation plugin, for some unholy reason, wraps regular expressions on the client side with `^...$`. Please see [this issue](https://github.com/jquery-validation/jquery-validation/issues/1967).[/notice] +[notice=warning]Regular expressions should _not_ be wrapped in quotes in YAML. Also the jQuery Validation plugin wraps regular expressions on the client side with `^...$`. Please see [this issue](https://github.com/jquery-validation/jquery-validation/issues/1967).[/notice] ### Limit rules to server or client only -Sometimes, you only want a validation rule to be applied server-side but not in Javascript on the client side, or vice versa. For example, there may be forms that contain hidden data that needs to be validated on the server-side, but is not directly manipulated by the user in the browser. Thus, these fields would not need client-side validation rules. +Sometimes, you want a validation rule to be only applied server-side but not in Javascript on the client side, or vice versa. For example, there may be forms that contain hidden data that needs to be validated on the server-side, but is not directly manipulated by the user in the browser. Thus, these fields would not need client-side validation rules. Alternatively, there might be fields that appear in the form that should be validated for the sake of user experience, but are not actually used by (or even sent to) the server. diff --git a/pages/08.routes-and-controllers/06.client-input/02.csrf-guard/docs.md b/pages/08.routes-and-controllers/06.client-input/02.csrf-guard/docs.md index c0f46f7f..729f9497 100644 --- a/pages/08.routes-and-controllers/06.client-input/02.csrf-guard/docs.md +++ b/pages/08.routes-and-controllers/06.client-input/02.csrf-guard/docs.md @@ -81,4 +81,4 @@ To bypass CSRF protection, you can map regular expressions to arrays of HTTP met Any requests whose URL matches one of these regular expressions, and whose method matches one of the mapped methods, will be automatically exempted from loading the CSRF middleware. This means that the CSRF token will not be retrieved (for `GET` requests) or checked (for `POST`, `PUT`, `DELETE`, and `PATCH` requests). -Requests for [raw assets](/asset-management/basic-usage#PublicassetURLs) are automatically exempted from CSRF protection in the `config` [service](/services/default-services#config). +Requests for [raw assets](/asset-management/basic-usage) are automatically exempted from CSRF protection in the `config` [service](/services/default-services#userfrosting-config-config). diff --git a/pages/08.routes-and-controllers/06.client-input/03.throttle/docs.md b/pages/08.routes-and-controllers/06.client-input/03.throttle/docs.md index c3a0c2a9..6ad3017f 100644 --- a/pages/08.routes-and-controllers/06.client-input/03.throttle/docs.md +++ b/pages/08.routes-and-controllers/06.client-input/03.throttle/docs.md @@ -10,7 +10,7 @@ People tend to be bad at picking strong passwords. [Publicly available lists of Complicated password policies (other than password length) [tend to backfire spectacularly](http://security.stackexchange.com/questions/6095/xkcd-936-short-complex-password-or-long-dictionary-passphrase/6116#6116). A good alternative then, is to slow down brute-force attackers to the point where it would take an inordinate amount of time to crack all but the easiest passwords. -This strategy is known as **throttling**, and should be employed in any route that could allow an attacker to gain unauthorized access or otherwise affect other users' accounts, such as the login and password recovery routes. UserFrosting supports throttling based on either IP address, or some other chosen pieces of information (e.g. username). +This strategy is known as **throttling**, and should be employed in any route that could allow an attacker to gain unauthorized access or otherwise affect other users' accounts, such as the login and password recovery routes. UserFrosting supports throttling based on either IP address or some other chosen pieces of information (e.g. username). ## Defining throttles diff --git a/pages/08.routes-and-controllers/06.client-input/docs.md b/pages/08.routes-and-controllers/06.client-input/docs.md index 794780c6..cabd41e2 100644 --- a/pages/08.routes-and-controllers/06.client-input/docs.md +++ b/pages/08.routes-and-controllers/06.client-input/docs.md @@ -6,11 +6,11 @@ taxonomy: category: docs --- -There is no such thing as a `$_GET` array or a `$_POST` array - at least, not according to the [HTTP specifications](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Message_Format). These superglobals are merely constructs offered by PHP to make your life more "convenient". +There is no such thing as a `$_GET` array or a `$_POST` array - at least, not according to the [HTTP specifications](https://en.wikipedia.org/wiki/HTTP#Message_Format). These superglobals are merely constructs offered by PHP to make your life more "convenient". The problem with `$_GET` and `$_POST` is that despite their names, they don't actually have anything to do with the request methods `GET` and `POST` at all. Rather, `$_GET` retrieves variables from the [URL query string](http://php.net/manual/en/reserved.variables.get.php), while `$_POST` retrieves variables submitted as part of a form. For some reason, PHP has arbitrarily tangled up the *data representation* with the request method. Not to mention that [global variables are a terrible idea](http://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil) in general! -Fortunately, Slim saves the day by providing a more HTTP-friendly way of accessing data supplied by the request. Rather than thinking in terms of "GET parameters" and "POST parameters", we should think in terms of the different components of the HTTP request. [Recall](/routes-and-controllers/rest) that a request consists of a **url**, **method**, **headers**, and optionally, a **body** - any of these could potentially contain data that we'd want to get at in one of our controller methods. +Fortunately, Slim saves the day by providing a more HTTP-friendly way of accessing data supplied by the request. Rather than thinking in terms of "GET parameters" and "POST parameters", we should think in terms of the different components of the HTTP request. [Recall](/routes-and-controllers/rest) that a request consists of a **url**, **method**, **headers**, and optionally, a **body** - any of these could potentially contain data that we'd want to use in one of our controller methods. ## Retrieving URL Parameters @@ -45,7 +45,7 @@ Slim [provides a number of methods](https://www.slimframework.com/docs/v4/object ### Form Data -To get client-submitted form data, simply use the `getParsedBody()` method on `$request`: +To get client-submitted form data, use the `getParsedBody()` method on `$request`: ```php // request was POST /api/users, with form values username => 'kevin' and password => 'hunter2' @@ -60,7 +60,7 @@ echo $params['username']; ### Uploaded Files -To get at files that have been [encoded in the HTTP request body](http://stackoverflow.com/a/26791188/2970321) (for example using an HTML `getUploadedFiles();