I want to share a variable of my views with:
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
\Schema::defaultStringLength(191);
$customers = Customer::get();
\View::share('customers', $customers);
}
}
it works as expected, but when I want to migrate my tables via artisan it throws an error, that the table for customers was not found because it is checked BEFORE the migration starts. So I need something like
if(!artisan_request) {
//request to laravel is via web and not artisan
}
But I haven't found anything in the documentation.
You can check if you are running in the console by using
app()->runningInConsole()
Underneath that, all it does is check the interface type
return php_sapi_name() == 'cli' || php_sapi_name() == 'phpdbg'
You can find more on the PHP Docs site
To detect whether the app is running in console, you can do something like this:
use Illuminate\Support\Facades\App;
if(App::runningInConsole())
{
// app is running in console
}
See, illuminate/Foundation/Application.php:520
Related
I have a custom service provider in which I am accessing a model in boot(). But when I run php artisan migrate, it shows the below error:
[Illuminate\Database\QueryException] SQLSTATE[42S02]: Base table or view not found: 1146 Table '********' doesn't exist
I found that if we add if (!app()->runningInConsole()) { inside boot(), it works successfully.
This is the code we have used in the service provider:
public function boot()
{
$this->bindCurrentPartToNav();
}
private function bindCurrentPartToNav(): void
{
$currentPartName = \App\Http\Helpers\Part::getPartName();
view()->composer(
'includes.partials.part',
function ($view) use ($currentPartName) {
$view->with('currentPartName', $currentPartName);
}
);
}
Helper file:
public static function getPartName(): ?string
{
return PartModel::PartKey()->active()->pluck('name')->first();
}
Model:
public function scopePartKey($query): Builder
{
return $query->where('identifier', config('env.PART_KEY'));
}
Is there any way to remove that service provider from php artisan migrate so that we can remove runningInConsole() check in each refresh?
Thanks for your help in advance.
As any environment configuration, in your case a general configuration, you should assign a default value fall back.
public static function getSectionName(): ?string
{
try {
return SectionModel::sectionKey()->active()->pluck('name')->first();
} catch (\Exception $e) {
return null;
}
}
This will simulate the case where the section model with that specific identification_key is missing in the database.
This will also prevent any issues with the initial migration.
But in the end, you tied a model with a view rendering code. You should find another solution to dissociate them. For example, you can move the boot() code out of the model and link it to a middleware.
You can also use Singleton pattern (since it's like a general unique config across the application)
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'm building a headless cms using laravel nova and vuejs.
I'm having an issue with trying to register the excellent CORS middleware from https://github.com/barryvdh/laravel-cors. I can get this working from the main app but I would like to add this as a dependency to my custom nova tool.
I just can't figure out how to do this.
I've tried adding the middleware in the routes function generated by the artisan nova:tool command.
/**
* Register the tool's routes.
*
* #return void
*/
protected function routes()
{
if ($this->app->routesAreCached()) {
return;
}
Route::middleware(\Barryvdh\Cors\HandleCors::class)
->prefix('api/meta-blog')
->group(__DIR__.'/../routes/api.php');
}
But I get an error Class Barryvdh\Cors\HandleCors does not exist from vendor/laravel/framework/src/Illuminate/Container/Container.php when I hit any of the api paths.
I think this is because the middleware is not registered with the main app. I'm looking to find out how to make this 3rd party nova tool dependency work with the main laravel routing system.
I have successfully used other 3rd party packages with success. But not this one. I can confirm that the package exists and has been loaded in my custom tools autoload file.
Thanks in advance.
I solved this.
In the boot function we can push a middleware to the api group.
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
...
$router = $this->app['router'];
$router->pushMiddlewareToGroup('api', Barryvdh\Cors\HandleCors::class);
}
Then in the routes function
/**
* Register the tool's routes.
*
* #return void
*/
protected function routes()
{
if ($this->app->routesAreCached()) {
return;
}
Route::prefix('api/meta-blog')
->group(__DIR__.'/../routes/api.php');
}
Hope this helps someone else.
I have a config file for my Laravel Custom package.
Currently I am using custom package's config file by publishing it as mentioned in the docs.
I just want to clarify that, Is there a way to use custom package's config file without publishing it in Laravel 5.*
Solved it.
Here's the code that I used.
public function register()
{
if ($this->app['config']->get('custom_package') === null) {
$this->app['config']->set('custom_package', require __DIR__.'/../Config/config.php');
}
in the ServiceProvider for the custom package.
You can use mergeConfigFrom method of your package's ServiceProvider
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/path/to/config/courier.php', 'courier'
);
}
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();
});