laravel7 override vendor package class - php

I'm new to this service container stuff in general.
Just looking for an easy way to override the getView() method of the Captcha class.
My idea was to create a new class extending the captcha class:
<?php
namespace App\Http\Helpers\Captcha;
use Igoshev\Captcha\Captcha\Storage\StorageInterface;
use Igoshev\Captcha\Captcha\Generator\GeneratorInterface;
use Igoshev\Captcha\Captcha\Code\CodeInterface;
class CaptchaNew extends Igoshev\Captcha\Captcha
{
/**
* Get html image tag.
*
* #return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function getView()
{
//new code...
}
}
Inside the AppServiceProvider under register method using:
$loader = AliasLoader::getInstance();
$loader->alias('App\Http\Helpers\Captcha\CaptchaNew', 'Igoshev\Captcha\Captcha');
I already tried the boot method too, doesn't work too. What's the best way to override the class? A `serviceProvider' is provided too, but I want to keep things simple and I have no idea about serviceProviders in general.

You could try this way below to register your service, use bind function instead.
Under your register function in AppServiceProvider, replace yours with the sample code below. But please be noticed that first parameter of bind function must be same as facade accessor of vendor library (in this case is \Igoshev\Captcha\Captcha\Captcha::class, you can take a look here). Register this facade in config/app.php after the service provider of package:
$this->app->bind(\Igoshev\Captcha\Captcha\Captcha::class, function(){
return new CaptchaNew($this->app['config']);
})

Related

Laravel 5.6 Facade/Alias not registering/working

I am trying to make my own custom Facade and register is with a custom service container and finally creating a custom alias for this facade.
I am not sure what part is not working, maybe there is a problem with the service container registering or maybe with the alias?
Let's start with my facade:
/**
*
* #see \App\Library\Facades\ViewWrapper\CustomView
*/
class CustomViewFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'customview';
}
}
My CustomView class with the logic and the show function
namespace App\Library\Facades\ViewWrapper;
...
class CustomView
{
public function show(...) { ... }
...
}
My CustomViewServiceProvider
namespace App\Providers;
...
class CustomViewServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(CustomViewFacade::class);
$this->app->alias(CustomViewFacade::class, 'customview');
}
}
How I register the provider in the config\app.php
App\Providers\CustomViewServiceProvider::class,
How I create the alias in the config\app.php
'CustomView' => App\Library\Facades\ViewWrapper\CustomViewFacade::class
In my controller I use the facade like this:
use CustomView;
...
public function show(ImageRequest $imagerequest)
{
return CustomView::show(...);
}
I get the following error in the controller:
Class 'CustomView' not found
What am I doing wrong here?
EDIT
After clearing config and composer autoload dump I get the following error:
Call to undefined method App\Library\Facades\ViewWrapper\CustomViewFacade::show()
I think you haven't quite clearly understood how Facades work. They are just an easy way to access your services without having to deal with dependency injection. I'm not a fan of this methodology, but here's how you do it properly.
You need to bind your actual service to the container, not the facade. The facade is almost just a symbolic link to your service within the container.
You need to import the actual service, not the facade. Laravel will automatically bind your dependency in the type-hinted variable, thanks to its behind the scenes magic.
Use:
use App\Library\Facades\ViewWrapper\CustomView;
(small note: your namespace here should be your service's namespace, be aware to not mix up the semantic between facade and service. The service contains the logic, the facade is just an accessor to a service that is already injected. This is important!!)
Instead of:
use CustomView;
It should solve the issue.
Also, I'd suggest you do define how the class should be constructed and injected in the Service Container by using a Closure in the bootstrap function.
class CustomViewServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(CustomView::class, function () {
return new CustomView(...);
);
}
}
Also, the alias function is not necessary in your case. It'd simply allow you to access the service by using the customview key in the Service Container.
Just define the Facade in your config/app.php file.
Another small suggestion: use PHP 7 class selectors instead of strings in your facade accessor definition. For example: CustomView::class intead of customview. It makes your code neater and easier to read.
Please run below command and check:
php artisan config:cache
php artisan cache:clear

