I'm trying to extend Laravel's Auth Guard class by one additional method, so I'm able to call Auth::myCustomMethod() at the end.
Following the documentation section Extending The Framework I'm stuck on how to exactly do this because the Guard class itself doesn't have an own IoC binding which I could override.
Here is some code demonstrating what I'm trying to do:
namespace Foobar\Extensions\Auth;
class Guard extends \Illuminate\Auth\Guard {
public function myCustomMethod()
{
// ...
}
}
Now how should I register the extended class Foobar\Extensions\Auth\Guard to be used instead of the original Illuminate\Auth\Guard so I'm able to call Auth::myCustomMethod() the same way as e.g. Auth::check()?
One way would be to replace the Auth alias in the app/config/app.php but I'm not sure if this is really the best way to solve this.
BTW: I'm using Laravel 4.1.
I would create my own UserProvider service that contain the methods I want and then extend Auth.
I recommend creating your own service provider, or straight up extending the Auth class in one of the start files (eg. start/global.php).
Auth::extend('nonDescriptAuth', function()
{
return new Guard(
new NonDescriptUserProvider(),
App::make('session.store')
);
});
This is a good tutorial you can follow to get a better understanding
There is another method you could use. It would involve extending one of the current providers such as Eloquent.
class MyProvider extends Illuminate\Auth\EloquentUserProvider {
public function myCustomMethod()
{
// Does something 'Authy'
}
}
Then you could just extend auth as above but with your custom provider.
\Auth::extend('nonDescriptAuth', function()
{
return new \Illuminate\Auth\Guard(
new MyProvider(
new \Illuminate\Hashing\BcryptHasher,
\Config::get('auth.model')
),
\App::make('session.store')
);
});
Once you've implemented the code you would change the driver in the auth.php config file to use 'nonDescriptAuth`.
Only way to add (and also replace existing functions) is to create copy of Guard.php file in your project and in app/start/global.php add:
require app_path().'/models/Guard.php';
Of course it's ugly method, but I spent over hour to test all possibilities [how to change things stored in Session by Auth] and it always end with error:
... _contruct of class HSGuard requires first parameter to be 'UserProviderInterface' and get 'EloquentUserProvider' ...
Related
To develop a project where I am creating some methods which are common in all the controllers.
Before this I have used codeigniter and there I wrote MY_Controller class in core directory and then I extended the controller in all the controllers in controller directory.
Same I want to do inside Laravel. But I am confused that where should I write the common methods like send_email, validate_captcha, ajax_file_upload and other common methods which remains same across whole application.
So please suggest me a good way to define such a class or middleware.. What should one do to create that?
OK. Let me suggest some stuffs
Need to write methods which is apply to all Controllers. You
can/should modify App\Http\Controllers\Controller.php. Because all
Controllers in Laravel extend it
Need to write class that available across whole application. It is
never easier
Step 1: Write any class you want in app folder. And follow psr-4 convention
Step 2: Register to Laravel application
In App\Providers\AppServiceProvider. In register() method. Add
$this->app->bind('bindname', function ($app) {
return new \App\YourClass;
// If you want to inject other class to YourClass contructor
// return new \App\YourClass($app->make('otherbindname'));
});
Step 3: Use it. There are several ways to access YourClass in your whole application:
app()->make('bindname');
app('bindname');
app()['bindname'];
\App::make('bindname');
//etc
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.
I have a question! I have a library, whenever I need to call, I'll include it into & new Class() like the link below.
Now, I want to include it to use with Lumen framework & call usually inton Controller, then how to register service, class in Lumen to make it comfortable so that when needing, just call new FileMaker();
http://laravel.io/bin/E3d9x
Thank you so much!
What you're looking for is a Service Provider. Instead of including files in your Controllers, and then new'ing up instances of a class it is better to register the class within a service provider and then resolve the object out of the IoC container.
An example of how you can register a provider:
public function register()
{
$this->app->singleton('Full\Vendor\Namespace\FileMaker', function($app) {
return new FileMaker('someparameters');
});
}
Doing it this way means that you can inject dependencies into your Controllers and Laravel, or Lumen in this case will automatically resolve the object without you needing to instantiate the object.
For example, in your controller:
public function someControllerMethod(FileMaker $filemaker)
{
// The $filemaker instance is available to use
}
I'm trying to figure out how to add a method to a class in a Laravel package, so that all controllers and models that call that class can access the new method. How do I replace this class in the IoC?
This is the package in question, Angel CMS. The package is my creation, so I can modify it if we need to add aliases or anything to accomplish this.
Let's say I want to add a method to this class:
vendor/angel/core/src/models/PageModule.php
Okay, so I copy the class file to here:
app/models/PageModule.php
And then I modify the copied file, adding a namespace and the desired custom_function method:
<?php namespace MyModels;
use Eloquent;
class PageModule extends Eloquent {
protected $table = 'pages_modules';
public static function custom_function()
{
return 'It works!';
}
}
As you can see, I am using the MyModels namespace here.
Then, I run a composer dump-autoload.
Next, I open up my app/routes.php and register the binding and set up a test route:
App::bind('PageModule', function($app) {
return new \MyModels\PageModule;
});
Route::get('test-binding', function() {
return PageModule::custom_function();
});
But, when visiting the test route, I always receive the same error that the method is undefined.
What am I doing wrong here? Thank you in advance for any help.
To Clarify:
I am attempting to replace the class application-wide so that all other classes (controllers/models/etc.) that call PageModule will have access to the custom_function method. Thanks.
To be honest, I'm pretty new to all this IoC, dependency inversion/injection concept too. But I think I've gone through the same struggle before. What I would do, as much as my knowledge allows, is...
Add a constructor to src/controllers/admin/AdminPageController.php:
protected $pageModule;
public function __construct(PageModule $pageModule)
{
$this->pageModule = $pageModule;
}
Then where you did $module = new PageModule in the same file. You replace it with:
$module = $this->pageModule;
The two modifications above makes use of Laravel's IoC to allow injecting a different PageModule object into your controller, instead of strictly creating PageModule in your code.
Now at this point Laravel should know that when it constructs the AdminPageController, it should create a PageModule and inject into the controller for you.
Since your controller now expects a PageModule class, you can no longer do class PageModule extends Eloquent in your app anymore, because even though the name is the same, PHP does not think that it is! You'll need to extend it:
So let's rename your app/models/PageModule.php to app/models/CustomPageModule.php, and in the file change the class to:
class CustomPageModule extends \PageModule {
Up to this point, you also have a CustomPageModule class that is a child of your package's PageModule. All you need to do now is to let Laravel knows that if any controllers ask for PageModule, it should serve the controller with your MyModels\CustomPageModule instead.
So at the top of your app's routes.php file:
App::bind('PageModule', 'MyModels\CustomPageModule');
Your AdminPageController should now be using your CustomPageModule and can use whatever public methods that are in there!
I'm expecting to be editing this answer heavily since this will be quite a long discussion. My first try at answering above isn't the best code you can write, but I hope it takes the least amount of edit to the original code, and then we can work up from there.
Or fast track by reading up articles like http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories
You probably have a alias for the PageModule facade, you should override this alias using your class \MyModels\PageModule in your app/config/app.php file.
Be careful, it seems like you are overwriting the PageModule class instead of extending it. You should probably extend the parent class instead of Eloquent.
I would like to extend Laravels Router class (Illuminate\Routing\Router) to add a method I need a lot in my application.
But sadly I can't get this to work. I already extended other classes successfully so I really have no idea where my wrong thinking comes from.
Anyway, right into the code:
<?php
namespace MyApp\Extensions;
use Illuminate\Routing\Router as IlluminateRouter;
class Router extends IlluminateRouter
{
public function test()
{
$route = $this->getCurrentRoute();
return $route->getParameter('test');
}
}
So as you see I want to get the parameter set by {test} in routes.php with a simple call like:
Router::test();
Not sure how to go on now. Tried to bind it to the IOC-Container within my ServiceProvider in register() and boot() but I got no luck.
Whatever I try I get either a constructor error or something else.
All solutions I found are too old and the API has changed since then.
Please help me!
edit:
I already tried binding my own Router within register() and boot() (as said above) but it doesn't work.
Here is my code:
<?php
namespace MyApp;
use Illuminate\Support\ServiceProvider;
use MyApp\Extensions\Router;
class MyAppServiceProvider extends ServiceProvider {
public function register()
{
$this->app['router'] = $this->app->share(function($app)
{
return new Router(new Illuminate\Events\Dispatcher);
}
// Other bindings ...
}
}
When I try to use my Router now I have the problem that it needs an Dispatcher.
So I have to do:
$router = new Router(new Illuminate\Events\Dispatcher); // Else I get an exception :(
Also it simply does nothing, if I call:
$router->test();
:(
And if I call
dd($router->test());
I get NULL
Look at: app/config/app.php and in the aliases array. You will see Route is an alias for the illuminate router via a facade class.
If you look at the facade class in Support/Facades/Route.php of illuminate source, you will see that it uses $app['router'].
Unlike a lot of service providers in laravel, the router is hard coded and cannot be swapped out without a lot of work rewiring laravel or editing the vendor source (both are not a good idea). You can see its hardcoded by going to Illuminate / Foundation / Application.php and searching for RoutingServiceProvider.
However, there's no reason i can think of that would stop you overriding the router class in a service provider. So if you create a service provider for your custom router, which binds to $app['router'], that should replace the default router with your own router.
I wouldn't expect any issues to arise from this method, as the providers should be loaded before any routing is done. So overriding the router, should happen before laravel starts to use the router class, but i've not this before, so be prepared for a bit of debugging if it doesn't work straight away.
So I was asking in the official Laravel IRC and it seems like you simply can't extend Router in 4.1 anymore. At least that's all I got as a response in a pretty long dialogue.
It worked in Laravel 4.0, but now it doesn't. Oh well, maybe it will work in 4.2 again.
Other packages suffer from this as well: https://github.com/jasonlewis/enhanced-router/issues/16
Anyway, personally I'll stick with my extended Request then. It's not that much of a difference, just that Router would've been more dynamic and better fitting.
I'm using Laravel 4.2, and the router is really hard coded into the Application, but I extended it this way:
Edit bootstrap/start.php, change Illuminate\Foundation\Application for YourNamespace\Application.
Create a class named YourNamespace\Application and extend \Illuminate\Foundation\Application.
class Application extends \Illuminate\Foundation\Application {
/**
* Register the routing service provider.
*
* #return void
*/
protected function registerRoutingProvider()
{
$this->register(new RoutingServiceProvider($this));
}
}
Create a class named YourNamespace\RoutingServiceProvider and extend \Illuminate\Routing\RoutingServiceProvider.
class RoutingServiceProvider extends \Illuminate\Routing\RoutingServiceProvider {
protected function registerRouter()
{
$this->app['router'] = $this->app->share(function($app)
{
$router = new Router($app['events'], $app);
// If the current application environment is "testing", we will disable the
// routing filters, since they can be tested independently of the routes
// and just get in the way of our typical controller testing concerns.
if ($app['env'] == 'testing')
{
$router->disableFilters();
}
return $router;
});
}
}
Finally, create YourNamespace\Router extending \Illuminate\Routing\Router and you're done.
NOTE: Although you're not changing the name of the class, like Router and RoutingServiceProvider, it will work because of the namespace resolution that will point it to YourNamespace\Router and so on.