I am getting following error when I try to use Illuminate\Http\Request in my class.
Error:
PHP Fatal error: Uncaught RuntimeException: A facade root has not been set. in /home/sasha/Documents/OffProjects/vetnearme/vetnearme/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:218
Stack trace:
#0 /home/sasha/Documents/OffProjects/vetnearme/vetnearme/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(396): Illuminate\Support\Facades\Facade::__callStatic('replaceNamespac...', Array)
#1 /home/sasha/Documents/OffProjects/vetnearme/vetnearme/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(373): Illuminate\Foundation\Exceptions\Handler->registerErrorViewPaths()
#2 /home/sasha/Documents/OffProjects/vetnearme/vetnearme/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(288): Illuminate\Foundation\Exceptions\Handler->renderHttpException(Object(Symfony\Component\HttpKernel\Exception\HttpException))
#3 /home/sasha/Documents/OffProjects/vetnearme/vetnearme/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(187): Illumina in /home/sasha/Documents/OffProjects/vetnearme/vetnearme/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php on line 218
The class in question:
namespace App\App\Components;
use Illuminate\Http\Request;
/**
* This class will be used to build menu for admin panel based on the user role
*/
class AdminPanelMenu {
static function menu(Request $request){
$user = $request->user();
if($user->hasRole['super_admin'])
return self::superAdmin();
if($user->hasRole['admin'])
return self::admin();
if($user->hasRole['user'])
return self::user();
return [];
}
private static function superAdmin()
{
return [
'MAIN NAVIGATION',
];
}
private static function admin()
{
return [
'MAIN NAVIGATION',
];
}
private static function user()
{
return [
'MAIN NAVIGATION',
];
}
}
What am I doing wrong here?
You need to create a new app container and then bind it to the Facade.
use \Illuminate\Container\Container as Container;
use \Illuminate\Support\Facades\Facade as Facade;
/**
* Setup a new app instance container
*
* #var Illuminate\Container\Container
*/
$app = new Container();
$app->singleton('app', 'Illuminate\Container\Container');
/**
* Set $app as FacadeApplication handler
*/
Facade::setFacadeApplication($app);
in lumen:
bootstrap/app.php
$app->withFacades();
Good luck!
I know this is old but maybe it will help someone. I ran into this problem after I was fiddling with my app/config.php file. I added some options and accidentally put a semi-colon instead of a comma after it.
I had:
'vapid_public_key' => env('VAPID_PUBLIC_KEY'); <--- offending semi-colon
'vapid_private_key' => env('VAPID_PRIVATE_KEY'),
Changed it to the proper comma and everything works as expected.
Really late but hopefully can help someone else. I found the easiest solution to this error was just to change the route (ie from /post to /posts) of the page that this error appears. And don't forget to change anywhere that has direct links to it.
I think the problem is that Laravel is confusing scope resolution to facade error. check your code so make sure you do not have any static variable not existing in a class. E.g if you have a PHP class like;
<?php
class StaticExample
{
public const EXAM = 'exam';
}
?>
then you try to call a non-existing const StaticExample::EXAMS. Laravel will give you the above error which makes no sense because it's very difficult to trace. No error in the logs and you're lost.
My solution is to use an editor like PHPStorm that will point out your development errors. Another way is that you should check your scope resolutions very well.
Related
I have installed the Laravel in sub-folder and is trying to install the horizon. After routing to "test.com/sub-folder/horizon", all the design in broken and also the internal links are pointing to main domain instead of main-domain-without-subfolder.
After the search, it seems to be the known issue which is already reported in github issue
Has there is any work around to make horizon work when Laravel is installed in sub-folder?
I have a solution that only involves PHP.
The issue, as pointed out by #Isaiahiroko, is the basePath defined for Horizon's interface. That code is in Laravel\Horizon\Http\Controllers\HomeController::index(). The idea is this: we are going to pass to Laravel's service container our own implementation of that controller that will override the basePath definition passed to Horizon's interface.
Create a new controller with code like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\Http\Controllers\HomeController;
class HorizonHomeController extends HomeController
{
/**
* Overrides default horizon route to support subdirectory hosting.
*/
public function index ()
{
// We use a plain request to check for the base url.
$request = request();
// Set up our base path.
$base_path = Str::substr($request->getBasePath(), 1);
if (!empty($base_path)) {
$base_path .= '/';
}
// Patch default horizon variables with our own base path.
$variables = Horizon::scriptVariables();
$variables['path'] = $base_path . config('horizon.path');
// Render horizon's home view.
return view('horizon::layout', [
'assetsAreCurrent' => Horizon::assetsAreCurrent(),
'horizonScriptVariables' => $variables,
'cssFile' => Horizon::$useDarkTheme ? 'app-dark.css' : 'app.css',
'isDownForMaintenance' => App::isDownForMaintenance(),
]);
}
}
What's left is telling Laravel's service container that when Horizon's HomeController is requested, it should provide our HorizonHomeController class. In your AppServiceProvider, at the end of the register() method, set this up:
// [...]
class AppServiceProvider extends ServiceProvider
{
// [...]
/**
* Register any application services.
*
* #return void
* #throws InvalidConfiguration
*/
public function register()
{
// [...]
// Horizon's subdirectory hack
$this->app->bind(
Laravel\Horizon\Http\Controllers\HomeController::class,
App\Http\Controllers\HorizonHomeController::class
);
}
// [...]
}
After that, you should be able to browse to http(s)://<your-host>/<your-sub-dir>/horizon normally.
Considerations:
To me this feels cleaner that patching a compiled js, which also has the downside that needs to be re-applied every time Horizon is updated (this can be mitigated with a post-update script in composer, tho). Also, for additional points, this solution is only overriding the method that renders the view, but not the route, which means all of Horizon's authentication mechanisms (middlewares and gates) are working exactly as described in the documentation.
If you desperately need to do this, here is a hack:
In public\vendor\horizon\app.js, search for window.Horizon.basePath
replace window.Horizon.basePath="/"+window.Horizon.path; with window.Horizon.basePath="/[you sub-directoy]/"+window.Horizon.path;
It should work...until you run update one day and it mysteriously stop working.
I have registered a Paypal service provider:
App\Providers\PaypalHelperServiceProvider::class,
and, when I type hint it in my controller it properly resolves:
public function refund(Request $request, PaypalHelper $paypal) {...
Here is my provider class:
class PaypalHelperServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register()
{
$this->app->bind('App\Helpers\PaypalHelper', function() {
$test = 'test';
return new PaypalHelper();
});
}
public function provides()
{
$test = 'test';
return [App\Helpers\PaypalHelper::class];
}
}
Everything works as expected. Now I wanted to be able to modify controller to take a PayPal interface. I would then update my service provider to conditionally pass in either the real class or a mock one for testing, using the APP_ENV variable to determine which one to use. I put some debuggers into the service provider class and could not get it to ever go in. I thought perhaps that it only loads them on need, so I put a breakpoint inside my controller. The class did resolve, but it still never went into the service provider class! Can someone explain to me why this is the case? Even when I modified the code to pass in a different class type it did not pick up.
EDIT:
Here is the code flow I see when I debug this:
ControllerDispatcher -> resolveClassMethodDependencies -> resolveMethodDependencies -> transformDependency. At this point we have the following laravel code in the RouteDependencyResolveerTrait:
protected function transformDependency(ReflectionParameter $parameter, $parameters, $originalParameters)
{
$class = $parameter->getClass();
// If the parameter has a type-hinted class, we will check to see if it is already in
// the list of parameters. If it is we will just skip it as it is probably a model
// binding and we do not want to mess with those; otherwise, we resolve it here.
if ($class && ! $this->alreadyInParameters($class->name, $parameters)) {
return $this->container->make($class->name);
}
}
Since getClass() always resolves to the interface name, when we call container->make(), it always fails with
Target [App\Helpers\PaypalHelperInterface] is not instantiable.
Change
$this->app->bind('App\Helpers\PaypalHelper', function() {
$test = 'test';
return new PaypalHelper();
});
To
if (app()->environment('testing')) {
$this->app->bind(
PaypalHelperInterface::class,
FakePaypalHelper::class
)
} else {
$this->app->bind(
PaypalHelperInterface::class,
PaypalHelper::class
);
}
I finally found out the issue. My problem was that my provider wasn't being picked up at all. Here was the solution
composer dumpautoload
php artisan cache:clear
https://laravel.io/forum/02-08-2015-laravel-5-service-provider-not-working
I am making packages for the first time in Laravel (v5.5). Unfortunately, the Route Model Bindings are giving me trouble. At first, I was receiving the id number raw without the model, and I realized that I had to force the package routes through the 'web' middleware grouping.
When I add the 'web' middleware grouping, I can see that laravel tries to load the model, but I receive an "Illegal offset type in isset or empty" in the first line of the Container->makeAlias method.
I have tried using custom binding loaders defined in the ServiceProvider using Route::model( $class, $closure ), but the error occurs before the loader is called. I've even tried moving this declaration from the ServiceProvider->boot method to the ServiceProvider->register method, just in case it is an order of operations issue.
I have tried adding a binding to the app by calling $this->app->bind inside the ServiceProvider, but this has had no effect.
I'm going to post the relevant code below, and I appreciate any insites that you may have! Thank you in advance!
Some Facts
The route
We are loading the url: http://127.0.0.1/profiles/1/edit for this test.
Operation without bindings
When no bindings or middleware are applied, and the url above is called, I can safely load the model from within the function manually and return the view. This indicates that the package is correctly loaded into the main application container.
The model can load in Tinker
To verify the model and database connection, we use tinker and run the command \Clemence\Profiles\Profile::find(1) to which the following is returned:
=> Clemence\Profiles\Profile {#777
id: 1,
name: "Clemence, Jared",
address: """
554 Christmas Tree Lane\n
Bakersfield, CA 93306
""",
email: "jaredclemence#gmail.com",
phone: "610-360-9558",
created_at: null,
updated_at: null,
deleted_at: null,
}
The stack trace
The Route->performBinding method is called with the following arguments:
[
Closure, /* Presumably the loader */
"1", /* the index that is passed into the route URL */
Route /* A self reference back to the containing object */
]
after this, the Closure is called with
[
"1",
Route
]
from the Closure, Application->make is called, the argument named $class is passed another Closure with the following definition:
Closure {#97 ▼
class: "Clemence\Profiles\ProfilesServiceProvider"
this: ProfilesServiceProvider {#87 …}
parameters: {▶}
}
lastly we end up in Container->makeAlias where the Exception is thrown, makeAlias is called with this Closure:
Closure {#97 ▼
class: "Clemence\Profiles\ProfilesServiceProvider"
this: ProfilesServiceProvider {#87 …}
parameters: {▶}
}
The Files
packages/clemence/profiles/src/routes.php
Route::group(['middleware' => ['web']], function() {
Route::get('/profiles/{profile}/edit', '\Clemence\Profiles\ProfileController#edit');
Route::get('/profiles/create', '\Clemence\Profiles\ProfileController#create');
Route::get('/profiles', '\Clemence\Profiles\ProfileController#showAll');
Route::get('/profiles/{profile}', '\Clemence\Profiles\ProfileController#show');
Route::post('/profiles/create', '\Clemence\Profiles\ProfileController#createWithFormData')->name('profile.create');
Route::put('/profiles/{profile}', '\Clemence\Profiles\ProfileController#updateWithFormData')->name('profile.update');
Route::delete('/profoiles/{profile}', '\Clemence\Profiles\ProfileController#destroy')->name('profile.delete');
Route::get('/roles', '\Clemence\Profiles\RoleController#showAll');
Route::get('/roles/{role_id}', '\Clemence\Profiles\RoleController#show');
Route::get('/roles/create', '\Clemence\Profiles\RoleController#create');
});
packages/clemence/profiles/src/ProfilesServiceProvider.php
namespace Clemence\Profiles;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;
class ProfilesServiceProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot() {
$this->loadRoutesFrom( __DIR__ . '/routes.php' );
if ($this->app->runningInConsole()) {
$this->commands([
\Clemence\Profiles\Commands\InstallProfiles::class,
\Clemence\Profiles\Commands\PrepareEnvironmentForProfiles::class
]);
}
}
/**
* Register the application services.
*
* #return void
*/
public function register() {
$this->app->make(\Clemence\Profiles\ProfileController::class);
$this->app->make(\Clemence\Profiles\RoleController::class);
$this->loadMigrationsFrom(__DIR__ . '/migrations');
$this->loadViewsFrom(__DIR__ . '/views', 'profiles');
}
}
packages/clemence/profile/src/ProfileController.php
namespace Clemence\Profiles;
use App\Http\Controllers\Controller;
use Clemence\Profiles\Profile;
use Illuminate\Http\Request;
use Clemence\Profiles\FormTemplateData;
use Illuminate\Support\Facades\URL;
class ProfileController extends Controller
{
/* ... collapsed ... */
/**
* Show the form for editing the specified resource.
*
* #param \App\Profile $profile
* #return \Illuminate\Http\Response
*/
public function edit(Profile $profile )
{
return dd( $profile );
$formData = new FormTemplateData();
$profile = Profile::find( $profile_id );
$formData->setProfile( $profile );
$formData->setFormActionMethod("POST");
$formData->setDeleteButtonText("Delete Profile");
$formData->setSubmitButtonText( "Update Profile" );
$on_cancel_route_url = $formData->getNamedUrl('profile.delete');
$on_submit_route_url = $formData->getNamedUrl('profile.update');
$formData->setFormCancelUrl($on_cancel_route_url);
$formData->setFormSubmitUrl($on_submit_route_url);
return $this->editForm($formData);
}
/* ... collapsed ... */
}
packages/clemence/profile/src/Profile.php
namespace Clemence\Profiles;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Model;
class Profile extends Model
{
use SoftDeletes;
}
RESOLVED: SOLUTION UNKNOWN
I will post the steps that I took to find the resolution here for those who may find this post in the future. I admit: I have no idea what fixed the problem.
What I did after asking the question
First and foremost, I got some sleep. When I have been coding too long, I find that sleep solves most problems.
On waking, I realized that I have a machine with XDebug installed and I can literally step through the PHP code as it executes, so I booted up that machine and did the following:
a. I moved the project to the local machine and opened it in NetBeans.
b. I placed breakpoints at several places in the middle ware so that I could verify that the system was running.
c. I changed the project config to run "as a website" and to load the url http://localhost:8000/profiles/1/edit as the start point.
d. I clicked 'Debug Program' and stepped through the bootup, the route loading, the model binding, and watched it all happen step-by-step looking for any line of code or parameter that looked out of sorts. I watched as the model was selected. I watched as it was passed the id 1 and asked to build the query. I watched as a collection of Profiles was returned (Oh! how excited I was to see the data I wanted sitting in that array and knowing that it was there but something would eventually block it, and I would find the block and release it!), and I watched as the collection was asked to return the first value. Then I watched as the parameter was set on the route. Lastly, I watched as the command $kernal->terminate() ran....
Needlessly to say, I was very disappointed and confused. How could I be getting an error, if the Kernal is running to completion?
So, I looked at the browser window, and there in the browser window was the very information that I was looking for! The Profile had loaded. I had changed nothing, and the Profile had loaded.
I moved the code back to the original machine, maybe it is an environment setting? I ran the web query without debugging, and it loaded... no error!
So, I honestly don't know what happened. I doubt that getting sleep helped the problem.
Maybe the Netbeans Debugger ran a cleanup service that fixed or patched a bad linkage? I honestly don't know, but I do know that this problem is resolved, so I post this answer in the hopes that it helps someone in the future, and that it saves other people the time of needlessly looking for an unnecessary solution.
I am trying to do a hello world service provider with the new Laravel 5.4.
I have created the following service provider file:
//File: app/TestProvider/TestServiceProvider.php
namespace App\TestProvider;
use Illuminate\Support\ServiceProvider;
class TestServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->bind('Test', function ($app) {
return new Test();
});
}
}
I have created a simple class under the same namespace:
//File: app/TestProvider/Test.php
namespace App\TestProvider;
class Test
{
/**
* Register bindings in the container.
*
* #return void
*/
public function helloWorld()
{
echo "hello world";
}
}
The problem is, this is not registering. The register method is executing as when I put a breaker before the 'bind' method, it executes:
public function register()
{
dd("BREAKER");
$this->app->bind('Test', function ($app) {
return new Test();
});
}
So this outputs "BREAKER" as expected. However if I put the breaker in the closure, nothing happens which suggests for some reason, that 'bind' method isn't being executed??
Any ideas?
EDIT:
Just some further info: I know that the Test class is registered and in the correct namespace as I can do:
dd(new Test());
in the registration method, and it outputs the resource id as expected.
Explanation
The closure provided only runs when the binding is being resolved. That's why it's a closure, it can be saved in the service container and resolved at any time while the program runs.
Solution
To see the resolved binding, create a controller and resolve the class in that controller:
// File: app/Http/Controllers/TestController.php
namespace App\Http\Controllers;
// This isn't the best way, but it works. See the best way below
class TestController extends Controller {
public function index()
{
return \App::make('Test')->helloWorld();
}
}
Of course, don't forget to register the route:
// File: routes/web.php
Route::get('/', 'TestController#index');
The binding will resolve when you hit the homepage.
However, as I said, it's not the best way, so here I prepared a better way. Change the way you register the binding:
// File: app/Providers/TestProvider.php
namespace App\TestProvider;
use Illuminate\Support\ServiceProvider;
use App\TestProvider\Test;
// Better way
class TestServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
// Note: we bind the exact complete class name!
$this->app->bind(Test::class, function ($app) {
return new Test();
});
}
}
After this change the controller so that it looks like this:
namespace App\Http\Controllers;
use App\TestProvider\Test;
class TestController extends Controller {
/**
* #var Test $test
*/
private $test;
// Let Laravel resolve the dependency on constructing the class
public function __construct(Test $test)
{
$this->test = $test;
}
public function index()
{
return $this->test->helloWorld();
}
}
You will see that the exact same thing happens, but it looks more elegant and avoids conflicts.
Details
Laravel gives only a high level overview of the service container, which doesn't help to learn how it works on the inside. The best way to see that is to go down the call stack.
When you do that, you find that Laravel registers every class in the project in the service container. That means that whether you create a service provider or not, the class will be in the container. How exactly?
When you run php artisan optimize, Laravel creates files that have array with all the classes of the project. When you run the app, after registering everything from the service providers, Laravel registers the rest of the classes from that file.
That means that in your case, if you don't specifically register the Test class, it will still be resolvable. Basically, you only need to register classes that need some specific instructions to be resolved.
So how does Laravel resolve the dependencies?
When you run \App::make(Test::class) or inject dependency via type hinting in the constructor (the "better way" from my solution), Laravel looks for that dependency among the bindings.
When it finds the dependency, it resolves either the closure associated to it or the constructor of the class directly.
When it resolves the constructor directly, it looks for type hints among the constructor parameters and recursively resolves all of them until there's nothing else to resolve.
After that it returns the resolved class.
Of course, bear in mind that for Laravel to analyze the contructor of a class, it needs to be resolved via the service container in the first place. You can't just call $test = new Test(); and expect Laravel to do all the magic :)
Conclusion
This is a rather quick overview of Laravel's service container. The best way for you to learn it is, of course, studying the sources for yourself. It's truly elegant and it uses PHP's functionality to the fullest.
I really hope this shed some light on the service container for you and can help you in the future :)
The closure passed to the bind() method is not executed until you actually attempt to resolve the alias you are binding.
So, if you dd('breaker') inside the closure, this won't actually get executed until Test is resolved (whatever your preferred resolution method is):
Service provider:
// bind the closure to the 'Test' alias
public function register()
{
$this->app->bind('Test', function ($app) {
dd("BREAKER");
return new Test();
});
}
Code that resolve Test alias:
// different ways of resolving the alias out of the container.
// any of these will execute the bound closure.
$test = resolve('Test');
$test = app('Test');
$test = app()->make('Test');
$test = \App::make('Test');
try:
$this->app->bind(Test::class, function ($app) {
return new Test();
});
I'm trying to develop a package, so I've followed this tutorial until Creating a Facade section because I don't need a facade.
The problem is:
/app/routes.php
Route::get('test', 'Aristona\Installer\Installer#install');
throws an exception: Call to undefined method Aristona\Installer\Installer::callAction()
My Installer.php is like this:
workbench/aristona/installer/src/Aristona/Installer/Installer.php
<?php namespace Aristona\Installer;
class Installer
{
public static function install()
{
return "Hello";
}
}
The class is loading. I've added it to my service providers list. Also I can confirm it is loading by adding one more install method, because PHP throws a fatal error about redeclaring same method twice.
I've tried different combinations on my method prefixes (e.g without static) Doesn't solve.
Anyone know what am I doing wrong?
Your getting an error because you're trying to use routing to controller where none exists. To be more specific, Laravel is trying to perform this method from it's core Controller class:
/**
* Execute an action on the controller.
*
* #param string $method
* #param array $parameters
* #return \Symfony\Component\HttpFoundation\Response
*/
public function callAction($method, $parameters)
{
$this->setupLayout();
$response = call_user_func_array(array($this, $method), $parameters);
// If no response is returned from the controller action and a layout is being
// used we will assume we want to just return the layout view as any nested
// views were probably bound on this view during this controller actions.
if (is_null($response) && ! is_null($this->layout))
{
$response = $this->layout;
}
return $response;
}
So unless the class you're specifying in Route::get() is extending either BaseController or Controller, this exception will be thrown. If you tested the same method inside a closure, it would work.
More about Laravel controller routing can be found here.
To fix this, you should either add a controller to your package or use the Installer class inside another controller.