Skip to content
This repository has been archived by the owner on May 26, 2023. It is now read-only.

Commit

Permalink
Better usage handling (#78)
Browse files Browse the repository at this point in the history
* Refactor function
* Do not check if usage has expired
* Deny record if cannot use
* Update docs
  • Loading branch information
bpuig committed Jun 17, 2021
1 parent a944172 commit 20901fd
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 17 deletions.
25 changes: 16 additions & 9 deletions docs/v5.x/models/plan-subscription-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,14 @@ $user->subscription();

## Subscription Feature Usage

There are multiple ways to determine the usage and ability of a particular feature in the subscriber's subscription, the
most common one is `canUseFeature`:
You can determine the usage and ability of a particular feature in the subscriber's subscription with `canUseFeature`:

The `canUseFeature` method returns `true` or `false` depending on multiple factors:

- Subscription has not ended.
- Feature _is enabled_.
- Subscription is active (on trial or currently in period).
- Feature _is enabled_ (`true`).
- Feature value isn't `0`/`false`/`NULL`.
- Or feature has remaining uses available.
- Feature has remaining uses available.

```php
$user->subscription('main')->canUseFeature('social_profiles');
Expand All @@ -144,9 +143,11 @@ Other feature methods on the user subscription instance are:
- `getFeatureRemainings`: returns available uses for a particular feature.
- `getFeatureValue`: returns the feature value.

> All methods share the same signature: e.g. `$user->subscription('main')->getFeatureUsage('social_profiles');`.
All methods share the same signature: e.g. `$user->subscription('main')->getFeatureUsage('social_profiles');`.

### Record Feature Usage
### Record Feature Usage <Badge text="updated in v5.0" type="tip"/>

> New in 5.0: Record feature check if can be used
In order to effectively use the ability methods you will need to keep track of every usage of each feature (or at least
those that require it). You may use the `recordFeatureUsage` method available through the user `subscription()` method:
Expand All @@ -155,9 +156,14 @@ those that require it). You may use the `recordFeatureUsage` method available th
$user->subscription('main')->recordFeatureUsage('social_profiles');
```

When recording feature `canUseFeature` is already called within the function, so you do not have to check every time.
Exception is thrown if subscriber cannot use the feature.

The `recordFeatureUsage` method accepts 3 parameters: the first one is the feature's tag, the second one is the quantity
of uses to add (default is `1`), and the third one indicates if the addition should be incremental (default behavior),
when disabled the usage will be overridden by the quantity provided. E.g.:
when disabled the usage will be overridden by the quantity provided.

::: details Click me to view example code

```php
// Increment by 1
Expand All @@ -167,6 +173,7 @@ $user->subscription('main')->recordFeatureUsage('social_profiles', 1);
$user->subscription('main')->recordFeatureUsage('social_profiles', 3, false);
```

:::
### Reduce Feature Usage

Reducing the feature usage is _almost_ the same as incrementing it. Here we only _substract_ a given quantity (default
Expand Down Expand Up @@ -239,7 +246,7 @@ $user->subscription('main')->isFree();
$user->isSubscribedTo($planId);
```

> Canceled subscriptions with an active trial or `ends_at` in the future are considered active.
Canceled subscriptions with an active trial or `ends_at` in the future are considered active.

## Renew a Subscription

Expand Down
17 changes: 17 additions & 0 deletions src/Exceptions/PlanSubscriptionFeatureUsageDenied.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php


namespace Bpuig\Subby\Exceptions;


use Throwable;

class PlanSubscriptionFeatureUsageDenied extends \InvalidArgumentException
{
public function __construct($featureTag = "", $code = 0, Throwable $previous = null)
{
$message = "Usage of feature '{$featureTag}' has been denied.";

parent::__construct($message, $code, $previous);
}
}
31 changes: 23 additions & 8 deletions src/Models/PlanSubscription.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Bpuig\Subby\Models;

use BadMethodCallException;
use Bpuig\Subby\Exceptions\PlanSubscriptionFeatureUsageDenied;
use Bpuig\Subby\Services\Period;
use Bpuig\Subby\Traits\BelongsToPlan;
use Bpuig\Subby\Traits\HasFeatures;
Expand Down Expand Up @@ -548,6 +549,10 @@ protected function setNewPeriod(?string $invoice_interval = null, ?int $invoice_
*/
public function recordFeatureUsage(string $featureTag, int $uses = 1, bool $incremental = true)
{
if (!$this->canUseFeature($featureTag)) {
throw new PlanSubscriptionFeatureUsageDenied($featureTag);
}

$feature = $this->getFeatureByTag($featureTag);


Expand Down Expand Up @@ -587,9 +592,9 @@ public function recordFeatureUsage(string $featureTag, int $uses = 1, bool $incr
*/
public function reduceFeatureUsage(string $featureTag, int $uses = 1): ?PlanSubscriptionUsage
{
$usage = $this->usage()->byFeatureTag($featureTag)->first();
$usage = $this->getUsageByFeatureTag($featureTag);

if (is_null($usage)) {
if (!$usage) {
return null;
}

Expand Down Expand Up @@ -617,23 +622,21 @@ public function canUseFeature(string $featureTag): bool
$featureValue = $this->getFeatureValue($featureTag);

if ($featureValue === 'true') {
// If feature value exists and has a written true value
// If feature value exists and has a written "true" value
return true;
} elseif (is_null($featureValue) || $featureValue === '0' || $featureValue === 'false') {
// If feature does not exist, it's 0 or written false
// If feature does not exist, it's 0 or written "false"
return false;
}

// Now that we know feature exists in plan, and does not meet any of
// previous requirements, check for usage
$usage = $this->usage()->byFeatureTag($featureTag)->first();
$usage = $this->getUsageByFeatureTag($featureTag);

if (!$usage) {
// If feature usage does not exist, it means it has never been used
// so subscriber has all usage available, since usage is inserted by recordFeatureUsage
return true;
} elseif ($usage->hasExpired()) {
return false;
}

// Check for available uses
Expand All @@ -649,11 +652,23 @@ public function canUseFeature(string $featureTag): bool
*/
public function getFeatureUsage(string $featureTag): int
{
$usage = $this->usage()->byFeatureTag($featureTag)->first();
$usage = $this->getUsageByFeatureTag($featureTag);

return (!$usage || $usage->hasExpired()) ? 0 : $usage->used;
}

/**
* Get feature usage
*
* @param string $featureTag
*
* @return mixed
*/
private function getUsageByFeatureTag(string $featureTag)
{
return $this->usage()->byFeatureTag($featureTag)->first();
}

/**
* Get the available uses.
*
Expand Down

0 comments on commit 20901fd

Please sign in to comment.