use config file of custom package without publishing in Laravel 5.* - laravel

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'
);
}

Related

Laravel Package: How to register a third party facade inside a custom package?

I'm creating a package that uses internally this hashid package.
How can I register a third party facade inside a custom package?
I tried three options and none of them worked.
Version - Composer
"aliases": {
"Hashids": "Vinkla\\Hashids\\Facades\\Hashids"
}
Version - inside my ServiceProvider with alias
class MyPackageServiceProvider extends ServiceProvider
{
public function register()
{
...
$this->app->alias(\Vinkla\Hashids\Facades\Hashids::class, 'Hashids');
}
Version - inside my ServiceProvider with AliasLoader
class MyPackageServiceProvider extends ServiceProvider
{
public function register()
{
...
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('Hashids', \Vinkla\Hashids\Facades\Hashids::class);
}
When I'm testing the code, I get the error:
Error: Call to undefined method Vinkla\Hashids\Facades\Hashids::encode()
inside
/** #test */
public something_to_test()
{
dd(\Hashids::encode(1));
}
Ok I found one solution, but I am still confused why it is like that.
In my "MyPackageServiceProvider" I need to add:
$this->app->register(HashidsServiceProvider::class);
Why do I need to register a Provider? I thought the composer is handling the work.
At the end only this works:
$this->app->register(HashidsServiceProvider::class);
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('Hashids', \Vinkla\Hashids\Facades\Hashids::class);
The other versions doesn't work as well :(

Laravel installation in sub-folder and horizon not working

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.

How can I add CORS middleware to routes defined in custom Laravel Nova tool?

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.

Check if input is from console

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

How i can extend laravel translator (any other component)?

I want to implement some extra features to Illuminate\Translate\Translator.
So, i create my folder in ~/vendor directory, place there My/Traslator class, that will implement Symfony\Component\Translation\TranslatorInterface. Right?
Is it OK to extend laravel translator class (a lot of functionality will be duplicated otherwise) in my package?
If it is ok - it will be necessary to tie to current laravel version to keep code stable. But what will happen in case enduser laravel version will differ from one required in my package?
What should i do then to make laravel use my translator class in application (facades,etc)?
Make a Translator class and make it extend Illuminate\Translation\Translator
<?php
namespace App\Helpers;
use Illuminate\Translation\Translator as LaravelTranslator;
class Translator extends LaravelTranslator
{
// here you can overwrite any functions you want/need
}
Create your own TranslationServiceProvider inside app/providers (just copy the laravel translation service provider and change the line where it uses Translator with your own Translator class where you have overwritten what you needed)
<?php
namespace App\Providers;
use App\Helpers\Translator; // <= Your own class
use Illuminate\Translation\FileLoader;
use Illuminate\Support\ServiceProvider;
class TranslationServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerLoader();
$this->app->singleton('translator', function ($app) {
$loader = $app['translation.loader'];
// When registering the translator component, we'll need to set the default
// locale as well as the fallback locale. So, we'll grab the application
// configuration so we can easily get both of these values from there.
$locale = $app['config']['app.locale'];
$trans = new Translator($loader, $locale);
$trans->setFallback($app['config']['app.fallback_locale']);
return $trans;
});
}
/**
* Register the translation line loader.
*
* #return void
*/
protected function registerLoader()
{
$this->app->singleton('translation.loader', function ($app) {
return new FileLoader($app['files'], $app['path.lang']);
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return ['translator', 'translation.loader'];
}
}
Comment out or delete the Laravels translator service line inside config/app.php:
//Illuminate\Translation\TranslationServiceProvider::class,
Add your own Provider in that same array
App\Providers\TranslationServiceProvider::class,
This page has more information: http://laravel.com/docs/5.0/extending#container-based-extension
So what you need to do is:
Extend the built-in class from the vendor directory
Create a new service provider that add your translation class to the service container
Replace Laravel’s translation service provider in your config/app.php file with the namespace of your translation service provider
Now when you ask for the translation service provider out of the service container—either directly (app('translator')) or with the Lang façade, it will return your translation class rather than Laravel’s.

Resources