5

Apologies if this is a dumb question as a lot of Laravel 4 is new to me. I'm trying to override a couple of the methods in the core password functionality as I want to define my own password validation rules (hardcoded into core at the time of posting), and change the method of error reporting ($errors array used on other forms, rather than than session-based).

So my approach was to create a new class in /app/lib/MyProject/User called Password.php that looks like this:

<?php namespace MyProject\User; use Closure; use Illuminate\Mail\Mailer; use Illuminate\Routing\Redirector; use Illuminate\Auth\UserProviderInterface; class Password extends \Illuminate\Support\Facades\Password { /** * Reset the password for the given token. * * @param array $credentials * @param Closure $callback * @return mixed */ public function reset(array $credentials, Closure $callback) { // If the responses from the validate method is not a user instance, we will // assume that it is a redirect and simply return it from this method and // the user is properly redirected having an error message on the post. $user = $this->validateReset($credentials); if ( ! $user instanceof RemindableInterface) { return $user; } $pass = $this->getPassword(); // Once we have called this callback, we will remove this token row from the // table and return the response from this callback so the user gets sent // to the destination given by the developers from the callback return. $response = call_user_func($callback, $user, $pass); $this->reminders->delete($this->getToken()); return $response; } } 

I've copied the reset method from /vendor/laravel/framework/src/Illuminate/Auth/Reminders/PasswordBroker.php, which appears to be where the core Password facade resolves to.

Then in my composer.json file, I've added the following to the autoload:classmap array:

"app/lib/MyProject/User" 

Finally, in my /app/config/app.php file I've amended the Password alias:

'Password' => 'MyProject\User\Password', 

OK. In my routes.php file I have the following which is pretty much taken straight from the docs:

Route::post('password/reset/{token}', function() { $credentials = array('email' => Input::get('email')); return Password::reset($credentials, function($user, $password) { $user->password = Hash::make($password); $user->save(); return 'saved - login'; }); }); 

When this reset() method runs, I get the following error:

Non-static method MyProject\User\Password::reset() should not be called statically

The reset() method in the class I'm extending isn't static so that surprised me, however setting my reset method to static clears that error. Next though, I get the following error:

Using $this when not in object context

Which comes when trying to run $this->validateReset($credentials).

I'm completely out of my depth now. Am I going about this the right way or completely off the right path?

Thanks for any advice

6
  • did you ever solve this? If so - can you please post answer or comments. Thanks Commented Jul 16, 2013 at 3:54
  • No I'm afraid I didn't. I logged an issue on GitHub initially in the Laravel/Laravel project and then in the Laravel/Framework project. Taylor closed it as a dupe, I think incorrectly so I'm going to reopen it now: github.com/laravel/framework/issues/1672 Commented Jul 17, 2013 at 6:32
  • Is this still an issue (or at least something you'd like an answer to)? I can probably explain the behaviour you're seeing, and maybe even suggest a solution. But I'm guessing this is so old you don't care anymore or fixed it. Commented Mar 24, 2014 at 22:29
  • @alexrussell in the end I abandoned this as I couldn't get it wok and the only answer posted didn't make enough sense to me to be able to get it working. Laravel may now have moved on now to include an easier way to achieve this. Commented Mar 26, 2014 at 13:30
  • Okay I'll add an answer anyway for posterity, explaining what's going on here. I don't think Laravel's really changed in this regard between then and now. Commented Mar 26, 2014 at 13:48

2 Answers 2

2

You should extends the Illuminate\Auth\Reminders\PasswordBroker class, not the \Illuminate\Support\Facades\Password.

the \Illuminate\Support\Facades\Password class is a Facade, not the final class.

You can see the final classes of the facades by:

get_class(Password::getFacadeRoot()); 
Sign up to request clarification or add additional context in comments.

1 Comment

Tried that but I still get this error: Non-static method Salesify\User\Password::reset() should not be called statically. And when making my reset() method static: Cannot make non static method Illuminate\Auth\Reminders\PasswordBroker::reset() static in class Salesify\User\Password
2

The previous answer here is generally correct but doesn't really do enough to explain what's going on, so I'll give it a go.

Suffice it to say that any facade method access is generally done statically, so if you define a (public) method on a facade you should ensure it's a static one. However, facades are actually, well, facades for an underlying non-static class, using magic methods to pass a static call on the facade to an instance method on the underlying class. As such, you generally wouldn't want to extend a facade by defining a static method on one but instead define an instance method on the underlying class. In the case of the Password facade, the underlying class is an instance of PasswordBroker. This underlying class dealio is generally set up by registering an instance on the IoC container, and then in the facade you define a getFacadeAccessor method on the facade class which returns the IoC key in which to find the instance. (In the case of the Password facade, the IoC key is auth.reminder.)

This is where a problem comes in for you though, because you can't just override a Laravel core class. The most correct thing to do is to probably extend PasswordBroker and then set your version of the class up as the key in the IoC that the Password facade normally uses (auth.reminder). That way, your added method will be available on the facade, btu anything else will still defer to the original PasswordBroker class.


You'll notice that you don't have to create a new facade, but just override the existing facade's IoC key. When to do this can be a bit of a tricky one. If you are not using your own service provider, and do this stuff in routes.php or something, then you're probably safe. However, if you are using a service provider to modify this Password behaviour, you can't (shouldn't) actually rely on the order of Laravel's calling of service providers' register methods (in reality it appears to go in the order it's set up in app/config/app.php, but hey ignore that, it's undefined behaviour so don't rely on it). As such, you don't know if your service provider has its register method called before or after Laravel's core auth service provider.

I don't know the official way to sort this out, but the only way I can think is to do the registration in the service provider's boot method instead of its register method. You can guarantee that any given boot method is always called after all other service providers' register methods, so you will be overriding the Laravel core key, and your key won't be overridden by any normal Laravel set up. However, doing it in a boot method means that you could theoretically be too late to do what you want, if the functionality you are attempting to augment may be called in another service provider's boot method.

Alternatively, do create yourself you own facade, use a key of your own choosing and set it all up as normal (i.e. register instance in your register method). Then replace the alias for Password with your facade. It's more boilerplate, but works more reliably.

2 Comments

This is really quite helpful, and confirms the behavior that I've been seeing. I get the feeling this is still the case in Laravel 5 because I'm having a very difficult time overriding the Mail facade (or anything to do with Mail). I don't think I've seen such limitations in previous frameworks I've used. Very painful.
Yeah I think L5 has the same system in place regarding the IoC container and the facade classes. However, I would imagine that overriding the Mail facade is much easier than the Auth system (as it has fewer moving parts). In fact I would say that just re-registering your own class in the IoC under the Mail facade's accessor key should do the job, but maybe I'm wrong.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.