Laravel 5.7: prevent auto load injection in controller constructor - laravel-5

I have a HomeController with his constructor that takes a Guzzle instance.
/**
* Create a new controller instance.
*
* #param \GuzzleHttp\Client|null $client
*
* #return void
*/
public function __construct(Client $client = null)
{
$this->middleware('auth');
$this->middleware('user.settings');
if ($client === null) {
$param = [
'base_uri' => 'http://httpbin.org/',
'defaults' => [
'exceptions' => false,
'verify' => false
]
];
$client = new Client($param);
}
$this->setClient($client);
}
I would use via __constructor() to be able to mock it in tests.
My issues is that Laravel automatically auto-load the injection and the Guzzle Client injected has blank defaults (and cannot anymore edit it). In other words: at first call of HomeController Client is not null. And I need as null.
How can I stop this behaviour (only for the __construct() for HomeController)? I really use the DI in every part of my webapp.
EDIT
I just find that if I don't type-hints the Client, of course Laravel cannot auto-load. Is this the right mode to work?
New constructor:
public function __construct($client = null)
Thank you

I had a simular situation when testing apis. I ended up binding an instance of GuzzleClient to the service container (see documentation). Something like:
$this->app->instance('GuzzleHttp\Client', new MockClient);
To successfully mock the instance, I then checked to see whether or not it had a certain property value (in my case base_url being set). That determined whether or not the instance was a test as base_url would be set.
Along side this method, GuzzleHttp\Client does have a MockHandler you may want to explore. This can be used to fake response bodies, headers and status codes.

Related

How to log every GET And POST data in Codeigniter 4?

