Extend Request class in Laravel 5
Asked Answered
O

6

17

I'm new to Laravel (only experienced Laravel 5, so no legacy hang up here)

I'd like to know how to extend the core Request class. In addition to how to extend it, i'd like to know if it's a wise design decision to do so.

I've read through the documentation extensively (especially with regards to registering service providers and the manner in which it provides Facades access to entries within the dependency container) - but I can see (and find) no way to replace the \Illuminate\Http\Request instance with my own

Ogren answered 10/5, 2015 at 19:3 Comment(3)
What are you trying to accomplish? Why would you want to replace it with your own?Alfano
I'm working on a package on GH atm - github.com/echo-it/laravel-jsonapi. You can see from the readme that they have defined their own request, but there is so much redundant code where they are populating it with information from the core Request. I actually have it pretty much working - by updating the request intance in the index.php - but this doesn't feel correct. In addition it doesnt work with unit testing..Ogren
I think it depends on the methods you are trying to overwrite e.g. json method. You can extend Illuminate\Http\Request and create a facadeSupervene
A
35

Here is Official Document: Request Lifecycle

Content of app/Http/CustomRequest.php

<?php namespace App\Http;

use Illuminate\Http\Request as BaseRequest;

class CustomRequest extends BaseRequest {
    // coding
}

add this line to public/index.php

$app->alias('request', 'App\Http\CustomRequest');

after

app = require_once __DIR__.'/../bootstrap/app.php';

change the code at public/index.php

Illuminate\Http\Request::capture()

to

App\Http\CustomRequest::capture()
Abatement answered 15/6, 2015 at 8:6 Comment(4)
I think as of 5.x the idea is to extend the abstract class app/Http/Requests/Request.php. This was made for convenience I think. Might be mistaken.Forum
This works, but I need extends the request only for specified controllers and not for all, but doing this, I'm getting the extended request in all controllers even I typehint the base request. Some ideas?Columelliform
To extend the request only for specific controllers or specific functions take a look at my answer.Bodoni
Not bad however works only if you use Request directly in your controller/service/etc. Your custom version won't be used when in a case where it's supposed to be a parent. Example: FormRequest will still use original Request.Ornithic
T
4

I guess you will have to extend also RequestForm. I use trait to avoid code duplication. Code below is relevant for Laravel 5.3.

app/Http/ExtendRequestTrait.php

<?php
namespace App\Http\ExtendRequestTrait;

trait ExtendRequestTrait {
  methodFoo(){}
  methodBar(){}
}

app/Http/Request.php

<?php
namespace App\Http;
use Illuminate\Http\Request as BaseRequest;

class Request extend BasicRequest {
  use ExtendRequestTrait;
}

app/Http/FormRequest.php

<?php
namespace App\Http;
use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;

class FormRequest extend BasicFormRequest {
  use ExtendRequestTrait;
}

For phpunit test working you will have to override call method to make it using right Request class here Request::create.

test/TestCase.php

<?php
use App\Http\Request;

abstract class TestCase extends Illuminate\Foundation\Testing\TestCase{

   // just copy Illuminate\Foundation\Testing\TestCase `call` method 
   // and set right Request class
   public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
    {
        $kernel = $this->app->make('Illuminate\Contracts\Http\Kernel');

        $this->currentUri = $this->prepareUrlForRequest($uri);

        $this->resetPageContext();

        $request = Request::create(
            $this->currentUri, $method, $parameters,
            $cookies, $files, 
            array_replace($this->serverVariables, $server), 
            $content
        );

        $response = $kernel->handle($request);

        $kernel->terminate($request, $response);

        return $this->response = $response;
    }

}

and don't forget to switch Illuminate\Http\Request::capture() to App\Http\Request::capture() in public/index.php file and to add $app->alias('request', 'App\Http\Request'); after or inside $app = require_once __DIR__.'/../bootstrap/app.php';

