Looking to hire Laravel developers? Try LaraJobs

laravel-pennant-devcycle maintained by bengiese22

Description
Laravel Pennant driver for DevCycle.
Last update
2026/04/14 19:29 (dev-main)
License
Links
Downloads
0

Comments
comments powered by Disqus

Laravel Pennant DevCycle Driver

Tests Code Style Static Analysis

A Laravel Pennant driver backed by the DevCycle PHP SDK. Pennant reads DevCycle Variables for feature evaluation; writes are intentionally unsupported (read-only).

Installation

composer require bengiese22/laravel-pennant-devcycle

The package is auto-discovered via the service provider. Publish the optional config file:

php artisan vendor:publish --tag=devcycle-config

Configuration

Add a devcycle store to config/pennant.php:

return [
    'default' => 'devcycle',

    'stores' => [
        'devcycle' => [
            'driver' => 'devcycle',
            'sdk_key' => env('DEVCYCLE_SERVER_SDK_KEY'),
            'default' => false, // default value when a variable is not found
        ],
    ],
];

Setting Up Your User Model

Scopes passed to Feature::for() must implement Pennant's FeatureScopeable and FeatureScopeSerializeable contracts and return a DevCycleUser instance.

The easiest way is to add the included trait to your User model:

use BenGiese22\LaravelPennantDevCycle\Concerns\HasDevCycleFeatureScope;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Pennant\Contracts\FeatureScopeable;
use Laravel\Pennant\Contracts\FeatureScopeSerializeable;

class User extends Authenticatable implements FeatureScopeable, FeatureScopeSerializeable
{
    use HasDevCycleFeatureScope;
}

The trait maps id, email, and name to the DevCycleUser automatically. If you need custom data (e.g., custom data fields for targeting), override toDevCycleUser():

use DevCycle\Model\DevCycleUser;

protected function toDevCycleUser(): DevCycleUser
{
    return new DevCycleUser([
        'user_id' => (string) $this->id,
        'email' => $this->email,
        'name' => $this->name,
        'customData' => [
            'plan' => $this->plan,
            'organization_id' => $this->organization_id,
        ],
    ]);
}

Usage

DevCycle Variable keys map directly to Pennant feature names:

use Laravel\Pennant\Feature;

// Boolean check
if (Feature::for($user)->active('checkout-redesign')) {
    // new checkout flow
}

// Get the variable value (strings, numbers, JSON)
$variant = Feature::for($user)->value('onboarding-flow');

Writes (define, set, activate, deactivate) are not supported and will throw a BadMethodCallException.

Default Values

When the DevCycle SDK cannot find a variable (the flag doesn't exist, isn't enabled, or the user isn't targeted), it returns the configured default value. There is no way to distinguish "flag is off" from "flag doesn't exist in DevCycle" — both return the default.

Set the default per-store in config/pennant.php:

'devcycle' => [
    'driver' => 'devcycle',
    'sdk_key' => env('DEVCYCLE_SERVER_SDK_KEY'),
    'default' => false,
],

Management API (optional)

The package can proxy DevCycle's Management API for listing and updating features. This is disabled by default.

Enable it in config/devcycle.php:

'register_routes' => true,

Routes

Method URI Description
GET /api/devcycle/features List features
GET /api/devcycle/features/{featureKey} Get a feature
PATCH /api/devcycle/features/{featureKey} Update a feature

Override the project key per-request with ?project=other-project.

Route Middleware

By default, routes use the api middleware group. You should add authentication middleware before enabling routes in production:

// config/devcycle.php
'routes' => [
    'middleware' => ['api', 'auth:sanctum'],
    'prefix' => 'api/devcycle',
],

Credentials

Set these in config/devcycle.php or via environment variables:

Variable Description
DEVCYCLE_MGMT_CLIENT_ID OAuth client ID
DEVCYCLE_MGMT_CLIENT_SECRET OAuth client secret
DEVCYCLE_MGMT_PROJECT_KEY Default project key
DEVCYCLE_MGMT_API_BASE API base URL (optional)
DEVCYCLE_MGMT_AUTH_BASE Auth base URL (optional)

OAuth access tokens are cached automatically using Laravel's cache driver.

Testing

composer test        # Run Pest tests
composer analyse     # Run PHPStan (level 6)
./vendor/bin/pint --test  # Check code style
composer ci          # Run tests + analysis

License

MIT. See LICENSE.md.