mirror of
https://github.com/fzaninotto/Faker.git
synced 2025-03-19 06:49:50 +01:00
Add unique() modifier
* Add `BaseProvider::unique($reset = false) * Add `UniqueGenerator` proxy class * Refactor `optional()` tests for better coverage * Merge `optional()` and `unique()` documentations Closes #171
This commit is contained in:
parent
34e1b2de0f
commit
fac6a3839c
43
readme.md
43
readme.md
@ -195,15 +195,46 @@ Each of the generator properties (like `name`, `address`, and `lorem`) are calle
|
||||
safeColorName // 'fuchsia'
|
||||
colorName // 'Gainsbor'
|
||||
|
||||
## Optional data
|
||||
## Unique and Optional modifiers
|
||||
|
||||
All formatters can be made optional by chaining `optional`. When optional, the formatter will randomly return `NULL`, which can be useful for seeding non-required fields. For example:
|
||||
Faker provides two special providers, `unique()` and `optional()`, to be called before any provider. `optional()` can be useful for seeding non-required fields, like a mobile telephone number ; `unique()` is required to populate fields that cannot accept twice the same value, like primary identifiers.
|
||||
|
||||
$faker->optional->country
|
||||
|
||||
You can skew the randomization towards more nulls or less by passing an argument to `optional()`. At 0, *only* `NULL` is returned. At 1, it is never returned.
|
||||
```php
|
||||
// unique() forces providers to return unique values
|
||||
$values = array();
|
||||
for ($i=0; $i < 10; $i++) {
|
||||
// get a random digit, but always a new one, to avoid duplicates
|
||||
$values []= $faker->unique()->randomDigit;
|
||||
}
|
||||
print_r($values); // [4, 1, 8, 5, 0, 2, 6, 9, 7, 3]
|
||||
|
||||
$faker->optional(.75)->country
|
||||
// providers with a limited range will throw an exception when no new unique value can be generated
|
||||
$values = array();
|
||||
try {
|
||||
for ($i=0; $i < 10; $i++) {
|
||||
$values []= $faker->unique()->randomDigitNotNull;
|
||||
}
|
||||
} catch (\OverflowException $e) {
|
||||
echo "There are only 9 unique digits not null, Faker can't generate 10 of them!";
|
||||
}
|
||||
|
||||
// you can reset the unique modifier for all providers by passing true as first argument
|
||||
$faker->unique($reset = true)->randomDigitNotNull; // will not throw OverflowException since unique() was reset
|
||||
// tip: unique() keeps one array of values per provider
|
||||
|
||||
// optional() sometimes bypasses the provider to return null instead
|
||||
$values = array();
|
||||
for ($i=0; $i < 10; $i++) {
|
||||
// get a random digit, but also null sometimes
|
||||
$values []= $faker->optional()->randomDigit;
|
||||
}
|
||||
print_r($values); // [1, 4, null, 9, 5, null, null, 4, 6, null]
|
||||
|
||||
// optional takes a weight argument to make the null occurrence impossible (value 0) or systematic (value 1)
|
||||
$faker->optional($weight = 0.1)->randomDigit; // 10% chance to get null
|
||||
$faker->optional($weight = 0.9)->randomDigit; // 90% chance to get null
|
||||
// the default $weight value is 0.5
|
||||
```
|
||||
|
||||
## Localization
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Faker\Provider;
|
||||
|
||||
use Faker\Generator;
|
||||
use Faker\UniqueGenerator;
|
||||
|
||||
class Base
|
||||
{
|
||||
@ -11,6 +12,11 @@ class Base
|
||||
*/
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* @var \Faker\UniqueGenerator
|
||||
*/
|
||||
protected $unique;
|
||||
|
||||
/**
|
||||
* @param \Faker\Generator $generator
|
||||
*/
|
||||
@ -203,4 +209,28 @@ class Base
|
||||
|
||||
return new \Faker\NullGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Chainable method for making any formatter unique.
|
||||
*
|
||||
* <code>
|
||||
* // will never return twice the same value
|
||||
* $faker->unique()->randomElement(array(1, 2, 3));
|
||||
* </code>
|
||||
*
|
||||
* @param boolean $reset If set to true, resets the list of existing values
|
||||
* @param integer $maxRetries Maximum number of retries to find a unique value,
|
||||
* After which an OverflowExcption is thrown.
|
||||
* @throws OverflowException When no unique value can be found by iterating $maxRetries times
|
||||
*
|
||||
* @return UniqueGenerator A proxy class returning only existing values
|
||||
*/
|
||||
public function unique($reset = false, $maxRetries = 10000)
|
||||
{
|
||||
if ($reset || !$this->unique) {
|
||||
$this->unique = new UniqueGenerator($this->generator, $maxRetries);
|
||||
}
|
||||
|
||||
return $this->unique;
|
||||
}
|
||||
}
|
||||
|
49
src/Faker/UniqueGenerator.php
Normal file
49
src/Faker/UniqueGenerator.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Faker;
|
||||
|
||||
/**
|
||||
* Proxy for other generators, to return only unique values. Works with
|
||||
* Faker\Generator\Base->unique()
|
||||
*/
|
||||
class UniqueGenerator
|
||||
{
|
||||
protected $generator;
|
||||
protected $maxRetries;
|
||||
protected $uniques = array();
|
||||
|
||||
public function __construct(Generator $generator, $maxRetries)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
$this->maxRetries = $maxRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch and proxy all generator calls but return only unique values
|
||||
*/
|
||||
public function __get($attribute)
|
||||
{
|
||||
return $this->__call($attribute, array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch and proxy all generator calls with arguments but return only unique values
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (!isset($this->uniques[$name])) {
|
||||
$this->uniques[$name] = array();
|
||||
}
|
||||
$i = 0;
|
||||
do {
|
||||
$res = call_user_func_array(array($this->generator, $name), $arguments);
|
||||
$i++;
|
||||
if ($i > $this->maxRetries) {
|
||||
throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries));
|
||||
}
|
||||
} while (in_array($res, $this->uniques[$name]));
|
||||
$this->uniques[$name][]= $res;
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
@ -131,17 +131,102 @@ class BaseTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertRegExp('/foo[a-z]Ba\dr/', BaseProvider::bothify('foo?Ba#r'));
|
||||
}
|
||||
|
||||
public function testOptionalChainingOfProperty()
|
||||
public function testOptionalReturnsProviderValueWhenCalledWithWeight1()
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
$this->assertNotNull($faker->optional(1)->randomNumber);
|
||||
$this->assertNull($faker->optional(0)->randomNumber);
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$this->assertNotNull($faker->optional(1)->randomDigit);
|
||||
}
|
||||
|
||||
public function testOptionalChainingOfMethod()
|
||||
public function testOptionalReturnsNullWhenCalledWithWeight0()
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
$this->assertNotNull($faker->optional(1)->randomNumber(4));
|
||||
$this->assertNull($faker->optional(0)->randomNumber(4));
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$this->assertNull($faker->optional(0)->randomDigit);
|
||||
}
|
||||
|
||||
public function testOptionalAllowsChainingPropertyAccess()
|
||||
{
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$faker->addProvider(new \ArrayObject(array(1))); // hack because method_exists forbids stubs
|
||||
$this->assertEquals(1, $faker->optional(1)->count);
|
||||
$this->assertNull($faker->optional(0)->count);
|
||||
}
|
||||
|
||||
public function testOptionalAllowsChainingMethodCall()
|
||||
{
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$faker->addProvider(new \ArrayObject(array(1))); // hack because method_exists forbids stubs
|
||||
$this->assertEquals(1, $faker->optional(1)->count());
|
||||
$this->assertNull($faker->optional(0)->count());
|
||||
}
|
||||
|
||||
public function testOptionalAllowsChainingProviderCallRandomlyReturnNull()
|
||||
{
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$values = array();
|
||||
for ($i=0; $i < 10; $i++) {
|
||||
$values[]= $faker->optional()->randomDigit;
|
||||
}
|
||||
$this->assertContains(null, $values);
|
||||
}
|
||||
|
||||
public function testUniqueAllowsChainingPropertyAccess()
|
||||
{
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$faker->addProvider(new \ArrayObject(array(1))); // hack because method_exists forbids stubs
|
||||
$this->assertEquals(1, $faker->unique()->count);
|
||||
}
|
||||
|
||||
public function testUniqueAllowsChainingMethodCall()
|
||||
{
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$faker->addProvider(new \ArrayObject(array(1))); // hack because method_exists forbids stubs
|
||||
$this->assertEquals(1, $faker->unique()->count());
|
||||
}
|
||||
|
||||
public function testUniqueReturnsOnlyUniqueValues()
|
||||
{
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$values = array();
|
||||
for ($i=0; $i < 10; $i++) {
|
||||
$values[]= $faker->unique()->randomDigit;
|
||||
}
|
||||
sort($values);
|
||||
$this->assertEquals(array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException OverflowException
|
||||
*/
|
||||
public function testUniqueThrowsExceptionWhenNoUniqueValueCanBeGenerated()
|
||||
{
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
for ($i=0; $i < 11; $i++) {
|
||||
$faker->unique()->randomDigit;
|
||||
}
|
||||
}
|
||||
|
||||
public function testUniqueCanResetUniquesWhenPassedTrueAsArgument()
|
||||
{
|
||||
$faker = new \Faker\Generator();
|
||||
$faker->addProvider(new \Faker\Provider\Base($faker));
|
||||
$values = array();
|
||||
for ($i=0; $i < 10; $i++) {
|
||||
$values[]= $faker->unique()->randomDigit;
|
||||
}
|
||||
$values[]= $faker->unique(true)->randomDigit;
|
||||
for ($i=0; $i < 9; $i++) {
|
||||
$values[]= $faker->unique()->randomDigit;
|
||||
}
|
||||
sort($values);
|
||||
$this->assertEquals(array(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9), $values);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user