Laravel Own ServiceProvider Client Call Type error: Argument 1 passed to ... must be an instance of

I want to swap out my client call or better i try to make a wrapper around this package, so i dont have to write this everytime, so i made a new ServiceProvider which should call
// Create a new client,
// so i dont have to type this in every Method
$client = new ShopwareClient('url', 'user', 'api_key');
on every request i make.
// Later after the Client is called i can make a Request
return $client->getArticleQuery()->findAll();
SwapiServiceProvider
<?php
namespace Chris\Swapi;
use Illuminate\Support\ServiceProvider;
use LeadCommerce\Shopware\SDK\ShopwareClient;
class SwapiServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* #return void
*/
public function boot()
{
}
/**
* Register any package services.
*
* #return void
*/
public function register()
{
$this->app->singleton(ShopwareClient::class, function () {
return new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
);
});
}
}
My Class
...
use LeadCommerce\Shopware\SDK\ShopwareClient as Shopware;
class Swapi
{
public function fetchAllArticles(Shopware $shopware)
{
return $shopware->getArticleQuery()->findAll();
}
}
Testing
I just call it in my routes.php for testing
use Chris\Swapi\Swapi;
Route::get('swapi', function () {
// Since this is a package i also made the Facade
return Swapi::fetchAllArticles();
});
But i get everytime the error
FatalThrowableError in Swapi.php line 18: Type error: Argument 1
passed to Chris\Swapi\Swapi::fetchAllArticles() must be an instance of
LeadCommerce\Shopware\SDK\ShopwareClient, none given, called in
/Users/chris/Desktop/code/swapi/app/Http/routes.php on line 7
So i am asking why this
return new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
);
is not called everytime i call a method e.g $shopware->getArticleQuery()->findAll();
Does anyone know why?
I think there might be some confusion here about Laravel's IoC. When you use return Swapi::fetchAllArticles();, Laravel doesn't know what you are doing because you haven't used the container to build out the Swapi class (even though you have registered one with the container) nor do you have a facade built to access it in that manner. Otherwise PHP is going to complain because your function isn't static.
I just wrote this code and verified that it works as far as Laravel putting it all together.
In my service provider, my register function was this...
public function register()
{
$this->app->singleton('swapi', function($app) {
return new SwapiRepository(
new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
)
);
});
}
Keep in mind, swapi is really just a key the container will use to find the actual class. There's no need to pass in the entire qualified class name when you can keep it simple and easy.
My SwapiRepository which is really the wrapper for the Shopware SDK.
use LeadCommerce\Shopware\SDK\ShopwareClient;
class SwapiRepository
{
protected $client;
public function __construct(ShopwareClient $client)
{
$this->client = $client;
}
public function fetchAllArticles()
{
return $this->client->getArticleQuery()->findAll();
}
}
At this point, you are basically done. Just add App\Providers\SwapiServiceProvider::class, in the providers array (which you probably have done already) in app/config.php and use your wrapper like so...
$swapi = app('swapi');
$swapi->fetchAllArticles();
Or you can have Laravel inject it into other classes as long as Laravel is building said class.
If you want to build out a facade for this to save yourself a line of code each time you want to use this or for snytactical sugar...
use Illuminate\Support\Facades\Facade;
class Swapi extends Facade
{
protected static function getFacadeAccessor() { return 'swapi'; }
}
Make sure to update your aliases array in app/config.php so that it contains 'Swapi' => App\Repositories\Swapi::class,
And finally you should be able to use it like so...
Swapi::fetchAllArticles();
Please note your namespaces are different than mine so you may need to replace mine with yours. You should also now be able to easily inject Swapi into other classes and even method injected into your controllers where needed.
Just remember if you do that though, make sure you are grabbing instances of those classes from Laravel's service container using the app() function. If you try to build them out yourself using new SomeClass, then you have the responsibility of injecting any dependencies yourself.

