Skip to content

Commit 7c44308

Browse files
committed
Merge pull request #1 from codinglabsau/improvements
Improvements
1 parent 6342231 commit 7c44308

23 files changed

+1349
-248
lines changed

.github/workflows/run-tests.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Test
2+
3+
on: [push]
4+
5+
jobs:
6+
test:
7+
runs-on: ubuntu-20.04
8+
9+
steps:
10+
- uses: actions/checkout@v2
11+
12+
- name: Setup PHP
13+
uses: shivammathur/setup-php@v2
14+
with:
15+
php-version: '8.0'
16+
extensions: zip, sqlite3
17+
coverage: none
18+
19+
- name: Restore caches
20+
uses: actions/cache@v2
21+
with:
22+
path: ~/.composer/cache/files
23+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
24+
restore-keys: |
25+
${{ runner.os }}-composer-
26+
27+
- name: Install composer dependencies
28+
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
29+
30+
- name: Code sniff
31+
run: vendor/bin/php-cs-fixer fix --dry-run
32+
33+
- name: Execute tests
34+
run: composer test

.php-cs-fixer.dist.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
$finder = PhpCsFixer\Finder::create()
4+
->in([
5+
__DIR__.'/src',
6+
__DIR__.'/config',
7+
__DIR__.'/database',
8+
__DIR__.'/tests',
9+
]);
10+
11+
return CodingLabs\styles($finder);

CHANGELOG.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

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

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 171 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,220 @@
1-
2-
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/support-ukraine.svg?t=1" />](https://supportukrainenow.org)
3-
41
# Dynamic feature flags for laravel.
52