my web application is doing a lot of AJAX calls (GET and POST) to our CodeIgniter 4 backend. My current approach to debug the AJAX call is setting manual logging messages within every method. Too time-consuming.
Do you know if there is a better way to do that? Overwriting a CI class?
I am happy about every help.
For logging the request you have to create the "after filters".
First You Define the Class which implements FilterInterface.
class Logger implements FilterInterface
{
use ResponseTrait;
public function before(RequestInterface $request)
{
...
}
public function after(RequestInterface $request, ResponseInterface $response)
{
...
}
}
In the after method, you will need to store the response and then save it using log_message.
public function after(RequestInterface $request, ResponseInterface $response)
{
$response_service = \Config\Services::response();
log_message('info', '{message}', ['message' => $response_service->getJSON()]
}
Here, I have stored used the response service explicitly and then simply called the getJSON to store the JSON body of the request. You will need to modify this for your problem. Also, do note you don't need to call the response service explicitly. There was another thread that showed how you can save the response implicitly, so you might want to refer to that.
Once the filter is done, you need to register the alias for the routes as below :
public $aliases = ['logger' => \App\Filters\Logger::class];
Once done you can either implement on individual routes or global routes.
Below is how you can implement it on global routes:
public $globals = [
'before' => [
...
],
'after' => [
'logger',
],
];
References : https://codeigniter4.github.io/userguide/incoming/filters.html?highlight=filter
https://codeigniter4.github.io/userguide/incoming/request.html?highlight=request
https://codeigniter4.github.io/userguide/general/logging.html?highlight=log_message
Just use
echo $this->request->getPost("usersemail"); die();
for usersemail input field

Why optional params passed while removed params fails in policy testing?

So, I have created a test for my app, the model is Subscription which is the index and show endpoint should be publicly accessible.
I created a Resource Controller to handle I/O from the client and a Policy to handle the authorization but here, I found something that seems kinda odd.
Inside the controller, I registered the policy on the constructor, like so:
public function __construct()
{
$this->middleware('auth:api')->except(['index', 'show']);
$this->authorizeResource(Subscription::class, 'subscription');
}
And then in the policy class, I modified the default generated methods like so:
/**
* Determine whether the user can view any models.
*
* #param \App\Models\User $user
* #return mixed
*/
public function viewAny(?User $user) // <-- notice here I make it optional, the original was required (without "?" mark).
{
return true; // publicly visible
}
When I run the test, it passed.
public function testSubscriptionIndexArePubliclyAccessible()
{
$subscriptions = Subscription::factory(10)->create()->toArray();
$response = $this->get(route('subscriptions.index'));
$response->assertOk();
$response->assertExactJson($subscriptions);
}
However, if I completely remove the User $user param from the method, the test would fail.
public function viewAny() <-- if I do this, the test fail. Saying that "this action is unauthorized".
{
return true; // publicly visible
}
So.. Why is this happen?
There are checks happening before a policy method or gate ability are called. One check is if the policy method can be called with a user, canBeCalledWithUser. This will check if there is an auth user and return true, if not it does other checks. The next check is if the method allows guest users, methodAllowsGuests, which will use reflection to get the parameter for that method and see if it has a type and is nullable, but there are no parameters so it returns false. So you end up with it not calling the method and treating it more like it doesn't exist, which is always false in terms of authorization checks.
https://github.com/laravel/framework/blob/8.x/src/Illuminate/Auth/Access/Gate.php#L371 #raw -> callAuthCallback -> resolveAuthCallback
https://github.com/laravel/framework/blob/8.x/src/Illuminate/Auth/Access/Gate.php#L530 #resolveAuthCallback
https://github.com/laravel/framework/blob/8.x/src/Illuminate/Auth/Access/Gate.php#L390 #canBeCalledWithUser
https://github.com/laravel/framework/blob/8.x/src/Illuminate/Auth/Access/Gate.php#L416 #methodAllowsGuests
https://github.com/laravel/framework/blob/8.x/src/Illuminate/Auth/Access/Gate.php#L456 #parameterAllowsGuests - it does not make it to this method call

How to assert $next in Laravel Middleware

Lavarvel application running 7.10
I have a middleware that gets called on several routes. It check the user has the right relationships in place to get to the desired destination (that's not important).
I want a test that simply asserts that the user gets to the $next($request) in the middleware. I could of course just hit one of the end points, and assert something about that which would in turn validate the middleware, but all the routes hit external apis to get some other data which I could mock in my tests, which all seem pretty ugly just to test a middleware class.
I my test I can factory up the relevant relations etc, so whenever the middleware is called it should allow me straight in.
If I do this...
/** #test */
public function middleware_successful_test()
{
$user = factory(User::class)->create();
// some other factoried user relations here
$this->actingAs($user);
$request = new Request;
$middleware = app(MiddlewareClass::class);
$result = $middleware->handle($request, function ($req) {});
// don't know what assert here. $result is basically empty
}
Any tips, advise welcome.
Normally i would not unit test middlewares, but test it as a feature.
For your case i would utilize that you provide the next closure and you get the result. Return true and assert it is what is returned and therefor the closure has been called.
$result = $middleware->handle($request, function ($req) { return true;});
$this->assertSame(true, $result);

Laravel - How do I inject automatically in Validation Rule Construct?

I'm not able to inject these variables through Laravel:
//...class AllowedUsername implements Rule...
public function __construct(Router $router, Filesystem $files, Repository $config)
{
$this->router = $router;
$this->files = $files;
$this->config = $config;
}
I get the error:
Type error: Too few arguments to function ... 0 passed in.
Why is Laravel not doing it automatically?
$request->validate([
'username' => ['required', new AllowedUsername],
]);
In order to leverage Laravel's injection magic you need to use Laravel's API which essentially is:
resolve($class) which is wrapper around app($class)
app($class, $params = []) which is wrapper around:
Note: I've changed $abstract for $class
if (is_null($class)) {
return Container::getInstance();
}
return Container::getInstance()->make($class, $parameters);
Classes that you want to resolve out of container (as seen in your code sample):
public function __construct(Router $router, Filesystem $files, Repository $config)
can be resolved only because Laravel maintainers already defined binding for Router::class, Filesystem:class (example: FilesystemServiceProvider).
Repository::class seems to be simple class that does not require parameters (or require parameters that container already knows how to resolve) while "newing up" - thus Laravel can resolve it without problem.
There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection.
Thats why resolve(AllowedUser::class) or resolve(Router::class)... work.
In order to let Laravel know what constructor's parameters should be sent during "newing up" you use bindings mentioned in documentation.

Is the database class in Laravel use the "Singleton Pattern"?

I am new to Laravel. Does the Laravel create a database connection every time for each query of the program or use the same database object throughout the program using the "Singleton" Pattern? Does the strategy have an impact on performance, especially for Enterprise Level Applications?
Short answer
No, it's not a singleton, but a factory pattern. However the same connection will be reused if possible and you don't manually request it to reconnect. There is no performance hit.
Long answer
At the beginning of ever request lifecycle, in app/bootstrap/start.php an instance of Illuminate\Foundation\Application gets created. This servers as IoC container.
Shortly after creating the application all service providers will be loaded. The service providers are defined in app/config/app.php
'providers' => array(
// ...
'Illuminate\Database\DatabaseServiceProvider',
// ...
),
Let's have a look at the Illuminate\Database\DatabaseServiceProvider shall we? The important part is the register function
$this->app->bindShared('db', function($app)
{
return new DatabaseManager($app, $app['db.factory']);
});
An instance of DatabaseManager gets bound to db. This instance will stay the same over the whole request and will be used for every database request.
Example from "reverse" direction
Say you call
DB::table('users')->get();
First, the DB facade will resolve to the instance of DatabaseManager that gets bound in the DatabaseServiceProvider using bindShared('db')
protected static function getFacadeAccessor() { return 'db'; }
Then the table('users') call gets forwarded because the method doesn't exist in Database Manager
public function __call($method, $parameters)
{
return call_user_func_array(array($this->connection(), $method), $parameters);
}
It is called on the return value of $this->connection()
public function connection($name = null)
{
list($name, $type) = $this->parseConnectionName($name);
// If we haven't created this connection, we'll create it based on the config
// provided in the application. Once we've created the connections we will
// set the "fetch mode" for PDO which determines the query return types.
if ( ! isset($this->connections[$name]))
{
$connection = $this->makeConnection($name);
$this->setPdoForType($connection, $type);
$this->connections[$name] = $this->prepare($connection);
}
return $this->connections[$name];
}
With if (!isset($this->connections[$name])) it will check if the connection already has been established and will only make a new connection if not.
Then it returns the connection and table('users')->get() will be executed.

Resources