I'm building a PHP app using Laravel Framework. I need to read some session values on every request and use this values on my controller methods.
How can I achieve this? Where to put this code?
I would like something like the Zend Framework Bootstrap class.
So, you can create a file named, for instance, BaseController.php which extends Controller. Then put your logic in the __construct()
Then, all of your other controllers can extend BaseController and in their __construct() they can perform a parent::__construct(); to make that fire.
The best practice is to use the Laravel Request Lifecycle (https://laravel.com/docs/8.x/lifecycle)
Following the documentation, the best place to place "onLoad" or global code, is on the boot method of the appServiceProvider. For example, if I wan't to set an specific timezone for all my project:
//app/Providers/AppServiceProvider.php
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
date_default_timezone_set('America/Argentina/Jujuy');
}
Related
I have an api and some routes are public some need to be protected via auth. I want to have them in one controller class as they are related. I can extend the controller and have beforeRoute function but it runs for any route that is in that controller. is it possible to add a middleware only to specific routes? I'm a js dev and in express I can just pass middleware functions for any route, even multiple middlewares.
class Clanky /*extends \controllers\ProtectedController */{
public function post_novy_clanek(\Base $base) {
//needs to be protected
}
public function get_clanky(\Base $base) {
}
public function get_clanek(\base $base) {
}
public function get_kategorie(\Base $base) {
}
}
PHP is new to me, I just want to know how I can implement the concepts I know from other languages and frameworks in this weird fatfree framework. Thanks.
Use can use f3-access plugin for that purpose https://github.com/xfra35/f3-access
Fatfree is not opinionated about how to do this.. other options to solve this ask might be:
Use php8 attributes on the method and check these in beforeroute.
Consider an own named route naming schema like #admin_routename and apply checking auth in beforeroute
Use f3-middleware plugin and add auth there
Extend an other admin controller that provides auth in beforeroute or use a trait.
I am using Laravel v8.35. I have created a middleware EnsureTokenIsValid and registered it in app/Http/Kernel.php:
protected $routeMiddleware = [
...
'valid.token' => \App\Http\Middleware\EnsureTokenIsValid::class,
];
Here is the middleware itself:
<?php
namespace App\Http\Middleware;
use Closure;
class EnsureTokenIsValid
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($request->input('token') !== 'my-secret-token') {
return redirect('home');
}
return $next($request);
}
}
Essentially this middleware will redirect the user to a login page if the token is not valid. Now I want this middleware to run on specific routes. So I tried doing this:
Route::get('/', [IndexController::class, 'index'])->middleware('valid.token');
However it seems the code in the constructor of the parent controller (app/Http/Controllers/Controller.php) is being called first. My controllers all extend from this parent controller, e.g:
class IndexController extends Controller
I have tried putting the middleware at the very beginning in the constructor of Controller.php, but that does not work either, i.e. it just proceeds to the next line without performing the redirect:
public function __construct()
{
$this->middleware('valid.token');
// code here which should not run if the above middleware performs a redirect
$this->userData = session()->get('userData');
// Error occurs here if 'userData' is null
if ($this->userData->username) {
// do stuff here
}
}
If I put the middleware in my IndexController constructor, it works. However I don't want to do this for every controller - I just want the middleware to exist in the parent controller.
If you have the web middleware group assigned to this route it doesn't have access to the session in the constructor of your controller any way. You will need to use another middleware or a closure based middleware in the constructor so that it will run in the middleware stack, not when the constructor is ran:
protected $userData;
public function __construct(...)
{
...
$this->middleware(function ($request, $next) {
$this->userData = session()->get('userData');
if ($this->userData && $this->userData->username) {
// not sure what you need to be doing
}
// let the request continue through the stack
return $next($request);
});
}
So, I've also run into this problem and from debugging through the laravel framework code. It runs all the global middleware, then gathers the router middleware, constructs the controller, then afterwards runs all the middleware from the router + all the controller middleware configured in the controller constructor.
I personally think this is a bug, but that doesn't really help you since you need a solution and not just complaining.
Basically, your route no doubt targets a method on the controller, put all your dependencies into that function call and any code that relies upon it into that function call too.
If you need to share a common set of code which runs for each method in that controller, just create a private method and call it from each of the methods.
My problem was that I was using the constructor for dependency injection, like we are all expected to do, since a fully constructed object should have all it's dependencies resolved so you don't end up in a half constructed object state where depending on the function calls, depends on whether you have all the dependencies or not. Which is bad.
However, controller methods are a little different than what you'd consider a typical object or service. They are effectively called as endpoints. So perhaps it's acceptable, in a roundabout way, to consider them not like functions of an object. But using (abusing perhaps), PHP classes to group together methods of related functionality merely for convenience because you can autoload PHP classes, but not PHP functions.
Therefore, maybe the better way to think about this is to be a little permissive about what we would typically do with object construction.
Controller methods, are effectively callbacks for the router to trigger when a router is hit. The fact that they are in an object is for convenience because of autoloading. Therefore we should not treat the constructor in the same way we might for a service. But treat each controller endpoint method as the constructor itself and ignore the constructor except for some situations where you know you can do certain things safely.
But in all other cases, you can't use the constructor in the normal way, because of how the framework executes. So therefore we have to make this little accommodation.
I think it's a bug and personally I think it should be fixed. Maybe it will. But for today, with laravel 9, it's still working like this and I think this at least will help to guide people who ran into the same problem.
So I'm working on a small CMS in Laravel 5, and the one of the first things that I don't fully get is passing variables to a view.
I have seen things like
return View('view', array('name' => 'your name here'));
Or variables in a view composer
public function compose($view) {
$view->with(Config::get('configfile'));
}
but I'm still wondering if there is a better/more elegant way to do this. For example, with the first approach, I have to give it that array at every view, which just seems like a hassle, and with the view composers, it just feels like there should be a better solution.
Is there a recommended way to push these variables on the the view?
Also, I'm talking about a set of variables that are needed in every view. for example the name and the slogan of the website.
Thanks.
Have you considered view()->share see the docs Sharing Data with All Views
<?php
namespace App\Providers;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
view()->share('key', 'value');
}
}
it allows you to share a piece to data with all your views. I don't see any reason why you couldn't load all your config in this way if your needing to access it on the majority of views.
You can use Route Model Binding: https://laravel.com/docs/5.2/routing#route-model-binding for model specific data binding. Other than that, there's no (practical) way to do it.
I'm wondering if there's a simple way to override a singleton service set in the core of the Laravel framework?
e.g. I'm trying to rewrite the app:name command service '' with the following provider:
use Hexavel\Console\AppNameCommand;
use Illuminate\Console\Events\ArtisanStarting;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\ServiceProvider;
class NameCommandProvider extends ServiceProvider
{
/**
* Register any other events for your application.
*
* #param \Illuminate\Contracts\Events\Dispatcher $events
* #return void
*/
public function boot(Dispatcher $events)
{
$events->listen(ArtisanStarting::class, function ($event) {
$event->artisan->resolve('command.app.name');
}, -1);
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->singleton('command.app.name', function ($app) {
return new AppNameCommand($app['composer'], $app['files']);
});
}
}
I'm 100% everything is working due to extensive checks put no matter what order I put my service provider (above or below ConsoleSupportServiceProvider) it still loads the original AppNameCommand over my custom one.
I've already got a work around BUT it would be nice to know about the behaviour of singleton services for the future if this is at all possible? (This is using Laravel 5.2 if that makes any difference.)
There's actually a cleaner way to do this. You basically want to extend a core binding, which can be achieved by using the extend method:
$this->app->extend('command.app.name', function ($command, $app) {
return new AppNameCommand($app['composer'], $app['files']);
});
Jason Lewis has a really nice article regarding Laravel's IoC on Tutsplus. Make sure to check it out ;)
I looked at this case and it seems it not the easy one. If you use singleton in your custom Provider it will be finally overridden by default provider (deferred one) so it seems it won't be the way.
After checking that simple approach doesn't work, what you need to do in such case is analysing what is happening when Laravel registers this command.
So in your case you search first for command.app.name - you see it's in Illuminate\Foundation\Providers\ArtisanServiceProvider and there is method registerAppNameCommand you would like to probably override.
So now you look for occurences of ArtisanServiceProvider to see where it's launched - you see it's in Illuminate\Foundation\Providers\ConsoleSupportServiceProvider in $providers property (which you would like probably to change).
So finally you should look for occurrences of ConsoleSupportServiceProvider and you see it's in config/app.php.
So what you need to do in this case:
Change in config/app.php - change Illuminate\Foundation\Providers\ConsoleSupportServiceProvider into your custom one ConsoleSupportServiceProvider
In your custom one you should extend from \Illuminate\Foundation\Providers\ConsoleSupportServiceProvider but change in $providers from Illuminate\Foundation\Providers\ArtisanServiceProvider into your custom ArtisanServiceProvider
finally create custom ArtisanServiceProvider which will extend from \Illuminate\Foundation\Providers\ArtisanServiceProvider where you override registerAppNameCommand using custom class in singleton
Using this way you will achieve your goal (I've verified it that custom class will be used running command php artisan app:name).
Alternatively you might want in your custom ArtisanServiceProvider remove 'AppName' => 'command.app.name', from $devCommands and use your custom service provider as you showed where you register your singleton but I haven't tried this approach.
I do need to change Zend_Controller_Front and use My_Controller_Front, but I can't figure it out... I have made this:
At My_Controller_Front
/**
* Set singleton instance
*/
public static function setInstance($instance = null) {
self::$_instance = $instance;
}
And at my bootstrap
protected function _replaceZendController() {
Busca_Controller_Front::setInstance(Busca_Controller_Front::getInstance());
return $this;
}
From looking at the code I don't think its possible to have Zend_Application use anything other than Zend_Controller_Front.
When you run a Zend_Application, the following things happen:
Zend_Application::bootstrap() runs
The bootstrap process creates Zend_Application_Bootstrap_Bootstrap which sets up the resource loader and then loads the FrontController resource
The Frontcontroller resource is hardcoded to load Zend_Controller_Front (see Zend/Application/Resource/Frontcontroller::getFrontController())
The only way you may be able to change this behavior would be to register your own resource loader which could intercept the loading of the FrontController resource which would load your Front Controller instead of the Zend Front Controller. Of course you would have to make sure your Front Controller supports every option the Zend Framework one does.
So the question is, why do you need to replace Zend_Controller_Front with your own? Can't you set the appropriate options or create plugins to accomplish your goal?