Pedro
Pedro

Reputation: 1178

How to implement subscription plan logic in Laravel 4 app

I am currently working on a subscription based application and curious to know the best way to deal with serving features to uses that are subscribe to different plans. This is how I am currently doing it but I am sure there are more efficient ways:

In my 'Plan' model I have the following function:

public static function checkSubscription($route)
{
    $user = Sentry::getUser();
    $account = $user->account()->first();

    if($account->plan->id == 1) //Free Plan
    {
        switch($route) {

            case 'apiaries':
                //Fetch all apiaries for this account
                $apiaries = Apiary::where('account_id','=',$account->id);

                if($apiaries->count() >= 1){
                    Session::put('plan-message', 'You have reached your maximum number of Apiaries. To add more please upgrade your plan.');
                    return true;
                }else{
                    Session::forget('plan-message');
                    return false;
                }
                break;

            case 'harvests':
                //Fetch all harvests for this account
                $harvests = Harvest::where('account_id','=',$account->id);

                if($harvests->count() >= 1){
                    Session::put('plan-message', 'You have reached your maximum number of Harvests. To add more please upgrade your plan.');
                    return true;
                }else{
                    Session::forget('plan-message');
                    return false;
                }
                break;

            case 'tasks':
                //Fetch all tasks for this account
                $tasks = Task::where('account_id','=',$account->id);

                if($tasks->count() >= 1){
                    Session::put('plan-message', 'You have reached your maximum number of Tasks. To add more please upgrade your plan.');
                    return true;
                }else{
                    Session::forget('plan-message');
                    return false;
                }
                break;

            default:
                Session::forget('plan-message');
                break;
        }
    }
    Session::forget('plan-message');
}

This is then called from the base controller:

$this->hasAccess = Plan::checkSubscription(Request::segment(1));

I then use the hasAccess variable in my view files to hide and show buttons etc

Thoughts on this approach? How are other people dealing with different subscription plans?

Upvotes: 0

Views: 934

Answers (1)

glendaviesnz
glendaviesnz

Reputation: 1899

Static methods and long switch statements can be a sign that you are taking a very 'procedural' approach to your app design - this may not be a big issues if your app is very small scale and unlikely to grow much in functionality. A more object oriented approach could be to take the switch statement above and replace it with some polymorphism.

I am guessing from the above that you already have a Model class for each of what I will call the modules, eg, harvests, tasks, etc. Instead of passing the route string to the function, you could instead pass an instance of the relevant module model which is created using the route string. Each of the module type Models would then have something like a countEntriesByAccount($account->id) method and your switch statement then disappears, eg.

public function checkSubscription($module_model_instance)
{
    $user = Sentry::getUser();
    $account = $user->account()->first();

    if($account->plan->id == 1) {

        if( $module_model_instance->countEntriesByAccount($account->id) >= 1){

            Session::put('plan-message', 'You have reached your maximum number of '.$module_model_instance->getModuleNamePlural() .'. To add more please upgrade your plan.');
            return true;

        }else{

             Session::forget('plan-message');
             return false;
        }
    }
}

This is just a very rough and dirty intro only, and based only on the small bit of code provided. Hard to know from this small snippet exactly which class should hold the business logic about how to judge the state of a particular subscription and the action to take - but hopefully it gets you started thinking on alternative approaches.

If you did want to go down this route you probably also want to have a look at creating an Interface that each of the module types implements to ensure that they all have the methods required when passed into the likes of the above method - http://www.php.net/manual/en/language.oop5.interfaces.php, and also type hinting the objects that you pass in is a good idea http://www.php.net/manual/en/language.oop5.typehinting.php.

From the small code snippet you give it is hard to tell why you are using a static method - you may have very good reasons, but it would be good to avoid the use of static functions if you can - they can end up being the oop equivalent of global variables and can make testing much more difficult - don't be confused by the Laravel static facades - these are a different kettle of fish to the straight static function you have implemented.

Upvotes: 2

Related Questions