【问题标题】:Extend Request class in Laravel 5在 Laravel 5 中扩展请求类
【发布时间】:2015-05-10 19:03:56
【问题描述】:

我是 Laravel 新手(只体验过 Laravel 5,所以这里没有遗留问题)

我想知道如何扩展核心 Request 类。除了如何扩展它,我想知道这样做是否是一个明智的设计决定。

我已经广泛阅读了文档(尤其是关于注册服务提供者以及它为 Facades 提供对依赖容器中条目的访问的方式)——但我可以看到(并发现)无法替换 @ 987654322@我自己的实例

【问题讨论】:

  • 你想完成什么?为什么要用自己的替换它?
  • 我正在 GH atm 上开发一个包 - github.com/echo-it/laravel-jsonapi。您可以从自述文件中看到他们已经定义了自己的请求,但是有太多冗余代码,他们用来自核心请求的信息填充它。实际上,我可以通过更新 index.php 中的请求实例来完成它的工作,但这感觉不正确。此外,它不适用于单元测试..
  • 我认为这取决于您尝试覆盖的方法,例如json 方法。你可以扩展 Illuminate\Http\Request 并创建一个外观

标签: php laravel laravel-5


【解决方案1】:

这里是官方文档:Request Lifecycle

app/Http/CustomRequest.php的内容

<?php namespace App\Http;

use Illuminate\Http\Request as BaseRequest;

class CustomRequest extends BaseRequest {
    // coding
}

将此行添加到 public/index.php

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

之后

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

更改 public/index.php 中的代码

Illuminate\Http\Request::capture()

App\Http\CustomRequest::capture()

【讨论】:

  • 我认为从 5.x 开始,想法是扩展抽象类 app/Http/Requests/Request.php。我认为这是为了方便而制作的。可能搞错了。
  • 这行得通,但我只需要为指定的控制器而不是所有控制器扩展请求,但是这样做,即使我输入基本请求,我也会在所有控制器中获得扩展请求。有什么想法吗?
  • 仅针对特定控制器或特定功能扩展请求,请查看我的答案。
  • 不错,但只有在您的控制器/服务/等中直接使用请求时才有效。在它应该是父级的情况下,将不会使用您的自定义版本。示例:FormRequest 仍将使用原始请求。
【解决方案2】:

Yerkes 的回答启发我编写了一个自定义类,用于分页,但仅限于特定请求

<?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();
    }
}

然后我还必须在 /config/app.php 中注册一个新的 ServiceProvider,看起来像

<?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);
        });
    }
}

现在我可以仅在需要时将 PaginatedRequest 注入到我的控制器方法中

<?php

namespace App\Http\Controllers;

use App\Http\Requests\PaginatedRequest;

class MyController
{
    public function __invoke(PaginatedRequest $request)
    {
        $request->page();
        // ...
    }
}

【讨论】:

    【解决方案3】:

    我今天也在处理同样的问题,我认为值得一提的是你可能会改变

    Illuminate\Http\Request::capture()
    

    App\Http\CustomRequest::capture()
    

    不加行

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

    因为在 capture() 方法内部,laravel 实际上将提供的类绑定到服务容器,并以“请求”为键

    【讨论】:

    • 注意!!! $app-&gt;alias('request', 'App\Http\CustomRequest'); 是必需的!!!如果省略,则,例如POST 参数不会出现在 App\Http\CustomRequest 对象中,而是旧的 Illuminate\Http\Request。
    • 如果您想对特定控制器使用自定义请求,则可以完美运行。谢谢! (顺便说一句:我不必将$app-&gt;alias('request', 'App\Http\CustomRequest'); 添加到我的应用程序中。)
    【解决方案4】:

    我猜你还得扩展RequestForm。我使用 trait 来避免代码重复。以下代码与 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;
    }
    

    对于 phpunit 测试工作,您必须覆盖 call 方法以使其在此处使用正确的 RequestRequest::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;
        }
    
    }
    

    不要忘记在public/index.php 文件中将Illuminate\Http\Request::capture() 切换为App\Http\Request::capture(),并在$app = require_once __DIR__.'/../bootstrap/app.php'; 之后或内部添加$app-&gt;alias('request', 'App\Http\Request');

    【讨论】:

    • 这对于 Laravel 5.4 仍然准确吗?在我的功能/集成测试中,我的请求的 $content 似乎是空的。
    • 感谢您的评论,帮助我进行了测试,您可以从 vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php 复制调用方法,因为它在从未版本的 Laravel 中略有不同。在极少数情况下也不起作用,所以我创建了一个函数来根据请求创建我的请求类。 ``` 受保护的静态函数 getBaseRequest() { $request = request(); if (is_a($request, \Illuminate\Http\Request::class)) { return BaseRequest::createFrom($request); } 返回$请求; } ```
    【解决方案5】:

    我能够在 Laravel 5.5 中使用 FormRequest 添加自定义请求对象,如下所示。

    首先,创建FormRequest:

    php artisan make:request MyRequest

    然后让它看起来像这样:

    <?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 [
                //
            ];
        }
    }
    

    然后,您可以在任何以Request 作为参数的函数中使用MyRequest 作为替代:

    public function get(MyRequest $request)
    {
    }
    

    我确实意识到FormRequests 实际上是为了用于不同的目的,但无论如何都行得通。

    FormRequest 上的文档:https://laravel.com/docs/5.0/validation#form-request-validation

    【讨论】:

    • 这就是问题所在。这些是为了处理表单请求,因此添加任何其他类型的功能都会造成可怕的混乱。相信我。如果可能,最好扩展原始请求并在那里添加其他功能。
    【解决方案6】:

    在 Laravel 5.5.x 中,我可以使用以下方法为特定请求扩展 Request 类:

    扩展请求类

    <?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();
            });
        }
    }
    

    然后在你的控制器中

    <?php
    
    namespace App\Controllers;
    
    use App\Http\Request\ExtendedRequest;
    
    class TestController extends Controller
    {
        public function get(ExtendedRequest $request) {
            echo $request->hello();
        }
    }
    

    希望这对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-03
      • 2018-04-29
      • 2017-11-23
      • 1970-01-01
      • 2015-12-18
      • 1970-01-01
      • 2017-09-06
      • 2017-03-11
      相关资源
      最近更新 更多