Laravel 5 Override a class method - php

I would like to override the method setContent() of the class Illuminate\Http\Response as :
<?php
namespace MyNameSpace\MyClass;
use Illuminate\Http\Response;
class myResponse extends Reponse {
public function setContent($content)
// Something
}
}
But I don't know how to tell Laravel to load my class instead of the original one.

Too late, but as i came up with same issue. but for reference i would want to post how i resolved this issue.
When i wanted to handle all the the response by myself without using a response macro or transformer and TO OVERRIDE MANY OTHER FRAMEWORK DEFAULT METHODs . this is how i completely took control of the response object.
just posted here for reference as i in my opinion it solves the
problem in a clearer way.
Lot of overriding as its done through pipeline and routing and so it`s registered as base service provider. here is how i managed to override all.
here I am using laravel 5.3
1 - create a new response class
<?php
namespace App\Extensions\Illuminate\Http;
// use Illuminate\Http\Response as BaseResponse;
use Symfony\Component\HttpFoundation\Response as BaseResponse;
class Response extends BaseResponse
{
public function setContent($content)
{
//do what ever you want to do with the content
//dd($content);
}
}
2 - create a new router and use the new response
<?php
namespace App\Extensions\Illuminate\Routing;
use Illuminate\Http\Request;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Routing\Router as IlluminateRouter;
use App\Extensions\Illuminate\Http\Response;
class Router extends IlluminateRouter
{
public function prepareResponse($request, $response)
{
if ($response instanceof PsrResponseInterface) {
$response = (new HttpFoundationFactory)->createResponse($response);
} elseif (! $response instanceof SymfonyResponse) {
$response = new Response($response);
}
return $response->prepare($request);
}
}
3 - create new routing service provider use new router
<?php
namespace App\Providers;
use Illuminate\Routing\RoutingServiceProvider as ServiceProvider;
use App\Extensions\Illuminate\Routing\Router;
class RoutingServiceProvider extends ServiceProvider
{
protected function registerRouter()
{
$this->app['router'] = $this->app->share(function ($app) {
return new Router($app['events'], $app);
});
}
}
4 - create new Application class and use new routing service provider
<?php
namespace App\Extensions\Illuminate\Foundation;
use Illuminate\Events\EventServiceProvider;
use Illuminate\Foundation\Application as App;
use App\Providers\RoutingServiceProvider;
class Application extends App
{
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
}
5 - and finally in bootstrap\app.php use the new Application
// $app = new Illuminate\Foundation\Application(
// realpath(__DIR__.'/../')
// );
$app = new App\Extensions\Illuminate\Foundation\Application(
realpath(__DIR__.'/../')
);

You will need to extend the Response facade to reflect the class you have, then change your applications /config/app.php to link to your new facade rather than laravels.

You need to create a facade like so
<?php namespace App\Facades;
use Illuminate\Support\Facades\Response as BaseResponse;
class Response extends BaseResponse {
public static function overwriteMethod()
{
//
}
}
Then go to config/app.php under facades comment out this line
//'Response' => 'Illuminate\Support\Facades\Response',
Then add this to the facades stack
'Response' => 'App\Facades\Response',

Related

Mock a service provider - Laravel 5.4

I am using a service provider in a specific controller, i registered the provider and resolved it successfully.
But the test i used to run by mocking the actual classes are not working any more.
I resolve the service in my controller using app():
$cart = app()->make('Cart');
This is how i used to mock it before switching to a service provider:
$this->cart = $this->createMock(Cart::class);
Now if i want to mock it, i am doing something like this:
$this->createMock(app()->make('Cart'));
And the test fails with the error:
PHPUnit_Framework_MockObject_Generator::getMock() must be a array or string
Any ideas ?
I used to have similar problem during testing my custom service provider. I created testing trait to mock my service provider.
As you can see I am re-declaring singleton for my Firebase service provider and later using use FirebaseProvider in my tests-cases.
// FirebaseServiceProvider
namespace App\Providers;
use Kreait\Firebase;
use Kreait\Firebase\ServiceAccount;
use Kreait\Firebase\Factory as FirebaseFactory;
use Illuminate\Support\ServiceProvider;
class FirebaseServiceProvider extends ServiceProvider
{
...
public function register()
{
$this->app->singleton(Firebase::class, function() {
return (new FirebaseFactory())
->withServiceAccount(ServiceAccount::fromJsonFile(env('FIREBASE_SERVICE_ACCOUNT')))
->create();
});
$this->app->alias(Firebase::class, 'firebase');
}
}
// In ./tests/FirebaseProvider.php
namespace Tests;
use Kreait\Firebase;
use GuzzleHttp\Psr7\Uri;
use Firebase\Auth\Token\Handler;
use Kreait\Firebase\ServiceAccount;
trait FirebaseProvider
{
public function mockFirebaseServiceProvider()
{
$serviceAccountMock = $this->createMock(ServiceAccount::class);
$serviceAccountMock->expects($this->any())
->method('getProjectId')
->willReturn('project');
$serviceAccountMock->expects($this->any())
->method('getClientId')
->willReturn('client');
$serviceAccountMock->expects($this->any())
->method('getClientEmail')
->willReturn('client#email.tld');
$serviceAccountMock->expects($this->any())
->method('getPrivateKey')
->willReturn('some private key');
uopz_flags(Handler::class, null, 0); // hack: removes `final` keyword so we can mock-it
$tokenHandlerMock = $this->createMock(Handler::class);
$tokenHandlerMock->expects($this->any())
->method('createCustomToken')
->willReturn(new \Lcobucci\JWT\Token());
$this->app->singleton(Firebase::class, function () {
return $this->createMock(Firebase::class);
});
}
}
abstract class TestCase extends BaseTestCase
{
....
protected function setUpTraits()
{
parent::setUpTraits();
$uses = array_flip(class_uses_recursive(static::class));
if (isset($uses[FirebaseProvider::class])) {
$this->mockFirebaseServiceProvider();
}
}
}

Having some trouble understanding Facades and Service Providers

I've been reading the documentation up and down now, still not sure what I'm doing wrong. In my opinion the documentation is very difficult to understand for a beginner.
Anyway, I'm trying to make something akin to the Auth::user() method, where it returns additional data about a logged in user that I will be needing for this application.
I have this helper class here:
namespace App\Helpers;
use Auth;
use Illuminate\Http\Request;
use App\Models\Grouping\User;
use App\Models\Grouping\Client;
use App\Models\Grouping\Rank;
class ClientUser {
public function __construct($request) {
$this->request = $request;
}
public function client() {
return Client::find($this->request->session()->get('client_id'));
}
public function auth() {
if (Auth::check()) {
// Get the client
$client = $this->client();
// Get the client's user
$user = $client->users()->find(Auth::user()['id']);
// Get the rank of the logged in user
$rank = Rank::find($user->pivot->rank_id);
return [
'user' => $user,
'rank' => $rank,
'client' => $client
];
}
return null;
}
}
This is responsible for doing what I described, returning additional data that I can't get through Auth::user(). Now I'm trying to register this class in the AuthServiceProvider
public function register()
{
// Register client auth
$request = $this->app->request;
$this->app->singleton(ClientUser::class, function ($app) {
return new ClientUser($request);
});
}
Now what I don't understand is how I'm supposed to make this globally accessible throughout my app like Auth::user() is.
The problem with just making "importing" it is that it needs the request object, which is why I'm passing it through the service container.
Now here's where I'm stuck. I'm not able to access app in my controller or anywhere, and I can't define a Facade because a Facade expects you to return a string of the bound service that it should "alias?"
Change your service provider like this :
$this->app->bind('client.user', function ($app) {
return new ClientUser($app->request);
});
Create another class extended from Illuminate\Support\Facades\Facade.
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class ClientUserFacade extends Facade {
public static function getFacadeAccessor(){
return "client.user";
}
}
Add 'ClientUser => ClientUserFacade::class in alias key of app.php

Symfony - Filter Request with FOSRestBundle and body_listener without Annotations?

I am using Symfony2 with the FOSRestBundle. Is it possible to have the functionality of the #QueryParam and #RequestParam annotations without using annotations?
I am trying to build a json api (format), so I want to allow query params like include, page, filter, fields, and sort. My ideal way to handle this would be:
Use the format_listener to detect it is json.
Use a custom body_listener json handler to process the request so that it's similar to this.
Have the controller validate the query/request params inside the action function, and throw an exception to be handled by the exception controller if it's invalid. (The body_listener would act as a helper to make extracting the data from the request easier in the controller, but the controller makes the decisions of what to do with that data.)
I'm mostly stuck on how to make a custom body_listener. I'm not sure if I would need to make a custom decoder or normalizer, and what that class might look like since they don't give any examples.
Rough code of what controller would look like:
<?php
namespace CoreBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\View\View;
use FOS\RestBundle\Context\Context;
use Psr\Http\Message\ServerRequestInterface;
class SiteController extends FOSRestController
{
public function getAction($id, ServerRequestInterface $request)
{
try {
// Validate $request. This is where the query/request
// param annotation functionality would be replaced.
} catch (Exception $e) {
throw new InvalidRequestException($e);
}
$siteService = $this->get('app.site_service');
$site = $siteService->getSite($id);
$context = new Context();
$context->setVersion($request->getVersion());
// Ex: /sites/63?fields[sites]=name,address&fields[company]=foo,bar
if ($request->hasIncludeFields()) {
$context->addAttribute('include_fields', $request->getIncludeFields()); // Or however to do this
}
$view = new View($site, 200);
$view->setContext($context);
return $view;
}
}
You can define parameters dynamically in param fetcher. It's described in documentation.
For example:
With annotations:
<?php
namespace ContentBundle\Controller\API;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Request\ParamFetcher;
class PostController extends FOSRestController
{
/**
* #QueryParam(name="sort", requirements="(asc|desc)", allowBlank=false, default="asc", description="Sort direction")
*/
public function getPostsAction(ParamFetcher $paramFetcher)
{
$sort = $paramFetcher->get('sort');
}
}
Without annotations:
<?php
namespace ContentBundle\Controller\API;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Request\ParamFetcher;
class PostController extends FOSRestController
{
public function getPostsAction(ParamFetcher $paramFetcher)
{
$sort = new QueryParam();
$sort->name = 'sort';
$sort->requirements = '(asc|desc)';
$sort->allowBlank = false;
$sort->default = 'asc';
$sort->description = 'Sort direction';
$paramFetcher->addParam($sort);
$param = $paramFetcher->get('sort');
//
}
}

Unable to use helper in controller of laravel app

I'm building an application, now i'm created a helper
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
now i'm unable to do something like this in controller
namespace App\Http\Controllers;
class WelcomeController extends Controller
{
public function index()
{
return view('student/homepage');
}
public function StudentData($first_name = null)
{
/* ********** unable to perform this action *********/
$students = Student::return_student_names();
/* ********** unable to perform this action *********/
}
}
this is my helper service provider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
foreach(glob(app_path().'/Helpers/*.php') as $filename){
require_once($filename);
}
}
}
i event added it as an alias in config/app.php file
'Student' => App\Helpers\Students::class,
Try putting use App\Helpers\Student; at the top of your controller beneath the namespace delcaration:
namespace App\Http\Controllers;
use App\Helpers\Student;
class WelcomeController extends Controller
{
// ...
Look more into PHP namespaces and how they are used, I believe you may have a deficient understanding about them. Their only purpose is to make so you can name and use two classes with the same name (e.g. App\Helpers\Student vs maybe App\Models\Student). If you needed to use both of those classes inside of the same source file, you can alias one of them like this:
use App\Helpers\Student;
use App\Models\Student as StudentModel;
// Will create an instance of App\Helpers\Student
$student = new Student();
// Will create an instance of App\Models\Student
$student2 = new StudentModel();
You do not need to have a service provider for this, just the normal language features. What you would need a service provider for is if you wanted to defer the construction of your Student object to the IoC:
public function register()
{
$app->bind('App\Helpers\Student', function() {
return new \App\Helpers\Student;
});
}
// ...
$student = app()->make('App\Helpers\Student');
You should never have to include or require a class file in laravel because that is one of the functions that composer provides.
You do not need a service provider to make it works. Just lets the Students class as you did:
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
all its methods should be static
You added the Facade correctly:
'Student' => App\Helpers\Students::class,
Finally, looks like your problem is caused by forgetting a backslash at facade name. Uses \Students instead of Students:
public function StudentData($first_name = null)
{
$students = \Student::return_student_names();
}
When using a facade, it is not necessary makes nay include, the facades were made to avoid complex includes in everywhere.

Extend controller - but how activate it? (Symfony 2)

I want to use this code in my application:
class ControllerExtension extends Symfony\Bundle\FrameworkBundle\Controller\Controller
{
public function render($view, array $parameters = array(), Response $response = null)
{
//etc.
}
}
But where do I put it and how do i activate it? I'm guessing it's something to do with the services.yml file. I've used Event Listeners, but this is obviously different.
From your code snippet (http://justpaste.it/2caz), it seems that you missed the "return" keyword in your call to parent.
class ControllerExtension extends Symfony\Bundle\FrameworkBundle\Controller\Controller
{
public function render($view, array $parameters = array(), Response $response = null)
{
if($this->getRequest()->getRequestFormat() == 'json') {
return new Response(json_encode($parameters));
} else {
// Missing 'return' in your snippet
return parent::render($view, $parameters, $response);
}
}
}
class MyController extends ControllerExtension
{
public function indexAction()
{
// This should now work
return $this->render(...);
}
}
You can put it in your bundle's Controller directory i.e src/YourNamespace/YourBundleName/Controller/ControllerExtension.php.
Make sure you provide the appropriate namespace in that file:
namespace YourNamespace\YourBundleName\Controller;
class ControllerExtension extends Symfony\Bundle\FrameworkBundle\Controller\Controller
{
...
To use it, either create a route for it in src/YourNamespace/YourBundlename/Resources/config/routing.yml
or
extend it:
namespace YourNamespace\YourBundleName\Controller;
class OtherController extends ControllerExtension
{
...
If what you are actually looking to do is override another bundle's controller, see the cookbook which describes overriding controllers.
Edit:
As far as I know, there's no way to automatically make this controller somehow take effect. You can have each of your controllers extend it as I've indicated above.
You might be able to create an event listener and use the response event to somehow change the response if the format is json. But, I'm not sure how you would access the view data from the event listener.

Categories