Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Test

on: [push]

jobs:
test:
runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v2

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.0'
extensions: zip, sqlite3
coverage: none

- name: Restore caches
uses: actions/cache@v2
with:
path: ~/.composer/cache/files
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-

- name: Install composer dependencies
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist

- name: Code sniff
run: vendor/bin/php-cs-fixer fix --dry-run

- name: Execute tests
run: composer test
11 changes: 11 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

$finder = PhpCsFixer\Finder::create()
->in([
__DIR__.'/src',
__DIR__.'/config',
__DIR__.'/database',
__DIR__.'/tests',
]);

return CodingLabs\styles($finder);
7 changes: 0 additions & 7 deletions CHANGELOG.md

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) Codinglabs <steve@codinglabs.com.au>
Copyright (c) Coding Labs Pty Ltd <steve@codinglabs.com.au>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
207 changes: 171 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,85 +1,220 @@

[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/support-ukraine.svg?t=1" />](https://supportukrainenow.org)

# Dynamic feature flags for laravel.

[![Latest Version on Packagist](https://img.shields.io/packagist/v/codinglabsau/laravel-feature-flags.svg?style=flat-square)](https://packagist.org/packages/codinglabsau/laravel-feature-flags)
[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/codinglabsau/laravel-feature-flags/run-tests?label=tests)](https://github.com/codinglabsau/laravel-feature-flags/actions?query=workflow%3Arun-tests+branch%3Amain)
[![GitHub Code Style Action Status](https://img.shields.io/github/workflow/status/codinglabsau/laravel-feature-flags/Check%20&%20fix%20styling?label=code%20style)](https://github.com/codinglabsau/laravel-feature-flags/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amain)
[![Test](https://github.com/codinglabsau/laravel-feature-flags/actions/workflows/run-tests.yml/badge.svg)](https://github.com/codinglabsau/laravel-feature-flags/actions/workflows/run-tests.yml)
[![Total Downloads](https://img.shields.io/packagist/dt/codinglabsau/laravel-feature-flags.svg?style=flat-square)](https://packagist.org/packages/codinglabsau/laravel-feature-flags)

This is where your description should go. Limit it to a paragraph or two. Consider adding a small example.

## Support us

[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/laravel-feature-flags.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/laravel-feature-flags)

We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).

This package offers the ability to implement feature flags in your application which can be easily toggled on or off. You can also set a feature to a dynamic state where you can define custom rules around whether that feature is enabled or not.
___
## Installation

You can install the package via composer:
### Install With Composer:

```bash
composer require codinglabsau/laravel-feature-flags
```

You can publish and run the migrations with:
### Database Migrations

```bash
php artisan vendor:publish --tag="laravel-feature-flags-migrations"
php artisan vendor:publish --tag="feature-flags-migrations"
php artisan migrate
```

You can publish the config file with:
### Publish Configuration:

```bash
php artisan vendor:publish --tag="laravel-feature-flags-config"
php artisan vendor:publish --tag="feature-flags-config"
```

This is the contents of the published config file:

### Cache Store
update your `.env`:
```php
return [
];
FEATURES_CACHE_STORE=file
```
Note that under the hood this package uses the `rememberForever()` method for caching and that if you are using the `Memcached` driver, items that are stored "forever" may be removed when the cache reaches its size limit.

Optionally, you can publish the views using
### Use Your Own Model

```bash
php artisan vendor:publish --tag="laravel-feature-flags-views"
To use your own model, update the config and replace the existing reference with your own model:

```php
// app/config/feature-flags.php

'feature_model' => \App\Models\Feature::class,
```

Make sure to also cast the state column to a feature state enum using the `FeatureStateCast`:

```php
// app/Models/Feature.php

use Codinglabs\FeatureFlags\Casts\FeatureStateCast;

protected $casts = [
'state' => FeatureStateCast::class
];
```

## Usage

Create a new feature in the database and give it a default state:
```php
$features = new Codinglabs\FeatureFlags();
echo $features->echoPhrase('Hello, Codinglabs!');
Feature::create([
'name' => 'search-v2',
'state' => Codinglabs\FeatureFlags\Enums\FeatureState::on()
]);
```

## Testing
Its recommended that you seed the features to your database before a new deployment or as soon as possible after a deployment.

```bash
composer test
A feature can be in one of three states:
```php
use Codinglabs\FeatureFlags\Enums\FeatureState;

FeatureState::on()
FeatureState::off()
FeatureState::dynamic()
```
### Check If A Feature Is Enabled

#### Blade View
```php
@feature('search-v2')
// new search goes here
@else
// legacy search here
@endfeature
```

#### In Your Code
```php
use Codinglabs\FeatureFlags\Facades\FeatureFlag;

if (FeatureFlag::isEnabled('search-v2')) {
// new feature code
} else {
// old code
}
```

## Changelog
#### Sharing features with UI (Inertiajs example)
```php
// config/app.php

'features' => [
[
'name' => 'search-v2',
'state' => \Codinglabs\FeatureFlags\Enums\FeatureState::dynamic()
]
],
```

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
```php
// app/Middleware/HandleInertiaRequest.php

Inertia::share([
'features' => function () {
return collect(config('app.features'))
->filter(fn ($feature) => FeatureFlag::isEnabled($feature['name']))
->pluck('name');
}
]);
```

## Contributing
```javascript
// app.js

Vue.mixin({
methods: {
hasFeature: function(feature) {
return this.$page.features.includes(feature)
}
}
})
```
```html
<!-- SomeComponent.vue -->

Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
<div v-if="hasFeature('search-v2')">Some cool new feature</div>
```

### Updating A Features State

To change a features state you can call the following methods:
```php
use Codinglabs\FeatureFlags\Facades\FeatureFlag;

FeatureFlag::turnOn('search-v2');
FeatureFlag::turnOff('search-v2');
FeatureFlag::makeDynamic('search-v2');
```
Alternatively you can set the state directly by passing a feature state enum:
```php
FeatureFlag::updateFeatureState('search-v2', FeatureState::on())
```
It is recommended that you only update a features state using the above methods as it will take care of updating the cache and dispatching the feature updated event:

```php
\Codinglabs\FeatureFlags\Events\FeatureUpdatedEvent::class
```
An example use case of the feature updated event would be if you were caching the result of a dynamic handler and need to clear that cache when a feature is updated.

___
## Advanced Usage

### Dynamic Features

A dynamic handler can be defined in the `boot()` method of your `AppServiceProvider`:
```php
use Codinglabs\FeatureFlags\Facades\FeatureFlag;

FeatureFlag::registerDynamicHandler('search-v2', function ($feature, $request) {
return $request->user() && $request->user()->hasRole('Tester')
});
```
Dynamic handlers will only be called when a feature is in the `dynamic` state. This will allow you to define custom rules around whether that feature is enabled like in the example above where the user can only access the feature if they have a tester role.

Each handler is provided with the features name and current request as arguments and must return a bool value.

### Default Handler For Dynamic Features

You may also define a default handler which will be the catch-all handler for features that don't have an explicit handler defined for them:

```php
FeatureFlag::registerDefaultDynamicHandler(function ($feature, $request) {
return $request->user() && $request->user()->hasRole('Tester');
});
```

An explicit handler defined using `registerDynamicHandler()` will take precedence over the default handler. If neither a default nor explicit handler has been defined then the feature will resolve to `off` by default.

### Handle Missing Features

Features must exist in the database otherwise a `MissingFeatureException` will be thrown. This behaviour can be turned off by explicitly handling cases where a feature doesn't exist:

```php
FeatureFlag::handleMissingFeaturesWith(function ($feature) {
// log or report this somewhere...
})
```

If a handler for missing features has been defined then an exception will **not** be thrown and the feature will resolve to `off`.

## Testing

```bash
composer test
```

## Security Vulnerabilities

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

## Credits

- [Steve Thomas](https://github.com/codinglabsau)
- [Jonathan Louw](https://github.com/JonathanLouw)
- [All Contributors](../../contributors)

## License
Expand Down
16 changes: 9 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
"license": "MIT",
"authors": [
{
"name": "Steve Thomas",
"email": "steve@codinglabs.com.au",
"name": "Jonathan Louw",
"email": "JonathanLouw@users.noreply.github.com",
"role": "Developer"
}
],
"require": {
"php": "^7.4|^8.0",
"spatie/laravel-package-tools": "^1.9.2",
"illuminate/contracts": "^7.0|^8.0|^9.0"
"php": "^8.0",
"codinglabsau/php-styles": "dev-main",
"illuminate/contracts": "^7.0|^8.0|^9.0",
"spatie/enum": "^3.12",
"spatie/laravel-package-tools": "^1.9.2"
},
"require-dev": {
"nunomaduro/collision": "^6.0",
Expand Down Expand Up @@ -55,10 +57,10 @@
"Codinglabs\\FeatureFlags\\FeatureFlagsServiceProvider"
],
"aliases": {
"Features": "Codinglabs\\FeatureFlags\\Facades\\Features"
"FeatureFlag": "Codinglabs\\FeatureFlags\\Facades\\FeatureFlags"
}
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
}
Loading