63
[![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)
7-
[![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)
8-
[![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)
4+
[![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)
95
[![Total Downloads](https://img.shields.io/packagist/dt/codinglabsau/laravel-feature-flags.svg?style=flat-square)](https://packagist.org/packages/codinglabsau/laravel-feature-flags)
106

11-
This is where your description should go. Limit it to a paragraph or two. Consider adding a small example.
12-
13-
## Support us
14-
15-
[<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)
16-
17-
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).
18-
19-
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).
20-
7+
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.
8+
___
219
## Installation
2210

23-
You can install the package via composer:
11+
### Install With Composer:
2412

2513
```bash
2614
composer require codinglabsau/laravel-feature-flags
2715
```
2816

29-
You can publish and run the migrations with:
17+
### Database Migrations
3018

3119
```bash
32-
php artisan vendor:publish --tag="laravel-feature-flags-migrations"
20+
php artisan vendor:publish --tag="feature-flags-migrations"
3321
php artisan migrate
3422
```
3523

36-
You can publish the config file with:
24+
### Publish Configuration:
3725

3826
```bash
39-
php artisan vendor:publish --tag="laravel-feature-flags-config"
27+
php artisan vendor:publish --tag="feature-flags-config"
4028
```
4129

42-
This is the contents of the published config file:
4330

31+
### Cache Store
32+
update your `.env`:
4433
```php
45-
return [
46-
];
34+
FEATURES_CACHE_STORE=file
4735
```
36+
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.
4837

49-
Optionally, you can publish the views using
38+
### Use Your Own Model
5039

51-
```bash
52-
php artisan vendor:publish --tag="laravel-feature-flags-views"
40+
To use your own model, update the config and replace the existing reference with your own model:
41+
42+
```php
43+
// app/config/feature-flags.php
44+
45+
'feature_model' => \App\Models\Feature::class,
46+
```
47+
48+
Make sure to also cast the state column to a feature state enum using the `FeatureStateCast`:
49+
50+
```php
51+
// app/Models/Feature.php
52+
53+
use Codinglabs\FeatureFlags\Casts\FeatureStateCast;
54+
55+
protected $casts = [
56+
'state' => FeatureStateCast::class
57+
];
5358
```
5459

5560
## Usage
5661

62+
Create a new feature in the database and give it a default state:
5763
```php
58-
$features = new Codinglabs\FeatureFlags();
59-
echo $features->echoPhrase('Hello, Codinglabs!');
64+
Feature::create([
65+
'name' => 'search-v2',
66+
'state' => Codinglabs\FeatureFlags\Enums\FeatureState::on()
67+
]);
6068
```
6169

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

64-
```bash
65-
composer test
72+
A feature can be in one of three states:
73+
```php
74+
use Codinglabs\FeatureFlags\Enums\FeatureState;
75+
76+
FeatureState::on()
77+
FeatureState::off()
78+
FeatureState::dynamic()
79+
```
80+
### Check If A Feature Is Enabled
81+
82+
#### Blade View
83+
```php
84+
@feature('search-v2')
85+
// new search goes here
86+
@else
87+
// legacy search here
88+
@endfeature
89+
```
90+
91+
#### In Your Code
92+
```php
93+
use Codinglabs\FeatureFlags\Facades\FeatureFlag;
94+
95+
if (FeatureFlag::isEnabled('search-v2')) {
96+
// new feature code
97+
} else {
98+
// old code
99+
}
66100
```
67101

68-
## Changelog
102+
#### Sharing features with UI (Inertiajs example)
103+
```php
104+
// config/app.php
105+
106+
'features' => [
107+
[
108+
'name' => 'search-v2',
109+
'state' => \Codinglabs\FeatureFlags\Enums\FeatureState::dynamic()
110+
]
111+
],
112+
```
69113

70-
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
114+
```php
115+
// app/Middleware/HandleInertiaRequest.php
116+
117+
Inertia::share([
118+
'features' => function () {
119+
return collect(config('app.features'))
120+
->filter(fn ($feature) => FeatureFlag::isEnabled($feature['name']))
121+
->pluck('name');
122+
}
123+
]);
124+
```
71125

72-
## Contributing
126+
```javascript
127+
// app.js
128+
129+
Vue.mixin({
130+
methods: {
131+
hasFeature: function(feature) {
132+
return this.$page.features.includes(feature)
133+
}
134+
}
135+
})
136+
```
137+
```html
138+
<!-- SomeComponent.vue -->
73139

74-
Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
140+
<div v-if="hasFeature('search-v2')">Some cool new feature</div>
141+
```
142+
143+
### Updating A Features State
144+
145+
To change a features state you can call the following methods:
146+
```php
147+
use Codinglabs\FeatureFlags\Facades\FeatureFlag;
148+
149+
FeatureFlag::turnOn('search-v2');
150+
FeatureFlag::turnOff('search-v2');
151+
FeatureFlag::makeDynamic('search-v2');
152+
```
153+
Alternatively you can set the state directly by passing a feature state enum:
154+
```php
155+
FeatureFlag::updateFeatureState('search-v2', FeatureState::on())
156+
```
157+
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:
158+
159+
```php
160+
\Codinglabs\FeatureFlags\Events\FeatureUpdatedEvent::class
161+
```
162+
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.
163+
164+
___
165+
## Advanced Usage
166+
167+
### Dynamic Features
168+
169+
A dynamic handler can be defined in the `boot()` method of your `AppServiceProvider`:
170+
```php
171+
use Codinglabs\FeatureFlags\Facades\FeatureFlag;
172+
173+
FeatureFlag::registerDynamicHandler('search-v2', function ($feature, $request) {
174+
return $request->user() && $request->user()->hasRole('Tester')
175+
});
176+
```
177+
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.
178+
179+
Each handler is provided with the features name and current request as arguments and must return a bool value.
180+
181+
### Default Handler For Dynamic Features
182+
183+
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:
184+
185+
```php
186+
FeatureFlag::registerDefaultDynamicHandler(function ($feature, $request) {
187+
return $request->user() && $request->user()->hasRole('Tester');
188+
});
189+
```
190+
191+
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.
192+
193+
### Handle Missing Features
194+
195+
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:
196+
197+
```php
198+
FeatureFlag::handleMissingFeaturesWith(function ($feature) {
199+
// log or report this somewhere...
200+
})
201+
```
202+
203+
If a handler for missing features has been defined then an exception will **not** be thrown and the feature will resolve to `off`.
204+
205+
## Testing
206+
207+
```bash
208+
composer test
209+
```
75210

76211
## Security Vulnerabilities
77212

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

80215
## Credits
81216

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

85220
## License

composer.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@
1212
"license": "MIT",
1313
"authors": [
1414
{
15-
"name": "Steve Thomas",
16-
"email": "steve@codinglabs.com.au",
15+
"name": "Jonathan Louw",
16+
"email": "JonathanLouw@users.noreply.github.com",
1717
"role": "Developer"
1818
}
1919
],
2020
"require": {
21-
"php": "^7.4|^8.0",
22-
"spatie/laravel-package-tools": "^1.9.2",
23-
"illuminate/contracts": "^7.0|^8.0|^9.0"
21+
"php": "^8.0",
22+
"codinglabsau/php-styles": "dev-main",
23+
"illuminate/contracts": "^7.0|^8.0|^9.0",
24+
"spatie/enum": "^3.12",
25+
"spatie/laravel-package-tools": "^1.9.2"
2426
},
2527
"require-dev": {
2628
"nunomaduro/collision": "^6.0",
@@ -55,10 +57,10 @@
5557
"Codinglabs\\FeatureFlags\\FeatureFlagsServiceProvider"
5658
],
5759
"aliases": {
58-
"Features": "Codinglabs\\FeatureFlags\\Facades\\Features"
60+
"FeatureFlag": "Codinglabs\\FeatureFlags\\Facades\\FeatureFlags"
5961
}
6062
}
6163
},
6264
"minimum-stability": "dev",
6365
"prefer-stable": true
64-
}
66+
}

0 commit comments

Comments
 (0)