如何扩展Laravel中的供应商包

4

我想扩展Kryptonit3/Counter。具体来说,我需要重写 Counter.php 类中的一个私有函数,以仅获取最近 24 小时的访问次数。

Counter.php 的私有函数:

private static function countHits($page)
{
    $page_record = self::createPageIfNotPresent($page);
    return number_format($page_record->visitors->count());
}

我需要的功能是:
private static function countHits($page)
{
    $page_record = self::createPageIfNotPresent($page);
    return number_format($page_record->visitors()->where('created_at', '>=', Carbon::now()->subDay())->count());
}

因此,我正在寻找正确的方法来覆盖这个软件包。
方法一: 我应该创建一个扩展Counter.php的自己的类,并在此类中包含我的自定义函数吗?如果是这样,原始类中包含的私有类会发生什么情况?我应该创建服务提供者吗?这个服务提供者会是什么样子?
方法二: 我应该复制供应商软件包并更新它以适应自己的需求吗?
我已经看过所有与此主题相关的stackoverflow问题,但它们不够清晰。
更新: 到目前为止,我所做的是:
创建MyCounter类:
<?php

namespace App\Helpers\Counter;

use Kryptonit3\Counter\Counter;
class MyCounter extends Counter
{

}

创建MyCounterServiceProvider:
<?php

namespace App\Providers;

use App\Helpers\MyCounter;
use Illuminate\Support\ServiceProvider;

class MyCounterServiceProvider extends ServiceProvider
{
/**
 * Bootstrap the application services.
 *
 * @return void
 */
public function boot()
{
    //
}

/**
 * Register the application services.
 *
 * @return void
 */
public function register()
{
    $this->app['counter'] = $this->app->share(function($app)
    {
        $visitor = $app['visitor'];
        return new MyCounter($visitor);
    });
}
}

创建 MyCounterFacade:

<?php

namespace App\Helpers\Counter;


use Illuminate\Support\Facades\Facade;

class MyCounterFacade extends Facade
{
    protected static function getFacadeAccessor() { return 'mycounter'; }
}

在config/app.php文件中包含提供者和别名:

App\Providers\MyCounterServiceProvider::class,

并且

'MyCounter'             => App\Helpers\Counter\MyCounterFacade::class,

你调用了什么/怎样调用导致了报错?第734行代码是什么? - teynon
我认为我已经找到了错误的来源。这是因为在我使用的服务提供者中,我使用了 $this->app['mycounter'] 而不是 $this->app['counter']。我已经修复了这个问题,但现在我遇到了更新后的错误。 - Diego Vidal
你的问题中缺少足够的信息来帮助你调试代码。我们看不到你如何调用这些函数或者那些代码行是什么。你需要发布出现问题的代码部分。我也不知道你使用的 Laravel 版本,但我认为你的门面方法不应该是静态的。 - teynon
看起来你正在从错误信息中调用 MyCounterFacade::showAndCount()。由于你没有发布显示如何调用它的代码,所以我不能确定。如果是这种情况,你不应该在 MyCounterFacade 上调用它。相反,你应该在 counter::showAndCount() 中调用它,因为你在服务提供者中将其命名为门面。 - teynon
回到这个话题,我想指出,在编程中“正确”的做法很少存在。你可以重写包,创建一个门面来访问它,编写自己的类/方法来完成它等等。你真的可以为任何事情争论支持或反对。现在被认为是“正确”的,未来可能就不再是了。 - teynon
显示剩余2条评论
2个回答

1
问题与MyCounterServiceProvider相关。下面的代码解决了这个问题。
 public function register()
{
    $this->app->singleton('mycounter', function() {
        return $this->app->make('App\Helpers\Counter\MyCounter');
    });
}

0

静态方法无法静态地被覆盖,但您可以使用 __callstatic 动态地覆盖它们:

Route::get('/override', function() {
    $b = new ClassB();

    dd($b::originalMethod());
});

class ClassA {
    private static function originalMethod() {
        return 'Original value from ClassA';
    }

    public function callingOriginalMethodMethod()
    {
        return static::originalMethod();
    }
}

class ClassB {
    public static function __callStatic($name, $arguments) {
        if ($name == 'originalMethod') {
            return static::overloadedMethod();
        }

        return forward_static_call_array(array(ClassA::class, $name), $arguments);
    }

    protected static function overloadedMethod() {
        return 'Overloaded value from ClassB';
    }
}

点击/覆盖,然后您应该会看到

Overloaded value from ClassB

话虽如此,你可以这样做:

为该类创建一个覆盖:

<?php

namespace App\Helpers;

use Kryptonit3\Counter\Counter;

class MyCounter extends Counter
{
   public static function __callStatic($name, $arguments) {
        if ($name == 'countHits') {
            return static::myCountHits($page);
        }

        return forward_static_call_array(array(Counter::class, $name), $arguments);
    }

    protected static function myCountHits($page) {
        return 'whatever';
    }
}

然后你只需要重写原始的counter实例。

app()->singleton('counter', function() {
    return app()->make(MyCounter::class);
});

谢谢你的回答。但是我对你分享的覆盖有一些疑问。第一个函数中没有声明$page变量,而myCountHits函数必须包含原始程序包中的其他私有函数。这可以做到吗? - Diego Vidal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接