Extend Request class in Laravel 5 - php

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

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()

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

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';

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

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

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.

Related

Attempted to call an undefined method named "redirect" error in Symfony 4

i have this error in my code and I have writing the "use" but i have this error:
Attempted to call an undefined method named "redirect" of class
"App\Controller\SetlocaleController".
My code:
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\RedirectResponse;
class SetlocaleController extends HomeController {
public function __construct(\Twig\Environment $twig)
{
$this->twig = $twig;
}
public function setLocaleAction(Request $request, $language = null)
{
if($language != null)
{
$session->set('_locale', $language);
}
$url = $request->headers->get('referer');
if(empty($url))
{
return new response($this->twig->render('page/home.html.twig'));
}
else{
return $this->redirect($url);
}
}
}
You have an answer for me please ?
Best practice solution
As proposed in the best practices for controllers documentation by Symfony, make sure you extend the Abstract controller on your controller.
// HomeController.php
// ...
class HomeController extends AbstractController {
// ...
No extra changes required to SetlocaleController. However if RedirectResponse is no longer used you can remove it's import
use Symfony\Component\HttpFoundation\RedirectResponse;
Solution using HttpFoundation\RedirectResponse
You need to use the already imported RedirectResponse object. Do not use the code: return $this->redirect($url); since, as the error states, there is no redirect(url) function defined for your class.
return new RedirectResponse($url);

Accessing method in another namespace

I'm trying to, from my controller, access a method in a model that is in another namespace and the only way I could do this was to make the method static. Is this the right way to do it, or is there any neater approach?
PagesController.php (controller):
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Helpers\ConnectedHost;
class PagesController extends Controller
{
/*
* REMOVED CODE HERE FOR READABILITY
* Below is where I instantiate the "connectedHost"-object
*/
$hosts[$hostKey] = new ConnectedHost($hostAttributes['ipv4'], $hostAttributes['mac']);
}
/* REMOVED CODE HERE FOR READABILITY AS WELL */
ConnectedHost.php (helper-file):
namespace App\Helpers;
class ConnectedHost
{
public $ipv4, $mac;
public function __construct($ipv4, $mac)
{
$this->ipv4 = $ipv4;
$this->mac = $mac;
// This is where I call the getName-function staticly,
$this->name = \App\Host::getName();
}
}
Host.php (model):
namespace App;
use Illuminate\Database\Eloquent\Model;
class Host extends Model
{
// The method below is declared static
public static function getName()
{
$name = 'wenzzzel';
return $name;
}
}
If you are directly accessing the method from model like
$data = \App\ModelName::methodName();
Then your method should be static.
if your method is not static you can access like,
$model = new \App\ModelName();
$data = $model->methodName();

Laravel: Is there a way to reuse a method that is using a Request variable as parameter?

I want to reuse my method store that is in generar\productoController
public function store(Request $request){}
and I want to reuse it in this class adquisicion\ComprasController, I know that I have to import the class to use the method i want, but the problem is the $request variable, should I create a new object of it with $request = new Request(), adding the data I want with this and sending it as parameter?
Thx for the help I'm really new with laravel
you can try it like this $this->store(request(),$otherData)
use the helper to get the current object of request
You can pass Request data to other method
productoController(Request $request){
// anything here
return redirect('your route name')->with('data', $request->all());
}
Here are two ways that can make methods reusable in laravel application:
Make a helper method
Create a Helpers folder in app folder, and create all static methods inside a helper.php
Helper.php
namespace App\Helpers;
class Helper {
public static function store() {
$request = request();
// ....
}
}
YourController.php
namespace App\Repositories;
use App\Helpers\Helper;
use Illuminate\Http\Request;
class YourController extends Controller
{
public function store(Request $request) {
// call the store method as
Helper::store();
}
}
The downside here is you will mix up all the non-related helper methods here and may difficult to organize.
Repository
You can use a Repository Pattern to architect your application, for example, if you store a foo object to your datastore, then you can first create Repositories folder in app folder, and create FooRepository.php in Repositories folder:
FooRepository.php
namespace App\Repositories;
class FooRepository {
public function store() {
$request = request();
// ...
}
}
YourController.php
namespace App\Http\Controllers;
use App\Repositories\FooRepository;
use Illuminate\Http\Request;
class YourController extends Controller
{
private $fooRepository = null;
public function __construct(FooRepository $fooRepository) {
parent::__construct();
$this->fooRepository = $fooRepository;
}
public function store(Request $request) {
// call the method as
$this->fooRepository->store();
}
}

How to write a php unit test case for symfony controller

I am trying to write a unit test case for symfony controller.
Controller Code
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
class Controller1 extends SecuredController
{
public function preExecute(Request $request) {
parent::preExecute($request);
}
public function indexAction()
{
return $this->render('help/index.html.twig');
}
I have tried writing a test case as shown below but it throws an internal error
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class Controller1Test extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$response = $client->getResponse();
$this->assertEquals(200, $response->getStatusCode());
}
}
Any suggestion would be helpful for me
First of all, is a UNIT test. You here dont need a client. You just need the class you want to test.
namespace Tests\AppBundle\Controller;
use PHPUnit\Framework\TestCase;
use AppBundle\Controller\Controller1;
class Controller1Test extends TestCase
{
public function testIndex()
{
$this->request = $this->getMockBuilder(
'Symfony\Component\HttpFoundation\Request'
)->disableOriginalConstructor()
->getMock();
$controller = new Controller1();
$controller->preExecute($this->request);
$response = $controller->indexAction();
$this->assertEquals(
200,
$response->getStatusCode()
);
}
}
and for example your code could be
namespace Tests\AppBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class Controller1 extends SecuredController
{
public function indexAction()
{
return new Response();
}
}

