Override a core Laravel 4 class method

放肆的年华 提交于 2020-01-12 05:37:05

问题


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


回答1:


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());



回答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.



来源:https://stackoverflow.com/questions/16762840/override-a-core-laravel-4-class-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!