Tridentine answered 27/9, 2016 at 22:4 Comment(2)
Is this still accurate for Laravel 5.4? My request's $content seems to be empty in my feature/integration tests.Deface
Thanks for the comment, helped me get tests working, you can copy the call method from vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php as it is slightly different in never versions of Laravel. In some rare cases that doesn't work either so I made a function to create my request class based on the request. ``` protected static function getBaseRequest() { $request = request(); if (is_a($request, \Illuminate\Http\Request::class)) { return BaseRequest::createFrom($request); } return $request; } ```Throughout
B
4

Yerkes answer inspired me to write a custom class, for use with pagination, but only on specific requests

<?php

namespace App\Http\Requests;

use Illuminate\Http\Request;

class PaginatedRequest extends Request
{
    public function page(): int
    {
        return max(1, (int) ($this['page'] ?? 1));
    }

    public function perPage(): int
    {
        $perPage = (int) ($this['per_page'] ?? 100);
        return max(1, min($perPage, 500));
    }

    public function offset(): int
    {
        return ($this->page() - 1) * $this->perPage();
    }
}

I then also had to register a new ServiceProvider in /config/app.php, which looks like

<?php

namespace App\Providers;

use App\Http\Requests\PaginatedRequest;
use Illuminate\Support\ServiceProvider;

class PaginatedRequestServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app->resolving(PaginatedRequest::class, function ($request, $app) {
            PaginatedRequest::createFrom($app['request'], $request);
        });
    }
}

Now I can simply inject the PaginatedRequest in my controller methods only when I need it

<?php

namespace App\Http\Controllers;

use App\Http\Requests\PaginatedRequest;

class MyController
{
    public function __invoke(PaginatedRequest $request)
    {
        $request->page();
        // ...
    }
}
Batish answered 18/10, 2018 at 8:32 Comment(0)
C
3

I was working on the same issue today and I think it's worth mention that you may just change

Illuminate\Http\Request::capture()

to

App\Http\CustomRequest::capture()

without adding line

$app->alias('request', 'App\Http\CustomRequest');

because inside capture() method laravel actually binds provided class to service container with 'request' as a key

Chanticleer answered 23/1, 2016 at 15:12 Comment(2)
NB!!! $app->alias('request', 'App\Http\CustomRequest'); is required!!! If omitted, then, e.g. POST parameters will not be present in App\Http\CustomRequest object, but old Illuminate\Http\Request.Padrone
Works perfectly if you want to use a custom request for specific controllers. Thanks! (btw: I didn't have to add $app->alias('request', 'App\Http\CustomRequest'); to my app.)Lambdoid
B
1

I was able to add custom request object using FormRequest in Laravel 5.5 as follows.

First, just create FormRequest:

php artisan make:request MyRequest

Then just make it look like this:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class MyRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

You can then use MyRequest as drop-in replacement in any function that takes Request as parameter:

public function get(MyRequest $request)
{
}

I do realize that FormRequests are actually meant to be used for a different purpose, but whatever works.

Documentation on FormRequest: https://laravel.com/docs/5.0/validation#form-request-validation

Bodoni answered 5/4, 2018 at 21:29 Comment(1)
That's the problem. These are meant to deal with form request so adding any other type of functionality there would create a horrible mess. Trust me. Better to extend original Request and add additional functionality there if possible.Ornithic
E
0

In Laravel 5.5.x, I was able to extend the Request class for specific requests using the following:

ExtendedRequest class

<?php declare(strict_types = 1);

namespace App\Http\Request;

use Illuminate\Http\Request;

class ExtendedRequest extends Request {
    public function hello() {
        echo 'hello world!';
    }
}

ExtendedRequestServiceProvider

<?php declare(strict_types = 1);

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Http\Request\ExtendedRequest;

class ExtendedRequestServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(ExtendedRequest::class, function ($app) {
            return ExtendedRequest::capture();
        });
    }
}

Then in your Controllers

<?php

namespace App\Controllers;

use App\Http\Request\ExtendedRequest;

class TestController extends Controller
{
    public function get(ExtendedRequest $request) {
        echo $request->hello();
    }
}

Hope this helps someone.

Electrolytic answered 6/8, 2020 at 12:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.