How to extend a Laravel vendor package

2.8k views Asked by At

I am trying to extend Kryptonit3/Counter. Particularly, I need to overwrite one private function inside the class Counter.php to retrieve only hits for the last 24 hours.

Counter.php private function:

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

The function I need:

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

Therefore, I am looking for the right way to overwrite this package.

Approach 1: Should I create my own class extending Counter.php and including my custom function in this class? If so, what happens with the private classes included in the original class? Should I create a service provider? How this service provider would look like?

Approach 2: Should I fork the vendor package and update this package to my own need?

I already looked at all stackoverflow questions related to this topic but they are not clear enough.

UPDATE: This is what I did so far:

Create MyCounter class:

<?php

namespace App\Helpers\Counter;

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

}

Create 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);
    });
}
}

Create MyCounterFacade:

<?php

namespace App\Helpers\Counter;


use Illuminate\Support\Facades\Facade;

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

Include the provider and the alias inside config/app.php:

App\Providers\MyCounterServiceProvider::class,

and

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

There are 2 answers

0
Diego Vidal On

Problem was related with MyCounterServiceProvider. The next piece of code solved the problem.

 public function register()
{
    $this->app->singleton('mycounter', function() {
        return $this->app->make('App\Helpers\Counter\MyCounter');
    });
}
1
Antonio Carlos Ribeiro On

Static methods cannot be overriden statically but you can use __callstatic to override them dinamically:

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';
    }
}

Hit /override and you should see

Overloaded value from ClassB

That being said, what you could do:

Create an override for that class:

<?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';
    }
}

Then you just have to override the original instance of counter.

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