alancole / vouchers
A Simple PHP library for gernating and validating voucher codes.
Installs: 269
Dependents: 0
Suggesters: 0
Security: 0
Stars: 97
Watchers: 6
Forks: 10
Open Issues: 3
Type:composer
This package is auto-updated.
Last update: 2025-02-19 16:05:24 UTC
README
Vouchers Lib
A PHP library for generating and validating vouchers. We make no assumptions about storage and instead offer the concept of Bags
which can take any number of Vouchers
. These bags can validate vouchers, generate new vouchers and apply validation
rules across the whole set.
Install
composer require alancole/vouchers
Example
$model = new Vouchers\Voucher\Model([ 'owner' => [ 'required' => true, 'immutable' => true, ], 'claimed_by' => [ 'required' => true, ] ]); $collection = new Vouchers\Bag($model); $collection->fill(1000); $voucher = $collection->pick(); print $voucher; // FHUW-JSUJ-KSIQ-JDUI
Vouchers
Vouchers can take on almost any form, however you can use Vouchers\Voucher\Model
to enforce validation and structure. The only required attribute is code
which by default is immutable.
$voucher = new Vouchers\Voucher(); print $voucher; // ABCD-EFGH-IJKL
You may also pass an array to the voucher to set pre existing values to the voucher. Matching fields (including code
) will be validated.
$voucher = new Voucher(['code' => 'ALAN-COLE-CODE', 'claimed_by' => '', 'claimed_on' => '']); print $voucher; // "ALAN-COLE-CODE"
Any value passed on voucher creation can be get and set using get()
and set()
on the voucher.
$voucher->set('owner', 'Alan'); echo $voucher->get('owner'); // Alan
Model
By creating a model you can set default values and validation on vouchers created or loaded. Models are passed as an array to Vouchers\Voucher\Model
$model = new Vouchers\Voucher\Model([ 'owner' => [ 'required' => true, 'immutable' => true, ], 'claimed_by' => [ 'required' => true, ] ]);
If you set a voucher attribute as immutable
then Voucher
will throw the ImmutableData
exception.
Code
You can change the way the code is generated by settings generator on a model. A generator must implement Vouchers\Voucher\Code\GeneratorInterface
namespace My\Voucher\Generator; use Vouchers\Voucher\Code\Interface as Generator; class MyCode implements Generator { public function part() { return bin2hex(openssl_random_pseudo_bytes(2)); } public function generate() { return strtoupper(sprintf("%s-%s-%s", $this->part(), $this->part(), $this->part())); } public function validate() { return true; } }
Then tell the model to use this generator.
$model = new Vouchers\Voucher\Model([ 'code' => [ 'generator' => \My\Voucher\Generator\MyCode::class ] ]);
Bags
Bags act as collections for vouchers and allow you to enforce validations on a whole set. Bags can also act as a selector for vouchers, allowing to you pick a voucher at random and enforce rules on that selection. Bags are also Iterable
so they can be used in loops.
$collection = new Vouchers\Bag(); $collection->fill(1000); foreach($collection as $voucher) { print $voucher; }
You can use Vouchers\Voucher\Model
to enfore a model on all items in a bag by passing a model as the first attribute on construction.
$collection = new Vouchers\Bag($model);
You can fill a model with existing vouchers by using add()
add will only accept an instance of Vouchers\Voucher
$vouchers = [$voucher1...$voucher100]; foreach ($vouchers as $voucher) { $collection->add(new Vouchers\Voucher($voucher)); }
You can also run a map on any array, mapping the return as new vouchers within the bag. This is handy if you need to transform data to fit a model.
$collection->map($vouchers, function ($voucher) { return new Vouchers\Voucher($voucher); });
You can get a voucher by code, which can be used to see if a voucher exists.
$collection = new Vouchers\Bag(); $voucher = new Vouchers\Voucher(['code' => 'special-voucher']); $collection->add($voucher); $v = $collection->find("special-voucher"); if ($v) { print (string)$v; } else { print "Voucher does not exist."; }
Pick
You can have the bag pick you a voucher at random by using pick()
on any bag.
$collection = new Vouchers\Bag(); $collection->fill(1000); $collection->pick();
If you wish to validate the selection you can pass a callback to pick which will run until it returns a true
or throw an Vouchers\Exceptions\NoValidVouchers
exception.
$collection->pick(function ($voucher) { return (bool)$voucher->owner == "Alan"; });
try { $collection->pick(function ($voucher) { return 2 == 1; }); } catch (Exception $e) { print $e->getMessage(); }
You may also ask pick()
to check all validators this bag might have (see Validate) and only return a voucher that is valid. Again this will throw Vouchers\Exceptions\NoValidVouchers
is it doesn't find a voucher.
$collection->pickValid();
Validate
You can add validators to a bag, these validators can be used to validate requirements of a voucher using validate()
on a bag and passing the voucher code as a parameter.
$collection->validate("ALAN-COLE-CODE");
Validators can be added as callbacks to the validator function, or as a class that implements Vouchers\Voucher\Validator
here is an example that assumes a voucher has an expire_date
and checks it has not passed.
$collection->validator(function ($voucher) { return $voucher->expire_date > new DateTime(); }, "Sorry, this voucher is expired"); try { $collection->validate("ALAN-COLE-CODE"); } catch (\Vouchers\Exceptions\VoucherNotValid $e) { return $e->getMessage(); // "Sorry, this voucher is expired"; }
Kitchen Sink
This shows how to get vouchers from the subscriptions api, take a requested voucher, validate it and the claim it on the API.
$api = new Discovery\Subscriptions\Api(); $api->setApiKey(getenv("SUBS_API_KEY")); $api->setAppId(getenv("SUBS_APP_ID")); $vouchers = $api->getAllVouchers(); $bag = new Vouchers\Bag(); $bag->map($vouchers, function($voucher) { return new Vouchers\Voucher($voucher); }); # Add some validators $bag->validator(function ($voucher) { return $voucher->owner == "Eurosport"; }, "Sorry, this voucher was not valid."); $bag->validator(function ($voucher) { return !$voucher->used; }, "Sorry, this voucher has been used."); $bag->validator(function ($voucher) { return new DateTime($voucher->valid_till) < new DateTime(); }, "Sorry, this voucher is expired."); try { $voucher = $collection->validate(filter_val(INPUT_POST, "voucher_code", FILTER_SANITIZE_STRING)); $voucher->set("used", true // not really needed. $api->putVoucherClaim($voucher); // because this takes care of it. } catch (\Vouchers\Exceptions\VoucherNotValid $e) { echo $e->getMessage(); }