Use of service providers within controllers in Laravel 5.2

As for the title I've googled about two hours searching for a efficient answer and read repeatedly the official documentation, but without any step further, considering I'm relatively new to the framework. The doubt arise while searching for a correct way to share some code between controllers and i stumbled in service providers, so:
I've created say a MyCustomServiceProvider;
I've added it to the providers and aliases arrays within the app.php file;
finally I've created a custom helpers class and registered it like:
class MyCustomServiceProvider extends ServiceProvider
{
public function boot()
{
//
}
public function register()
{
$this->app->bind('App\Helpers\Commander', function(){
return new Commander();
});
}
}
So far, however, if I use that custom class within a controller I necessarily need to add the path to it through the use statement:
use App\Helpers\Commander;
otherwise I get a nice class not found exception and obviously my controller does not his job.
I suspect there's something which escapes to me on service providers! :-)
So far, however, if I use that custom class within a controller I
necessarily need to add the path to it through the use statement:
`use App\Helpers\Commander;`
otherwise I get a nice class not found
exception and obviously my controller does not his job.
Yes, that's how it works. If you don't want to use the full name, you can use a Facade instead.
Create the Facade class like this:
class Commander extends Facade
{
protected static function getFacadeAccessor() { return 'commander'; }
}
register the service:
$this->app->singleton('commander', function ($app) {
return new Commander();
});
add the alias to your config/app.php:
'aliases' => [
//...
'Commander' => Path\To\Facades\Commander::class,
//...
],
and use it like a Facade:
\Commander::doStuff();
On why your code still works, even when you remove the bind:
When you type-hint a parameter to a function, and Laravel does not know about the type you want (through binding), Laravel will do its best to create that class for you, if it is possible. So even though you didn't bind the class, Laravel will happily create a instance of that class for you. Where you actually need the binding is when you use interfaces. Usually, you'd not type-hint specific classes but a interface. But Laravel can not create a instance of an interface and pass it to you, so Laravel needs to know how it can construct a class which implements the interface you need. In this case, you'd bind the class (or the closure which creates the class) to the interface.

Override Singleton in Laravel Container

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.

How do I add a 'validator' class to this code to get it to work?

This is the error that I keep receiving: Reflection Exception Class validator does not exist This is the code causing the problems:
use Illuminate\Support\ServiceProvider;
class DeskServiceProvider extends ServiceProvider
{
/**
* Register bindings
*
* #return void
*/
public function register()
{
$this->repositories();
$this->app->bind('Desk\Forms\MessageForm', function($app) {
$validator = $app->make('validator')->make([], []);
return new \Desk\Forms\MessageForm($validator);
});
}
}
I now know that I need to add a Validator class but I am not sure where or what to put in it. Thank you for all your help.
Your question is a little confusing, as is your code. If looks like you're trying to bind a service.
$this->app->bind('Desk\Forms\MessageForm'
However, instead of telling Laravel the service name you want to use to identify your service (like db, or message_form, etc.) you're passing it a class name (Desk\Forms\MessageForm).
Then, you're using the application's make factory to instantiate a validator object. It's not clear if you're trying to use make to instantiate an object from a class named Validator, or if you're trying to instantiate a service object from a service named validator. If the later, it doesn't look like a validator service exists in your application. If the former, it doesn't look like a class named Validator is defined anywhere Laravel can autoload from.
Regarding the next obvious question: Where can Laravel autoload from, you either want this Validator class in your composer package's src folder, named in a way that's PSR valid. If you're not using composer and this is a local application, the easiest thing to do is drop the file in
app/models/Validator.php
However, it's also not clear from your question if you're trying to use the Laravel built-in Validator service facade/object. A better question might yield a better answer. (possibly of interest, and a self link, I'm in the middle of writing a series of articles that explains the Laravel application container, which you may find useful.).

Categories