Laravel Testing Request - Service Provider - Middleware Issue - laravel

I have a Laravel 5.5 App where I have a Service Provider which I use to put some stuff in the request->attributes to access it everywhere (simplified):
namespace App\Providers;
use App\Models\Domain;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
class GlobalVarsServiceProvider extends ServiceProvider
{
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap the application services.
*
* #param Request $request
*
* #return void
*/
public function boot(Request $request)
{
$domain = .. get domain with language and some logic and cache because of multiple domains ..
$request->attributes->add(['domain' => $domain]);
}
}
I do this in a Service Provider, because then I can already use it in other Service Providers like my ViewComposerServiceProvider, where I compose some stuff for the Views. I'm able to access $domain everywhere like this:
$this->domain = $request->attributes->get('domain');
It works great. BUT not in testing. When I want to access $domain in a Unit Test in a middleware the $request->attributes are empty (In UnitTests as in DuskTests either).
It looks like the testing environment uses a different Request Lifecycle? If yes, what else is different in the testing environment?
What am I doing wrong?
-- Edit --
Test Example:
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}

TestCase uses trait MakesHttpRequests which has method call. When you use get method in your tests, it's simply a shortcut to this.
In your test you can use it like this:
$this->call('GET', '/url/here', $yourRequestParametersHere);

Related

How to overcome repeative blade component queries in laravel

As in laravel we make blade component to reuse that on multiple pages. so for example we have a cardcomponent that has a query in render function
User::count();
and we use that card component on almost every page so whenever a page is loaded the above query is executed again and again. is there a way to overcome it?
You can put the query in the AppServiceProvider as shown in the documentation here https://laravel.com/docs/9.x/views#sharing-data-with-all-views. The result will be available in all views and the query should only be executed once.
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
View::share('key', 'value');
}
}
Alternatively you can use a blade component: https://laravel.com/docs/9.x/blade#components

Laravel/Octane: How to reset route controllers' state

In Laravel v9/Octane/Swoole, I do have private properties in route controllers, e.g.
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
class SignupController extends Controller
{
/** #var ?\App\SignupCode A verification code object */
protected $code;
It looks like the property is "shared" between requests under Octane. I have more controllers like this. How do I make sure the controller state gets reset on every request? I've read the whole Octane documentation a few times, and it's still unclear how to do that.
I solved it by created listener
<?php
namespace App\Listeners;
use Illuminate\Routing\Router;
class ResetControllerState
{
/**
* Handle the event.
*
* #param mixed $event
* #return void
*/
public function handle($event): void
{
/** #var Router $router */
$router = $event->sandbox->make(Router::class);
$currentRoute = $router->current();
if($currentRoute && $currentRoute->controller)
$currentRoute->controller = null;
}
}
and add it to array of listeners in octane config
RequestReceived::class => [
...Octane::prepareApplicationForNextOperation(),
...Octane::prepareApplicationForNextRequest(),
\App\Listeners\ResetControllerState::class
//
],
I do not know what the consequences may be, but so far it works well.

Get a list of database statements executed in Laravel API

To see if there's an n + 1 problem in my Laravel API, I want to see how many and which query statements were executed.
I use barryvdh/laravel-debugbar to see the query statements on web.php routes but this doesn't provide me the wanted info on api.php routes.
You can create a ServiceProvider that "listens" to all the queries being executed. You can print them on screen, write them to a log file, whatever you want.
Check https://laravel.com/docs/8.x/database#listening-for-query-events for the docs on Laravel 8.
Literal copy/paste example from the docs:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
DB::listen(function ($query) {
// Print it, log it, whatever :)
// $query->sql
// $query->bindings
// $query->time
});
}
}

Problems creating a ComposerServiceProvider in Laravel 5.4

Im working on a Laravel 5 app and im trying to set up a ComposerServiceProvider to pass data to a couple of views (im trying now to add it to the layout/app.blade.php).
I did this following the documentation but the data im trying to add is still undefined..
In my config/app.php I added to the providers:
App\Providers\ComposerServiceProvider::class,
On ComposerServiceProvider.php
boot method:
View::composer(['layouts.app'], 'App\ViewComposers\LayoutAppComposer');
On the new created LayoutAppComposer.php
compose(View $view) method:
$metaTitle = 'MetaTitle';
$view->with('metaTitle', $metaTitle)
But When i access the url I still get:
Undefined variable: metaTitle (View: .../resources/views/layouts/app.blade.php)
Am I missing something here??
ServiceProvider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
View::composer(['layouts.app'], 'App\ViewComposers\LayoutAppComposer');
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
}
LayoutAppComposer
<?php
namespace App\ViewComposers;
use Illuminate\Support\Facades\Session;
use Illuminate\View\View;
class LayoutAppComposer {
protected $metaTitle;
public function __construct($metaTitle)
{
$this->metaTitle = $metaTitle;
}
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view) {
$this->metaTitle = 'MetaTitle';
$view->with('metaTitle', $this->metaTitle);
}
}
Try changing:
$metaTitle = 'MetaTitle';
$view->with('metaTitle', $metaTitle)
to
$this->metaTitle = 'metaTitle';
$view->with('metaTitle', $this->metaTitle)
setup $this->metaTitle as a protected class member and assign it in the composer constructor. it may be that $metaTitle is getting garbage collected before you use it since this being resolved at the service provider level.
Since you're registering the composer with your app layout, instead you may need to use the wildcard character in place of app.layout like such:
View::composer('*', function ($view) {
//
});
To resolve $metaTile for the View Composer, try binding in your AppServiceProvider:
$this->app->bind('metaTitle', 'the string i want displayed across all views');

Laravel Architecture - Event vs. Controller

My question is related more about the architecture of an Laravel application. I'm developing an application and I have some problems about the positioning of my codes. Let's assume I have a controller to control the comments of my post, but now, after a while, I need to add an action each time a comment is registered, in this case, I create an event or simply add this new action to my controller action?
Thank you.
As Bogdan's says, you should read Model Events.
A sample approach could be the next.
Create a service provider:
php artisan make:provider CommentServiceProvider
Then a sample CommentServiceProvider class:
<?php
namespace App\Providers;
use App\Comment;
use Illuminate\Support\ServiceProvider;
class CommentServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Comment::creating(function ($comment) {
//... do stuff here
});
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
//
}
}

Resources