I'm trying to set a handful of default route parameters that will work globally in my application regardless of context. In the documentation for URL generation the example given is using middleware which is fine for HTTP, but won't get called during non-HTTP contexts. I also need this to work when called from the CLI.
My first idea is to have a Service Provider that calls the defaults method on boot:
<?php
namespace App\Providers;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\ServiceProvider;
class UrlDefaults extends ServiceProvider
{
public function boot(UrlGenerator $urlGenerator): void
{
$urlGenerator->defaults([
'foo' => 'abc',
'bar' => 'xyz',
]);
}
}
But this does not work for HTTP requests:
Route::get('test', function (\Illuminate\Routing\UrlGenerator $urlGenerator) {
dump($urlGenerator->getDefaultParameters());
});
Outputs []
I believe this is because in the UrlGenerator, the setRequest method unconditionally sets the routeGenerator property to null. My Service Provider's boot method is called during the bootstrapping process, but then the request is set afterwards clobbering my defaults.
//Illuminate/Routing/UrlGenerator.php
public function setRequest(Request $request)
{
$this->request = $request;
$this->cachedRoot = null;
$this->cachedSchema = null;
$this->routeGenerator = null;
}
Dumping the UrlGenerator during boot and then again in my routes file can demonstrate this:
As you can see, the UrlGenerator instance is the same both times, but the RouteUrlGenerator on the routeGenerator property has changed.
I am unsure of a better way to set these defaults.
Not sure why this is getting attention almost a year later, but I ended up finding a solution by myself.
To add a bit more information to the original question, the purpose of this was to allow us to have the same instance of the code powering both our live and sandbox application. There's more involved to get this working, but this issue was just about URL generation for links in views. All links generated always both a subdomain and tld, so this code injects these values always.
These views are rendered both as a response to a HTTP request, e.g. in our client areas, but also as part of a non HTTP request, e.g. a scheduled task generating invoices and emailing them to clients.
Anyway, the solution:
For non HTTP contexts, a service provider can set the defaults:
<?php namespace App\Providers;
use App\Support\UrlDefaults;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\ServiceProvider;
class UrlDefaultsServiceProvider extends ServiceProvider
{
public function boot(UrlGenerator $urlGenerator): void
{
$urlGenerator->defaults(UrlDefaults::getDefaults());
}
}
Since the there's no routing going on to cause the problem I asked originally, this just works.
For HTTP contexts, the RouteMatched event is listened for and the defaults injected then:
<?php namespace App\Listeners;
use App\Support\UrlDefaults;
use Illuminate\Routing\Router;
use Illuminate\Routing\UrlGenerator;
/**
* Class SetUrlDefaults
*
* This class listeners for the RouteMatched event, and when it fires, injects the route paramaters (subdomain, tld,
* etc) into the defaults of the UrlGenerator
*
* #package App\Listeners
*/
class SetUrlDefaults
{
private $urlGenerator;
private $router;
public function __construct(UrlGenerator $urlGenerator, Router $router)
{
$this->urlGenerator = $urlGenerator;
$this->router = $router;
}
public function handle(): void
{
$paramaters = array_merge(UrlDefaults::getDefaults(), $this->router->current()->parameters);
$this->urlGenerator->defaults($paramaters);
}
}
UrlDefaults is just a simple class that returns an array:
<?php namespace App\Support;
class UrlDefaults
{
public static function getDefaults(): array
{
return [
'tld' => config('app.url.tld'),
'api' => config('app.url.api'),
'foo' => config('app.url.foo'),
'bar' => config('app.url.bar'),
];
}
}
So digging into the source for routing classes a bit more, there’s a defaults() method on the UrlGenerator class, but it’s not a singleton, so any defaults you set in a service provider aren’t persisted.
I seem to have got it working by setting the defaults in some middleware:
Route::domain('{domain}')->middleware('route.domain')->group(function () {
//
});
namespace App\Http\Middleware;
use Illuminate\Contracts\Routing\UrlGenerator;
class SetRouteDomain
{
private $url;
public function __construct(UrlGenerator $url)
{
$this->url = $url;
}
public function handle($request, Closure $next)
{
$this->url->defaults([
'domain' => $request->getHost(),
]);
return $next($request);
}
}
Related
A lot of pieces to this so here's the meat. Code very slightly tweaked for brevity.
Extended class:
<?php
namespace App\Http;
use Illuminate\Http\Request as LaravelRequest;
class Request extends LaravelRequest
{
}
Middleware:
<?php
namespace App\Http\Middleware;
use App\Http\Request as CustomizedRequest;
use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\Request;
class CustomizeRequest
{
protected $app;
protected $customizedRequest;
public function __construct(Application $app, CustomizedRequest $customizedRequest){
$this->app = $app;
$this->customizedRequest = $customizedRequest;
}
public function handle(Request $request, Closure $next){
$this->app->instance(
'request',
Request::createFrom($request, $this->customizedRequest);
);
return $next($this->customizedRequest);
}
}
Routes:
Route::get('/books1/{id}',[BookController::class, 'frontend1']);
Route::get('/books2/{id}',[BookController::class, 'frontend2']);
Controller:
<?php
namespace App\Http\Controllers;
use App\Models\Book;
class BookController extends Controller
{
public function frontend1(\Illuminate\Http\Request $request){
dump($request);
dump($request->all());
dump($request->route('id'));
return Book::all();
}
public function frontend2(\App\Http\Request $request){
dump($request);
dump($request->all());
dump($request->route('id'));
return Book::all();
}
}
The /books1/5?foo=bar and frontend1() path works. $request is populated as expected.
The /books2/5?foo=bar and frontend2() path is broken. $request has vast amounts of missing data, like it was instantiated with nothing.
Evidently if I type-hint my subclass instead of the more generic parent, it's causing some kind of broken instantiation. From an OO perspective I think this should be perfectly fine and I do specifically need my subclass being provided so prefer that type-hint. Is something deep within Laravel tripping this up? Is this some obscure PHP behavior I haven't seen before?
This is kind of tricky.
First of all, you need to be familiar with the service container and dependency injection. Here is the full doc: https://laravel.com/docs/8.x/container
When you type hint a class inside a controller method, Laravel will try to understand what it should do with it.
If nothing is registered inside the service container, it will try to make a new instance of it.
\Illuminate\Http\Request is bound as a singleton (https://laravel.com/docs/8.x/container#binding-a-singleton).
While a simple bind will return a new instance at each call, a singleton will always return the exact same instance.
Here is a quick demo:
\App\Models\User::class is a class that is not explicitly bound.
When you try to resolve it using the service container, it will not find it and will try to make a new instance:
$u1 = app(\App\Models\User::class);
// Searching \App\Models\User::class...
// Cannot find \App\Models\User::class...
// returning new \App\Models\User();
$u2 = app(\App\Models\User::class);
// same process again
$u3 = app(\App\Models\User::class);
// and again
// You can check these instances are indeed different by checking their hash:
dd(
spl_object_hash($u1), // 000000004af5213500000000220f0bc0 (52135)
spl_object_hash($u2), // 000000004af5213400000000220f0bc0 (52134)
spl_object_hash($u3) // 000000004af5213700000000220f0bc0 (52137)
);
But since \Illuminate\Http\Request::class is bound by Laravel, it follows a different path:
$r1 = app(\Illuminate\Http\Request::class);
// Searching \Illuminate\Http\Request::class...
// Found it! Bound as a singleton.
// returning new \Illuminate\Http\Request() and storing the
// instance in case it is required again later;
$r2 = app(\Illuminate\Http\Request::class);
// Searching \Illuminate\Http\Request::class...
// Found it and already called! Returning the stored instance ($r1)
$r3 = app(\Illuminate\Http\Request::class);
// Searching \Illuminate\Http\Request::class...
// Found it and already called! Returning the stored instance ($r1)
// Their hash are the same
dd(
spl_object_hash($u1), // 0000000011f522cf0000000077704cd1
spl_object_hash($u2), // 0000000011f522cf0000000077704cd1
spl_object_hash($u3) // 0000000011f522cf0000000077704cd1
);
Now, what's happening?
Under the hood, when a new request is made to your app and before hitting the controller method, Laravel will do a lot of things to prepare the \Illuminate\Http\Request instance.
For instance, it will setup the route resolver inside Illuminate\Routing\Router:
/**
* Return the response for the given route.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Routing\Route $route
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function runRoute(Request $request, Route $route)
{
// here
$request->setRouteResolver(function () use ($route) {
return $route;
});
//
$this->events->dispatch(new RouteMatched($route, $request));
return $this->prepareResponse($request,
$this->runRouteWithinStack($route, $request)
);
}
Each time Laravel internally call a method like this:
protected function method(Request $request){
// do something to $request
}
$request is always the same instance, because it is bound as a singleton.
We are now in your controller.
public function frontend1(\Illuminate\Http\Request $request){
// Searching \Illuminate\Http\Request::class...
// Found it and already called!
// Returning the stored instance that has been prepared through all
// Laravel core classes
dump($request);
dump($request->all()); //well prepared
dump($request->route('id')); //well setup
return Book::all();
}
public function frontend2(\App\Http\Request $request){
// Searching \App\Http\Request::class...
// Cannot find \App\Http\Request::class...
// returning new \App\Http\Request();
dump($request);
dump($request->all()); //nothing
dump($request->route('id')); //empty
return Book::all();
}
If you are still here, how to solve this problem?
The easiest way is to use a FormRequest, initially designed to handle form validation, but if you return an empty rules array, you should be able to do everything you did with your custom \App\Http\Request instance:
<?php
namespace App\Http;
use Illuminate\Foundation\Http\FormRequest;
class Request extends FormRequest
{
public function rules()
{
return [];
}
}
Try again, everything should work fine, since this is a feature specially designed to replace the initial \Illuminate\Http\Request object.
The full doc is here: https://laravel.com/docs/8.x/validation#creating-form-requests
I've been reading the documentation up and down now, still not sure what I'm doing wrong. In my opinion the documentation is very difficult to understand for a beginner.
Anyway, I'm trying to make something akin to the Auth::user() method, where it returns additional data about a logged in user that I will be needing for this application.
I have this helper class here:
namespace App\Helpers;
use Auth;
use Illuminate\Http\Request;
use App\Models\Grouping\User;
use App\Models\Grouping\Client;
use App\Models\Grouping\Rank;
class ClientUser {
public function __construct($request) {
$this->request = $request;
}
public function client() {
return Client::find($this->request->session()->get('client_id'));
}
public function auth() {
if (Auth::check()) {
// Get the client
$client = $this->client();
// Get the client's user
$user = $client->users()->find(Auth::user()['id']);
// Get the rank of the logged in user
$rank = Rank::find($user->pivot->rank_id);
return [
'user' => $user,
'rank' => $rank,
'client' => $client
];
}
return null;
}
}
This is responsible for doing what I described, returning additional data that I can't get through Auth::user(). Now I'm trying to register this class in the AuthServiceProvider
public function register()
{
// Register client auth
$request = $this->app->request;
$this->app->singleton(ClientUser::class, function ($app) {
return new ClientUser($request);
});
}
Now what I don't understand is how I'm supposed to make this globally accessible throughout my app like Auth::user() is.
The problem with just making "importing" it is that it needs the request object, which is why I'm passing it through the service container.
Now here's where I'm stuck. I'm not able to access app in my controller or anywhere, and I can't define a Facade because a Facade expects you to return a string of the bound service that it should "alias?"
Change your service provider like this :
$this->app->bind('client.user', function ($app) {
return new ClientUser($app->request);
});
Create another class extended from Illuminate\Support\Facades\Facade.
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class ClientUserFacade extends Facade {
public static function getFacadeAccessor(){
return "client.user";
}
}
Add 'ClientUser => ClientUserFacade::class in alias key of app.php
I have a BaseController that provides the foundation for most HTTP methods for my API server, e.g. the store method:
BaseController.php
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
I then extend on this BaseController in a more specific controller, such as the UserController, like so:
UserController.php
class UserController extends BaseController {
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
}
This works great. However, I now want to extend UserController to inject Laravel 5's new FormRequest class, which takes care of things like validation and authentication for the User resource. I would like to do this like so, by overwriting the store method and using Laravel's type hint dependency injection for its Form Request class.
UserController.php
public function store(UserFormRequest $request)
{
return parent::store($request);
}
Where the UserFormRequest extends from Request, which itself extends from FormRequest:
UserFormRequest.php
class UserFormRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required',
'email' => 'required'
];
}
}
The problem is that the BaseController requires a Illuminate\Http\Request object whereas I pass a UserFormRequest object. Therefore I get this error:
in UserController.php line 6
at HandleExceptions->handleError('2048', 'Declaration of Bloomon\Bloomapi3\Repositories\User\UserController::store() should be compatible with Bloomon\Bloomapi3\Http\Controllers\BaseController::store(Illuminate\Http\Request $request)', '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php', '6', array('file' => '/home/tom/projects/bloomon/bloomapi3/app/Repositories/User/UserController.php')) in UserController.php line 6
So, how can I type hint inject the UserFormRequest while still adhering to the BaseController's Request requirement? I cannot force the BaseController to require a UserFormRequest, because it should work for any resource.
I could use an interface like RepositoryFormRequest in both the BaseController and the UserController, but then the problem is that Laravel no longer injects the UserFormController through its type hinting dependency injection.
In contrast to many 'real' object oriented languages, this kind of type hinting design in overridden methods is just not possible in PHP, see:
class X {}
class Y extends X {}
class A {
function a(X $x) {}
}
class B extends A {
function a(Y $y) {} // error! Methods with the same name must be compatible with the parent method, this includes the typehints
}
This produces the same kind of error as your code. I would just not put a store() method in your BaseController. If you feel that you are repeating code, consider introducing for example a service class or maybe a trait.
Using a service class
Below a solution that makes use of an extra service class. This might be overkill for your situation. But if you add more functionality to the StoringServices store() method (like validation), it could be useful. You can also add more methods to the StoringService like destroy(), update(), create(), but then you probably want to name the service differently.
class StoringService {
private $repo;
public function __construct(Repository $repo)
{
$this->repo = $repo;
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
class UserController {
// ... other code (including member variable $repo)
public function store(UserRequest $request)
{
$service = new StoringService($this->repo); // Or put this in your BaseController's constructor and make $service a member variable
return $service->store($request);
}
}
Using a trait
You can also use a trait, but you have to rename the trait's store() method then:
trait StoringTrait {
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
class UserController {
use {
StoringTrait::store as baseStore;
}
// ... other code (including member variable $repo)
public function store(UserRequest $request)
{
return $this->baseStore($request);
}
}
The advantage of this solution is that if you do not have to add extra functionality to the store() method, you can just use the trait without renaming and you do not have to write an extra store() method.
Using inheritance
In my opinion, inheritance is not so suitable for the kind of code reuse that you need here, at least not in PHP. But if you want to only use inheritance for this code reuse problem, give the store() method in your BaseController another name, make sure that all classes have their own store() method and call the method in the BaseController. Something like this:
BaseController.php
/**
* Store a newly created resource in storage.
*
* #return Response
*/
protected function createResource(Request $request)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
UserController.php
public function store(UserFormRequest $request)
{
return $this->createResource($request);
}
You can move your logic from BaseController to trait, service, facade.
You can not override existing function and force it to use different type of argument, it would break stuff. For example, if you later would write this:
function foo(BaseController $baseController, Request $request) {
$baseController->store($request);
}
It would break with your UserController and OtherRequest because UserController expects UserController, not OtherRequest (which extends Request and is valid argument from foo() perspective).
As others have mentioned, you cannot do what you want to do for a host of reasons. As mentioned, you can solve this problem with traits or similar. I am presenting an alternative approach.
At a guess, it sounds like you are trying to follow the naming convention put forth by Laravel's RESTful Resource Controllers, which is kind of forcing you to use a particular method on a controller, in this case, store.
Looking at the source of ResourceRegistrar.php we can see that in the getResourceMethods method, Laravel does either a diff or intersect with the options array you pass in and against the default values. However, the those defaults are protected, and include store.
What this means is that you can't pass anything to Route::resource to force some override of the route names. So let's rule that out.
A simpler approach would be to simply set up a different method just for this route. This can be achieved by doing:
Route::post('user/save', 'UserController#save');
Route::resource('users', 'UserController');
Note: As per the documentation, the custom routes must come prior to the Route::resource call.
The declaration of UserController::store() should be compatible with BaseController::store(), which means (among other things) that the given parameters for both the BaseController as well as UserController should be exactly the same.
You actually cán force the BaseController to require a UserFormRequest, it's not the prettiest solution, but it works.
By overwriting there is no way you can replace Request with UserFormRequest, so why not use both? Giving both methods an optional parameter for injecting the UserFormRequest object. Which would result in:
BaseController.php
class BaseController {
public function store(Request $request, UserFormRequest $userFormRequest = null)
{
$result = $this->repo->create($request);
return response()->json($result, 200);
}
}
UserController.php
class UserController extends BaseController {
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
public function store(UserFormRequest $request, UserFormRequest $userFormRequest = null)
{
return parent::store($request);
}
}
This way you can ignore the parameter when using BaseController::store() and inject it when using UserController::store().
The easiest and cleanest way I found to circumvent that problem was to prefix the parent methods with an underscore. For example:
BaseController:
_store(Request $request) { ... }
_update(Request $request) { ... }
UserController:
store(UserFormRequest $request) { return parent::_store($request); }
update(UserFormRequest $request) { return parent::_update($request); }
I feel like creating service providers is an overkill. What we're trying to circumvent here is not the Liskov substitution principle, but simply the lack of proper PHP reflection. Type-hinting methods is, in itself, a hack after all.
This will force you to manually implement a store and update in every child controller. I don't know if that's bothersome for your design, but in mine, I use custom requests for each controller, so I had to do it anyway.
For a project I am required to have a persistent session for a visitor.
A couple of years ago I faced the issue with an Apple update temporary rendering all iPhones unable to set PHPSESSID cookies.
I created a fall back method which checked for the SESSION ID in the URL and use that to persist the session between requests. I am aware of the fact this can be enabled in php.ini using the session.use_trans_sid.
Point is I do not want this to happen always. When possible I prefer the cookie method.
Is there a way within Symfony to add this logic to the route methods adding the session identifier?
Can anyone help me to explain where to extend the twig "path" method to add the logic to optionally append the session id to all URL's generated by that method.
UPDATE
Let me post an update on my progress and perhaps someone can help me. I managed to find how to extend the UrlGenerator with my own code by replacing the generator_base_class in a parameter.
Now I have the following issue.
I wish to use a session to do some logic. I however can not reach this core component as a service. I already tried makign a compilerPass for both the UrlGenerator and an extended Router class to be able to make a dependency injection in one of these classes.
However until now it sadly failed.
What would be the best partice to get the Session component within the UrlGenerator class?
I was able to create my solution thanks to this post:
Override router and add parameter to specific routes (before path/url used)
In the end this is the code I came up with.
In my service.xml
<parameters>
<parameter key="router.class">Acme\CoreBundle\Component\Routing\Router</parameter>
<parameter key="router.options.generator_base_class">Acme\CoreBundle\Component\Routing\Generator\UrlGenerator</parameter>
</parameters>
Extending Symfony's core router to make in ContainerAware and force that container to the UrlGenerator.
namespace Acme\CoreBundle\Component\Routing;
use Symfony\Bundle\FrameworkBundle\Routing\Router as BaseRouter;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\RequestContext;
class Router extends BaseRouter implements ContainerAwareInterface
{
private $container;
public function __construct(ContainerInterface $container, $resource, array $options = array(), RequestContext $context = null)
{
parent::__construct($container, $resource, $options, $context);
$this->setContainer($container);
}
public function getGenerator()
{
$generator = parent::getGenerator();
$generator->setContainer($this->container);
return $generator;
}
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
}
Extending the UrlGenerator class.
namespace Acme\CoreBundle\Component\Routing\Generator;
use Symfony\Component\Routing\Generator\UrlGenerator as BaseUrlGenerator;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* UrlGenerator generates URL based on a set of routes, this class extends the basics from Symfony.
*/
class UrlGenerator extends BaseUrlGenerator implements ContainerAwareInterface
{
private $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, array $requiredSchemes = array())
{
/** #var \Symfony\Component\HttpFoundation\Session\Session $session */
$session = $this->container->get('session');
if (true !== $session->get('acceptCookies')) {
$parameters[$session->getName()] = $session->getId();
}
return parent::doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes);
}
}
In the end this results in the session name and id being appended to the generated URL when the session value acceptCookies is not equal to true.
I want to extend/overwrite the method logAttempt in class Confide (Confide on GitHub) in order to execute some extra code whenever someone logs in successfully. This would be cleaner than copying the same code to all controllers where logAttempt is called.
I read through the Laravel documentation and several answers here on stackoverflow, but I just can't get it working.
I created a new folder app/extensions with a file named Confide.php:
<?php
namespace Extensions;
class Confide extends \Zizaco\Confide\Confide {
public function __construct(ConfideRepository $repo) {
die('no way!');
$this->repo = $repo;
$this->app = app();
}
public function logAttempt($credentials, $confirmed_only = false, $identity_columns = array()) {
die('yeah man!');
}
}
I added the directory to my app/start/global.php:
ClassLoader::addDirectories(array(
// ...
app_path().'/extensions',
));
I also added it to composer.json and ran composer dump-autoload:
"autoload": {
"classmap": [
...,
"app/extensions"
]
},
My own Confide class seems not to be loaded at all, because Confide works as normal – without ever die()-ing.
And if I use \Extensions\Confide::logAttempt($input, true); in my controller including the namespace, I get this ErrorException:
Non-static method Extensions\Confide::logAttempt() should not be called statically, assuming $this from incompatible context
Do I really need my own ConfideServiceProvider class as well? I tried that, too, but I'm not sure at all what to put in there to make Confide use my extended class.
Is there no simple way to extend a tiny bit of a class? There must be, I'm just missing something here.
If you are looking to execute some code when a user logs in, you should just listen for that event. In this case, I believe Confide uses the Auth class to login, so you should be able to listen for that event.
Event::listen('auth.login', function($user)
{
$user->last_login = new DateTime;
$user->save();
});
I find this much easier and cleaner than worrying about extending classes.
EDIT: Made a mistake
I think you need to call the method like this:
\Extensions\Confide->logAttempt($input, true);
because you are using:
\Extensions\Confide::logAttempt($input, true);
Which is how you call static methods.
I think I finally figured it out.
I had to extend ConfideServiceProvider as well like so:
<?php
namespace Extensions;
class ConfideServiceProvider extends \Zizaco\Confide\ConfideServiceProvider {
/**
* Bootstrap the service provider.
*
* #return void
*/
public function boot() {
$this->package('extensions/confide');
}
/**
* Register the application bindings.
*
* #return void
*/
protected function registerConfide() {
$this->app->bind('confide', function($app) {
return new Confide($app->make('confide.repository'));
});
}
}
The code above goes into app/extensions/ConfideServiceProvider.php. Note: In boot() I replaced "zizaco" with "extensions" and in registerConfide() I made no changes at all, but if this method is not present in the extended class, the original class will be used. I've got no idea why.
Then in app/config/app.php I replaced Zizaco\Confide\ConfideServiceProvider with Extensions\ConfideServiceProvider.
My own extended Confide class looks like this now:
<?php
namespace Extensions;
class Confide extends \Zizaco\Confide\Confide {
public function logAttempt($credentials, $confirmed_only = false, $identity_columns = array()) {
$result = parent::logAttempt($credentials, $confirmed_only, $identity_columns);
if ($result) {
// Login successful. Do some additional stuff.
\Log::info('User ' . \Auth::user()->username . ' logged in.');
}
return $result;
}
}
Note: If you want to use any other standard Laravel class like Log, Session etc., prefix it with one backslash as shown in the example above, or add a use operator for each class you use (e.g. use \Log;).