PHP/Laravel - How to initiate a class with it's dependencies in a trait

I would like to use a method from a class that I have in one trait. The class that I need looks like this:
namespace App\Http\Controllers;
use App\Libraries\Content\ContentInterface;
use Illuminate\Http\Request;
use App\Http\Requests;
use Corcel\Post;
use EllipseSynergie\ApiResponse\Laravel\Response;
use App\Transformers\IndexTransformer;
class ImportController extends Controller
{
private $indexable;
function __construct(Response $response, ContentInterface $contentInterface)
{
$this->indexable = \Config::get('middleton.wp.content.indexable_types');
$this->response = $response;
$this->contentInterface = $contentInterface;
}
public function updateOrCreateInventory($remoteId)
{
$this->contentInterface->updateOrCreateInventory($remoteId);
}
}
I would like to use the updateOrCreateInventory method in a trait that I have or to be more specific in it's method :
namespace App\Libraries\Content;
use App\Inventory;
use GuzzleHttp\Client;
use App\Http\Controllers\ImportController;
trait SupplementaryFormatter
{
private static function getInventoryUrl($id)
{
if (is_numeric($id)) {
$inventory = Inventory::where('remote_id', $id)->first();
if(!$inventory) {
ImportController::updateOrCreateInventory($id);
} else {
return '/' . $inventory->url;
}
}
return $id;
}
}
But, I am not sure how can I initiate the class in the trait with it's dependencies, because when I import the Response and ContentInterface class to a trait and try to pass it to the constructor of the ImportController class, like so:
(new ImportController(new Response, new ImportController))->updateOrCreateInventory($id);
I get an error that I am not passing dependencies to Response and ImportController. How can I make this work?
Have you tried to use app()->make()?
If not, try it:
if(!$inventory) {
$controller = app()->make(ImportController::class);
$controller->updateOrCreateInventory($id);
} else {
return '/' . $inventory->url;
}
Also, I don't think you are doing it in the right way. I think you should move the function updateOrCreateInventory to some service and in your trait create a instance of this service.

Categories