4

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,
Diego Vidal
  • 1,022
  • 12
  • 21
  • What / how are you calling that causes the error mentioned. What line of code is at 734? – teynon Dec 20 '16 at 14:03
  • I think I detected where the error comes from. It was because in the service provider I was using $this->app['mycounter'] instead of $this->app['counter']. I fixed that but now I am getting the error I updated. – Diego Vidal Dec 20 '16 at 14:13
  • Your question contains little to help with debugging your code for you. We can't see how you are calling those functions or what those lines of code are. You need to post the parts of the code that are having the problems. I also don't know what version of Laravel you are using, but I believe your facade method should not be static. – teynon Dec 20 '16 at 14:25
  • I am basically trying to understand how to overwrite a laravel vendor package. The update I shared is what I tried so far but I don't know if I am doing it correctly. I am calling the function in the same way I call the original package function but I am trying to see if I can call those functions from MyCounter.php class. That is why I created the service provider and the facade. – Diego Vidal Dec 20 '16 at 14:31
  • It looks like you are calling `MyCounterFacade::showAndCount()` from the error message. You didn't post code showing how you were calling it, so I can't be sure. If this is the case, you should not call it on `MyCounterFacade`. Instead, you should call it in `counter::showAndCount()` as you named your facade in your service provider – teynon Dec 20 '16 at 14:40
  • Circling back to this. I want to note that "the" correct way of doing something in coding rarely exists. You could rewrite the package as your own, create a facade to access it, write your own class / method to do it, etc. You can really argue for and against anything. What is "correct" now, won't be in the future. – teynon Dec 20 '16 at 15:11
  • Thank you for the effort. I am calling it including an use statement in my controller: use MyCounter. And then inside a function MyCounter:showAndCount($page, $id); It is the same syntax I used for the original vendor package and it was working. – Diego Vidal Dec 20 '16 at 16:05

2 Answers2

1

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');
    });
}
Diego Vidal
  • 1,022
  • 12
  • 21
0

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);
});
Antonio Carlos Ribeiro
  • 86,191
  • 22
  • 213
  • 204
  • Thank your for your answer. However I have a few doubts regarding the override you shared. The $page variable is not declare anywhere in the first function, and the myCountHits function must include other private functions from the original package. Can this be done? – Diego Vidal Dec 20 '16 at 16:08